| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "gpu/command_buffer/service/buffer_manager.h" | 
 |  | 
 | #include <stdint.h> | 
 |  | 
 | #include <limits> | 
 |  | 
 | #include "base/format_macros.h" | 
 | #include "base/logging.h" | 
 | #include "base/numerics/safe_math.h" | 
 | #include "base/strings/stringprintf.h" | 
 | #include "base/threading/thread_task_runner_handle.h" | 
 | #include "base/trace_event/memory_dump_manager.h" | 
 | #include "base/trace_event/trace_event.h" | 
 | #include "gpu/command_buffer/common/gles2_cmd_utils.h" | 
 | #include "gpu/command_buffer/service/context_state.h" | 
 | #include "gpu/command_buffer/service/error_state.h" | 
 | #include "gpu/command_buffer/service/feature_info.h" | 
 | #include "gpu/command_buffer/service/memory_tracking.h" | 
 | #include "gpu/command_buffer/service/transform_feedback_manager.h" | 
 | #include "ui/gl/gl_bindings.h" | 
 | #include "ui/gl/gl_version_info.h" | 
 | #include "ui/gl/trace_util.h" | 
 |  | 
 | namespace gpu { | 
 | namespace gles2 { | 
 | namespace { | 
 | static const GLsizeiptr kDefaultMaxBufferSize = 1u << 30;  // 1GB | 
 | } | 
 |  | 
 | BufferManager::BufferManager(MemoryTracker* memory_tracker, | 
 |                              FeatureInfo* feature_info) | 
 |     : memory_type_tracker_( | 
 |           new MemoryTypeTracker(memory_tracker)), | 
 |       memory_tracker_(memory_tracker), | 
 |       feature_info_(feature_info), | 
 |       max_buffer_size_(kDefaultMaxBufferSize), | 
 |       allow_buffers_on_multiple_targets_(false), | 
 |       allow_fixed_attribs_(false), | 
 |       buffer_count_(0), | 
 |       primitive_restart_fixed_index_(0), | 
 |       lost_context_(false), | 
 |       use_client_side_arrays_for_stream_buffers_( | 
 |           feature_info | 
 |               ? feature_info->workarounds() | 
 |                     .use_client_side_arrays_for_stream_buffers | 
 |               : 0) { | 
 |   // When created from InProcessCommandBuffer, we won't have a |memory_tracker_| | 
 |   // so don't register a dump provider. | 
 |   if (memory_tracker_) { | 
 |     base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( | 
 |         this, "gpu::BufferManager", base::ThreadTaskRunnerHandle::Get()); | 
 |   } | 
 | } | 
 |  | 
 | BufferManager::~BufferManager() { | 
 |   DCHECK(buffers_.empty()); | 
 |   CHECK_EQ(buffer_count_, 0u); | 
 |  | 
 |   base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( | 
 |       this); | 
 | } | 
 |  | 
 | void BufferManager::MarkContextLost() { | 
 |   lost_context_ = true; | 
 | } | 
 |  | 
 | void BufferManager::Destroy() { | 
 |   buffers_.clear(); | 
 |   DCHECK_EQ(0u, memory_type_tracker_->GetMemRepresented()); | 
 | } | 
 |  | 
 | void BufferManager::CreateBuffer(GLuint client_id, GLuint service_id) { | 
 |   scoped_refptr<Buffer> buffer(new Buffer(this, service_id)); | 
 |   std::pair<BufferMap::iterator, bool> result = | 
 |       buffers_.insert(std::make_pair(client_id, buffer)); | 
 |   DCHECK(result.second); | 
 | } | 
 |  | 
 | Buffer* BufferManager::GetBuffer( | 
 |     GLuint client_id) { | 
 |   BufferMap::iterator it = buffers_.find(client_id); | 
 |   return it != buffers_.end() ? it->second.get() : NULL; | 
 | } | 
 |  | 
 | void BufferManager::RemoveBuffer(GLuint client_id) { | 
 |   BufferMap::iterator it = buffers_.find(client_id); | 
 |   if (it != buffers_.end()) { | 
 |     Buffer* buffer = it->second.get(); | 
 |     buffer->MarkAsDeleted(); | 
 |     buffers_.erase(it); | 
 |   } | 
 | } | 
 |  | 
 | void BufferManager::StartTracking(Buffer* /* buffer */) { | 
 |   ++buffer_count_; | 
 | } | 
 |  | 
 | void BufferManager::StopTracking(Buffer* buffer) { | 
 |   memory_type_tracker_->TrackMemFree(buffer->size()); | 
 |   --buffer_count_; | 
 | } | 
 |  | 
 | Buffer::MappedRange::MappedRange( | 
 |     GLintptr offset, GLsizeiptr size, GLenum access, void* pointer, | 
 |     scoped_refptr<gpu::Buffer> shm, unsigned int shm_offset) | 
 |     : offset(offset), | 
 |       size(size), | 
 |       access(access), | 
 |       pointer(pointer), | 
 |       shm(shm), | 
 |       shm_offset(shm_offset) { | 
 |   DCHECK(pointer); | 
 |   DCHECK(shm.get() && GetShmPointer()); | 
 | } | 
 |  | 
 | Buffer::MappedRange::~MappedRange() { | 
 | } | 
 |  | 
 | void* Buffer::MappedRange::GetShmPointer() const { | 
 |   DCHECK(shm.get()); | 
 |   return shm->GetDataAddress(shm_offset, static_cast<unsigned int>(size)); | 
 | } | 
 |  | 
 | Buffer::Buffer(BufferManager* manager, GLuint service_id) | 
 |     : manager_(manager), | 
 |       size_(0), | 
 |       deleted_(false), | 
 |       is_client_side_array_(false), | 
 |       service_id_(service_id), | 
 |       initial_target_(0), | 
 |       usage_(GL_STATIC_DRAW) { | 
 |   manager_->StartTracking(this); | 
 | } | 
 |  | 
 | Buffer::~Buffer() { | 
 |   if (manager_) { | 
 |     if (!manager_->lost_context_) { | 
 |       GLuint id = service_id(); | 
 |       glDeleteBuffersARB(1, &id); | 
 |     } | 
 |     RemoveMappedRange(); | 
 |     manager_->StopTracking(this); | 
 |     manager_ = nullptr; | 
 |   } | 
 | } | 
 |  | 
 | const GLvoid* Buffer::StageShadow(bool use_shadow, | 
 |                                   GLsizeiptr size, | 
 |                                   const GLvoid* data) { | 
 |   shadow_.clear(); | 
 |   if (use_shadow) { | 
 |     if (data) { | 
 |       shadow_.insert(shadow_.begin(), | 
 |                      static_cast<const uint8_t*>(data), | 
 |                      static_cast<const uint8_t*>(data) + size); | 
 |     } else { | 
 |       shadow_.resize(size); | 
 |       memset(shadow_.data(), 0, static_cast<size_t>(size)); | 
 |     } | 
 |     return shadow_.data(); | 
 |   } else { | 
 |     return data; | 
 |   } | 
 | } | 
 |  | 
 | void Buffer::SetInfo(GLsizeiptr size, | 
 |                      GLenum usage, | 
 |                      bool use_shadow, | 
 |                      bool is_client_side_array) { | 
 |   usage_ = usage; | 
 |   is_client_side_array_ = is_client_side_array; | 
 |   ClearCache(); | 
 |  | 
 |   // Shadow must have been setup already. | 
 |   DCHECK_EQ(shadow_.size(), static_cast<size_t>(use_shadow ? size : 0u)); | 
 |   size_ = size; | 
 |  | 
 |   mapped_range_.reset(nullptr); | 
 | } | 
 |  | 
 | bool Buffer::CheckRange(GLintptr offset, GLsizeiptr size) const { | 
 |   if (offset < 0 || offset > std::numeric_limits<int32_t>::max() || | 
 |       size < 0 || size > std::numeric_limits<int32_t>::max()) { | 
 |     return false; | 
 |   } | 
 |   base::CheckedNumeric<int32_t> max = offset; | 
 |   max += size; | 
 |   return max.IsValid() && max.ValueOrDefault(0) <= size_; | 
 | } | 
 |  | 
 | void Buffer::SetRange(GLintptr offset, GLsizeiptr size, const GLvoid * data) { | 
 |   DCHECK(CheckRange(offset, size)); | 
 |   if (!shadow_.empty()) { | 
 |     DCHECK_LE(static_cast<size_t>(offset + size), shadow_.size()); | 
 |     memcpy(shadow_.data() + offset, data, size); | 
 |     ClearCache(); | 
 |   } | 
 | } | 
 |  | 
 | const void* Buffer::GetRange(GLintptr offset, GLsizeiptr size) const { | 
 |   if (shadow_.empty()) { | 
 |     return NULL; | 
 |   } | 
 |   if (!CheckRange(offset, size)) { | 
 |     return NULL; | 
 |   } | 
 |   DCHECK_LE(static_cast<size_t>(offset + size), shadow_.size()); | 
 |   return shadow_.data() + offset; | 
 | } | 
 |  | 
 | void Buffer::ClearCache() { | 
 |   range_set_.clear(); | 
 | } | 
 |  | 
 | template <typename T> | 
 | GLuint GetMaxValue(const void* data, GLuint offset, GLsizei count, | 
 |     GLuint primitive_restart_index) { | 
 |   GLuint max_value = 0; | 
 |   const T* element = | 
 |       reinterpret_cast<const T*>(static_cast<const int8_t*>(data) + offset); | 
 |   const T* end = element + count; | 
 |   for (; element < end; ++element) { | 
 |     if (*element > max_value) { | 
 |       if (*element == primitive_restart_index) { | 
 |         continue; | 
 |       } | 
 |       max_value = *element; | 
 |     } | 
 |   } | 
 |   return max_value; | 
 | } | 
 |  | 
 | bool Buffer::GetMaxValueForRange( | 
 |     GLuint offset, GLsizei count, GLenum type, bool primitive_restart_enabled, | 
 |     GLuint* max_value) { | 
 |   GLuint primitive_restart_index = 0; | 
 |   if (primitive_restart_enabled) { | 
 |     switch (type) { | 
 |       case GL_UNSIGNED_BYTE: | 
 |         primitive_restart_index = 0xFF; | 
 |         break; | 
 |       case GL_UNSIGNED_SHORT: | 
 |         primitive_restart_index = 0xFFFF; | 
 |         break; | 
 |       case GL_UNSIGNED_INT: | 
 |         primitive_restart_index = 0xFFFFFFFF; | 
 |         break; | 
 |       default: | 
 |         NOTREACHED();  // should never get here by validation. | 
 |         break; | 
 |     } | 
 |   } | 
 |  | 
 |   Range range(offset, count, type, primitive_restart_enabled); | 
 |   RangeToMaxValueMap::iterator it = range_set_.find(range); | 
 |   if (it != range_set_.end()) { | 
 |     *max_value = it->second; | 
 |     return true; | 
 |   } | 
 |   // Optimization. If: | 
 |   //  - primitive restart is enabled | 
 |   //  - we don't have an entry in the range set for these parameters | 
 |   //    for the situation when primitive restart is enabled | 
 |   //  - we do have an entry in the range set for these parameters for | 
 |   //    the situation when primitive restart is disabled | 
 |   //  - this entry is less than the primitive restart index | 
 |   // Then we can repurpose this entry for the situation when primitive | 
 |   // restart is enabled. Otherwise, we need to compute the max index | 
 |   // from scratch. | 
 |   if (primitive_restart_enabled) { | 
 |     Range disabled_range(offset, count, type, false); | 
 |     RangeToMaxValueMap::iterator it = range_set_.find(disabled_range); | 
 |     if (it != range_set_.end() && it->second < primitive_restart_index) { | 
 |       // This reuses the max value for the case where primitive | 
 |       // restart is enabled. | 
 |       range_set_.insert(std::make_pair(range, it->second)); | 
 |       *max_value = it->second; | 
 |       return true; | 
 |     } | 
 |   } | 
 |  | 
 |   uint32_t size; | 
 |   if (!SafeMultiplyUint32( | 
 |       count, GLES2Util::GetGLTypeSizeForBuffers(type), &size)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   if (!SafeAddUint32(offset, size, &size)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   if (size > static_cast<uint32_t>(size_)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   if (shadow_.empty()) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   // Scan the range for the max value and store | 
 |   GLuint max_v = 0; | 
 |   switch (type) { | 
 |     case GL_UNSIGNED_BYTE: | 
 |       max_v = GetMaxValue<uint8_t>(shadow_.data(), offset, count, | 
 |                                    primitive_restart_index); | 
 |       break; | 
 |     case GL_UNSIGNED_SHORT: | 
 |       // Check we are not accessing an odd byte for a 2 byte value. | 
 |       if ((offset & 1) != 0) { | 
 |         return false; | 
 |       } | 
 |       max_v = GetMaxValue<uint16_t>(shadow_.data(), offset, count, | 
 |                                     primitive_restart_index); | 
 |       break; | 
 |     case GL_UNSIGNED_INT: | 
 |       // Check we are not accessing a non aligned address for a 4 byte value. | 
 |       if ((offset & 3) != 0) { | 
 |         return false; | 
 |       } | 
 |       max_v = GetMaxValue<uint32_t>(shadow_.data(), offset, count, | 
 |                                     primitive_restart_index); | 
 |       break; | 
 |     default: | 
 |       NOTREACHED();  // should never get here by validation. | 
 |       break; | 
 |   } | 
 |   range_set_.insert(std::make_pair(range, max_v)); | 
 |   *max_value = max_v; | 
 |   return true; | 
 | } | 
 |  | 
 | void Buffer::SetMappedRange(GLintptr offset, GLsizeiptr size, GLenum access, | 
 |                             void* pointer, scoped_refptr<gpu::Buffer> shm, | 
 |                             unsigned int shm_offset) { | 
 |   mapped_range_.reset( | 
 |       new MappedRange(offset, size, access, pointer, shm, shm_offset)); | 
 | } | 
 |  | 
 | void Buffer::RemoveMappedRange() { | 
 |   mapped_range_.reset(nullptr); | 
 | } | 
 |  | 
 | bool BufferManager::GetClientId(GLuint service_id, GLuint* client_id) const { | 
 |   // This doesn't need to be fast. It's only used during slow queries. | 
 |   for (BufferMap::const_iterator it = buffers_.begin(); | 
 |        it != buffers_.end(); ++it) { | 
 |     if (it->second->service_id() == service_id) { | 
 |       *client_id = it->first; | 
 |       return true; | 
 |     } | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | bool BufferManager::IsUsageClientSideArray(GLenum usage) { | 
 |   return usage == GL_STREAM_DRAW && use_client_side_arrays_for_stream_buffers_; | 
 | } | 
 |  | 
 | bool BufferManager::UseNonZeroSizeForClientSideArrayBuffer() { | 
 |   return feature_info_.get() && | 
 |          feature_info_->workarounds() | 
 |              .use_non_zero_size_for_client_side_stream_buffers; | 
 | } | 
 |  | 
 | bool BufferManager::UseShadowBuffer(GLenum target, GLenum usage) { | 
 |   const bool is_client_side_array = IsUsageClientSideArray(usage); | 
 |   // feature_info_ can be null in some unit tests. | 
 |   const bool support_fixed_attribs = | 
 |       !feature_info_ || feature_info_->gl_version_info().SupportsFixedType(); | 
 |  | 
 |   // TODO(zmo): Don't shadow buffer data on ES3. crbug.com/491002. | 
 |   return ( | 
 |       target == GL_ELEMENT_ARRAY_BUFFER || allow_buffers_on_multiple_targets_ || | 
 |       (allow_fixed_attribs_ && !support_fixed_attribs) || is_client_side_array); | 
 | } | 
 |  | 
 | void BufferManager::SetInfo(Buffer* buffer, | 
 |                             GLenum target, | 
 |                             GLsizeiptr size, | 
 |                             GLenum usage, | 
 |                             bool use_shadow) { | 
 |   DCHECK(buffer); | 
 |   memory_type_tracker_->TrackMemFree(buffer->size()); | 
 |   buffer->SetInfo(size, usage, use_shadow, IsUsageClientSideArray(usage)); | 
 |   memory_type_tracker_->TrackMemAlloc(buffer->size()); | 
 | } | 
 |  | 
 | void BufferManager::ValidateAndDoBufferData( | 
 |     ContextState* context_state, GLenum target, GLsizeiptr size, | 
 |     const GLvoid* data, GLenum usage) { | 
 |   ErrorState* error_state = context_state->GetErrorState(); | 
 |   if (!feature_info_->validators()->buffer_target.IsValid(target)) { | 
 |     ERRORSTATE_SET_GL_ERROR_INVALID_ENUM( | 
 |         error_state, "glBufferData", target, "target"); | 
 |     return; | 
 |   } | 
 |   if (!feature_info_->validators()->buffer_usage.IsValid(usage)) { | 
 |     ERRORSTATE_SET_GL_ERROR_INVALID_ENUM( | 
 |         error_state, "glBufferData", usage, "usage"); | 
 |     return; | 
 |   } | 
 |   if (size < 0) { | 
 |     ERRORSTATE_SET_GL_ERROR( | 
 |         error_state, GL_INVALID_VALUE, "glBufferData", "size < 0"); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (size > max_buffer_size_) { | 
 |     ERRORSTATE_SET_GL_ERROR(error_state, GL_OUT_OF_MEMORY, "glBufferData", | 
 |                             "cannot allocate more than 1GB."); | 
 |     return; | 
 |   } | 
 |  | 
 |   Buffer* buffer = GetBufferInfoForTarget(context_state, target); | 
 |   if (!buffer) { | 
 |     ERRORSTATE_SET_GL_ERROR( | 
 |         error_state, GL_INVALID_VALUE, "glBufferData", "unknown buffer"); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (!memory_type_tracker_->EnsureGPUMemoryAvailable(size)) { | 
 |     ERRORSTATE_SET_GL_ERROR( | 
 |         error_state, GL_OUT_OF_MEMORY, "glBufferData", "out of memory"); | 
 |     return; | 
 |   } | 
 |  | 
 |   DoBufferData(error_state, buffer, target, size, usage, data); | 
 |  | 
 |   if (context_state->bound_transform_feedback.get()) { | 
 |     // buffer size might have changed, and on Desktop GL lower than 4.2, | 
 |     // we might need to reset transform feedback buffer range. | 
 |     context_state->bound_transform_feedback->OnBufferData(target, buffer); | 
 |   } | 
 | } | 
 |  | 
 |  | 
 | void BufferManager::DoBufferData( | 
 |     ErrorState* error_state, | 
 |     Buffer* buffer, | 
 |     GLenum target, | 
 |     GLsizeiptr size, | 
 |     GLenum usage, | 
 |     const GLvoid* data) { | 
 |   // Stage the shadow buffer first if we are using a shadow buffer so that we | 
 |   // validate what we store internally. | 
 |   const bool use_shadow = UseShadowBuffer(buffer->initial_target(), usage); | 
 |   data = buffer->StageShadow(use_shadow, size, data); | 
 |  | 
 |   ERRORSTATE_COPY_REAL_GL_ERRORS_TO_WRAPPER(error_state, "glBufferData"); | 
 |   if (IsUsageClientSideArray(usage)) { | 
 |     GLsizei empty_size = UseNonZeroSizeForClientSideArrayBuffer() ? 1 : 0; | 
 |     glBufferData(target, empty_size, nullptr, usage); | 
 |   } else { | 
 |     if (data || !size) { | 
 |       glBufferData(target, size, data, usage); | 
 |     } else { | 
 |       std::unique_ptr<char[]> zero(new char[size]); | 
 |       memset(zero.get(), 0, size); | 
 |       glBufferData(target, size, zero.get(), usage); | 
 |     } | 
 |   } | 
 |   GLenum error = ERRORSTATE_PEEK_GL_ERROR(error_state, "glBufferData"); | 
 |   if (error != GL_NO_ERROR) { | 
 |     DCHECK_EQ(static_cast<GLenum>(GL_OUT_OF_MEMORY), error); | 
 |     size = 0; | 
 |     // TODO(zmo): This doesn't seem correct. There might be shadow data from | 
 |     // a previous successful BufferData() call. | 
 |     buffer->StageShadow(false, 0, nullptr);  // Also clear the shadow. | 
 |     return; | 
 |   } | 
 |  | 
 |   SetInfo(buffer, target, size, usage, use_shadow); | 
 | } | 
 |  | 
 | void BufferManager::ValidateAndDoBufferSubData( | 
 |   ContextState* context_state, GLenum target, GLintptr offset, GLsizeiptr size, | 
 |   const GLvoid * data) { | 
 |   Buffer* buffer = RequestBufferAccess( | 
 |       context_state, target, offset, size, "glBufferSubData"); | 
 |   if (!buffer) { | 
 |     return; | 
 |   } | 
 |   DoBufferSubData(buffer, target, offset, size, data); | 
 | } | 
 |  | 
 | void BufferManager::DoBufferSubData( | 
 |     Buffer* buffer, GLenum target, GLintptr offset, GLsizeiptr size, | 
 |     const GLvoid* data) { | 
 |   buffer->SetRange(offset, size, data); | 
 |  | 
 |   if (!buffer->IsClientSideArray()) { | 
 |     glBufferSubData(target, offset, size, data); | 
 |   } | 
 | } | 
 |  | 
 | void BufferManager::ValidateAndDoCopyBufferSubData( | 
 |     ContextState* context_state, GLenum readtarget, GLenum writetarget, | 
 |     GLintptr readoffset, GLintptr writeoffset, GLsizeiptr size) { | 
 |   const char* func_name = "glCopyBufferSubData"; | 
 |   Buffer* readbuffer = RequestBufferAccess( | 
 |       context_state, readtarget, readoffset, size, func_name); | 
 |   if (!readbuffer) | 
 |     return; | 
 |   Buffer* writebuffer = RequestBufferAccess( | 
 |       context_state, writetarget, writeoffset, size, func_name); | 
 |   if (!writebuffer) | 
 |     return; | 
 |  | 
 |   ErrorState* error_state = context_state->GetErrorState(); | 
 |   if (readbuffer == writebuffer && | 
 |       ((writeoffset >= readoffset && writeoffset < readoffset + size) || | 
 |        (readoffset >= writeoffset && readoffset < writeoffset + size))) { | 
 |     ERRORSTATE_SET_GL_ERROR(error_state, GL_INVALID_VALUE, func_name, | 
 |         "read/write ranges overlap"); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (!allow_buffers_on_multiple_targets_) { | 
 |     if ((readbuffer->initial_target() == GL_ELEMENT_ARRAY_BUFFER && | 
 |          writebuffer->initial_target() != GL_ELEMENT_ARRAY_BUFFER) || | 
 |         (writebuffer->initial_target() == GL_ELEMENT_ARRAY_BUFFER && | 
 |          readbuffer->initial_target() != GL_ELEMENT_ARRAY_BUFFER)) { | 
 |       ERRORSTATE_SET_GL_ERROR(error_state, GL_INVALID_OPERATION, func_name, | 
 |           "copying between ELEMENT_ARRAY_BUFFER and another buffer type"); | 
 |       return; | 
 |     } | 
 |   } | 
 |  | 
 |   DoCopyBufferSubData(readbuffer, readtarget, readoffset, | 
 |                       writebuffer, writetarget, writeoffset, size); | 
 | } | 
 |  | 
 | void BufferManager::DoCopyBufferSubData( | 
 |     Buffer* readbuffer, | 
 |     GLenum readtarget, | 
 |     GLintptr readoffset, | 
 |     Buffer* writebuffer, | 
 |     GLenum writetarget, | 
 |     GLintptr writeoffset, | 
 |     GLsizeiptr size) { | 
 |   DCHECK(readbuffer); | 
 |   DCHECK(writebuffer); | 
 |   if (writebuffer->shadowed()) { | 
 |     const void* data = readbuffer->GetRange(readoffset, size); | 
 |     DCHECK(data); | 
 |     writebuffer->SetRange(writeoffset, size, data); | 
 |   } | 
 |  | 
 |   glCopyBufferSubData(readtarget, writetarget, readoffset, writeoffset, size); | 
 | } | 
 |  | 
 | void BufferManager::ValidateAndDoGetBufferParameteri64v( | 
 |     ContextState* context_state, GLenum target, GLenum pname, GLint64* params) { | 
 |   Buffer* buffer = GetBufferInfoForTarget(context_state, target); | 
 |   if (!buffer) { | 
 |     ERRORSTATE_SET_GL_ERROR( | 
 |         context_state->GetErrorState(), GL_INVALID_OPERATION, | 
 |         "glGetBufferParameteri64v", "no buffer bound for target"); | 
 |     return; | 
 |   } | 
 |   switch (pname) { | 
 |     case GL_BUFFER_SIZE: | 
 |       *params = buffer->size(); | 
 |       break; | 
 |     case GL_BUFFER_MAP_LENGTH: | 
 |       { | 
 |         const Buffer::MappedRange* mapped_range = buffer->GetMappedRange(); | 
 |         *params = mapped_range ? mapped_range->size : 0; | 
 |         break; | 
 |       } | 
 |     case GL_BUFFER_MAP_OFFSET: | 
 |       { | 
 |         const Buffer::MappedRange* mapped_range = buffer->GetMappedRange(); | 
 |         *params = mapped_range ? mapped_range->offset : 0; | 
 |         break; | 
 |       } | 
 |     default: | 
 |       NOTREACHED(); | 
 |   } | 
 | } | 
 |  | 
 | void BufferManager::ValidateAndDoGetBufferParameteriv( | 
 |     ContextState* context_state, GLenum target, GLenum pname, GLint* params) { | 
 |   Buffer* buffer = GetBufferInfoForTarget(context_state, target); | 
 |   if (!buffer) { | 
 |     ERRORSTATE_SET_GL_ERROR( | 
 |         context_state->GetErrorState(), GL_INVALID_OPERATION, | 
 |         "glGetBufferParameteriv", "no buffer bound for target"); | 
 |     return; | 
 |   } | 
 |   switch (pname) { | 
 |     case GL_BUFFER_SIZE: | 
 |       *params = buffer->size(); | 
 |       break; | 
 |     case GL_BUFFER_USAGE: | 
 |       *params = buffer->usage(); | 
 |       break; | 
 |     case GL_BUFFER_ACCESS_FLAGS: | 
 |       { | 
 |         const Buffer::MappedRange* mapped_range = buffer->GetMappedRange(); | 
 |         *params = mapped_range ? mapped_range->access : 0; | 
 |         break; | 
 |       } | 
 |     case GL_BUFFER_MAPPED: | 
 |       *params = buffer->GetMappedRange() == nullptr ? false : true; | 
 |       break; | 
 |     default: | 
 |       NOTREACHED(); | 
 |   } | 
 | } | 
 |  | 
 | bool BufferManager::SetTarget(Buffer* buffer, GLenum target) { | 
 |   if (!allow_buffers_on_multiple_targets_) { | 
 |     // After being bound to ELEMENT_ARRAY_BUFFER target, a buffer cannot be | 
 |     // bound to any other targets except for COPY_READ/WRITE_BUFFER target; | 
 |     // After being bound to non ELEMENT_ARRAY_BUFFER target, a buffer cannot | 
 |     // be bound to ELEMENT_ARRAY_BUFFER target. | 
 |  | 
 |     // Note that we don't force the WebGL 2 rule that a buffer bound to | 
 |     // TRANSFORM_FEEDBACK_BUFFER target should not be bound to any other | 
 |     // targets, because that is not a security threat, so we only enforce it | 
 |     // in the WebGL2RenderingContextBase. | 
 |     switch (buffer->initial_target()) { | 
 |       case GL_ELEMENT_ARRAY_BUFFER: | 
 |         switch (target) { | 
 |           case GL_ARRAY_BUFFER: | 
 |           case GL_PIXEL_PACK_BUFFER: | 
 |           case GL_PIXEL_UNPACK_BUFFER: | 
 |           case GL_TRANSFORM_FEEDBACK_BUFFER: | 
 |           case GL_UNIFORM_BUFFER: | 
 |             return false; | 
 |           default: | 
 |             break; | 
 |         } | 
 |         break; | 
 |       case GL_ARRAY_BUFFER: | 
 |       case GL_COPY_READ_BUFFER: | 
 |       case GL_COPY_WRITE_BUFFER: | 
 |       case GL_PIXEL_PACK_BUFFER: | 
 |       case GL_PIXEL_UNPACK_BUFFER: | 
 |       case GL_TRANSFORM_FEEDBACK_BUFFER: | 
 |       case GL_UNIFORM_BUFFER: | 
 |         if (target == GL_ELEMENT_ARRAY_BUFFER) { | 
 |           return false; | 
 |         } | 
 |         break; | 
 |       default: | 
 |         break; | 
 |     } | 
 |   } | 
 |   if (buffer->initial_target() == 0) | 
 |     buffer->set_initial_target(target); | 
 |   return true; | 
 | } | 
 |  | 
 | // Since one BufferManager can be shared by multiple decoders, ContextState is | 
 | // passed in each time and not just passed in during initialization. | 
 | Buffer* BufferManager::GetBufferInfoForTarget( | 
 |     ContextState* state, GLenum target) const { | 
 |   switch (target) { | 
 |     case GL_ARRAY_BUFFER: | 
 |       return state->bound_array_buffer.get(); | 
 |     case GL_ELEMENT_ARRAY_BUFFER: | 
 |       return state->vertex_attrib_manager->element_array_buffer(); | 
 |     case GL_COPY_READ_BUFFER: | 
 |       return state->bound_copy_read_buffer.get(); | 
 |     case GL_COPY_WRITE_BUFFER: | 
 |       return state->bound_copy_write_buffer.get(); | 
 |     case GL_PIXEL_PACK_BUFFER: | 
 |       return state->bound_pixel_pack_buffer.get(); | 
 |     case GL_PIXEL_UNPACK_BUFFER: | 
 |       return state->bound_pixel_unpack_buffer.get(); | 
 |     case GL_TRANSFORM_FEEDBACK_BUFFER: | 
 |       return state->bound_transform_feedback_buffer.get(); | 
 |     case GL_UNIFORM_BUFFER: | 
 |       return state->bound_uniform_buffer.get(); | 
 |     default: | 
 |       NOTREACHED(); | 
 |       return nullptr; | 
 |   } | 
 | } | 
 |  | 
 | void BufferManager::SetPrimitiveRestartFixedIndexIfNecessary(GLenum type) { | 
 |   GLuint index = 0; | 
 |   switch (type) { | 
 |     case GL_UNSIGNED_BYTE: | 
 |       index = 0xFF; | 
 |       break; | 
 |     case GL_UNSIGNED_SHORT: | 
 |       index = 0xFFFF; | 
 |       break; | 
 |     case GL_UNSIGNED_INT: | 
 |       index = 0xFFFFFFFF; | 
 |       break; | 
 |     default: | 
 |       NOTREACHED();  // should never get here by validation. | 
 |       break; | 
 |   } | 
 |   if (primitive_restart_fixed_index_ != index) { | 
 |     glPrimitiveRestartIndex(index); | 
 |     primitive_restart_fixed_index_ = index; | 
 |   } | 
 | } | 
 |  | 
 | bool BufferManager::OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, | 
 |                                  base::trace_event::ProcessMemoryDump* pmd) { | 
 |   using base::trace_event::MemoryAllocatorDump; | 
 |   using base::trace_event::MemoryDumpLevelOfDetail; | 
 |  | 
 |   if (args.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND) { | 
 |     std::string dump_name = | 
 |         base::StringPrintf("gpu/gl/buffers/share_group_%" PRIu64 "", | 
 |                            memory_tracker_->ShareGroupTracingGUID()); | 
 |     MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(dump_name); | 
 |     dump->AddScalar(MemoryAllocatorDump::kNameSize, | 
 |                     MemoryAllocatorDump::kUnitsBytes, mem_represented()); | 
 |  | 
 |     // Early out, no need for more detail in a BACKGROUND dump. | 
 |     return true; | 
 |   } | 
 |  | 
 |   const uint64_t share_group_tracing_guid = | 
 |       memory_tracker_->ShareGroupTracingGUID(); | 
 |   for (const auto& buffer_entry : buffers_) { | 
 |     const auto& client_buffer_id = buffer_entry.first; | 
 |     const auto& buffer = buffer_entry.second; | 
 |  | 
 |     std::string dump_name = | 
 |         base::StringPrintf("gpu/gl/buffers/share_group_%" PRIu64 "/buffer_%d", | 
 |                            share_group_tracing_guid, client_buffer_id); | 
 |     MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(dump_name); | 
 |     dump->AddScalar(MemoryAllocatorDump::kNameSize, | 
 |                     MemoryAllocatorDump::kUnitsBytes, | 
 |                     static_cast<uint64_t>(buffer->size())); | 
 |  | 
 |     auto guid = gl::GetGLBufferGUIDForTracing(share_group_tracing_guid, | 
 |                                               client_buffer_id); | 
 |     pmd->CreateSharedGlobalAllocatorDump(guid); | 
 |     pmd->AddOwnershipEdge(dump->guid(), guid); | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | Buffer* BufferManager::RequestBufferAccess(ContextState* context_state, | 
 |                                            GLenum target, | 
 |                                            GLintptr offset, | 
 |                                            GLsizeiptr size, | 
 |                                            const char* func_name) { | 
 |   DCHECK(context_state); | 
 |   ErrorState* error_state = context_state->GetErrorState(); | 
 |  | 
 |   Buffer* buffer = GetBufferInfoForTarget(context_state, target); | 
 |   if (!RequestBufferAccess(error_state, buffer, func_name, | 
 |                            "bound to target 0x%04x", target)) { | 
 |     return nullptr; | 
 |   } | 
 |   if (!buffer->CheckRange(offset, size)) { | 
 |     std::string msg = base::StringPrintf( | 
 |         "bound to target 0x%04x : offset/size out of range", target); | 
 |     ERRORSTATE_SET_GL_ERROR( | 
 |         error_state, GL_INVALID_VALUE, func_name, msg.c_str()); | 
 |     return nullptr; | 
 |   } | 
 |   return buffer; | 
 | } | 
 |  | 
 | Buffer* BufferManager::RequestBufferAccess(ContextState* context_state, | 
 |                                            GLenum target, | 
 |                                            const char* func_name) { | 
 |   DCHECK(context_state); | 
 |   ErrorState* error_state = context_state->GetErrorState(); | 
 |  | 
 |   Buffer* buffer = GetBufferInfoForTarget(context_state, target); | 
 |   return RequestBufferAccess( | 
 |       error_state, buffer, func_name, | 
 |       "bound to target 0x%04x", target) ? buffer : nullptr; | 
 | } | 
 |  | 
 | bool BufferManager::RequestBufferAccess(ErrorState* error_state, | 
 |                                         Buffer* buffer, | 
 |                                         const char* func_name, | 
 |                                         const char* error_message_format, ...) { | 
 |   DCHECK(error_state); | 
 |  | 
 |   va_list varargs; | 
 |   va_start(varargs, error_message_format); | 
 |   bool result = RequestBufferAccessV(error_state, buffer, func_name, | 
 |                                      error_message_format, varargs); | 
 |   va_end(varargs); | 
 |   return result; | 
 | } | 
 |  | 
 | bool BufferManager::RequestBufferAccess(ErrorState* error_state, | 
 |                                         Buffer* buffer, | 
 |                                         GLintptr offset, | 
 |                                         GLsizeiptr size, | 
 |                                         const char* func_name, | 
 |                                         const char* error_message) { | 
 |   if (!RequestBufferAccess(error_state, buffer, func_name, error_message)) { | 
 |     return false; | 
 |   } | 
 |   if (!buffer->CheckRange(offset, size)) { | 
 |     std::string msg = base::StringPrintf( | 
 |         "%s : offset/size out of range", error_message); | 
 |     ERRORSTATE_SET_GL_ERROR( | 
 |         error_state, GL_INVALID_OPERATION, func_name, msg.c_str()); | 
 |     return false; | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | bool BufferManager::RequestBuffersAccess( | 
 |     ErrorState* error_state, | 
 |     const IndexedBufferBindingHost* bindings, | 
 |     const std::vector<GLsizeiptr>& variable_sizes, | 
 |     GLsizei count, | 
 |     const char* func_name, | 
 |     const char* message_tag) { | 
 |   DCHECK(error_state); | 
 |   DCHECK(bindings); | 
 |   for (size_t ii = 0; ii < variable_sizes.size(); ++ii) { | 
 |     if (variable_sizes[ii] == 0) | 
 |       continue; | 
 |     Buffer* buffer = bindings->GetBufferBinding(ii); | 
 |     if (!buffer) { | 
 |       std::string msg = base::StringPrintf( | 
 |           "%s : no buffer bound at index %zu", message_tag, ii); | 
 |       ERRORSTATE_SET_GL_ERROR( | 
 |           error_state, GL_INVALID_OPERATION, func_name, msg.c_str()); | 
 |       return false; | 
 |     } | 
 |     if (buffer->GetMappedRange()) { | 
 |       std::string msg = base::StringPrintf( | 
 |           "%s : buffer is mapped at index %zu", message_tag, ii); | 
 |       ERRORSTATE_SET_GL_ERROR( | 
 |           error_state, GL_INVALID_OPERATION, func_name, msg.c_str()); | 
 |       return false; | 
 |     } | 
 |     GLsizeiptr size = bindings->GetEffectiveBufferSize(ii); | 
 |     base::CheckedNumeric<GLsizeiptr> required_size = variable_sizes[ii]; | 
 |     required_size *= count; | 
 |     if (size < required_size.ValueOrDefault( | 
 |             std::numeric_limits<GLsizeiptr>::max())) { | 
 |       std::string msg = base::StringPrintf( | 
 |           "%s : buffer or buffer range not large enough at index %zu", | 
 |           message_tag, ii); | 
 |       ERRORSTATE_SET_GL_ERROR( | 
 |           error_state, GL_INVALID_OPERATION, func_name, msg.c_str()); | 
 |       return false; | 
 |     } | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | bool BufferManager::RequestBufferAccessV(ErrorState* error_state, | 
 |                                          Buffer* buffer, | 
 |                                          const char* func_name, | 
 |                                          const char* error_message_format, | 
 |                                          va_list varargs) { | 
 |   DCHECK(error_state); | 
 |  | 
 |   if (!buffer || buffer->IsDeleted()) { | 
 |     std::string message_tag = base::StringPrintV(error_message_format, varargs); | 
 |     std::string msg = base::StringPrintf("%s : no buffer", message_tag.c_str()); | 
 |     ERRORSTATE_SET_GL_ERROR(error_state, GL_INVALID_OPERATION, func_name, | 
 |                             msg.c_str()); | 
 |     return false; | 
 |   } | 
 |   if (buffer->GetMappedRange()) { | 
 |     std::string message_tag = base::StringPrintV(error_message_format, varargs); | 
 |     std::string msg = base::StringPrintf("%s : buffer is mapped", | 
 |                                          message_tag.c_str()); | 
 |     ERRORSTATE_SET_GL_ERROR(error_state, GL_INVALID_OPERATION, func_name, | 
 |                             msg.c_str()); | 
 |     return false; | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | }  // namespace gles2 | 
 | }  // namespace gpu |