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 |
+ |
+ |