Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(24)

Side by Side Diff: gpu/command_buffer/client/vertex_array_object_manager.cc

Issue 11413094: Fix VAOs and client side arrays (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 "../client/vertex_array_object_manager.h"
6
7 #include "../client/gles2_cmd_helper.h"
8 #include "../client/gles2_implementation.h"
9 #include "../common/logging.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 GLuint 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 for (size_t ii = 0; ii < vertex_attribs_.size(); ++ii) {
204 VertexAttrib& attrib = vertex_attribs_[ii];
205 if (attrib.buffer_id() == id) {
206 attrib.set_buffer_id(0);
207 if (attrib.enabled()) {
208 ++num_client_side_pointers_enabled_;
209 }
210 }
211 }
212 }
213
214 bool VertexArrayObject::BindElementArray(GLuint id) {
215 if (id == bound_element_array_buffer_id_) {
216 return false;
217 }
218 bound_element_array_buffer_id_ = id;
219 return true;
220 }
221 bool VertexArrayObject::HaveEnabledClientSideBuffers() const {
222 return num_client_side_pointers_enabled_ > 0;
223 }
224
225 void VertexArrayObject::SetAttribEnable(GLuint index, bool enabled) {
226 if (index < vertex_attribs_.size()) {
227 VertexAttrib& attrib = vertex_attribs_[index];
228 if (attrib.enabled() != enabled) {
229 if (attrib.IsClientSide()) {
230 num_client_side_pointers_enabled_ += enabled ? 1 : -1;
231 }
232 attrib.set_enabled(enabled);
233 }
234 }
235 }
236
237 void VertexArrayObject::SetAttribPointer(
238 GLuint buffer_id,
239 GLuint index,
240 GLint size,
241 GLenum type,
242 GLboolean normalized,
243 GLsizei stride,
244 const void* ptr) {
245 if (index < vertex_attribs_.size()) {
246 VertexAttrib& attrib = vertex_attribs_[index];
247 if (attrib.IsClientSide() && attrib.enabled()) {
248 --num_client_side_pointers_enabled_;
249 }
250
251 attrib.SetInfo(buffer_id, size, type, normalized, stride, ptr);
252
253 if (attrib.IsClientSide() && attrib.enabled()) {
254 ++num_client_side_pointers_enabled_;
255 }
256 }
257 }
258
259 bool VertexArrayObject::GetVertexAttrib(
260 GLuint index, GLenum pname, uint32* param) const {
261 const VertexAttrib* attrib = GetAttrib(index);
262 if (!attrib) {
263 return false;
264 }
265
266 switch (pname) {
267 case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
268 *param = attrib->buffer_id();
269 break;
270 case GL_VERTEX_ATTRIB_ARRAY_ENABLED:
271 *param = attrib->enabled();
272 break;
273 case GL_VERTEX_ATTRIB_ARRAY_SIZE:
274 *param = attrib->size();
275 break;
276 case GL_VERTEX_ATTRIB_ARRAY_STRIDE:
277 *param = attrib->stride();
278 break;
279 case GL_VERTEX_ATTRIB_ARRAY_TYPE:
280 *param = attrib->type();
281 break;
282 case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED:
283 *param = attrib->normalized();
284 break;
285 default:
286 return false; // pass through to service side.
287 break;
288 }
289 return true;
290 }
291
292 void VertexArrayObject::SetAttribDivisor(GLuint index, GLuint divisor) {
293 if (index < vertex_attribs_.size()) {
294 VertexAttrib& attrib = vertex_attribs_[index];
295 attrib.SetDivisor(divisor);
296 }
297 }
298
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());
306 return true;
307 }
308 return false;
309 }
310
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];
316 return attrib;
317 }
318 return NULL;
319 }
320
321 VertexArrayObjectManager::VertexArrayObjectManager(
322 GLuint max_vertex_attribs,
323 GLuint array_buffer_id,
324 GLuint element_array_buffer_id)
325 : max_vertex_attribs_(max_vertex_attribs),
326 array_buffer_id_(array_buffer_id),
327 array_buffer_size_(0),
328 array_buffer_offset_(0),
329 element_array_buffer_id_(element_array_buffer_id),
330 element_array_buffer_size_(0),
331 collection_buffer_size_(0),
332 default_vertex_array_object_(new VertexArrayObject(max_vertex_attribs)),
333 bound_vertex_array_object_(default_vertex_array_object_) {
334 }
335
336 VertexArrayObjectManager::~VertexArrayObjectManager() {
337 for (VertexArrayObjectMap::iterator it = vertex_array_objects_.begin();
338 it != vertex_array_objects_.end(); ++it) {
339 delete it->second;
340 }
341 delete default_vertex_array_object_;
342 }
343
344 bool VertexArrayObjectManager::IsReservedId(GLuint id) const {
345 return (id != 0 &&
346 (id == array_buffer_id_ || id == element_array_buffer_id_));
347 }
348
349 GLuint VertexArrayObjectManager::bound_element_array_buffer() const {
350 return bound_vertex_array_object_->bound_element_array_buffer();
351 }
352
353 void VertexArrayObjectManager::UnbindBuffer(GLuint id) {
354 bound_vertex_array_object_->UnbindBuffer(id);
355 }
356
357 bool VertexArrayObjectManager::BindElementArray(GLuint id) {
358 return bound_vertex_array_object_->BindElementArray(id);
359 }
360
361 void VertexArrayObjectManager::GenVertexArrays(
362 GLsizei n, const GLuint* arrays) {
363 GPU_DCHECK_GE(n, 0);
364 for (GLsizei i = 0; i < n; ++i) {
365 std::pair<VertexArrayObjectMap::iterator, bool> result =
366 vertex_array_objects_.insert(std::make_pair(
367 arrays[i], new VertexArrayObject(max_vertex_attribs_)));
368 GPU_DCHECK(result.second);
369 }
370 }
371
372 void VertexArrayObjectManager::DeleteVertexArrays(
373 GLsizei n, const GLuint* arrays) {
374 GPU_DCHECK_GE(n, 0);
375 for (GLsizei i = 0; i < n; ++i) {
376 GLuint id = arrays[i];
377 if (id) {
378 VertexArrayObjectMap::iterator it = vertex_array_objects_.find(id);
379 if (it != vertex_array_objects_.end()) {
380 if (bound_vertex_array_object_ == it->second) {
381 bound_vertex_array_object_ = default_vertex_array_object_;
382 }
383 delete it->second;
384 vertex_array_objects_.erase(it);
385 }
386 }
387 }
388 }
389
390 bool VertexArrayObjectManager::BindVertexArray(GLuint array, bool* changed) {
391 *changed = false;
392 VertexArrayObject* vertex_array_object = default_vertex_array_object_;
393 if (array != 0) {
394 VertexArrayObjectMap::iterator it = vertex_array_objects_.find(array);
395 if (it == vertex_array_objects_.end()) {
396 return false;
397 }
398 vertex_array_object = it->second;
399 }
400 *changed = vertex_array_object != bound_vertex_array_object_;
401 bound_vertex_array_object_ = vertex_array_object;
402 return true;
403 }
404
405 bool VertexArrayObjectManager::HaveEnabledClientSideBuffers() const {
406 return bound_vertex_array_object_->HaveEnabledClientSideBuffers();
407 }
408
409 void VertexArrayObjectManager::SetAttribEnable(GLuint index, bool enabled) {
410 bound_vertex_array_object_->SetAttribEnable(index, enabled);
411 }
412
413 bool VertexArrayObjectManager::GetVertexAttrib(
414 GLuint index, GLenum pname, uint32* param) {
415 return bound_vertex_array_object_->GetVertexAttrib(index, pname, param);
416 }
417
418 bool VertexArrayObjectManager::GetAttribPointer(
419 GLuint index, GLenum pname, void** ptr) const {
420 return bound_vertex_array_object_->GetAttribPointer(index, pname, ptr);
421 }
422
423 bool VertexArrayObjectManager::SetAttribPointer(
424 GLuint buffer_id,
425 GLuint index,
426 GLint size,
427 GLenum type,
428 GLboolean normalized,
429 GLsizei stride,
430 const void* ptr) {
431 // Client side arrays are not allowed in vaos.
432 if (buffer_id == 0 && !IsDefaultVAOBound()) {
433 return false;
434 }
435 bound_vertex_array_object_->SetAttribPointer(
436 buffer_id, index, size, type, normalized, stride, ptr);
437 return true;
438 }
439
440 void VertexArrayObjectManager::SetAttribDivisor(GLuint index, GLuint divisor) {
441 bound_vertex_array_object_->SetAttribDivisor(index, divisor);
442 }
443
444 // Collects the data into the collection buffer and returns the number of
445 // bytes collected.
446 GLsizei VertexArrayObjectManager::CollectData(
447 const void* data,
448 GLsizei bytes_per_element,
449 GLsizei real_stride,
450 GLsizei num_elements) {
451 GLsizei bytes_needed = bytes_per_element * num_elements;
452 if (collection_buffer_size_ < bytes_needed) {
453 collection_buffer_.reset(new int8[bytes_needed]);
454 collection_buffer_size_ = bytes_needed;
455 }
456 const int8* src = static_cast<const int8*>(data);
457 int8* dst = collection_buffer_.get();
458 int8* end = dst + bytes_per_element * num_elements;
459 for (; dst < end; src += real_stride, dst += bytes_per_element) {
460 memcpy(dst, src, bytes_per_element);
461 }
462 return bytes_needed;
463 }
464
465 bool VertexArrayObjectManager::IsDefaultVAOBound() const {
466 return bound_vertex_array_object_ == default_vertex_array_object_;
467 }
468
469 // Returns true if buffers were setup.
470 bool VertexArrayObjectManager::SetupSimulatedClientSideBuffers(
471 const char* function_name,
472 GLES2Implementation* gl,
473 GLES2CmdHelper* gl_helper,
474 GLsizei num_elements,
475 GLsizei primcount,
476 bool* simulated) {
477 *simulated = false;
478 #if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
479 if (!bound_vertex_array_object_->HaveEnabledClientSideBuffers()) {
480 return true;
481 }
482 if (!IsDefaultVAOBound()) {
483 gl->SetGLError(
484 GL_INVALID_OPERATION, function_name,
485 "client side arrays not allowed with vertex array object");
486 return false;
487 }
488 *simulated = true;
489 GLsizei total_size = 0;
490 // Compute the size of the buffer we need.
491 const VertexArrayObject::VertexAttribs& vertex_attribs =
492 bound_vertex_array_object_->vertex_attribs();
493 for (GLuint ii = 0; ii < vertex_attribs.size(); ++ii) {
494 const VertexArrayObject::VertexAttrib& attrib = vertex_attribs[ii];
495 if (attrib.IsClientSide() && attrib.enabled()) {
496 size_t bytes_per_element =
497 GLES2Util::GetGLTypeSizeForTexturesAndBuffers(attrib.type()) *
498 attrib.size();
499 GLsizei elements = (primcount && attrib.divisor() > 0) ?
500 ((primcount - 1) / attrib.divisor() + 1) : num_elements;
501 total_size += RoundUpToMultipleOf4(bytes_per_element * elements);
502 }
503 }
504 gl_helper->BindBuffer(GL_ARRAY_BUFFER, array_buffer_id_);
505 array_buffer_offset_ = 0;
506 if (total_size > array_buffer_size_) {
507 gl->BufferDataHelper(GL_ARRAY_BUFFER, total_size, NULL, GL_DYNAMIC_DRAW);
508 array_buffer_size_ = total_size;
509 }
510 for (GLuint ii = 0; ii < vertex_attribs.size(); ++ii) {
511 const VertexArrayObject::VertexAttrib& attrib = vertex_attribs[ii];
512 if (attrib.IsClientSide() && attrib.enabled()) {
513 size_t bytes_per_element =
514 GLES2Util::GetGLTypeSizeForTexturesAndBuffers(attrib.type()) *
515 attrib.size();
516 GLsizei real_stride = attrib.stride() ?
517 attrib.stride() : static_cast<GLsizei>(bytes_per_element);
518 GLsizei elements = (primcount && attrib.divisor() > 0) ?
519 ((primcount - 1) / attrib.divisor() + 1) : num_elements;
520 GLsizei bytes_collected = CollectData(
521 attrib.pointer(), bytes_per_element, real_stride, elements);
522 gl->BufferSubDataHelper(
523 GL_ARRAY_BUFFER, array_buffer_offset_, bytes_collected,
524 collection_buffer_.get());
525 gl_helper->VertexAttribPointer(
526 ii, attrib.size(), attrib.type(), attrib.normalized(), 0,
527 array_buffer_offset_);
528 array_buffer_offset_ += RoundUpToMultipleOf4(bytes_collected);
529 GPU_DCHECK_LE(array_buffer_offset_, array_buffer_size_);
530 }
531 }
532 #endif // defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
533 return true;
534 }
535
536 // Copies in indices to the service and returns the highest index accessed + 1
537 bool VertexArrayObjectManager::SetupSimulatedIndexAndClientSideBuffers(
538 const char* function_name,
539 GLES2Implementation* gl,
540 GLES2CmdHelper* gl_helper,
541 GLsizei count,
542 GLenum type,
543 GLsizei primcount,
544 const void* indices,
545 GLuint* offset,
546 bool* simulated) {
547 *simulated = false;
548 *offset = ToGLuint(indices);
549 #if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
550 GLsizei num_elements = 0;
551 if (bound_vertex_array_object_->bound_element_array_buffer() == 0) {
552 *simulated = true;
553 *offset = 0;
554 GLsizei max_index = -1;
555 switch (type) {
556 case GL_UNSIGNED_BYTE: {
557 const uint8* src = static_cast<const uint8*>(indices);
558 for (GLsizei ii = 0; ii < count; ++ii) {
559 if (src[ii] > max_index) {
560 max_index = src[ii];
561 }
562 }
563 break;
564 }
565 case GL_UNSIGNED_SHORT: {
566 const uint16* src = static_cast<const uint16*>(indices);
567 for (GLsizei ii = 0; ii < count; ++ii) {
568 if (src[ii] > max_index) {
569 max_index = src[ii];
570 }
571 }
572 break;
573 }
574 case GL_UNSIGNED_INT: {
575 uint32 max_glsizei = static_cast<uint32>(
576 std::numeric_limits<GLsizei>::max());
577 const uint32* src = static_cast<const uint32*>(indices);
578 for (GLsizei ii = 0; ii < count; ++ii) {
579 // Other parts of the API use GLsizei (signed) to store limits.
580 // As such, if we encounter a index that cannot be represented with
581 // an unsigned int we need to flag it as an error here.
582 if(src[ii] > max_glsizei) {
583 gl->SetGLError(
584 GL_INVALID_OPERATION, function_name, "index too large.");
585 return false;
586 }
587 GLsizei signed_index = static_cast<GLsizei>(src[ii]);
588 if (signed_index > max_index) {
589 max_index = signed_index;
590 }
591 }
592 break;
593 }
594 default:
595 break;
596 }
597 gl_helper->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_array_buffer_id_);
598 GLsizei bytes_per_element =
599 GLES2Util::GetGLTypeSizeForTexturesAndBuffers(type);
600 GLsizei bytes_needed = bytes_per_element * count;
601 if (bytes_needed > element_array_buffer_size_) {
602 element_array_buffer_size_ = bytes_needed;
603 gl->BufferDataHelper(
604 GL_ELEMENT_ARRAY_BUFFER, bytes_needed, NULL, GL_DYNAMIC_DRAW);
605 }
606 gl->BufferSubDataHelper(
607 GL_ELEMENT_ARRAY_BUFFER, 0, bytes_needed, indices);
608
609 num_elements = max_index + 1;
610 } else if (bound_vertex_array_object_->HaveEnabledClientSideBuffers()) {
611 // Index buffer is GL buffer. Ask the service for the highest vertex
612 // that will be accessed. Note: It doesn't matter if another context
613 // changes the contents of any of the buffers. The service will still
614 // validate the indices. We just need to know how much to copy across.
615 num_elements = gl->GetMaxValueInBufferCHROMIUMHelper(
616 bound_vertex_array_object_->bound_element_array_buffer(),
617 count, type, ToGLuint(indices)) + 1;
618 }
619
620 bool simulated_client_side_buffers = false;
621 SetupSimulatedClientSideBuffers(
622 function_name, gl, gl_helper, num_elements, primcount,
623 &simulated_client_side_buffers);
624 *simulated = *simulated || simulated_client_side_buffers;
625 #endif // defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
626 return true;
627 }
628
629 } // namespace gles2
630 } // namespace gpu
631
632
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698