Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(140)

Unified Diff: gpu/command_buffer/client/vertex_array_object_manager.cc

Issue 11413094: Fix VAOs and client side arrays (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: gpu/command_buffer/client/vertex_array_object_manager.cc
diff --git a/gpu/command_buffer/client/vertex_array_object_manager.cc b/gpu/command_buffer/client/vertex_array_object_manager.cc
new file mode 100644
index 0000000000000000000000000000000000000000..4c76fb56899d7651c8acdf82228ba1e4364abfbc
--- /dev/null
+++ b/gpu/command_buffer/client/vertex_array_object_manager.cc
@@ -0,0 +1,632 @@
+// 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 "../client/vertex_array_object_manager.h"
+
+#include "../client/gles2_cmd_helper.h"
+#include "../client/gles2_implementation.h"
+#include "../common/logging.h"
+
+#if defined(__native_client__) && !defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
+#define GLES2_SUPPORT_CLIENT_SIDE_ARRAYS
+#endif
+
+namespace gpu {
+namespace gles2 {
+
+#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
+
+static GLsizei RoundUpToMultipleOf4(GLsizei size) {
+ return (size + 3) & ~3;
+}
+
+#endif // defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
+
+// A 32-bit and 64-bit compatible way of converting a pointer to a GLuint.
+static GLuint ToGLuint(const void* ptr) {
+ return static_cast<GLuint>(reinterpret_cast<size_t>(ptr));
+}
+
+// This class tracks VertexAttribPointers and helps emulate client side buffers.
+//
+// The way client side buffers work is we shadow all the Vertex Attribs so we
+// know which ones are pointing to client side buffers.
+//
+// At Draw time, for any attribs pointing to client side buffers we copy them
+// to a special VBO and reset the actual vertex attrib pointers to point to this
+// VBO.
+//
+// This also means we have to catch calls to query those values so that when
+// an attrib is a client side buffer we pass the info back the user expects.
+
+class GLES2_IMPL_EXPORT VertexArrayObject {
+ public:
+ // Info about Vertex Attributes. This is used to track what the user currently
+ // has bound on each Vertex Attribute so we can simulate client side buffers
+ // at glDrawXXX time.
+ class VertexAttrib {
+ public:
+ VertexAttrib()
+ : enabled_(false),
+ buffer_id_(0),
+ size_(4),
+ type_(GL_FLOAT),
+ normalized_(GL_FALSE),
+ pointer_(NULL),
+ gl_stride_(0),
+ divisor_(0) {
+ }
+
+ bool enabled() const {
+ return enabled_;
+ }
+
+ void set_enabled(bool enabled) {
+ enabled_ = enabled;
+ }
+
+ GLuint buffer_id() const {
+ return buffer_id_;
+ }
+
+ void set_buffer_id(GLuint id) {
+ buffer_id_ = id;
+ }
+
+ GLenum type() const {
+ return type_;
+ }
+
+ GLint size() const {
+ return size_;
+ }
+
+ GLsizei stride() const {
+ return gl_stride_;
+ }
+
+ GLboolean normalized() const {
+ return normalized_;
+ }
+
+ const GLvoid* pointer() const {
+ return pointer_;
+ }
+
+ bool IsClientSide() const {
+ return buffer_id_ == 0;
+ }
+
+ GLuint divisor() const {
+ return divisor_;
+ }
+
+ void SetInfo(
+ GLuint buffer_id,
+ GLint size,
+ GLenum type,
+ GLboolean normalized,
+ GLsizei gl_stride,
+ const GLvoid* pointer) {
+ buffer_id_ = buffer_id;
+ size_ = size;
+ type_ = type;
+ normalized_ = normalized;
+ gl_stride_ = gl_stride;
+ pointer_ = pointer;
+ }
+
+ void SetDivisor(GLuint divisor) {
+ divisor_ = divisor;
+ }
+
+ private:
+ // Whether or not this attribute is enabled.
+ bool enabled_;
+
+ // The id of the buffer. 0 = client side buffer.
+ GLuint buffer_id_;
+
+ // Number of components (1, 2, 3, 4).
+ GLint size_;
+
+ // GL_BYTE, GL_FLOAT, etc. See glVertexAttribPointer.
+ GLenum type_;
+
+ // GL_TRUE or GL_FALSE
+ GLboolean normalized_;
+
+ // The pointer/offset into the buffer.
+ const GLvoid* pointer_;
+
+ // The stride that will be used to access the buffer. This is the bogus GL
+ // stride where 0 = compute the stride based on size and type.
+ GLsizei gl_stride_;
+
+ // Divisor, for geometry instancing.
+ GLuint divisor_;
+ };
+
+ typedef std::vector<VertexAttrib> VertexAttribs;
+
+ explicit VertexArrayObject(GLuint max_vertex_attribs);
+
+ void UnbindBuffer(GLuint id);
+
+ bool BindElementArray(GLuint id);
+
+ bool HaveEnabledClientSideBuffers() const;
+
+ void SetAttribEnable(GLuint index, bool enabled);
+
+ void SetAttribPointer(
+ GLuint buffer_id,
+ GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride,
+ const void* ptr);
+
+ bool GetVertexAttrib(
+ GLuint index, GLenum pname, uint32* param) const;
+
+ void SetAttribDivisor(GLuint index, GLuint divisor);
+
+ bool GetAttribPointer(GLuint index, GLenum pname, void** ptr) const;
+
+ const VertexAttribs& vertex_attribs() const {
+ return vertex_attribs_;
+ }
+
+ GLuint bound_element_array_buffer() const {
+ return bound_element_array_buffer_id_;
+ }
+
+ private:
+ const VertexAttrib* GetAttrib(GLuint index) const;
+
+ GLuint num_client_side_pointers_enabled_;
+
+ // The currently bound element array buffer.
+ GLuint bound_element_array_buffer_id_;
+
+ VertexAttribs vertex_attribs_;
+
+ DISALLOW_COPY_AND_ASSIGN(VertexArrayObject);
+};
+
+VertexArrayObject::VertexArrayObject(GLuint max_vertex_attribs)
+ : num_client_side_pointers_enabled_(0),
+ bound_element_array_buffer_id_(0) {
+ vertex_attribs_.resize(max_vertex_attribs);
+}
+
+void VertexArrayObject::UnbindBuffer(GLuint id) {
+ for (size_t ii = 0; ii < vertex_attribs_.size(); ++ii) {
+ VertexAttrib& attrib = vertex_attribs_[ii];
+ if (attrib.buffer_id() == id) {
+ attrib.set_buffer_id(0);
+ if (attrib.enabled()) {
+ ++num_client_side_pointers_enabled_;
+ }
+ }
+ }
+}
+
+bool VertexArrayObject::BindElementArray(GLuint id) {
+ if (id == bound_element_array_buffer_id_) {
+ return false;
+ }
+ bound_element_array_buffer_id_ = id;
+ return true;
+}
+bool VertexArrayObject::HaveEnabledClientSideBuffers() const {
+ return num_client_side_pointers_enabled_ > 0;
+}
+
+void VertexArrayObject::SetAttribEnable(GLuint index, bool enabled) {
+ if (index < vertex_attribs_.size()) {
+ VertexAttrib& attrib = vertex_attribs_[index];
+ if (attrib.enabled() != enabled) {
+ if (attrib.IsClientSide()) {
+ num_client_side_pointers_enabled_ += enabled ? 1 : -1;
+ }
+ attrib.set_enabled(enabled);
+ }
+ }
+}
+
+void VertexArrayObject::SetAttribPointer(
+ GLuint buffer_id,
+ GLuint index,
+ GLint size,
+ GLenum type,
+ GLboolean normalized,
+ GLsizei stride,
+ const void* ptr) {
+ if (index < vertex_attribs_.size()) {
+ VertexAttrib& attrib = vertex_attribs_[index];
+ if (attrib.IsClientSide() && attrib.enabled()) {
+ --num_client_side_pointers_enabled_;
+ }
+
+ attrib.SetInfo(buffer_id, size, type, normalized, stride, ptr);
+
+ if (attrib.IsClientSide() && attrib.enabled()) {
+ ++num_client_side_pointers_enabled_;
+ }
+ }
+}
+
+bool VertexArrayObject::GetVertexAttrib(
+ GLuint index, GLenum pname, uint32* param) const {
+ const VertexAttrib* attrib = GetAttrib(index);
+ if (!attrib) {
+ return false;
+ }
+
+ switch (pname) {
+ case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
+ *param = attrib->buffer_id();
+ break;
+ case GL_VERTEX_ATTRIB_ARRAY_ENABLED:
+ *param = attrib->enabled();
+ break;
+ case GL_VERTEX_ATTRIB_ARRAY_SIZE:
+ *param = attrib->size();
+ break;
+ case GL_VERTEX_ATTRIB_ARRAY_STRIDE:
+ *param = attrib->stride();
+ break;
+ case GL_VERTEX_ATTRIB_ARRAY_TYPE:
+ *param = attrib->type();
+ break;
+ case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED:
+ *param = attrib->normalized();
+ break;
+ default:
+ return false; // pass through to service side.
+ break;
+ }
+ return true;
+}
+
+void VertexArrayObject::SetAttribDivisor(GLuint index, GLuint divisor) {
+ if (index < vertex_attribs_.size()) {
+ VertexAttrib& attrib = vertex_attribs_[index];
+ attrib.SetDivisor(divisor);
+ }
+}
+
+// Gets the Attrib pointer for an attrib but only if it's a client side
+// pointer. Returns true if it got the pointer.
+bool VertexArrayObject::GetAttribPointer(
+ GLuint index, GLenum pname, void** ptr) const {
+ const VertexAttrib* attrib = GetAttrib(index);
+ if (attrib && pname == GL_VERTEX_ATTRIB_ARRAY_POINTER) {
+ *ptr = const_cast<void*>(attrib->pointer());
+ return true;
+ }
+ return false;
+}
+
+// Gets an attrib if it's in range and it's client side.
+const VertexArrayObject::VertexAttrib* VertexArrayObject::GetAttrib(
+ GLuint index) const {
+ if (index < vertex_attribs_.size()) {
+ const VertexAttrib* attrib = &vertex_attribs_[index];
+ return attrib;
+ }
+ return NULL;
+}
+
+VertexArrayObjectManager::VertexArrayObjectManager(
+ GLuint max_vertex_attribs,
+ GLuint array_buffer_id,
+ GLuint element_array_buffer_id)
+ : max_vertex_attribs_(max_vertex_attribs),
+ array_buffer_id_(array_buffer_id),
+ array_buffer_size_(0),
+ array_buffer_offset_(0),
+ element_array_buffer_id_(element_array_buffer_id),
+ element_array_buffer_size_(0),
+ collection_buffer_size_(0),
+ default_vertex_array_object_(new VertexArrayObject(max_vertex_attribs)),
+ bound_vertex_array_object_(default_vertex_array_object_) {
+}
+
+VertexArrayObjectManager::~VertexArrayObjectManager() {
+ for (VertexArrayObjectMap::iterator it = vertex_array_objects_.begin();
+ it != vertex_array_objects_.end(); ++it) {
+ delete it->second;
+ }
+ delete default_vertex_array_object_;
+}
+
+bool VertexArrayObjectManager::IsReservedId(GLuint id) const {
+ return (id != 0 &&
+ (id == array_buffer_id_ || id == element_array_buffer_id_));
+}
+
+GLuint VertexArrayObjectManager::bound_element_array_buffer() const {
+ return bound_vertex_array_object_->bound_element_array_buffer();
+}
+
+void VertexArrayObjectManager::UnbindBuffer(GLuint id) {
+ bound_vertex_array_object_->UnbindBuffer(id);
+}
+
+bool VertexArrayObjectManager::BindElementArray(GLuint id) {
+ return bound_vertex_array_object_->BindElementArray(id);
+}
+
+void VertexArrayObjectManager::GenVertexArrays(
+ GLsizei n, const GLuint* arrays) {
+ GPU_DCHECK_GE(n, 0);
+ for (GLsizei i = 0; i < n; ++i) {
+ std::pair<VertexArrayObjectMap::iterator, bool> result =
+ vertex_array_objects_.insert(std::make_pair(
+ arrays[i], new VertexArrayObject(max_vertex_attribs_)));
+ GPU_DCHECK(result.second);
+ }
+}
+
+void VertexArrayObjectManager::DeleteVertexArrays(
+ GLsizei n, const GLuint* arrays) {
+ GPU_DCHECK_GE(n, 0);
+ for (GLsizei i = 0; i < n; ++i) {
+ GLuint id = arrays[i];
+ if (id) {
+ VertexArrayObjectMap::iterator it = vertex_array_objects_.find(id);
+ if (it != vertex_array_objects_.end()) {
+ if (bound_vertex_array_object_ == it->second) {
+ bound_vertex_array_object_ = default_vertex_array_object_;
+ }
+ delete it->second;
+ vertex_array_objects_.erase(it);
+ }
+ }
+ }
+}
+
+bool VertexArrayObjectManager::BindVertexArray(GLuint array, bool* changed) {
+ *changed = false;
+ VertexArrayObject* vertex_array_object = default_vertex_array_object_;
+ if (array != 0) {
+ VertexArrayObjectMap::iterator it = vertex_array_objects_.find(array);
+ if (it == vertex_array_objects_.end()) {
+ return false;
+ }
+ vertex_array_object = it->second;
+ }
+ *changed = vertex_array_object != bound_vertex_array_object_;
+ bound_vertex_array_object_ = vertex_array_object;
+ return true;
+}
+
+bool VertexArrayObjectManager::HaveEnabledClientSideBuffers() const {
+ return bound_vertex_array_object_->HaveEnabledClientSideBuffers();
+}
+
+void VertexArrayObjectManager::SetAttribEnable(GLuint index, bool enabled) {
+ bound_vertex_array_object_->SetAttribEnable(index, enabled);
+}
+
+bool VertexArrayObjectManager::GetVertexAttrib(
+ GLuint index, GLenum pname, uint32* param) {
+ return bound_vertex_array_object_->GetVertexAttrib(index, pname, param);
+}
+
+bool VertexArrayObjectManager::GetAttribPointer(
+ GLuint index, GLenum pname, void** ptr) const {
+ return bound_vertex_array_object_->GetAttribPointer(index, pname, ptr);
+}
+
+bool VertexArrayObjectManager::SetAttribPointer(
+ GLuint buffer_id,
+ GLuint index,
+ GLint size,
+ GLenum type,
+ GLboolean normalized,
+ GLsizei stride,
+ const void* ptr) {
+ // Client side arrays are not allowed in vaos.
+ if (buffer_id == 0 && !IsDefaultVAOBound()) {
+ return false;
+ }
+ bound_vertex_array_object_->SetAttribPointer(
+ buffer_id, index, size, type, normalized, stride, ptr);
+ return true;
+}
+
+void VertexArrayObjectManager::SetAttribDivisor(GLuint index, GLuint divisor) {
+ bound_vertex_array_object_->SetAttribDivisor(index, divisor);
+}
+
+// Collects the data into the collection buffer and returns the number of
+// bytes collected.
+GLsizei VertexArrayObjectManager::CollectData(
+ const void* data,
+ GLsizei bytes_per_element,
+ GLsizei real_stride,
+ GLsizei num_elements) {
+ GLsizei bytes_needed = bytes_per_element * num_elements;
+ if (collection_buffer_size_ < bytes_needed) {
+ collection_buffer_.reset(new int8[bytes_needed]);
+ collection_buffer_size_ = bytes_needed;
+ }
+ const int8* src = static_cast<const int8*>(data);
+ int8* dst = collection_buffer_.get();
+ int8* end = dst + bytes_per_element * num_elements;
+ for (; dst < end; src += real_stride, dst += bytes_per_element) {
+ memcpy(dst, src, bytes_per_element);
+ }
+ return bytes_needed;
+}
+
+bool VertexArrayObjectManager::IsDefaultVAOBound() const {
+ return bound_vertex_array_object_ == default_vertex_array_object_;
+}
+
+// Returns true if buffers were setup.
+bool VertexArrayObjectManager::SetupSimulatedClientSideBuffers(
+ const char* function_name,
+ GLES2Implementation* gl,
+ GLES2CmdHelper* gl_helper,
+ GLsizei num_elements,
+ GLsizei primcount,
+ bool* simulated) {
+ *simulated = false;
+#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
+ if (!bound_vertex_array_object_->HaveEnabledClientSideBuffers()) {
+ return true;
+ }
+ if (!IsDefaultVAOBound()) {
+ gl->SetGLError(
+ GL_INVALID_OPERATION, function_name,
+ "client side arrays not allowed with vertex array object");
+ return false;
+ }
+ *simulated = true;
+ GLsizei total_size = 0;
+ // Compute the size of the buffer we need.
+ const VertexArrayObject::VertexAttribs& vertex_attribs =
+ bound_vertex_array_object_->vertex_attribs();
+ for (GLuint ii = 0; ii < vertex_attribs.size(); ++ii) {
+ const VertexArrayObject::VertexAttrib& attrib = vertex_attribs[ii];
+ if (attrib.IsClientSide() && attrib.enabled()) {
+ size_t bytes_per_element =
+ GLES2Util::GetGLTypeSizeForTexturesAndBuffers(attrib.type()) *
+ attrib.size();
+ GLsizei elements = (primcount && attrib.divisor() > 0) ?
+ ((primcount - 1) / attrib.divisor() + 1) : num_elements;
+ total_size += RoundUpToMultipleOf4(bytes_per_element * elements);
+ }
+ }
+ gl_helper->BindBuffer(GL_ARRAY_BUFFER, array_buffer_id_);
+ array_buffer_offset_ = 0;
+ if (total_size > array_buffer_size_) {
+ gl->BufferDataHelper(GL_ARRAY_BUFFER, total_size, NULL, GL_DYNAMIC_DRAW);
+ array_buffer_size_ = total_size;
+ }
+ for (GLuint ii = 0; ii < vertex_attribs.size(); ++ii) {
+ const VertexArrayObject::VertexAttrib& attrib = vertex_attribs[ii];
+ if (attrib.IsClientSide() && attrib.enabled()) {
+ size_t bytes_per_element =
+ GLES2Util::GetGLTypeSizeForTexturesAndBuffers(attrib.type()) *
+ attrib.size();
+ GLsizei real_stride = attrib.stride() ?
+ attrib.stride() : static_cast<GLsizei>(bytes_per_element);
+ GLsizei elements = (primcount && attrib.divisor() > 0) ?
+ ((primcount - 1) / attrib.divisor() + 1) : num_elements;
+ GLsizei bytes_collected = CollectData(
+ attrib.pointer(), bytes_per_element, real_stride, elements);
+ gl->BufferSubDataHelper(
+ GL_ARRAY_BUFFER, array_buffer_offset_, bytes_collected,
+ collection_buffer_.get());
+ gl_helper->VertexAttribPointer(
+ ii, attrib.size(), attrib.type(), attrib.normalized(), 0,
+ array_buffer_offset_);
+ array_buffer_offset_ += RoundUpToMultipleOf4(bytes_collected);
+ GPU_DCHECK_LE(array_buffer_offset_, array_buffer_size_);
+ }
+ }
+#endif // defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
+ return true;
+}
+
+// Copies in indices to the service and returns the highest index accessed + 1
+bool VertexArrayObjectManager::SetupSimulatedIndexAndClientSideBuffers(
+ const char* function_name,
+ GLES2Implementation* gl,
+ GLES2CmdHelper* gl_helper,
+ GLsizei count,
+ GLenum type,
+ GLsizei primcount,
+ const void* indices,
+ GLuint* offset,
+ bool* simulated) {
+ *simulated = false;
+ *offset = ToGLuint(indices);
+#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
+ GLsizei num_elements = 0;
+ if (bound_vertex_array_object_->bound_element_array_buffer() == 0) {
+ *simulated = true;
+ *offset = 0;
+ GLsizei max_index = -1;
+ switch (type) {
+ case GL_UNSIGNED_BYTE: {
+ const uint8* src = static_cast<const uint8*>(indices);
+ for (GLsizei ii = 0; ii < count; ++ii) {
+ if (src[ii] > max_index) {
+ max_index = src[ii];
+ }
+ }
+ break;
+ }
+ case GL_UNSIGNED_SHORT: {
+ const uint16* src = static_cast<const uint16*>(indices);
+ for (GLsizei ii = 0; ii < count; ++ii) {
+ if (src[ii] > max_index) {
+ max_index = src[ii];
+ }
+ }
+ break;
+ }
+ case GL_UNSIGNED_INT: {
+ uint32 max_glsizei = static_cast<uint32>(
+ std::numeric_limits<GLsizei>::max());
+ const uint32* src = static_cast<const uint32*>(indices);
+ for (GLsizei ii = 0; ii < count; ++ii) {
+ // Other parts of the API use GLsizei (signed) to store limits.
+ // As such, if we encounter a index that cannot be represented with
+ // an unsigned int we need to flag it as an error here.
+ if(src[ii] > max_glsizei) {
+ gl->SetGLError(
+ GL_INVALID_OPERATION, function_name, "index too large.");
+ return false;
+ }
+ GLsizei signed_index = static_cast<GLsizei>(src[ii]);
+ if (signed_index > max_index) {
+ max_index = signed_index;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ gl_helper->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_array_buffer_id_);
+ GLsizei bytes_per_element =
+ GLES2Util::GetGLTypeSizeForTexturesAndBuffers(type);
+ GLsizei bytes_needed = bytes_per_element * count;
+ if (bytes_needed > element_array_buffer_size_) {
+ element_array_buffer_size_ = bytes_needed;
+ gl->BufferDataHelper(
+ GL_ELEMENT_ARRAY_BUFFER, bytes_needed, NULL, GL_DYNAMIC_DRAW);
+ }
+ gl->BufferSubDataHelper(
+ GL_ELEMENT_ARRAY_BUFFER, 0, bytes_needed, indices);
+
+ num_elements = max_index + 1;
+ } else if (bound_vertex_array_object_->HaveEnabledClientSideBuffers()) {
+ // Index buffer is GL buffer. Ask the service for the highest vertex
+ // that will be accessed. Note: It doesn't matter if another context
+ // changes the contents of any of the buffers. The service will still
+ // validate the indices. We just need to know how much to copy across.
+ num_elements = gl->GetMaxValueInBufferCHROMIUMHelper(
+ bound_vertex_array_object_->bound_element_array_buffer(),
+ count, type, ToGLuint(indices)) + 1;
+ }
+
+ bool simulated_client_side_buffers = false;
+ SetupSimulatedClientSideBuffers(
+ function_name, gl, gl_helper, num_elements, primcount,
+ &simulated_client_side_buffers);
+ *simulated = *simulated || simulated_client_side_buffers;
+#endif // defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
+ return true;
+}
+
+} // namespace gles2
+} // namespace gpu
+
+

Powered by Google App Engine
This is Rietveld 408576698