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
),
53 bool enabled() const {
57 void set_enabled(bool enabled
) {
61 GLuint
buffer_id() const {
65 void set_buffer_id(GLuint id
) {
77 GLsizei
stride() const {
81 GLboolean
normalized() const {
85 const GLvoid
* pointer() const {
89 bool IsClientSide() const {
90 return buffer_id_
== 0;
93 GLuint
divisor() const {
101 GLboolean normalized
,
103 const GLvoid
* pointer
) {
104 buffer_id_
= buffer_id
;
107 normalized_
= normalized
;
108 gl_stride_
= gl_stride
;
112 void SetDivisor(GLuint divisor
) {
117 // Whether or not this attribute is enabled.
120 // The id of the buffer. 0 = client side buffer.
123 // Number of components (1, 2, 3, 4).
126 // GL_BYTE, GL_FLOAT, etc. See glVertexAttribPointer.
129 // GL_TRUE or GL_FALSE
130 GLboolean normalized_
;
132 // The pointer/offset into the buffer.
133 const GLvoid
* pointer_
;
135 // The stride that will be used to access the buffer. This is the bogus GL
136 // stride where 0 = compute the stride based on size and type.
139 // Divisor, for geometry instancing.
143 typedef std::vector
<VertexAttrib
> VertexAttribs
;
145 explicit VertexArrayObject(GLuint max_vertex_attribs
);
147 void UnbindBuffer(GLuint id
);
149 bool BindElementArray(GLuint id
);
151 bool HaveEnabledClientSideBuffers() const;
153 void SetAttribEnable(GLuint index
, bool enabled
);
155 void SetAttribPointer(
157 GLuint index
, GLint size
, GLenum type
, GLboolean normalized
, GLsizei stride
,
160 bool GetVertexAttrib(
161 GLuint index
, GLenum pname
, uint32
* param
) const;
163 void SetAttribDivisor(GLuint index
, GLuint divisor
);
165 bool GetAttribPointer(GLuint index
, GLenum pname
, void** ptr
) const;
167 const VertexAttribs
& vertex_attribs() const {
168 return vertex_attribs_
;
171 GLuint
bound_element_array_buffer() const {
172 return bound_element_array_buffer_id_
;
176 const VertexAttrib
* GetAttrib(GLuint index
) const;
178 int num_client_side_pointers_enabled_
;
180 // The currently bound element array buffer.
181 GLuint bound_element_array_buffer_id_
;
183 VertexAttribs vertex_attribs_
;
185 DISALLOW_COPY_AND_ASSIGN(VertexArrayObject
);
188 VertexArrayObject::VertexArrayObject(GLuint max_vertex_attribs
)
189 : num_client_side_pointers_enabled_(0),
190 bound_element_array_buffer_id_(0) {
191 vertex_attribs_
.resize(max_vertex_attribs
);
194 void VertexArrayObject::UnbindBuffer(GLuint id
) {
198 for (size_t ii
= 0; ii
< vertex_attribs_
.size(); ++ii
) {
199 VertexAttrib
& attrib
= vertex_attribs_
[ii
];
200 if (attrib
.buffer_id() == id
) {
201 attrib
.set_buffer_id(0);
202 if (attrib
.enabled()) {
203 ++num_client_side_pointers_enabled_
;
207 if (bound_element_array_buffer_id_
== id
) {
208 bound_element_array_buffer_id_
= 0;
212 bool VertexArrayObject::BindElementArray(GLuint id
) {
213 if (id
== bound_element_array_buffer_id_
) {
216 bound_element_array_buffer_id_
= id
;
219 bool VertexArrayObject::HaveEnabledClientSideBuffers() const {
220 return num_client_side_pointers_enabled_
> 0;
223 void VertexArrayObject::SetAttribEnable(GLuint index
, bool enabled
) {
224 if (index
< vertex_attribs_
.size()) {
225 VertexAttrib
& attrib
= vertex_attribs_
[index
];
226 if (attrib
.enabled() != enabled
) {
227 if (attrib
.IsClientSide()) {
228 num_client_side_pointers_enabled_
+= enabled
? 1 : -1;
229 DCHECK_GE(num_client_side_pointers_enabled_
, 0);
231 attrib
.set_enabled(enabled
);
236 void VertexArrayObject::SetAttribPointer(
241 GLboolean normalized
,
244 if (index
< vertex_attribs_
.size()) {
245 VertexAttrib
& attrib
= vertex_attribs_
[index
];
246 if (attrib
.IsClientSide() && attrib
.enabled()) {
247 --num_client_side_pointers_enabled_
;
248 DCHECK_GE(num_client_side_pointers_enabled_
, 0);
251 attrib
.SetInfo(buffer_id
, size
, type
, normalized
, stride
, ptr
);
253 if (attrib
.IsClientSide() && attrib
.enabled()) {
254 ++num_client_side_pointers_enabled_
;
259 bool VertexArrayObject::GetVertexAttrib(
260 GLuint index
, GLenum pname
, uint32
* param
) const {
261 const VertexAttrib
* attrib
= GetAttrib(index
);
267 case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING
:
268 *param
= attrib
->buffer_id();
270 case GL_VERTEX_ATTRIB_ARRAY_ENABLED
:
271 *param
= attrib
->enabled();
273 case GL_VERTEX_ATTRIB_ARRAY_SIZE
:
274 *param
= attrib
->size();
276 case GL_VERTEX_ATTRIB_ARRAY_STRIDE
:
277 *param
= attrib
->stride();
279 case GL_VERTEX_ATTRIB_ARRAY_TYPE
:
280 *param
= attrib
->type();
282 case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED
:
283 *param
= attrib
->normalized();
286 return false; // pass through to service side.
292 void VertexArrayObject::SetAttribDivisor(GLuint index
, GLuint divisor
) {
293 if (index
< vertex_attribs_
.size()) {
294 VertexAttrib
& attrib
= vertex_attribs_
[index
];
295 attrib
.SetDivisor(divisor
);
299 // Gets the Attrib pointer for an attrib but only if it's a client side
300 // pointer. Returns true if it got the pointer.
301 bool VertexArrayObject::GetAttribPointer(
302 GLuint index
, GLenum pname
, void** ptr
) const {
303 const VertexAttrib
* attrib
= GetAttrib(index
);
304 if (attrib
&& pname
== GL_VERTEX_ATTRIB_ARRAY_POINTER
) {
305 *ptr
= const_cast<void*>(attrib
->pointer());
311 // Gets an attrib if it's in range and it's client side.
312 const VertexArrayObject::VertexAttrib
* VertexArrayObject::GetAttrib(
313 GLuint index
) const {
314 if (index
< vertex_attribs_
.size()) {
315 const VertexAttrib
* attrib
= &vertex_attribs_
[index
];
321 VertexArrayObjectManager::VertexArrayObjectManager(
322 GLuint max_vertex_attribs
,
323 GLuint array_buffer_id
,
324 GLuint element_array_buffer_id
,
325 bool support_client_side_arrays
)
326 : max_vertex_attribs_(max_vertex_attribs
),
327 array_buffer_id_(array_buffer_id
),
328 array_buffer_size_(0),
329 array_buffer_offset_(0),
330 element_array_buffer_id_(element_array_buffer_id
),
331 element_array_buffer_size_(0),
332 collection_buffer_size_(0),
333 default_vertex_array_object_(new VertexArrayObject(max_vertex_attribs
)),
334 bound_vertex_array_object_(default_vertex_array_object_
),
335 support_client_side_arrays_(support_client_side_arrays
) {
338 VertexArrayObjectManager::~VertexArrayObjectManager() {
339 for (VertexArrayObjectMap::iterator it
= vertex_array_objects_
.begin();
340 it
!= vertex_array_objects_
.end(); ++it
) {
343 delete default_vertex_array_object_
;
346 bool VertexArrayObjectManager::IsReservedId(GLuint id
) const {
348 (id
== array_buffer_id_
|| id
== element_array_buffer_id_
));
351 GLuint
VertexArrayObjectManager::bound_element_array_buffer() const {
352 return bound_vertex_array_object_
->bound_element_array_buffer();
355 void VertexArrayObjectManager::UnbindBuffer(GLuint id
) {
356 bound_vertex_array_object_
->UnbindBuffer(id
);
359 bool VertexArrayObjectManager::BindElementArray(GLuint id
) {
360 return bound_vertex_array_object_
->BindElementArray(id
);
363 void VertexArrayObjectManager::GenVertexArrays(
364 GLsizei n
, const GLuint
* arrays
) {
366 for (GLsizei i
= 0; i
< n
; ++i
) {
367 std::pair
<VertexArrayObjectMap::iterator
, bool> result
=
368 vertex_array_objects_
.insert(std::make_pair(
369 arrays
[i
], new VertexArrayObject(max_vertex_attribs_
)));
370 DCHECK(result
.second
);
374 void VertexArrayObjectManager::DeleteVertexArrays(
375 GLsizei n
, const GLuint
* arrays
) {
377 for (GLsizei i
= 0; i
< n
; ++i
) {
378 GLuint id
= arrays
[i
];
380 VertexArrayObjectMap::iterator it
= vertex_array_objects_
.find(id
);
381 if (it
!= vertex_array_objects_
.end()) {
382 if (bound_vertex_array_object_
== it
->second
) {
383 bound_vertex_array_object_
= default_vertex_array_object_
;
386 vertex_array_objects_
.erase(it
);
392 bool VertexArrayObjectManager::BindVertexArray(GLuint array
, bool* changed
) {
394 VertexArrayObject
* vertex_array_object
= default_vertex_array_object_
;
396 VertexArrayObjectMap::iterator it
= vertex_array_objects_
.find(array
);
397 if (it
== vertex_array_objects_
.end()) {
400 vertex_array_object
= it
->second
;
402 *changed
= vertex_array_object
!= bound_vertex_array_object_
;
403 bound_vertex_array_object_
= vertex_array_object
;
407 bool VertexArrayObjectManager::HaveEnabledClientSideBuffers() const {
408 return bound_vertex_array_object_
->HaveEnabledClientSideBuffers();
411 void VertexArrayObjectManager::SetAttribEnable(GLuint index
, bool enabled
) {
412 bound_vertex_array_object_
->SetAttribEnable(index
, enabled
);
415 bool VertexArrayObjectManager::GetVertexAttrib(
416 GLuint index
, GLenum pname
, uint32
* param
) {
417 return bound_vertex_array_object_
->GetVertexAttrib(index
, pname
, param
);
420 bool VertexArrayObjectManager::GetAttribPointer(
421 GLuint index
, GLenum pname
, void** ptr
) const {
422 return bound_vertex_array_object_
->GetAttribPointer(index
, pname
, ptr
);
425 bool VertexArrayObjectManager::SetAttribPointer(
430 GLboolean normalized
,
433 // Client side arrays are not allowed in vaos.
434 if (buffer_id
== 0 && !IsDefaultVAOBound()) {
437 bound_vertex_array_object_
->SetAttribPointer(
438 buffer_id
, index
, size
, type
, normalized
, stride
, ptr
);
442 void VertexArrayObjectManager::SetAttribDivisor(GLuint index
, GLuint divisor
) {
443 bound_vertex_array_object_
->SetAttribDivisor(index
, divisor
);
446 // Collects the data into the collection buffer and returns the number of
448 GLsizei
VertexArrayObjectManager::CollectData(
450 GLsizei bytes_per_element
,
452 GLsizei num_elements
) {
453 GLsizei bytes_needed
= bytes_per_element
* num_elements
;
454 if (collection_buffer_size_
< bytes_needed
) {
455 collection_buffer_
.reset(new int8
[bytes_needed
]);
456 collection_buffer_size_
= bytes_needed
;
458 const int8
* src
= static_cast<const int8
*>(data
);
459 int8
* dst
= collection_buffer_
.get();
460 int8
* end
= dst
+ bytes_per_element
* num_elements
;
461 for (; dst
< end
; src
+= real_stride
, dst
+= bytes_per_element
) {
462 memcpy(dst
, src
, bytes_per_element
);
467 bool VertexArrayObjectManager::IsDefaultVAOBound() const {
468 return bound_vertex_array_object_
== default_vertex_array_object_
;
471 // Returns true if buffers were setup.
472 bool VertexArrayObjectManager::SetupSimulatedClientSideBuffers(
473 const char* function_name
,
474 GLES2Implementation
* gl
,
475 GLES2CmdHelper
* gl_helper
,
476 GLsizei num_elements
,
480 if (!support_client_side_arrays_
)
482 if (!bound_vertex_array_object_
->HaveEnabledClientSideBuffers()) {
485 if (!IsDefaultVAOBound()) {
487 GL_INVALID_OPERATION
, function_name
,
488 "client side arrays not allowed with vertex array object");
492 GLsizei total_size
= 0;
493 // Compute the size of the buffer we need.
494 const VertexArrayObject::VertexAttribs
& vertex_attribs
=
495 bound_vertex_array_object_
->vertex_attribs();
496 for (GLuint ii
= 0; ii
< vertex_attribs
.size(); ++ii
) {
497 const VertexArrayObject::VertexAttrib
& attrib
= vertex_attribs
[ii
];
498 if (attrib
.IsClientSide() && attrib
.enabled()) {
499 size_t bytes_per_element
=
500 GLES2Util::GetGLTypeSizeForTexturesAndBuffers(attrib
.type()) *
502 GLsizei elements
= (primcount
&& attrib
.divisor() > 0) ?
503 ((primcount
- 1) / attrib
.divisor() + 1) : num_elements
;
504 total_size
+= RoundUpToMultipleOf4(bytes_per_element
* elements
);
507 gl_helper
->BindBuffer(GL_ARRAY_BUFFER
, array_buffer_id_
);
508 array_buffer_offset_
= 0;
509 if (total_size
> array_buffer_size_
) {
510 gl
->BufferDataHelper(GL_ARRAY_BUFFER
, total_size
, NULL
, GL_DYNAMIC_DRAW
);
511 array_buffer_size_
= total_size
;
513 for (GLuint ii
= 0; ii
< vertex_attribs
.size(); ++ii
) {
514 const VertexArrayObject::VertexAttrib
& attrib
= vertex_attribs
[ii
];
515 if (attrib
.IsClientSide() && attrib
.enabled()) {
516 size_t bytes_per_element
=
517 GLES2Util::GetGLTypeSizeForTexturesAndBuffers(attrib
.type()) *
519 GLsizei real_stride
= attrib
.stride() ?
520 attrib
.stride() : static_cast<GLsizei
>(bytes_per_element
);
521 GLsizei elements
= (primcount
&& attrib
.divisor() > 0) ?
522 ((primcount
- 1) / attrib
.divisor() + 1) : num_elements
;
523 GLsizei bytes_collected
= CollectData(
524 attrib
.pointer(), bytes_per_element
, real_stride
, elements
);
525 gl
->BufferSubDataHelper(
526 GL_ARRAY_BUFFER
, array_buffer_offset_
, bytes_collected
,
527 collection_buffer_
.get());
528 gl_helper
->VertexAttribPointer(
529 ii
, attrib
.size(), attrib
.type(), attrib
.normalized(), 0,
530 array_buffer_offset_
);
531 array_buffer_offset_
+= RoundUpToMultipleOf4(bytes_collected
);
532 DCHECK_LE(array_buffer_offset_
, array_buffer_size_
);
538 // Copies in indices to the service and returns the highest index accessed + 1
539 bool VertexArrayObjectManager::SetupSimulatedIndexAndClientSideBuffers(
540 const char* function_name
,
541 GLES2Implementation
* gl
,
542 GLES2CmdHelper
* gl_helper
,
550 *offset
= ToGLuint(indices
);
551 if (!support_client_side_arrays_
)
553 GLsizei num_elements
= 0;
554 if (bound_vertex_array_object_
->bound_element_array_buffer() == 0) {
557 GLsizei max_index
= -1;
559 case GL_UNSIGNED_BYTE
: {
560 const uint8
* src
= static_cast<const uint8
*>(indices
);
561 for (GLsizei ii
= 0; ii
< count
; ++ii
) {
562 if (src
[ii
] > max_index
) {
568 case GL_UNSIGNED_SHORT
: {
569 const uint16
* src
= static_cast<const uint16
*>(indices
);
570 for (GLsizei ii
= 0; ii
< count
; ++ii
) {
571 if (src
[ii
] > max_index
) {
577 case GL_UNSIGNED_INT
: {
578 uint32 max_glsizei
= static_cast<uint32
>(
579 std::numeric_limits
<GLsizei
>::max());
580 const uint32
* src
= static_cast<const uint32
*>(indices
);
581 for (GLsizei ii
= 0; ii
< count
; ++ii
) {
582 // Other parts of the API use GLsizei (signed) to store limits.
583 // As such, if we encounter a index that cannot be represented with
584 // an unsigned int we need to flag it as an error here.
585 if(src
[ii
] > max_glsizei
) {
587 GL_INVALID_OPERATION
, function_name
, "index too large.");
590 GLsizei signed_index
= static_cast<GLsizei
>(src
[ii
]);
591 if (signed_index
> max_index
) {
592 max_index
= signed_index
;
600 gl_helper
->BindBuffer(GL_ELEMENT_ARRAY_BUFFER
, element_array_buffer_id_
);
601 GLsizei bytes_per_element
=
602 GLES2Util::GetGLTypeSizeForTexturesAndBuffers(type
);
603 GLsizei bytes_needed
= bytes_per_element
* count
;
604 if (bytes_needed
> element_array_buffer_size_
) {
605 element_array_buffer_size_
= bytes_needed
;
606 gl
->BufferDataHelper(
607 GL_ELEMENT_ARRAY_BUFFER
, bytes_needed
, NULL
, GL_DYNAMIC_DRAW
);
609 gl
->BufferSubDataHelper(
610 GL_ELEMENT_ARRAY_BUFFER
, 0, bytes_needed
, indices
);
612 num_elements
= max_index
+ 1;
613 } else if (bound_vertex_array_object_
->HaveEnabledClientSideBuffers()) {
614 // Index buffer is GL buffer. Ask the service for the highest vertex
615 // that will be accessed. Note: It doesn't matter if another context
616 // changes the contents of any of the buffers. The service will still
617 // validate the indices. We just need to know how much to copy across.
618 num_elements
= gl
->GetMaxValueInBufferCHROMIUMHelper(
619 bound_vertex_array_object_
->bound_element_array_buffer(),
620 count
, type
, ToGLuint(indices
)) + 1;
623 bool simulated_client_side_buffers
= false;
624 SetupSimulatedClientSideBuffers(
625 function_name
, gl
, gl_helper
, num_elements
, primcount
,
626 &simulated_client_side_buffers
);
627 *simulated
= *simulated
|| simulated_client_side_buffers
;