Home | History | Annotate | Download | only in client
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "gpu/command_buffer/client/vertex_array_object_manager.h"
      6 
      7 #include "base/logging.h"
      8 #include "gpu/command_buffer/client/gles2_cmd_helper.h"
      9 #include "gpu/command_buffer/client/gles2_implementation.h"
     10 
     11 #if defined(__native_client__) && !defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
     12 #define GLES2_SUPPORT_CLIENT_SIDE_ARRAYS
     13 #endif
     14 
     15 namespace gpu {
     16 namespace gles2 {
     17 
     18 #if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
     19 
     20 static GLsizei RoundUpToMultipleOf4(GLsizei size) {
     21   return (size + 3) & ~3;
     22 }
     23 
     24 #endif  // defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
     25 
     26 // A 32-bit and 64-bit compatible way of converting a pointer to a GLuint.
     27 static GLuint ToGLuint(const void* ptr) {
     28   return static_cast<GLuint>(reinterpret_cast<size_t>(ptr));
     29 }
     30 
     31 // This class tracks VertexAttribPointers and helps emulate client side buffers.
     32 //
     33 // The way client side buffers work is we shadow all the Vertex Attribs so we
     34 // know which ones are pointing to client side buffers.
     35 //
     36 // At Draw time, for any attribs pointing to client side buffers we copy them
     37 // to a special VBO and reset the actual vertex attrib pointers to point to this
     38 // VBO.
     39 //
     40 // This also means we have to catch calls to query those values so that when
     41 // an attrib is a client side buffer we pass the info back the user expects.
     42 
     43 class GLES2_IMPL_EXPORT VertexArrayObject {
     44  public:
     45   // Info about Vertex Attributes. This is used to track what the user currently
     46   // has bound on each Vertex Attribute so we can simulate client side buffers
     47   // at glDrawXXX time.
     48   class VertexAttrib {
     49    public:
     50     VertexAttrib()
     51         : enabled_(false),
     52           buffer_id_(0),
     53           size_(4),
     54           type_(GL_FLOAT),
     55           normalized_(GL_FALSE),
     56           pointer_(NULL),
     57           gl_stride_(0),
     58           divisor_(0) {
     59     }
     60 
     61     bool enabled() const {
     62       return enabled_;
     63     }
     64 
     65     void set_enabled(bool enabled) {
     66       enabled_ = enabled;
     67     }
     68 
     69     GLuint buffer_id() const {
     70       return buffer_id_;
     71     }
     72 
     73     void set_buffer_id(GLuint id) {
     74       buffer_id_ = id;
     75     }
     76 
     77     GLenum type() const {
     78       return type_;
     79     }
     80 
     81     GLint size() const {
     82       return size_;
     83     }
     84 
     85     GLsizei stride() const {
     86       return gl_stride_;
     87     }
     88 
     89     GLboolean normalized() const {
     90       return normalized_;
     91     }
     92 
     93     const GLvoid* pointer() const {
     94       return pointer_;
     95     }
     96 
     97     bool IsClientSide() const {
     98       return buffer_id_ == 0;
     99     }
    100 
    101     GLuint divisor() const {
    102       return divisor_;
    103     }
    104 
    105     void SetInfo(
    106         GLuint buffer_id,
    107         GLint size,
    108         GLenum type,
    109         GLboolean normalized,
    110         GLsizei gl_stride,
    111         const GLvoid* pointer) {
    112       buffer_id_ = buffer_id;
    113       size_ = size;
    114       type_ = type;
    115       normalized_ = normalized;
    116       gl_stride_ = gl_stride;
    117       pointer_ = pointer;
    118     }
    119 
    120     void SetDivisor(GLuint divisor) {
    121       divisor_ = divisor;
    122     }
    123 
    124    private:
    125     // Whether or not this attribute is enabled.
    126     bool enabled_;
    127 
    128     // The id of the buffer. 0 = client side buffer.
    129     GLuint buffer_id_;
    130 
    131     // Number of components (1, 2, 3, 4).
    132     GLint size_;
    133 
    134     // GL_BYTE, GL_FLOAT, etc. See glVertexAttribPointer.
    135     GLenum type_;
    136 
    137     // GL_TRUE or GL_FALSE
    138     GLboolean normalized_;
    139 
    140     // The pointer/offset into the buffer.
    141     const GLvoid* pointer_;
    142 
    143     // The stride that will be used to access the buffer. This is the bogus GL
    144     // stride where 0 = compute the stride based on size and type.
    145     GLsizei gl_stride_;
    146 
    147     // Divisor, for geometry instancing.
    148     GLuint divisor_;
    149   };
    150 
    151   typedef std::vector<VertexAttrib> VertexAttribs;
    152 
    153   explicit VertexArrayObject(GLuint max_vertex_attribs);
    154 
    155   void UnbindBuffer(GLuint id);
    156 
    157   bool BindElementArray(GLuint id);
    158 
    159   bool HaveEnabledClientSideBuffers() const;
    160 
    161   void SetAttribEnable(GLuint index, bool enabled);
    162 
    163   void SetAttribPointer(
    164     GLuint buffer_id,
    165     GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride,
    166     const void* ptr);
    167 
    168   bool GetVertexAttrib(
    169       GLuint index, GLenum pname, uint32* param) const;
    170 
    171   void SetAttribDivisor(GLuint index, GLuint divisor);
    172 
    173   bool GetAttribPointer(GLuint index, GLenum pname, void** ptr) const;
    174 
    175   const VertexAttribs& vertex_attribs() const {
    176     return vertex_attribs_;
    177   }
    178 
    179   GLuint bound_element_array_buffer() const {
    180     return bound_element_array_buffer_id_;
    181   }
    182 
    183  private:
    184   const VertexAttrib* GetAttrib(GLuint index) const;
    185 
    186   int num_client_side_pointers_enabled_;
    187 
    188   // The currently bound element array buffer.
    189   GLuint bound_element_array_buffer_id_;
    190 
    191   VertexAttribs vertex_attribs_;
    192 
    193   DISALLOW_COPY_AND_ASSIGN(VertexArrayObject);
    194 };
    195 
    196 VertexArrayObject::VertexArrayObject(GLuint max_vertex_attribs)
    197     : num_client_side_pointers_enabled_(0),
    198       bound_element_array_buffer_id_(0) {
    199   vertex_attribs_.resize(max_vertex_attribs);
    200 }
    201 
    202 void VertexArrayObject::UnbindBuffer(GLuint id) {
    203   if (id == 0) {
    204     return;
    205   }
    206   for (size_t ii = 0; ii < vertex_attribs_.size(); ++ii) {
    207     VertexAttrib& attrib = vertex_attribs_[ii];
    208     if (attrib.buffer_id() == id) {
    209       attrib.set_buffer_id(0);
    210       if (attrib.enabled()) {
    211         ++num_client_side_pointers_enabled_;
    212       }
    213     }
    214   }
    215   if (bound_element_array_buffer_id_ == id) {
    216     bound_element_array_buffer_id_ = 0;
    217   }
    218 }
    219 
    220 bool VertexArrayObject::BindElementArray(GLuint id) {
    221   if (id == bound_element_array_buffer_id_) {
    222     return false;
    223   }
    224   bound_element_array_buffer_id_ = id;
    225   return true;
    226 }
    227 bool VertexArrayObject::HaveEnabledClientSideBuffers() const {
    228   return num_client_side_pointers_enabled_ > 0;
    229 }
    230 
    231 void VertexArrayObject::SetAttribEnable(GLuint index, bool enabled) {
    232   if (index < vertex_attribs_.size()) {
    233     VertexAttrib& attrib = vertex_attribs_[index];
    234     if (attrib.enabled() != enabled) {
    235       if (attrib.IsClientSide()) {
    236         num_client_side_pointers_enabled_ += enabled ? 1 : -1;
    237         DCHECK_GE(num_client_side_pointers_enabled_, 0);
    238       }
    239       attrib.set_enabled(enabled);
    240     }
    241   }
    242 }
    243 
    244 void VertexArrayObject::SetAttribPointer(
    245     GLuint buffer_id,
    246     GLuint index,
    247     GLint size,
    248     GLenum type,
    249     GLboolean normalized,
    250     GLsizei stride,
    251     const void* ptr) {
    252   if (index < vertex_attribs_.size()) {
    253     VertexAttrib& attrib = vertex_attribs_[index];
    254     if (attrib.IsClientSide() && attrib.enabled()) {
    255       --num_client_side_pointers_enabled_;
    256       DCHECK_GE(num_client_side_pointers_enabled_, 0);
    257     }
    258 
    259     attrib.SetInfo(buffer_id, size, type, normalized, stride, ptr);
    260 
    261     if (attrib.IsClientSide() && attrib.enabled()) {
    262       ++num_client_side_pointers_enabled_;
    263     }
    264   }
    265 }
    266 
    267 bool VertexArrayObject::GetVertexAttrib(
    268     GLuint index, GLenum pname, uint32* param) const {
    269   const VertexAttrib* attrib = GetAttrib(index);
    270   if (!attrib) {
    271     return false;
    272   }
    273 
    274   switch (pname) {
    275     case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
    276       *param = attrib->buffer_id();
    277       break;
    278     case GL_VERTEX_ATTRIB_ARRAY_ENABLED:
    279       *param = attrib->enabled();
    280       break;
    281     case GL_VERTEX_ATTRIB_ARRAY_SIZE:
    282       *param = attrib->size();
    283       break;
    284     case GL_VERTEX_ATTRIB_ARRAY_STRIDE:
    285       *param = attrib->stride();
    286       break;
    287     case GL_VERTEX_ATTRIB_ARRAY_TYPE:
    288       *param = attrib->type();
    289       break;
    290     case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED:
    291       *param = attrib->normalized();
    292       break;
    293     default:
    294       return false;  // pass through to service side.
    295       break;
    296   }
    297   return true;
    298 }
    299 
    300 void VertexArrayObject::SetAttribDivisor(GLuint index, GLuint divisor) {
    301   if (index < vertex_attribs_.size()) {
    302     VertexAttrib& attrib = vertex_attribs_[index];
    303     attrib.SetDivisor(divisor);
    304   }
    305 }
    306 
    307 // Gets the Attrib pointer for an attrib but only if it's a client side
    308 // pointer. Returns true if it got the pointer.
    309 bool VertexArrayObject::GetAttribPointer(
    310     GLuint index, GLenum pname, void** ptr) const {
    311   const VertexAttrib* attrib = GetAttrib(index);
    312   if (attrib && pname == GL_VERTEX_ATTRIB_ARRAY_POINTER) {
    313     *ptr = const_cast<void*>(attrib->pointer());
    314     return true;
    315   }
    316   return false;
    317 }
    318 
    319 // Gets an attrib if it's in range and it's client side.
    320 const VertexArrayObject::VertexAttrib* VertexArrayObject::GetAttrib(
    321     GLuint index) const {
    322   if (index < vertex_attribs_.size()) {
    323     const VertexAttrib* attrib = &vertex_attribs_[index];
    324     return attrib;
    325   }
    326   return NULL;
    327 }
    328 
    329 VertexArrayObjectManager::VertexArrayObjectManager(
    330     GLuint max_vertex_attribs,
    331     GLuint array_buffer_id,
    332     GLuint element_array_buffer_id)
    333     : max_vertex_attribs_(max_vertex_attribs),
    334       array_buffer_id_(array_buffer_id),
    335       array_buffer_size_(0),
    336       array_buffer_offset_(0),
    337       element_array_buffer_id_(element_array_buffer_id),
    338       element_array_buffer_size_(0),
    339       collection_buffer_size_(0),
    340       default_vertex_array_object_(new VertexArrayObject(max_vertex_attribs)),
    341       bound_vertex_array_object_(default_vertex_array_object_) {
    342 }
    343 
    344 VertexArrayObjectManager::~VertexArrayObjectManager() {
    345   for (VertexArrayObjectMap::iterator it = vertex_array_objects_.begin();
    346        it != vertex_array_objects_.end(); ++it) {
    347     delete it->second;
    348   }
    349   delete default_vertex_array_object_;
    350 }
    351 
    352 bool VertexArrayObjectManager::IsReservedId(GLuint id) const {
    353   return (id != 0 &&
    354           (id == array_buffer_id_ || id == element_array_buffer_id_));
    355 }
    356 
    357 GLuint VertexArrayObjectManager::bound_element_array_buffer() const {
    358   return bound_vertex_array_object_->bound_element_array_buffer();
    359 }
    360 
    361 void VertexArrayObjectManager::UnbindBuffer(GLuint id) {
    362   bound_vertex_array_object_->UnbindBuffer(id);
    363 }
    364 
    365 bool VertexArrayObjectManager::BindElementArray(GLuint id) {
    366   return  bound_vertex_array_object_->BindElementArray(id);
    367 }
    368 
    369 void VertexArrayObjectManager::GenVertexArrays(
    370     GLsizei n, const GLuint* arrays) {
    371   DCHECK_GE(n, 0);
    372   for (GLsizei i = 0; i < n; ++i) {
    373     std::pair<VertexArrayObjectMap::iterator, bool> result =
    374         vertex_array_objects_.insert(std::make_pair(
    375             arrays[i], new VertexArrayObject(max_vertex_attribs_)));
    376     DCHECK(result.second);
    377   }
    378 }
    379 
    380 void VertexArrayObjectManager::DeleteVertexArrays(
    381     GLsizei n, const GLuint* arrays) {
    382   DCHECK_GE(n, 0);
    383   for (GLsizei i = 0; i < n; ++i) {
    384     GLuint id = arrays[i];
    385     if (id) {
    386       VertexArrayObjectMap::iterator it = vertex_array_objects_.find(id);
    387       if (it != vertex_array_objects_.end()) {
    388         if (bound_vertex_array_object_ == it->second) {
    389           bound_vertex_array_object_ = default_vertex_array_object_;
    390         }
    391         delete it->second;
    392         vertex_array_objects_.erase(it);
    393       }
    394     }
    395   }
    396 }
    397 
    398 bool VertexArrayObjectManager::BindVertexArray(GLuint array, bool* changed) {
    399   *changed = false;
    400   VertexArrayObject* vertex_array_object = default_vertex_array_object_;
    401   if (array != 0) {
    402     VertexArrayObjectMap::iterator it = vertex_array_objects_.find(array);
    403     if (it == vertex_array_objects_.end()) {
    404       return false;
    405     }
    406     vertex_array_object = it->second;
    407   }
    408   *changed = vertex_array_object != bound_vertex_array_object_;
    409   bound_vertex_array_object_ = vertex_array_object;
    410   return true;
    411 }
    412 
    413 bool VertexArrayObjectManager::HaveEnabledClientSideBuffers() const {
    414   return bound_vertex_array_object_->HaveEnabledClientSideBuffers();
    415 }
    416 
    417 void VertexArrayObjectManager::SetAttribEnable(GLuint index, bool enabled) {
    418   bound_vertex_array_object_->SetAttribEnable(index, enabled);
    419 }
    420 
    421 bool VertexArrayObjectManager::GetVertexAttrib(
    422     GLuint index, GLenum pname, uint32* param) {
    423   return bound_vertex_array_object_->GetVertexAttrib(index, pname, param);
    424 }
    425 
    426 bool VertexArrayObjectManager::GetAttribPointer(
    427     GLuint index, GLenum pname, void** ptr) const {
    428   return bound_vertex_array_object_->GetAttribPointer(index, pname, ptr);
    429 }
    430 
    431 bool VertexArrayObjectManager::SetAttribPointer(
    432     GLuint buffer_id,
    433     GLuint index,
    434     GLint size,
    435     GLenum type,
    436     GLboolean normalized,
    437     GLsizei stride,
    438     const void* ptr) {
    439   // Client side arrays are not allowed in vaos.
    440   if (buffer_id == 0 && !IsDefaultVAOBound()) {
    441     return false;
    442   }
    443   bound_vertex_array_object_->SetAttribPointer(
    444       buffer_id, index, size, type, normalized, stride, ptr);
    445   return true;
    446 }
    447 
    448 void VertexArrayObjectManager::SetAttribDivisor(GLuint index, GLuint divisor) {
    449   bound_vertex_array_object_->SetAttribDivisor(index, divisor);
    450 }
    451 
    452 // Collects the data into the collection buffer and returns the number of
    453 // bytes collected.
    454 GLsizei VertexArrayObjectManager::CollectData(
    455     const void* data,
    456     GLsizei bytes_per_element,
    457     GLsizei real_stride,
    458     GLsizei num_elements) {
    459   GLsizei bytes_needed = bytes_per_element * num_elements;
    460   if (collection_buffer_size_ < bytes_needed) {
    461     collection_buffer_.reset(new int8[bytes_needed]);
    462     collection_buffer_size_ = bytes_needed;
    463   }
    464   const int8* src = static_cast<const int8*>(data);
    465   int8* dst = collection_buffer_.get();
    466   int8* end = dst + bytes_per_element * num_elements;
    467   for (; dst < end; src += real_stride, dst += bytes_per_element) {
    468     memcpy(dst, src, bytes_per_element);
    469   }
    470   return bytes_needed;
    471 }
    472 
    473 bool VertexArrayObjectManager::IsDefaultVAOBound() const {
    474   return bound_vertex_array_object_ == default_vertex_array_object_;
    475 }
    476 
    477 // Returns true if buffers were setup.
    478 bool VertexArrayObjectManager::SetupSimulatedClientSideBuffers(
    479     const char* function_name,
    480     GLES2Implementation* gl,
    481     GLES2CmdHelper* gl_helper,
    482     GLsizei num_elements,
    483     GLsizei primcount,
    484     bool* simulated) {
    485   *simulated = false;
    486 #if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
    487   if (!bound_vertex_array_object_->HaveEnabledClientSideBuffers()) {
    488     return true;
    489   }
    490   if (!IsDefaultVAOBound()) {
    491     gl->SetGLError(
    492         GL_INVALID_OPERATION, function_name,
    493         "client side arrays not allowed with vertex array object");
    494     return false;
    495   }
    496   *simulated = true;
    497   GLsizei total_size = 0;
    498   // Compute the size of the buffer we need.
    499   const VertexArrayObject::VertexAttribs& vertex_attribs =
    500       bound_vertex_array_object_->vertex_attribs();
    501   for (GLuint ii = 0; ii < vertex_attribs.size(); ++ii) {
    502     const VertexArrayObject::VertexAttrib& attrib = vertex_attribs[ii];
    503     if (attrib.IsClientSide() && attrib.enabled()) {
    504       size_t bytes_per_element =
    505           GLES2Util::GetGLTypeSizeForTexturesAndBuffers(attrib.type()) *
    506           attrib.size();
    507       GLsizei elements = (primcount && attrib.divisor() > 0) ?
    508           ((primcount - 1) / attrib.divisor() + 1) : num_elements;
    509       total_size += RoundUpToMultipleOf4(bytes_per_element * elements);
    510     }
    511   }
    512   gl_helper->BindBuffer(GL_ARRAY_BUFFER, array_buffer_id_);
    513   array_buffer_offset_ = 0;
    514   if (total_size > array_buffer_size_) {
    515     gl->BufferDataHelper(GL_ARRAY_BUFFER, total_size, NULL, GL_DYNAMIC_DRAW);
    516     array_buffer_size_ = total_size;
    517   }
    518   for (GLuint ii = 0; ii < vertex_attribs.size(); ++ii) {
    519     const VertexArrayObject::VertexAttrib& attrib = vertex_attribs[ii];
    520     if (attrib.IsClientSide() && attrib.enabled()) {
    521       size_t bytes_per_element =
    522           GLES2Util::GetGLTypeSizeForTexturesAndBuffers(attrib.type()) *
    523           attrib.size();
    524       GLsizei real_stride = attrib.stride() ?
    525           attrib.stride() : static_cast<GLsizei>(bytes_per_element);
    526       GLsizei elements = (primcount && attrib.divisor() > 0) ?
    527           ((primcount - 1) / attrib.divisor() + 1) : num_elements;
    528       GLsizei bytes_collected = CollectData(
    529           attrib.pointer(), bytes_per_element, real_stride, elements);
    530       gl->BufferSubDataHelper(
    531           GL_ARRAY_BUFFER, array_buffer_offset_, bytes_collected,
    532           collection_buffer_.get());
    533       gl_helper->VertexAttribPointer(
    534           ii, attrib.size(), attrib.type(), attrib.normalized(), 0,
    535           array_buffer_offset_);
    536       array_buffer_offset_ += RoundUpToMultipleOf4(bytes_collected);
    537       DCHECK_LE(array_buffer_offset_, array_buffer_size_);
    538     }
    539   }
    540 #endif  // defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
    541   return true;
    542 }
    543 
    544 // Copies in indices to the service and returns the highest index accessed + 1
    545 bool VertexArrayObjectManager::SetupSimulatedIndexAndClientSideBuffers(
    546     const char* function_name,
    547     GLES2Implementation* gl,
    548     GLES2CmdHelper* gl_helper,
    549     GLsizei count,
    550     GLenum type,
    551     GLsizei primcount,
    552     const void* indices,
    553     GLuint* offset,
    554     bool* simulated) {
    555   *simulated = false;
    556   *offset = ToGLuint(indices);
    557 #if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
    558   GLsizei num_elements = 0;
    559   if (bound_vertex_array_object_->bound_element_array_buffer() == 0) {
    560     *simulated = true;
    561     *offset = 0;
    562     GLsizei max_index = -1;
    563     switch (type) {
    564       case GL_UNSIGNED_BYTE: {
    565         const uint8* src = static_cast<const uint8*>(indices);
    566         for (GLsizei ii = 0; ii < count; ++ii) {
    567           if (src[ii] > max_index) {
    568             max_index = src[ii];
    569           }
    570         }
    571         break;
    572       }
    573       case GL_UNSIGNED_SHORT: {
    574         const uint16* src = static_cast<const uint16*>(indices);
    575         for (GLsizei ii = 0; ii < count; ++ii) {
    576           if (src[ii] > max_index) {
    577             max_index = src[ii];
    578           }
    579         }
    580         break;
    581       }
    582       case GL_UNSIGNED_INT: {
    583         uint32 max_glsizei = static_cast<uint32>(
    584             std::numeric_limits<GLsizei>::max());
    585         const uint32* src = static_cast<const uint32*>(indices);
    586         for (GLsizei ii = 0; ii < count; ++ii) {
    587           // Other parts of the API use GLsizei (signed) to store limits.
    588           // As such, if we encounter a index that cannot be represented with
    589           // an unsigned int we need to flag it as an error here.
    590           if(src[ii] > max_glsizei) {
    591             gl->SetGLError(
    592                 GL_INVALID_OPERATION, function_name, "index too large.");
    593             return false;
    594           }
    595           GLsizei signed_index = static_cast<GLsizei>(src[ii]);
    596           if (signed_index > max_index) {
    597             max_index = signed_index;
    598           }
    599         }
    600         break;
    601       }
    602       default:
    603         break;
    604     }
    605     gl_helper->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_array_buffer_id_);
    606     GLsizei bytes_per_element =
    607         GLES2Util::GetGLTypeSizeForTexturesAndBuffers(type);
    608     GLsizei bytes_needed = bytes_per_element * count;
    609     if (bytes_needed > element_array_buffer_size_) {
    610       element_array_buffer_size_ = bytes_needed;
    611       gl->BufferDataHelper(
    612           GL_ELEMENT_ARRAY_BUFFER, bytes_needed, NULL, GL_DYNAMIC_DRAW);
    613     }
    614     gl->BufferSubDataHelper(
    615         GL_ELEMENT_ARRAY_BUFFER, 0, bytes_needed, indices);
    616 
    617     num_elements = max_index + 1;
    618   } else if (bound_vertex_array_object_->HaveEnabledClientSideBuffers()) {
    619     // Index buffer is GL buffer. Ask the service for the highest vertex
    620     // that will be accessed. Note: It doesn't matter if another context
    621     // changes the contents of any of the buffers. The service will still
    622     // validate the indices. We just need to know how much to copy across.
    623     num_elements = gl->GetMaxValueInBufferCHROMIUMHelper(
    624         bound_vertex_array_object_->bound_element_array_buffer(),
    625         count, type, ToGLuint(indices)) + 1;
    626   }
    627 
    628   bool simulated_client_side_buffers = false;
    629   SetupSimulatedClientSideBuffers(
    630       function_name, gl, gl_helper, num_elements, primcount,
    631       &simulated_client_side_buffers);
    632   *simulated = *simulated || simulated_client_side_buffers;
    633 #endif  // defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
    634   return true;
    635 }
    636 
    637 }  // namespace gles2
    638 }  // namespace gpu
    639 
    640 
    641