// 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 <limits> #include "base/debug/trace_event.h" #include "base/logging.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 "ui/gl/gl_bindings.h" namespace gpu { namespace gles2 { BufferManager::BufferManager( MemoryTracker* memory_tracker, FeatureInfo* feature_info) : memory_tracker_( new MemoryTypeTracker(memory_tracker, MemoryTracker::kManaged)), feature_info_(feature_info), allow_buffers_on_multiple_targets_(false), buffer_count_(0), have_context_(true), use_client_side_arrays_for_stream_buffers_( feature_info ? feature_info->workarounds( ).use_client_side_arrays_for_stream_buffers : 0) { } BufferManager::~BufferManager() { DCHECK(buffers_.empty()); CHECK_EQ(buffer_count_, 0u); } void BufferManager::Destroy(bool have_context) { have_context_ = have_context; buffers_.clear(); DCHECK_EQ(0u, memory_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_tracker_->TrackMemFree(buffer->size()); --buffer_count_; } Buffer::Buffer(BufferManager* manager, GLuint service_id) : manager_(manager), size_(0), deleted_(false), shadowed_(false), is_client_side_array_(false), service_id_(service_id), target_(0), usage_(GL_STATIC_DRAW) { manager_->StartTracking(this); } Buffer::~Buffer() { if (manager_) { if (manager_->have_context_) { GLuint id = service_id(); glDeleteBuffersARB(1, &id); } manager_->StopTracking(this); manager_ = NULL; } } void Buffer::SetInfo( GLsizeiptr size, GLenum usage, bool shadow, const GLvoid* data, bool is_client_side_array) { usage_ = usage; is_client_side_array_ = is_client_side_array; ClearCache(); if (size != size_ || shadow != shadowed_) { shadowed_ = shadow; size_ = size; if (shadowed_) { shadow_.reset(new int8[size]); } else { shadow_.reset(); } } if (shadowed_) { if (data) { memcpy(shadow_.get(), data, size); } else { memset(shadow_.get(), 0, size); } } } bool Buffer::CheckRange( GLintptr offset, GLsizeiptr size) const { int32 end = 0; return offset >= 0 && size >= 0 && offset <= std::numeric_limits<int32>::max() && size <= std::numeric_limits<int32>::max() && SafeAddInt32(offset, size, &end) && end <= size_; } bool Buffer::SetRange( GLintptr offset, GLsizeiptr size, const GLvoid * data) { if (!CheckRange(offset, size)) { return false; } if (shadowed_) { memcpy(shadow_.get() + offset, data, size); ClearCache(); } return true; } const void* Buffer::GetRange( GLintptr offset, GLsizeiptr size) const { if (!shadowed_) { return NULL; } if (!CheckRange(offset, size)) { return NULL; } return shadow_.get() + offset; } void Buffer::ClearCache() { range_set_.clear(); } template <typename T> GLuint GetMaxValue(const void* data, GLuint offset, GLsizei count) { GLuint max_value = 0; const T* element = reinterpret_cast<const T*>( static_cast<const int8*>(data) + offset); const T* end = element + count; for (; element < end; ++element) { if (*element > max_value) { max_value = *element; } } return max_value; } bool Buffer::GetMaxValueForRange( GLuint offset, GLsizei count, GLenum type, GLuint* max_value) { Range range(offset, count, type); RangeToMaxValueMap::iterator it = range_set_.find(range); if (it != range_set_.end()) { *max_value = it->second; return true; } uint32 size; if (!SafeMultiplyUint32( count, GLES2Util::GetGLTypeSizeForTexturesAndBuffers(type), &size)) { return false; } if (!SafeAddUint32(offset, size, &size)) { return false; } if (size > static_cast<uint32>(size_)) { return false; } if (!shadowed_) { 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>(shadow_.get(), offset, count); 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>(shadow_.get(), offset, count); 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>(shadow_.get(), offset, count); 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; } 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; } void BufferManager::SetInfo( Buffer* buffer, GLsizeiptr size, GLenum usage, const GLvoid* data) { DCHECK(buffer); memory_tracker_->TrackMemFree(buffer->size()); bool is_client_side_array = IsUsageClientSideArray(usage); bool shadow = buffer->target() == GL_ELEMENT_ARRAY_BUFFER || allow_buffers_on_multiple_targets_ || is_client_side_array; buffer->SetInfo(size, usage, shadow, data, is_client_side_array); memory_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; } Buffer* buffer = GetBufferInfoForTarget(context_state, target); if (!buffer) { ERRORSTATE_SET_GL_ERROR( error_state, GL_INVALID_VALUE, "glBufferData", "unknown buffer"); return; } if (!memory_tracker_->EnsureGPUMemoryAvailable(size)) { ERRORSTATE_SET_GL_ERROR( error_state, GL_OUT_OF_MEMORY, "glBufferData", "out of memory"); return; } DoBufferData(error_state, buffer, size, usage, data); } void BufferManager::DoBufferData( ErrorState* error_state, Buffer* buffer, GLsizeiptr size, GLenum usage, const GLvoid* data) { // Clear the buffer to 0 if no initial data was passed in. scoped_ptr<int8[]> zero; if (!data) { zero.reset(new int8[size]); memset(zero.get(), 0, size); data = zero.get(); } ERRORSTATE_COPY_REAL_GL_ERRORS_TO_WRAPPER(error_state, "glBufferData"); if (IsUsageClientSideArray(usage)) { GLsizei empty_size = UseNonZeroSizeForClientSideArrayBuffer() ? 1 : 0; glBufferData(buffer->target(), empty_size, NULL, usage); } else { glBufferData(buffer->target(), size, data, usage); } GLenum error = ERRORSTATE_PEEK_GL_ERROR(error_state, "glBufferData"); if (error == GL_NO_ERROR) { SetInfo(buffer, size, usage, data); } else { SetInfo(buffer, 0, usage, NULL); } } void BufferManager::ValidateAndDoBufferSubData( ContextState* context_state, GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid * data) { ErrorState* error_state = context_state->GetErrorState(); Buffer* buffer = GetBufferInfoForTarget(context_state, target); if (!buffer) { ERRORSTATE_SET_GL_ERROR(error_state, GL_INVALID_VALUE, "glBufferSubData", "unknown buffer"); return; } DoBufferSubData(error_state, buffer, offset, size, data); } void BufferManager::DoBufferSubData( ErrorState* error_state, Buffer* buffer, GLintptr offset, GLsizeiptr size, const GLvoid* data) { if (!buffer->SetRange(offset, size, data)) { ERRORSTATE_SET_GL_ERROR( error_state, GL_INVALID_VALUE, "glBufferSubData", "out of range"); return; } if (!buffer->IsClientSideArray()) { glBufferSubData(buffer->target(), offset, size, data); } } 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; default: NOTREACHED(); } } bool BufferManager::SetTarget(Buffer* buffer, GLenum target) { // Check that we are not trying to bind it to a different target. if (buffer->target() != 0 && buffer->target() != target && !allow_buffers_on_multiple_targets_) { return false; } if (buffer->target() == 0) { buffer->set_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) { DCHECK(target == GL_ARRAY_BUFFER || target == GL_ELEMENT_ARRAY_BUFFER); if (target == GL_ARRAY_BUFFER) { return state->bound_array_buffer.get(); } else { return state->vertex_attrib_manager->element_array_buffer(); } } } // namespace gles2 } // namespace gpu