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"
14 static GLsizei
RoundUpToMultipleOf4(GLsizei size
) {
15 return (size
+ 3) & ~3;
18 // A 32-bit and 64-bit compatible way of converting a pointer to a GLuint.
19 static GLuint
ToGLuint(const void* ptr
) {
20 return static_cast<GLuint
>(reinterpret_cast<size_t>(ptr
));
23 // This class tracks VertexAttribPointers and helps emulate client side buffers.
25 // The way client side buffers work is we shadow all the Vertex Attribs so we
26 // know which ones are pointing to client side buffers.
28 // At Draw time, for any attribs pointing to client side buffers we copy them
29 // to a special VBO and reset the actual vertex attrib pointers to point to this
32 // This also means we have to catch calls to query those values so that when
33 // an attrib is a client side buffer we pass the info back the user expects.
35 class GLES2_IMPL_EXPORT VertexArrayObject
{
37 // Info about Vertex Attributes. This is used to track what the user currently
38 // has bound on each Vertex Attribute so we can simulate client side buffers
47 normalized_(GL_FALSE
),
54 bool enabled() const {
58 void set_enabled(bool enabled
) {
62 GLuint
buffer_id() const {
66 void set_buffer_id(GLuint id
) {
78 GLsizei
stride() const {
82 GLboolean
normalized() const {
86 const GLvoid
* pointer() const {
90 bool IsClientSide() const {
91 return buffer_id_
== 0;
94 GLuint
divisor() const {
98 GLboolean
integer() const {
106 GLboolean normalized
,
108 const GLvoid
* pointer
,
110 buffer_id_
= buffer_id
;
113 normalized_
= normalized
;
114 gl_stride_
= gl_stride
;
119 void SetDivisor(GLuint divisor
) {
124 // Whether or not this attribute is enabled.
127 // The id of the buffer. 0 = client side buffer.
130 // Number of components (1, 2, 3, 4).
133 // GL_BYTE, GL_FLOAT, etc. See glVertexAttribPointer.
136 // GL_TRUE or GL_FALSE
137 GLboolean normalized_
;
139 // The pointer/offset into the buffer.
140 const GLvoid
* pointer_
;
142 // The stride that will be used to access the buffer. This is the bogus GL
143 // stride where 0 = compute the stride based on size and type.
146 // Divisor, for geometry instancing.
152 typedef std::vector
<VertexAttrib
> VertexAttribs
;
154 explicit VertexArrayObject(GLuint max_vertex_attribs
);
156 void UnbindBuffer(GLuint id
);
158 bool BindElementArray(GLuint id
);
160 bool HaveEnabledClientSideBuffers() const;
162 void SetAttribEnable(GLuint index
, bool enabled
);
164 void SetAttribPointer(
166 GLuint index
, GLint size
, GLenum type
, GLboolean normalized
, GLsizei stride
,
167 const void* ptr
, GLboolean integer
);
169 bool GetVertexAttrib(
170 GLuint index
, GLenum pname
, uint32
* param
) const;
172 void SetAttribDivisor(GLuint index
, GLuint divisor
);
174 bool GetAttribPointer(GLuint index
, GLenum pname
, void** ptr
) const;
176 const VertexAttribs
& vertex_attribs() const {
177 return vertex_attribs_
;
180 GLuint
bound_element_array_buffer() const {
181 return bound_element_array_buffer_id_
;
185 const VertexAttrib
* GetAttrib(GLuint index
) const;
187 int num_client_side_pointers_enabled_
;
189 // The currently bound element array buffer.
190 GLuint bound_element_array_buffer_id_
;
192 VertexAttribs vertex_attribs_
;
194 DISALLOW_COPY_AND_ASSIGN(VertexArrayObject
);
197 VertexArrayObject::VertexArrayObject(GLuint max_vertex_attribs
)
198 : num_client_side_pointers_enabled_(0),
199 bound_element_array_buffer_id_(0) {
200 vertex_attribs_
.resize(max_vertex_attribs
);
203 void VertexArrayObject::UnbindBuffer(GLuint id
) {
207 for (size_t ii
= 0; ii
< vertex_attribs_
.size(); ++ii
) {
208 VertexAttrib
& attrib
= vertex_attribs_
[ii
];
209 if (attrib
.buffer_id() == id
) {
210 attrib
.set_buffer_id(0);
211 if (attrib
.enabled()) {
212 ++num_client_side_pointers_enabled_
;
216 if (bound_element_array_buffer_id_
== id
) {
217 bound_element_array_buffer_id_
= 0;
221 bool VertexArrayObject::BindElementArray(GLuint id
) {
222 if (id
== bound_element_array_buffer_id_
) {
225 bound_element_array_buffer_id_
= id
;
228 bool VertexArrayObject::HaveEnabledClientSideBuffers() const {
229 return num_client_side_pointers_enabled_
> 0;
232 void VertexArrayObject::SetAttribEnable(GLuint index
, bool enabled
) {
233 if (index
< vertex_attribs_
.size()) {
234 VertexAttrib
& attrib
= vertex_attribs_
[index
];
235 if (attrib
.enabled() != enabled
) {
236 if (attrib
.IsClientSide()) {
237 num_client_side_pointers_enabled_
+= enabled
? 1 : -1;
238 DCHECK_GE(num_client_side_pointers_enabled_
, 0);
240 attrib
.set_enabled(enabled
);
245 void VertexArrayObject::SetAttribPointer(
250 GLboolean normalized
,
254 if (index
< vertex_attribs_
.size()) {
255 VertexAttrib
& attrib
= vertex_attribs_
[index
];
256 if (attrib
.IsClientSide() && attrib
.enabled()) {
257 --num_client_side_pointers_enabled_
;
258 DCHECK_GE(num_client_side_pointers_enabled_
, 0);
261 attrib
.SetInfo(buffer_id
, size
, type
, normalized
, stride
, ptr
, integer
);
263 if (attrib
.IsClientSide() && attrib
.enabled()) {
264 ++num_client_side_pointers_enabled_
;
269 bool VertexArrayObject::GetVertexAttrib(
270 GLuint index
, GLenum pname
, uint32
* param
) const {
271 const VertexAttrib
* attrib
= GetAttrib(index
);
277 case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING
:
278 *param
= attrib
->buffer_id();
280 case GL_VERTEX_ATTRIB_ARRAY_ENABLED
:
281 *param
= attrib
->enabled();
283 case GL_VERTEX_ATTRIB_ARRAY_SIZE
:
284 *param
= attrib
->size();
286 case GL_VERTEX_ATTRIB_ARRAY_STRIDE
:
287 *param
= attrib
->stride();
289 case GL_VERTEX_ATTRIB_ARRAY_TYPE
:
290 *param
= attrib
->type();
292 case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED
:
293 *param
= attrib
->normalized();
295 case GL_VERTEX_ATTRIB_ARRAY_INTEGER
:
296 *param
= attrib
->integer();
299 return false; // pass through to service side.
304 void VertexArrayObject::SetAttribDivisor(GLuint index
, GLuint divisor
) {
305 if (index
< vertex_attribs_
.size()) {
306 VertexAttrib
& attrib
= vertex_attribs_
[index
];
307 attrib
.SetDivisor(divisor
);
311 // Gets the Attrib pointer for an attrib but only if it's a client side
312 // pointer. Returns true if it got the pointer.
313 bool VertexArrayObject::GetAttribPointer(
314 GLuint index
, GLenum pname
, void** ptr
) const {
315 const VertexAttrib
* attrib
= GetAttrib(index
);
316 if (attrib
&& pname
== GL_VERTEX_ATTRIB_ARRAY_POINTER
) {
317 *ptr
= const_cast<void*>(attrib
->pointer());
323 // Gets an attrib if it's in range and it's client side.
324 const VertexArrayObject::VertexAttrib
* VertexArrayObject::GetAttrib(
325 GLuint index
) const {
326 if (index
< vertex_attribs_
.size()) {
327 const VertexAttrib
* attrib
= &vertex_attribs_
[index
];
333 VertexArrayObjectManager::VertexArrayObjectManager(
334 GLuint max_vertex_attribs
,
335 GLuint array_buffer_id
,
336 GLuint element_array_buffer_id
,
337 bool support_client_side_arrays
)
338 : max_vertex_attribs_(max_vertex_attribs
),
339 array_buffer_id_(array_buffer_id
),
340 array_buffer_size_(0),
341 array_buffer_offset_(0),
342 element_array_buffer_id_(element_array_buffer_id
),
343 element_array_buffer_size_(0),
344 collection_buffer_size_(0),
345 default_vertex_array_object_(new VertexArrayObject(max_vertex_attribs
)),
346 bound_vertex_array_object_(default_vertex_array_object_
),
347 support_client_side_arrays_(support_client_side_arrays
) {
350 VertexArrayObjectManager::~VertexArrayObjectManager() {
351 for (VertexArrayObjectMap::iterator it
= vertex_array_objects_
.begin();
352 it
!= vertex_array_objects_
.end(); ++it
) {
355 delete default_vertex_array_object_
;
358 bool VertexArrayObjectManager::IsReservedId(GLuint id
) const {
360 (id
== array_buffer_id_
|| id
== element_array_buffer_id_
));
363 GLuint
VertexArrayObjectManager::bound_element_array_buffer() const {
364 return bound_vertex_array_object_
->bound_element_array_buffer();
367 void VertexArrayObjectManager::UnbindBuffer(GLuint id
) {
368 bound_vertex_array_object_
->UnbindBuffer(id
);
371 bool VertexArrayObjectManager::BindElementArray(GLuint id
) {
372 return bound_vertex_array_object_
->BindElementArray(id
);
375 void VertexArrayObjectManager::GenVertexArrays(
376 GLsizei n
, const GLuint
* arrays
) {
378 for (GLsizei i
= 0; i
< n
; ++i
) {
379 std::pair
<VertexArrayObjectMap::iterator
, bool> result
=
380 vertex_array_objects_
.insert(std::make_pair(
381 arrays
[i
], new VertexArrayObject(max_vertex_attribs_
)));
382 DCHECK(result
.second
);
386 void VertexArrayObjectManager::DeleteVertexArrays(
387 GLsizei n
, const GLuint
* arrays
) {
389 for (GLsizei i
= 0; i
< n
; ++i
) {
390 GLuint id
= arrays
[i
];
392 VertexArrayObjectMap::iterator it
= vertex_array_objects_
.find(id
);
393 if (it
!= vertex_array_objects_
.end()) {
394 if (bound_vertex_array_object_
== it
->second
) {
395 bound_vertex_array_object_
= default_vertex_array_object_
;
398 vertex_array_objects_
.erase(it
);
404 bool VertexArrayObjectManager::BindVertexArray(GLuint array
, bool* changed
) {
406 VertexArrayObject
* vertex_array_object
= default_vertex_array_object_
;
408 VertexArrayObjectMap::iterator it
= vertex_array_objects_
.find(array
);
409 if (it
== vertex_array_objects_
.end()) {
412 vertex_array_object
= it
->second
;
414 *changed
= vertex_array_object
!= bound_vertex_array_object_
;
415 bound_vertex_array_object_
= vertex_array_object
;
419 bool VertexArrayObjectManager::HaveEnabledClientSideBuffers() const {
420 return bound_vertex_array_object_
->HaveEnabledClientSideBuffers();
423 void VertexArrayObjectManager::SetAttribEnable(GLuint index
, bool enabled
) {
424 bound_vertex_array_object_
->SetAttribEnable(index
, enabled
);
427 bool VertexArrayObjectManager::GetVertexAttrib(
428 GLuint index
, GLenum pname
, uint32
* param
) {
429 return bound_vertex_array_object_
->GetVertexAttrib(index
, pname
, param
);
432 bool VertexArrayObjectManager::GetAttribPointer(
433 GLuint index
, GLenum pname
, void** ptr
) const {
434 return bound_vertex_array_object_
->GetAttribPointer(index
, pname
, ptr
);
437 bool VertexArrayObjectManager::SetAttribPointer(
442 GLboolean normalized
,
446 // Client side arrays are not allowed in vaos.
447 if (buffer_id
== 0 && !IsDefaultVAOBound()) {
450 bound_vertex_array_object_
->SetAttribPointer(
451 buffer_id
, index
, size
, type
, normalized
, stride
, ptr
, integer
);
455 void VertexArrayObjectManager::SetAttribDivisor(GLuint index
, GLuint divisor
) {
456 bound_vertex_array_object_
->SetAttribDivisor(index
, divisor
);
459 // Collects the data into the collection buffer and returns the number of
461 GLsizei
VertexArrayObjectManager::CollectData(
463 GLsizei bytes_per_element
,
465 GLsizei num_elements
) {
466 GLsizei bytes_needed
= bytes_per_element
* num_elements
;
467 if (collection_buffer_size_
< bytes_needed
) {
468 collection_buffer_
.reset(new int8
[bytes_needed
]);
469 collection_buffer_size_
= bytes_needed
;
471 const int8
* src
= static_cast<const int8
*>(data
);
472 int8
* dst
= collection_buffer_
.get();
473 int8
* end
= dst
+ bytes_per_element
* num_elements
;
474 for (; dst
< end
; src
+= real_stride
, dst
+= bytes_per_element
) {
475 memcpy(dst
, src
, bytes_per_element
);
480 bool VertexArrayObjectManager::IsDefaultVAOBound() const {
481 return bound_vertex_array_object_
== default_vertex_array_object_
;
484 // Returns true if buffers were setup.
485 bool VertexArrayObjectManager::SetupSimulatedClientSideBuffers(
486 const char* function_name
,
487 GLES2Implementation
* gl
,
488 GLES2CmdHelper
* gl_helper
,
489 GLsizei num_elements
,
493 if (!support_client_side_arrays_
)
495 if (!bound_vertex_array_object_
->HaveEnabledClientSideBuffers()) {
498 if (!IsDefaultVAOBound()) {
500 GL_INVALID_OPERATION
, function_name
,
501 "client side arrays not allowed with vertex array object");
505 GLsizei total_size
= 0;
506 // Compute the size of the buffer we need.
507 const VertexArrayObject::VertexAttribs
& vertex_attribs
=
508 bound_vertex_array_object_
->vertex_attribs();
509 for (GLuint ii
= 0; ii
< vertex_attribs
.size(); ++ii
) {
510 const VertexArrayObject::VertexAttrib
& attrib
= vertex_attribs
[ii
];
511 if (attrib
.IsClientSide() && attrib
.enabled()) {
512 size_t bytes_per_element
=
513 GLES2Util::GetGLTypeSizeForTexturesAndBuffers(attrib
.type()) *
515 GLsizei elements
= (primcount
&& attrib
.divisor() > 0) ?
516 ((primcount
- 1) / attrib
.divisor() + 1) : num_elements
;
517 total_size
+= RoundUpToMultipleOf4(bytes_per_element
* elements
);
520 gl_helper
->BindBuffer(GL_ARRAY_BUFFER
, array_buffer_id_
);
521 array_buffer_offset_
= 0;
522 if (total_size
> array_buffer_size_
) {
523 gl
->BufferDataHelper(GL_ARRAY_BUFFER
, total_size
, NULL
, GL_DYNAMIC_DRAW
);
524 array_buffer_size_
= total_size
;
526 for (GLuint ii
= 0; ii
< vertex_attribs
.size(); ++ii
) {
527 const VertexArrayObject::VertexAttrib
& attrib
= vertex_attribs
[ii
];
528 if (attrib
.IsClientSide() && attrib
.enabled()) {
529 size_t bytes_per_element
=
530 GLES2Util::GetGLTypeSizeForTexturesAndBuffers(attrib
.type()) *
532 GLsizei real_stride
= attrib
.stride() ?
533 attrib
.stride() : static_cast<GLsizei
>(bytes_per_element
);
534 GLsizei elements
= (primcount
&& attrib
.divisor() > 0) ?
535 ((primcount
- 1) / attrib
.divisor() + 1) : num_elements
;
536 GLsizei bytes_collected
= CollectData(
537 attrib
.pointer(), bytes_per_element
, real_stride
, elements
);
538 gl
->BufferSubDataHelper(
539 GL_ARRAY_BUFFER
, array_buffer_offset_
, bytes_collected
,
540 collection_buffer_
.get());
541 gl_helper
->VertexAttribPointer(
542 ii
, attrib
.size(), attrib
.type(), attrib
.normalized(), 0,
543 array_buffer_offset_
);
544 array_buffer_offset_
+= RoundUpToMultipleOf4(bytes_collected
);
545 DCHECK_LE(array_buffer_offset_
, array_buffer_size_
);
551 // Copies in indices to the service and returns the highest index accessed + 1
552 bool VertexArrayObjectManager::SetupSimulatedIndexAndClientSideBuffers(
553 const char* function_name
,
554 GLES2Implementation
* gl
,
555 GLES2CmdHelper
* gl_helper
,
563 *offset
= ToGLuint(indices
);
564 if (!support_client_side_arrays_
)
566 GLsizei num_elements
= 0;
567 if (bound_vertex_array_object_
->bound_element_array_buffer() == 0) {
570 GLsizei max_index
= -1;
572 case GL_UNSIGNED_BYTE
: {
573 const uint8
* src
= static_cast<const uint8
*>(indices
);
574 for (GLsizei ii
= 0; ii
< count
; ++ii
) {
575 if (src
[ii
] > max_index
) {
581 case GL_UNSIGNED_SHORT
: {
582 const uint16
* src
= static_cast<const uint16
*>(indices
);
583 for (GLsizei ii
= 0; ii
< count
; ++ii
) {
584 if (src
[ii
] > max_index
) {
590 case GL_UNSIGNED_INT
: {
591 uint32 max_glsizei
= static_cast<uint32
>(
592 std::numeric_limits
<GLsizei
>::max());
593 const uint32
* src
= static_cast<const uint32
*>(indices
);
594 for (GLsizei ii
= 0; ii
< count
; ++ii
) {
595 // Other parts of the API use GLsizei (signed) to store limits.
596 // As such, if we encounter a index that cannot be represented with
597 // an unsigned int we need to flag it as an error here.
598 if(src
[ii
] > max_glsizei
) {
600 GL_INVALID_OPERATION
, function_name
, "index too large.");
603 GLsizei signed_index
= static_cast<GLsizei
>(src
[ii
]);
604 if (signed_index
> max_index
) {
605 max_index
= signed_index
;
613 gl_helper
->BindBuffer(GL_ELEMENT_ARRAY_BUFFER
, element_array_buffer_id_
);
614 GLsizei bytes_per_element
=
615 GLES2Util::GetGLTypeSizeForTexturesAndBuffers(type
);
616 GLsizei bytes_needed
= bytes_per_element
* count
;
617 if (bytes_needed
> element_array_buffer_size_
) {
618 element_array_buffer_size_
= bytes_needed
;
619 gl
->BufferDataHelper(
620 GL_ELEMENT_ARRAY_BUFFER
, bytes_needed
, NULL
, GL_DYNAMIC_DRAW
);
622 gl
->BufferSubDataHelper(
623 GL_ELEMENT_ARRAY_BUFFER
, 0, bytes_needed
, indices
);
625 num_elements
= max_index
+ 1;
626 } else if (bound_vertex_array_object_
->HaveEnabledClientSideBuffers()) {
627 // Index buffer is GL buffer. Ask the service for the highest vertex
628 // that will be accessed. Note: It doesn't matter if another context
629 // changes the contents of any of the buffers. The service will still
630 // validate the indices. We just need to know how much to copy across.
631 num_elements
= gl
->GetMaxValueInBufferCHROMIUMHelper(
632 bound_vertex_array_object_
->bound_element_array_buffer(),
633 count
, type
, ToGLuint(indices
)) + 1;
636 bool simulated_client_side_buffers
= false;
637 SetupSimulatedClientSideBuffers(
638 function_name
, gl
, gl_helper
, num_elements
, primcount
,
639 &simulated_client_side_buffers
);
640 *simulated
= *simulated
|| simulated_client_side_buffers
;