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
15namespace gpu {
16namespace gles2 {
17
18#if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
19
20static 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.
27static 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
43class 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
196VertexArrayObject::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
202void 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
220bool 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}
227bool VertexArrayObject::HaveEnabledClientSideBuffers() const {
228  return num_client_side_pointers_enabled_ > 0;
229}
230
231void 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
244void 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
267bool 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
300void 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.
309bool 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.
320const 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
329VertexArrayObjectManager::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
344VertexArrayObjectManager::~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
352bool VertexArrayObjectManager::IsReservedId(GLuint id) const {
353  return (id != 0 &&
354          (id == array_buffer_id_ || id == element_array_buffer_id_));
355}
356
357GLuint VertexArrayObjectManager::bound_element_array_buffer() const {
358  return bound_vertex_array_object_->bound_element_array_buffer();
359}
360
361void VertexArrayObjectManager::UnbindBuffer(GLuint id) {
362  bound_vertex_array_object_->UnbindBuffer(id);
363}
364
365bool VertexArrayObjectManager::BindElementArray(GLuint id) {
366  return  bound_vertex_array_object_->BindElementArray(id);
367}
368
369void 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
380void 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
398bool 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
413bool VertexArrayObjectManager::HaveEnabledClientSideBuffers() const {
414  return bound_vertex_array_object_->HaveEnabledClientSideBuffers();
415}
416
417void VertexArrayObjectManager::SetAttribEnable(GLuint index, bool enabled) {
418  bound_vertex_array_object_->SetAttribEnable(index, enabled);
419}
420
421bool VertexArrayObjectManager::GetVertexAttrib(
422    GLuint index, GLenum pname, uint32* param) {
423  return bound_vertex_array_object_->GetVertexAttrib(index, pname, param);
424}
425
426bool VertexArrayObjectManager::GetAttribPointer(
427    GLuint index, GLenum pname, void** ptr) const {
428  return bound_vertex_array_object_->GetAttribPointer(index, pname, ptr);
429}
430
431bool 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
448void 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.
454GLsizei 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
473bool VertexArrayObjectManager::IsDefaultVAOBound() const {
474  return bound_vertex_array_object_ == default_vertex_array_object_;
475}
476
477// Returns true if buffers were setup.
478bool 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
545bool 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