summaryrefslogtreecommitdiff
path: root/chromium/gpu/command_buffer/service/buffer_manager.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/gpu/command_buffer/service/buffer_manager.cc')
-rw-r--r--chromium/gpu/command_buffer/service/buffer_manager.cc407
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
+
+