diff options
Diffstat (limited to 'chromium/gpu/command_buffer/service/buffer_manager.cc')
-rw-r--r-- | chromium/gpu/command_buffer/service/buffer_manager.cc | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/chromium/gpu/command_buffer/service/buffer_manager.cc b/chromium/gpu/command_buffer/service/buffer_manager.cc new file mode 100644 index 00000000000..f9cec7ace9a --- /dev/null +++ b/chromium/gpu/command_buffer/service/buffer_manager.cc @@ -0,0 +1,407 @@ +// 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), + deleted_(false), + service_id_(service_id), + target_(0), + size_(0), + usage_(GL_STATIC_DRAW), + shadowed_(false), + is_client_side_array_(false) { + 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 + + |