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

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 == array_buffer_id_ || id == element_array_buffer_id_);
346 }
347
348 GLuint VertexArrayObjectManager::bound_element_array_buffer() const {
349 return bound_vertex_array_object_->bound_element_array_buffer();
350 }
351
352 void VertexArrayObjectManager::UnbindBuffer(GLuint id) {
353 bound_vertex_array_object_->UnbindBuffer(id);
354 }
355
356 bool VertexArrayObjectManager::BindElementArray(GLuint id) {
357 return bound_vertex_array_object_->BindElementArray(id);
358 }
359
360 void VertexArrayObjectManager::GenVertexArrays(
361 GLsizei n, const GLuint* arrays) {
362 GPU_DCHECK_GE(n, 0);
363 for (GLsizei i = 0; i < n; ++i) {
364 std::pair<VertexArrayObjectMap::iterator, bool> result =
365 vertex_array_objects_.insert(std::make_pair(
366 arrays[i], new VertexArrayObject(max_vertex_attribs_)));
367 GPU_DCHECK(result.second);
368 }
369 }
370
371 void VertexArrayObjectManager::DeleteVertexArrays(
372 GLsizei n, const GLuint* arrays) {
373 GPU_DCHECK_GE(n, 0);
374 for (GLsizei i = 0; i < n; ++i) {
375 GLuint id = arrays[i];
376 if (id) {
377 VertexArrayObjectMap::iterator it = vertex_array_objects_.find(id);
378 if (it != vertex_array_objects_.end()) {
379 if (bound_vertex_array_object_ == it->second) {
380 bound_vertex_array_object_ = default_vertex_array_object_;
381 }
382 delete it->second;
383 vertex_array_objects_.erase(it);
384 }
385 }
386 }
387 }
388
389 bool VertexArrayObjectManager::BindVertexArray(GLuint array, bool* changed) {
390 *changed = false;
391 VertexArrayObject* vertex_array_object = default_vertex_array_object_;
392 if (array != 0) {
393 VertexArrayObjectMap::iterator it = vertex_array_objects_.find(array);
394 if (it == vertex_array_objects_.end()) {
395 return false;
396 }
397 vertex_array_object = it->second;
398 }
399 *changed = vertex_array_object != bound_vertex_array_object_;
400 bound_vertex_array_object_ = vertex_array_object;
401 return true;
402 }
403
404 bool VertexArrayObjectManager::HaveEnabledClientSideBuffers() const {
405 return bound_vertex_array_object_->HaveEnabledClientSideBuffers();
406 }
407
408 void VertexArrayObjectManager::SetAttribEnable(GLuint index, bool enabled) {
409 bound_vertex_array_object_->SetAttribEnable(index, enabled);
410 }
411
412 bool VertexArrayObjectManager::GetVertexAttrib(
413 GLuint index, GLenum pname, uint32* param) {
414 return bound_vertex_array_object_->GetVertexAttrib(index, pname, param);
415 }
416
417 bool VertexArrayObjectManager::GetAttribPointer(
418 GLuint index, GLenum pname, void** ptr) const {
419 return bound_vertex_array_object_->GetAttribPointer(index, pname, ptr);
420 }
421
422 bool VertexArrayObjectManager::SetAttribPointer(
423 GLuint buffer_id,
424 GLuint index,
425 GLint size,
426 GLenum type,
427 GLboolean normalized,
428 GLsizei stride,
429 const void* ptr) {
430 // Client side arrays are not allowed in vaos.
431 if (buffer_id == 0 && !IsDefaultVAOBound()) {
432 return false;
433 }
434 bound_vertex_array_object_->SetAttribPointer(
435 buffer_id, index, size, type, normalized, stride, ptr);
436 return true;
437 }
438
439 void VertexArrayObjectManager::SetAttribDivisor(GLuint index, GLuint divisor) {
440 bound_vertex_array_object_->SetAttribDivisor(index, divisor);
441 }
442
443 // Collects the data into the collection buffer and returns the number of
444 // bytes collected.
445 GLsizei VertexArrayObjectManager::CollectData(
446 const void* data,
447 GLsizei bytes_per_element,
448 GLsizei real_stride,
449 GLsizei num_elements) {
450 GLsizei bytes_needed = bytes_per_element * num_elements;
451 if (collection_buffer_size_ < bytes_needed) {
452 collection_buffer_.reset(new int8[bytes_needed]);
453 collection_buffer_size_ = bytes_needed;
454 }
455 const int8* src = static_cast<const int8*>(data);
456 int8* dst = collection_buffer_.get();
457 int8* end = dst + bytes_per_element * num_elements;
458 for (; dst < end; src += real_stride, dst += bytes_per_element) {
459 memcpy(dst, src, bytes_per_element);
460 }
461 return bytes_needed;
462 }
463
464 bool VertexArrayObjectManager::IsDefaultVAOBound() const {
465 return bound_vertex_array_object_ == default_vertex_array_object_;
466 }
467
468 // Returns true if buffers were setup.
469 bool VertexArrayObjectManager::SetupSimulatedClientSideBuffers(
470 const char* function_name,
471 GLES2Implementation* gl,
472 GLES2CmdHelper* gl_helper,
473 GLsizei num_elements,
474 GLsizei primcount,
475 bool* simulated) {
476 *simulated = false;
477 #if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
478 if (!bound_vertex_array_object_->HaveEnabledClientSideBuffers()) {
479 return true;
480 }
481 if (!IsDefaultVAOBound()) {
482 gl->SetGLError(
483 GL_INVALID_OPERATION, function_name,
484 "client side arrays not allowed with vertex array object");
485 return false;
486 }
487 *simulated = true;
488 GLsizei total_size = 0;
489 // Compute the size of the buffer we need.
490 const VertexArrayObject::VertexAttribs& vertex_attribs =
491 bound_vertex_array_object_->vertex_attribs();
492 for (GLuint ii = 0; ii < vertex_attribs.size(); ++ii) {
493 const VertexArrayObject::VertexAttrib& attrib = vertex_attribs[ii];
494 if (attrib.IsClientSide() && attrib.enabled()) {
495 size_t bytes_per_element =
496 GLES2Util::GetGLTypeSizeForTexturesAndBuffers(attrib.type()) *
497 attrib.size();
498 GLsizei elements = (primcount && attrib.divisor() > 0) ?
499 ((primcount - 1) / attrib.divisor() + 1) : num_elements;
500 total_size += RoundUpToMultipleOf4(bytes_per_element * elements);
501 }
502 }
503 gl_helper->BindBuffer(GL_ARRAY_BUFFER, array_buffer_id_);
504 array_buffer_offset_ = 0;
505 if (total_size > array_buffer_size_) {
506 gl->BufferDataHelper(GL_ARRAY_BUFFER, total_size, NULL, GL_DYNAMIC_DRAW);
507 array_buffer_size_ = total_size;
508 }
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()) *
514 attrib.size();
515 GLsizei real_stride = attrib.stride() ?
516 attrib.stride() : static_cast<GLsizei>(bytes_per_element);
517 GLsizei elements = (primcount && attrib.divisor() > 0) ?
518 ((primcount - 1) / attrib.divisor() + 1) : num_elements;
519 GLsizei bytes_collected = CollectData(
520 attrib.pointer(), bytes_per_element, real_stride, elements);
521 gl->BufferSubDataHelper(
522 GL_ARRAY_BUFFER, array_buffer_offset_, bytes_collected,
523 collection_buffer_.get());
524 gl_helper->VertexAttribPointer(
525 ii, attrib.size(), attrib.type(), attrib.normalized(), 0,
526 array_buffer_offset_);
527 array_buffer_offset_ += RoundUpToMultipleOf4(bytes_collected);
528 GPU_DCHECK_LE(array_buffer_offset_, array_buffer_size_);
529 }
530 }
531 #endif // defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
532 return true;
533 }
534
535 // Copies in indices to the service and returns the highest index accessed + 1
536 bool VertexArrayObjectManager::SetupSimulatedIndexAndClientSideBuffers(
537 const char* function_name,
538 GLES2Implementation* gl,
539 GLES2CmdHelper* gl_helper,
540 GLsizei count,
541 GLenum type,
542 GLsizei primcount,
543 const void* indices,
544 GLuint* offset,
545 bool* simulated) {
546 *simulated = false;
547 *offset = ToGLuint(indices);
548 #if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
549 GLsizei num_elements = 0;
550 if (bound_vertex_array_object_->bound_element_array_buffer() == 0) {
551 *simulated = true;
552 *offset = 0;
553 GLsizei max_index = -1;
554 switch (type) {
555 case GL_UNSIGNED_BYTE: {
556 const uint8* src = static_cast<const uint8*>(indices);
557 for (GLsizei ii = 0; ii < count; ++ii) {
558 if (src[ii] > max_index) {
559 max_index = src[ii];
560 }
561 }
562 break;
563 }
564 case GL_UNSIGNED_SHORT: {
565 const uint16* src = static_cast<const uint16*>(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_INT: {
574 uint32 max_glsizei = static_cast<uint32>(
575 std::numeric_limits<GLsizei>::max());
576 const uint32* src = static_cast<const uint32*>(indices);
577 for (GLsizei ii = 0; ii < count; ++ii) {
578 // Other parts of the API use GLsizei (signed) to store limits.
579 // As such, if we encounter a index that cannot be represented with
580 // an unsigned int we need to flag it as an error here.
581 if(src[ii] > max_glsizei) {
582 gl->SetGLError(
583 GL_INVALID_OPERATION, function_name, "index too large.");
584 return false;
585 }
586 GLsizei signed_index = static_cast<GLsizei>(src[ii]);
587 if (signed_index > max_index) {
588 max_index = signed_index;
589 }
590 }
591 break;
592 }
593 default:
594 break;
595 }
596 gl_helper->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_array_buffer_id_);
597 GLsizei bytes_per_element =
598 GLES2Util::GetGLTypeSizeForTexturesAndBuffers(type);
599 GLsizei bytes_needed = bytes_per_element * count;
600 if (bytes_needed > element_array_buffer_size_) {
601 element_array_buffer_size_ = bytes_needed;
602 gl->BufferDataHelper(
603 GL_ELEMENT_ARRAY_BUFFER, bytes_needed, NULL, GL_DYNAMIC_DRAW);
604 }
605 gl->BufferSubDataHelper(
606 GL_ELEMENT_ARRAY_BUFFER, 0, bytes_needed, indices);
607
608 num_elements = max_index + 1;
609 } else if (bound_vertex_array_object_->HaveEnabledClientSideBuffers()) {
610 // Index buffer is GL buffer. Ask the service for the highest vertex
611 // that will be accessed. Note: It doesn't matter if another context
612 // changes the contents of any of the buffers. The service will still
613 // validate the indices. We just need to know how much to copy across.
614 num_elements = gl->GetMaxValueInBufferCHROMIUMHelper(
615 bound_vertex_array_object_->bound_element_array_buffer(),
616 count, type, ToGLuint(indices)) + 1;
617 }
618
619 bool simulated_client_side_buffers = false;
620 SetupSimulatedClientSideBuffers(
621 function_name, gl, gl_helper, num_elements, primcount,
622 &simulated_client_side_buffers);
623 *simulated = *simulated || simulated_client_side_buffers;
624 #endif // defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS)
625 return true;
626 }
627
628 } // namespace gles2
629 } // namespace gpu
630
631
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698