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.
5 #include "gpu/command_buffer/client/vertex_array_object_manager.h"
7 #include "base/logging.h"
8 #include "gpu/command_buffer/client/gles2_cmd_helper.h"
9 #include "gpu/command_buffer/client/gles2_implementation.h"
11 #if defined(__native_client__) && !defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
12 #define GLES2_SUPPORT_CLIENT_SIDE_ARRAYS
18 #if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
20 static GLsizei
RoundUpToMultipleOf4(GLsizei size
) {
21 return (size
+ 3) & ~3;
24 #endif // defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
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
));
31 // This class tracks VertexAttribPointers and helps emulate client side buffers.
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.
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
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.
43 class GLES2_IMPL_EXPORT VertexArrayObject
{
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
55 normalized_(GL_FALSE
),
61 bool enabled() const {
65 void set_enabled(bool enabled
) {
69 GLuint
buffer_id() const {
73 void set_buffer_id(GLuint id
) {
85 GLsizei
stride() const {
89 GLboolean
normalized() const {
93 const GLvoid
* pointer() const {
97 bool IsClientSide() const {
98 return buffer_id_
== 0;
101 GLuint
divisor() const {
109 GLboolean normalized
,
111 const GLvoid
* pointer
) {
112 buffer_id_
= buffer_id
;
115 normalized_
= normalized
;
116 gl_stride_
= gl_stride
;
120 void SetDivisor(GLuint divisor
) {
125 // Whether or not this attribute is enabled.
128 // The id of the buffer. 0 = client side buffer.
131 // Number of components (1, 2, 3, 4).
134 // GL_BYTE, GL_FLOAT, etc. See glVertexAttribPointer.
137 // GL_TRUE or GL_FALSE
138 GLboolean normalized_
;
140 // The pointer/offset into the buffer.
141 const GLvoid
* pointer_
;
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.
147 // Divisor, for geometry instancing.
151 typedef std::vector
<VertexAttrib
> VertexAttribs
;
153 explicit VertexArrayObject(GLuint max_vertex_attribs
);
155 void UnbindBuffer(GLuint id
);
157 bool BindElementArray(GLuint id
);
159 bool HaveEnabledClientSideBuffers() const;
161 void SetAttribEnable(GLuint index
, bool enabled
);
163 void SetAttribPointer(
165 GLuint index
, GLint size
, GLenum type
, GLboolean normalized
, GLsizei stride
,
168 bool GetVertexAttrib(
169 GLuint index
, GLenum pname
, uint32
* param
) const;
171 void SetAttribDivisor(GLuint index
, GLuint divisor
);
173 bool GetAttribPointer(GLuint index
, GLenum pname
, void** ptr
) const;
175 const VertexAttribs
& vertex_attribs() const {
176 return vertex_attribs_
;
179 GLuint
bound_element_array_buffer() const {
180 return bound_element_array_buffer_id_
;
184 const VertexAttrib
* GetAttrib(GLuint index
) const;
186 int num_client_side_pointers_enabled_
;
188 // The currently bound element array buffer.
189 GLuint bound_element_array_buffer_id_
;
191 VertexAttribs vertex_attribs_
;
193 DISALLOW_COPY_AND_ASSIGN(VertexArrayObject
);
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
);
202 void VertexArrayObject::UnbindBuffer(GLuint id
) {
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_
;
215 if (bound_element_array_buffer_id_
== id
) {
216 bound_element_array_buffer_id_
= 0;
220 bool VertexArrayObject::BindElementArray(GLuint id
) {
221 if (id
== bound_element_array_buffer_id_
) {
224 bound_element_array_buffer_id_
= id
;
227 bool VertexArrayObject::HaveEnabledClientSideBuffers() const {
228 return num_client_side_pointers_enabled_
> 0;
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);
239 attrib
.set_enabled(enabled
);
244 void VertexArrayObject::SetAttribPointer(
249 GLboolean normalized
,
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);
259 attrib
.SetInfo(buffer_id
, size
, type
, normalized
, stride
, ptr
);
261 if (attrib
.IsClientSide() && attrib
.enabled()) {
262 ++num_client_side_pointers_enabled_
;
267 bool VertexArrayObject::GetVertexAttrib(
268 GLuint index
, GLenum pname
, uint32
* param
) const {
269 const VertexAttrib
* attrib
= GetAttrib(index
);
275 case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING
:
276 *param
= attrib
->buffer_id();
278 case GL_VERTEX_ATTRIB_ARRAY_ENABLED
:
279 *param
= attrib
->enabled();
281 case GL_VERTEX_ATTRIB_ARRAY_SIZE
:
282 *param
= attrib
->size();
284 case GL_VERTEX_ATTRIB_ARRAY_STRIDE
:
285 *param
= attrib
->stride();
287 case GL_VERTEX_ATTRIB_ARRAY_TYPE
:
288 *param
= attrib
->type();
290 case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED
:
291 *param
= attrib
->normalized();
294 return false; // pass through to service side.
300 void VertexArrayObject::SetAttribDivisor(GLuint index
, GLuint divisor
) {
301 if (index
< vertex_attribs_
.size()) {
302 VertexAttrib
& attrib
= vertex_attribs_
[index
];
303 attrib
.SetDivisor(divisor
);
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());
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
];
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_
) {
344 VertexArrayObjectManager::~VertexArrayObjectManager() {
345 for (VertexArrayObjectMap::iterator it
= vertex_array_objects_
.begin();
346 it
!= vertex_array_objects_
.end(); ++it
) {
349 delete default_vertex_array_object_
;
352 bool VertexArrayObjectManager::IsReservedId(GLuint id
) const {
354 (id
== array_buffer_id_
|| id
== element_array_buffer_id_
));
357 GLuint
VertexArrayObjectManager::bound_element_array_buffer() const {
358 return bound_vertex_array_object_
->bound_element_array_buffer();
361 void VertexArrayObjectManager::UnbindBuffer(GLuint id
) {
362 bound_vertex_array_object_
->UnbindBuffer(id
);
365 bool VertexArrayObjectManager::BindElementArray(GLuint id
) {
366 return bound_vertex_array_object_
->BindElementArray(id
);
369 void VertexArrayObjectManager::GenVertexArrays(
370 GLsizei n
, const GLuint
* arrays
) {
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
);
380 void VertexArrayObjectManager::DeleteVertexArrays(
381 GLsizei n
, const GLuint
* arrays
) {
383 for (GLsizei i
= 0; i
< n
; ++i
) {
384 GLuint id
= arrays
[i
];
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_
;
392 vertex_array_objects_
.erase(it
);
398 bool VertexArrayObjectManager::BindVertexArray(GLuint array
, bool* changed
) {
400 VertexArrayObject
* vertex_array_object
= default_vertex_array_object_
;
402 VertexArrayObjectMap::iterator it
= vertex_array_objects_
.find(array
);
403 if (it
== vertex_array_objects_
.end()) {
406 vertex_array_object
= it
->second
;
408 *changed
= vertex_array_object
!= bound_vertex_array_object_
;
409 bound_vertex_array_object_
= vertex_array_object
;
413 bool VertexArrayObjectManager::HaveEnabledClientSideBuffers() const {
414 return bound_vertex_array_object_
->HaveEnabledClientSideBuffers();
417 void VertexArrayObjectManager::SetAttribEnable(GLuint index
, bool enabled
) {
418 bound_vertex_array_object_
->SetAttribEnable(index
, enabled
);
421 bool VertexArrayObjectManager::GetVertexAttrib(
422 GLuint index
, GLenum pname
, uint32
* param
) {
423 return bound_vertex_array_object_
->GetVertexAttrib(index
, pname
, param
);
426 bool VertexArrayObjectManager::GetAttribPointer(
427 GLuint index
, GLenum pname
, void** ptr
) const {
428 return bound_vertex_array_object_
->GetAttribPointer(index
, pname
, ptr
);
431 bool VertexArrayObjectManager::SetAttribPointer(
436 GLboolean normalized
,
439 // Client side arrays are not allowed in vaos.
440 if (buffer_id
== 0 && !IsDefaultVAOBound()) {
443 bound_vertex_array_object_
->SetAttribPointer(
444 buffer_id
, index
, size
, type
, normalized
, stride
, ptr
);
448 void VertexArrayObjectManager::SetAttribDivisor(GLuint index
, GLuint divisor
) {
449 bound_vertex_array_object_
->SetAttribDivisor(index
, divisor
);
452 // Collects the data into the collection buffer and returns the number of
454 GLsizei
VertexArrayObjectManager::CollectData(
456 GLsizei bytes_per_element
,
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
;
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
);
473 bool VertexArrayObjectManager::IsDefaultVAOBound() const {
474 return bound_vertex_array_object_
== default_vertex_array_object_
;
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
,
486 #if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
487 if (!bound_vertex_array_object_
->HaveEnabledClientSideBuffers()) {
490 if (!IsDefaultVAOBound()) {
492 GL_INVALID_OPERATION
, function_name
,
493 "client side arrays not allowed with vertex array object");
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()) *
507 GLsizei elements
= (primcount
&& attrib
.divisor() > 0) ?
508 ((primcount
- 1) / attrib
.divisor() + 1) : num_elements
;
509 total_size
+= RoundUpToMultipleOf4(bytes_per_element
* elements
);
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
;
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()) *
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_
);
540 #endif // defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
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
,
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) {
562 GLsizei max_index
= -1;
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
) {
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
) {
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
) {
592 GL_INVALID_OPERATION
, function_name
, "index too large.");
595 GLsizei signed_index
= static_cast<GLsizei
>(src
[ii
]);
596 if (signed_index
> max_index
) {
597 max_index
= signed_index
;
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
);
614 gl
->BufferSubDataHelper(
615 GL_ELEMENT_ARRAY_BUFFER
, 0, bytes_needed
, indices
);
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;
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)