1 //
2 // Copyright 2013 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // Implementation of the state class for mananging GLES 3 Vertex Array Objects.
7 //
8
9 #include "libANGLE/VertexArray.h"
10
11 #include "common/utilities.h"
12 #include "libANGLE/Buffer.h"
13 #include "libANGLE/Context.h"
14 #include "libANGLE/renderer/BufferImpl.h"
15 #include "libANGLE/renderer/GLImplFactory.h"
16 #include "libANGLE/renderer/VertexArrayImpl.h"
17
18 namespace gl
19 {
20 namespace
21 {
IsElementArrayBufferSubjectIndex(angle::SubjectIndex subjectIndex)22 bool IsElementArrayBufferSubjectIndex(angle::SubjectIndex subjectIndex)
23 {
24 return (subjectIndex == kElementArrayBufferIndex);
25 }
26 } // namespace
27
28 // VertexArrayState implementation.
VertexArrayState(VertexArray * vertexArray,size_t maxAttribs,size_t maxAttribBindings)29 VertexArrayState::VertexArrayState(VertexArray *vertexArray,
30 size_t maxAttribs,
31 size_t maxAttribBindings)
32 : mElementArrayBuffer(vertexArray, kElementArrayBufferIndex)
33 {
34 ASSERT(maxAttribs <= maxAttribBindings);
35
36 for (size_t i = 0; i < maxAttribs; i++)
37 {
38 mVertexAttributes.emplace_back(static_cast<GLuint>(i));
39 mVertexBindings.emplace_back(static_cast<GLuint>(i));
40 }
41
42 // Initially all attributes start as "client" with no buffer bound.
43 mClientMemoryAttribsMask.set();
44 }
45
~VertexArrayState()46 VertexArrayState::~VertexArrayState() {}
47
hasEnabledNullPointerClientArray() const48 bool VertexArrayState::hasEnabledNullPointerClientArray() const
49 {
50 return (mNullPointerClientMemoryAttribsMask & mEnabledAttributesMask).any();
51 }
52
getBindingToAttributesMask(GLuint bindingIndex) const53 AttributesMask VertexArrayState::getBindingToAttributesMask(GLuint bindingIndex) const
54 {
55 ASSERT(bindingIndex < mVertexBindings.size());
56 return mVertexBindings[bindingIndex].getBoundAttributesMask();
57 }
58
59 // Set an attribute using a new binding.
setAttribBinding(const Context * context,size_t attribIndex,GLuint newBindingIndex)60 void VertexArrayState::setAttribBinding(const Context *context,
61 size_t attribIndex,
62 GLuint newBindingIndex)
63 {
64 ASSERT(attribIndex < mVertexAttributes.size() && newBindingIndex < mVertexBindings.size());
65
66 VertexAttribute &attrib = mVertexAttributes[attribIndex];
67
68 // Update the binding-attribute map.
69 const GLuint oldBindingIndex = attrib.bindingIndex;
70 ASSERT(oldBindingIndex != newBindingIndex);
71
72 VertexBinding &oldBinding = mVertexBindings[oldBindingIndex];
73 VertexBinding &newBinding = mVertexBindings[newBindingIndex];
74
75 ASSERT(oldBinding.getBoundAttributesMask().test(attribIndex) &&
76 !newBinding.getBoundAttributesMask().test(attribIndex));
77
78 oldBinding.resetBoundAttribute(attribIndex);
79 newBinding.setBoundAttribute(attribIndex);
80
81 // Set the attribute using the new binding.
82 attrib.bindingIndex = newBindingIndex;
83
84 if (context->isBufferAccessValidationEnabled())
85 {
86 attrib.updateCachedElementLimit(newBinding);
87 }
88
89 bool isMapped = newBinding.getBuffer().get() && newBinding.getBuffer()->isMapped();
90 mCachedMappedArrayBuffers.set(attribIndex, isMapped);
91 mEnabledAttributesMask.set(attribIndex, attrib.enabled);
92 updateCachedMutableOrNonPersistentArrayBuffers(attribIndex);
93 mCachedInvalidMappedArrayBuffer = mCachedMappedArrayBuffers & mEnabledAttributesMask &
94 mCachedMutableOrImpersistentArrayBuffers;
95 }
96
updateCachedMutableOrNonPersistentArrayBuffers(size_t index)97 void VertexArrayState::updateCachedMutableOrNonPersistentArrayBuffers(size_t index)
98 {
99 const VertexBinding &vertexBinding = mVertexBindings[index];
100 const BindingPointer<Buffer> &buffer = vertexBinding.getBuffer();
101 bool isMutableOrImpersistentArrayBuffer =
102 buffer.get() &&
103 (!buffer->isImmutable() || (buffer->getAccessFlags() & GL_MAP_PERSISTENT_BIT_EXT) == 0);
104 mCachedMutableOrImpersistentArrayBuffers.set(index, isMutableOrImpersistentArrayBuffer);
105 }
106
107 // VertexArray implementation.
VertexArray(rx::GLImplFactory * factory,VertexArrayID id,size_t maxAttribs,size_t maxAttribBindings)108 VertexArray::VertexArray(rx::GLImplFactory *factory,
109 VertexArrayID id,
110 size_t maxAttribs,
111 size_t maxAttribBindings)
112 : mId(id),
113 mState(this, maxAttribs, maxAttribBindings),
114 mVertexArray(factory->createVertexArray(mState)),
115 mBufferAccessValidationEnabled(false),
116 mContentsObservers(this)
117 {
118 for (size_t attribIndex = 0; attribIndex < maxAttribBindings; ++attribIndex)
119 {
120 mArrayBufferObserverBindings.emplace_back(this, attribIndex);
121 }
122
123 mVertexArray->setContentsObservers(&mContentsObservers);
124 }
125
onDestroy(const Context * context)126 void VertexArray::onDestroy(const Context *context)
127 {
128 bool isBound = context->isCurrentVertexArray(this);
129 for (uint32_t bindingIndex = 0; bindingIndex < mState.mVertexBindings.size(); ++bindingIndex)
130 {
131 VertexBinding &binding = mState.mVertexBindings[bindingIndex];
132 Buffer *buffer = binding.getBuffer().get();
133 if (isBound)
134 {
135 if (buffer)
136 {
137 buffer->onNonTFBindingChanged(-1);
138 }
139 }
140 if (buffer)
141 {
142 // Note: the non-contents observer is unbound in the ObserverBinding destructor.
143 buffer->removeContentsObserver(this, bindingIndex);
144 }
145 binding.setBuffer(context, nullptr);
146 }
147 if (mState.mElementArrayBuffer.get())
148 {
149 if (isBound)
150 {
151 mState.mElementArrayBuffer->onNonTFBindingChanged(-1);
152 }
153 mState.mElementArrayBuffer->removeContentsObserver(this, kElementArrayBufferIndex);
154 }
155 mState.mElementArrayBuffer.bind(context, nullptr);
156 mVertexArray->destroy(context);
157 SafeDelete(mVertexArray);
158 delete this;
159 }
160
~VertexArray()161 VertexArray::~VertexArray()
162 {
163 ASSERT(!mVertexArray);
164 }
165
setLabel(const Context * context,const std::string & label)166 void VertexArray::setLabel(const Context *context, const std::string &label)
167 {
168 mState.mLabel = label;
169 }
170
getLabel() const171 const std::string &VertexArray::getLabel() const
172 {
173 return mState.mLabel;
174 }
175
detachBuffer(const Context * context,BufferID bufferID)176 bool VertexArray::detachBuffer(const Context *context, BufferID bufferID)
177 {
178 bool isBound = context->isCurrentVertexArray(this);
179 bool anyBufferDetached = false;
180 for (uint32_t bindingIndex = 0; bindingIndex < mState.mVertexBindings.size(); ++bindingIndex)
181 {
182 VertexBinding &binding = mState.mVertexBindings[bindingIndex];
183 const BindingPointer<Buffer> &bufferBinding = binding.getBuffer();
184 if (bufferBinding.id() == bufferID)
185 {
186 if (isBound)
187 {
188 if (bufferBinding.get())
189 bufferBinding->onNonTFBindingChanged(-1);
190 }
191 bufferBinding->removeContentsObserver(this, bindingIndex);
192 binding.setBuffer(context, nullptr);
193 mArrayBufferObserverBindings[bindingIndex].reset();
194
195 if (context->getClientVersion() >= ES_3_1)
196 {
197 setDirtyBindingBit(bindingIndex, DIRTY_BINDING_BUFFER);
198 }
199 else
200 {
201 static_assert(gl::MAX_VERTEX_ATTRIB_BINDINGS < 8 * sizeof(uint32_t),
202 "Not enough bits in bindingIndex");
203 // The redundant uint32_t cast here is required to avoid a warning on MSVC.
204 ASSERT(binding.getBoundAttributesMask() ==
205 AttributesMask(static_cast<uint32_t>(1 << bindingIndex)));
206 setDirtyAttribBit(bindingIndex, DIRTY_ATTRIB_POINTER);
207 }
208
209 anyBufferDetached = true;
210 mState.mClientMemoryAttribsMask |= binding.getBoundAttributesMask();
211 }
212 }
213
214 if (mState.mElementArrayBuffer.get() && mState.mElementArrayBuffer->id() == bufferID)
215 {
216 if (isBound && mState.mElementArrayBuffer.get())
217 mState.mElementArrayBuffer->onNonTFBindingChanged(-1);
218 mState.mElementArrayBuffer.bind(context, nullptr);
219 mDirtyBits.set(DIRTY_BIT_ELEMENT_ARRAY_BUFFER);
220 anyBufferDetached = true;
221 }
222
223 return anyBufferDetached;
224 }
225
getVertexAttribute(size_t attribIndex) const226 const VertexAttribute &VertexArray::getVertexAttribute(size_t attribIndex) const
227 {
228 ASSERT(attribIndex < getMaxAttribs());
229 return mState.mVertexAttributes[attribIndex];
230 }
231
getVertexBinding(size_t bindingIndex) const232 const VertexBinding &VertexArray::getVertexBinding(size_t bindingIndex) const
233 {
234 ASSERT(bindingIndex < getMaxBindings());
235 return mState.mVertexBindings[bindingIndex];
236 }
237
GetVertexIndexFromDirtyBit(size_t dirtyBit)238 size_t VertexArray::GetVertexIndexFromDirtyBit(size_t dirtyBit)
239 {
240 static_assert(gl::MAX_VERTEX_ATTRIBS == gl::MAX_VERTEX_ATTRIB_BINDINGS,
241 "The stride of vertex attributes should equal to that of vertex bindings.");
242 ASSERT(dirtyBit > DIRTY_BIT_ELEMENT_ARRAY_BUFFER);
243 return (dirtyBit - DIRTY_BIT_ATTRIB_0) % gl::MAX_VERTEX_ATTRIBS;
244 }
245
setDirtyAttribBit(size_t attribIndex,DirtyAttribBitType dirtyAttribBit)246 ANGLE_INLINE void VertexArray::setDirtyAttribBit(size_t attribIndex,
247 DirtyAttribBitType dirtyAttribBit)
248 {
249 mDirtyBits.set(DIRTY_BIT_ATTRIB_0 + attribIndex);
250 mDirtyAttribBits[attribIndex].set(dirtyAttribBit);
251 }
252
clearDirtyAttribBit(size_t attribIndex,DirtyAttribBitType dirtyAttribBit)253 ANGLE_INLINE void VertexArray::clearDirtyAttribBit(size_t attribIndex,
254 DirtyAttribBitType dirtyAttribBit)
255 {
256 mDirtyAttribBits[attribIndex].set(dirtyAttribBit, false);
257 if (mDirtyAttribBits[attribIndex].any())
258 {
259 return;
260 }
261 mDirtyBits.set(DIRTY_BIT_ATTRIB_0 + attribIndex, false);
262 }
263
setDirtyBindingBit(size_t bindingIndex,DirtyBindingBitType dirtyBindingBit)264 ANGLE_INLINE void VertexArray::setDirtyBindingBit(size_t bindingIndex,
265 DirtyBindingBitType dirtyBindingBit)
266 {
267 mDirtyBits.set(DIRTY_BIT_BINDING_0 + bindingIndex);
268 mDirtyBindingBits[bindingIndex].set(dirtyBindingBit);
269 }
270
updateCachedBufferBindingSize(VertexBinding * binding)271 ANGLE_INLINE void VertexArray::updateCachedBufferBindingSize(VertexBinding *binding)
272 {
273 if (!mBufferAccessValidationEnabled)
274 return;
275
276 for (size_t boundAttribute : binding->getBoundAttributesMask())
277 {
278 mState.mVertexAttributes[boundAttribute].updateCachedElementLimit(*binding);
279 }
280 }
281
updateCachedArrayBuffersMasks(bool isMapped,bool isImmutable,bool isPersistent,const AttributesMask & boundAttributesMask)282 ANGLE_INLINE void VertexArray::updateCachedArrayBuffersMasks(
283 bool isMapped,
284 bool isImmutable,
285 bool isPersistent,
286 const AttributesMask &boundAttributesMask)
287 {
288 if (isMapped)
289 {
290 mState.mCachedMappedArrayBuffers |= boundAttributesMask;
291 }
292 else
293 {
294 mState.mCachedMappedArrayBuffers &= ~boundAttributesMask;
295 }
296
297 if (!isImmutable || !isPersistent)
298 {
299 mState.mCachedMutableOrImpersistentArrayBuffers |= boundAttributesMask;
300 }
301 else
302 {
303 mState.mCachedMutableOrImpersistentArrayBuffers &= ~boundAttributesMask;
304 }
305
306 mState.mCachedInvalidMappedArrayBuffer = mState.mCachedMappedArrayBuffers &
307 mState.mEnabledAttributesMask &
308 mState.mCachedMutableOrImpersistentArrayBuffers;
309 }
310
updateCachedMappedArrayBuffersBinding(const VertexBinding & binding)311 ANGLE_INLINE void VertexArray::updateCachedMappedArrayBuffersBinding(const VertexBinding &binding)
312 {
313 const Buffer *buffer = binding.getBuffer().get();
314 bool isMapped = buffer && buffer->isMapped();
315 bool isImmutable = buffer && buffer->isImmutable();
316 bool isPersistent = buffer && (buffer->getAccessFlags() & GL_MAP_PERSISTENT_BIT_EXT) != 0;
317 return updateCachedArrayBuffersMasks(isMapped, isImmutable, isPersistent,
318 binding.getBoundAttributesMask());
319 }
320
updateCachedTransformFeedbackBindingValidation(size_t bindingIndex,const Buffer * buffer)321 ANGLE_INLINE void VertexArray::updateCachedTransformFeedbackBindingValidation(size_t bindingIndex,
322 const Buffer *buffer)
323 {
324 const bool hasConflict = buffer && buffer->hasWebGLXFBBindingConflict(true);
325 mCachedTransformFeedbackConflictedBindingsMask.set(bindingIndex, hasConflict);
326 }
327
bindVertexBufferImpl(const Context * context,size_t bindingIndex,Buffer * boundBuffer,GLintptr offset,GLsizei stride)328 bool VertexArray::bindVertexBufferImpl(const Context *context,
329 size_t bindingIndex,
330 Buffer *boundBuffer,
331 GLintptr offset,
332 GLsizei stride)
333 {
334 ASSERT(bindingIndex < getMaxBindings());
335 ASSERT(context->isCurrentVertexArray(this));
336
337 VertexBinding *binding = &mState.mVertexBindings[bindingIndex];
338
339 Buffer *oldBuffer = binding->getBuffer().get();
340
341 const bool sameBuffer = oldBuffer == boundBuffer;
342 const bool sameStride = static_cast<GLuint>(stride) == binding->getStride();
343 const bool sameOffset = offset == binding->getOffset();
344
345 if (sameBuffer && sameStride && sameOffset)
346 {
347 return false;
348 }
349
350 angle::ObserverBinding *observer = &mArrayBufferObserverBindings[bindingIndex];
351 observer->assignSubject(boundBuffer);
352
353 // Several nullptr checks are combined here for optimization purposes.
354 if (oldBuffer)
355 {
356 oldBuffer->onNonTFBindingChanged(-1);
357 oldBuffer->removeObserver(observer);
358 oldBuffer->removeContentsObserver(this, static_cast<uint32_t>(bindingIndex));
359 oldBuffer->release(context);
360 }
361
362 binding->assignBuffer(boundBuffer);
363 binding->setOffset(offset);
364 binding->setStride(stride);
365 updateCachedBufferBindingSize(binding);
366
367 // Update client memory attribute pointers. Affects all bound attributes.
368 if (boundBuffer)
369 {
370 boundBuffer->addRef();
371 boundBuffer->onNonTFBindingChanged(1);
372 boundBuffer->addObserver(observer);
373 if (context->isWebGL())
374 {
375 mCachedTransformFeedbackConflictedBindingsMask.set(
376 bindingIndex, boundBuffer->hasWebGLXFBBindingConflict(true));
377 }
378 mState.mClientMemoryAttribsMask &= ~binding->getBoundAttributesMask();
379
380 bool isMapped = boundBuffer->isMapped() == GL_TRUE;
381 bool isImmutable = boundBuffer->isImmutable() == GL_TRUE;
382 bool isPersistent = (boundBuffer->getAccessFlags() & GL_MAP_PERSISTENT_BIT_EXT) != 0;
383 updateCachedArrayBuffersMasks(isMapped, isImmutable, isPersistent,
384 binding->getBoundAttributesMask());
385 }
386 else
387 {
388 if (context->isWebGL())
389 {
390 mCachedTransformFeedbackConflictedBindingsMask.set(bindingIndex, false);
391 }
392 mState.mClientMemoryAttribsMask |= binding->getBoundAttributesMask();
393 updateCachedArrayBuffersMasks(false, false, false, binding->getBoundAttributesMask());
394 }
395
396 return true;
397 }
398
bindVertexBuffer(const Context * context,size_t bindingIndex,Buffer * boundBuffer,GLintptr offset,GLsizei stride)399 void VertexArray::bindVertexBuffer(const Context *context,
400 size_t bindingIndex,
401 Buffer *boundBuffer,
402 GLintptr offset,
403 GLsizei stride)
404 {
405 if (bindVertexBufferImpl(context, bindingIndex, boundBuffer, offset, stride))
406 {
407 setDirtyBindingBit(bindingIndex, DIRTY_BINDING_BUFFER);
408 }
409 }
410
setVertexAttribBinding(const Context * context,size_t attribIndex,GLuint bindingIndex)411 void VertexArray::setVertexAttribBinding(const Context *context,
412 size_t attribIndex,
413 GLuint bindingIndex)
414 {
415 ASSERT(attribIndex < getMaxAttribs() && bindingIndex < getMaxBindings());
416
417 if (mState.mVertexAttributes[attribIndex].bindingIndex == bindingIndex)
418 {
419 return;
420 }
421
422 // In ES 3.0 contexts, the binding cannot change, hence the code below is unreachable.
423 ASSERT(context->getClientVersion() >= ES_3_1);
424
425 mState.setAttribBinding(context, attribIndex, bindingIndex);
426
427 setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_BINDING);
428
429 // Update client attribs mask.
430 bool hasBuffer = mState.mVertexBindings[bindingIndex].getBuffer().get() != nullptr;
431 mState.mClientMemoryAttribsMask.set(attribIndex, !hasBuffer);
432 }
433
setVertexBindingDivisor(const Context * context,size_t bindingIndex,GLuint divisor)434 void VertexArray::setVertexBindingDivisor(const Context *context,
435 size_t bindingIndex,
436 GLuint divisor)
437 {
438 ASSERT(bindingIndex < getMaxBindings());
439
440 VertexBinding &binding = mState.mVertexBindings[bindingIndex];
441
442 if (binding.getDivisor() == divisor)
443 {
444 return;
445 }
446
447 binding.setDivisor(divisor);
448 setDirtyBindingBit(bindingIndex, DIRTY_BINDING_DIVISOR);
449
450 // Trigger updates in all bound attributes.
451 if (context->isBufferAccessValidationEnabled())
452 {
453 for (size_t attribIndex : binding.getBoundAttributesMask())
454 {
455 mState.mVertexAttributes[attribIndex].updateCachedElementLimit(binding);
456 }
457 }
458 }
459
setVertexAttribFormatImpl(VertexAttribute * attrib,GLint size,VertexAttribType type,bool normalized,bool pureInteger,GLuint relativeOffset)460 ANGLE_INLINE bool VertexArray::setVertexAttribFormatImpl(VertexAttribute *attrib,
461 GLint size,
462 VertexAttribType type,
463 bool normalized,
464 bool pureInteger,
465 GLuint relativeOffset)
466 {
467 angle::FormatID formatID = gl::GetVertexFormatID(type, normalized, size, pureInteger);
468
469 if (formatID != attrib->format->id || attrib->relativeOffset != relativeOffset)
470 {
471 attrib->relativeOffset = relativeOffset;
472 attrib->format = &angle::Format::Get(formatID);
473 return true;
474 }
475
476 return false;
477 }
478
setVertexAttribFormat(size_t attribIndex,GLint size,VertexAttribType type,bool normalized,bool pureInteger,GLuint relativeOffset)479 void VertexArray::setVertexAttribFormat(size_t attribIndex,
480 GLint size,
481 VertexAttribType type,
482 bool normalized,
483 bool pureInteger,
484 GLuint relativeOffset)
485 {
486 VertexAttribute &attrib = mState.mVertexAttributes[attribIndex];
487
488 ComponentType componentType = GetVertexAttributeComponentType(pureInteger, type);
489 SetComponentTypeMask(componentType, attribIndex, &mState.mVertexAttributesTypeMask);
490
491 if (setVertexAttribFormatImpl(&attrib, size, type, normalized, pureInteger, relativeOffset))
492 {
493 setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_FORMAT);
494 }
495
496 attrib.updateCachedElementLimit(mState.mVertexBindings[attrib.bindingIndex]);
497 }
498
setVertexAttribDivisor(const Context * context,size_t attribIndex,GLuint divisor)499 void VertexArray::setVertexAttribDivisor(const Context *context, size_t attribIndex, GLuint divisor)
500 {
501 ASSERT(attribIndex < getMaxAttribs());
502
503 setVertexAttribBinding(context, attribIndex, static_cast<GLuint>(attribIndex));
504 setVertexBindingDivisor(context, attribIndex, divisor);
505 }
506
enableAttribute(size_t attribIndex,bool enabledState)507 void VertexArray::enableAttribute(size_t attribIndex, bool enabledState)
508 {
509 ASSERT(attribIndex < getMaxAttribs());
510
511 VertexAttribute &attrib = mState.mVertexAttributes[attribIndex];
512
513 if (mState.mEnabledAttributesMask.test(attribIndex) == enabledState)
514 {
515 return;
516 }
517
518 attrib.enabled = enabledState;
519
520 // Update state cache
521 mState.mEnabledAttributesMask.set(attribIndex, enabledState);
522 bool enableChanged = (mState.mEnabledAttributesMask.test(attribIndex) !=
523 mState.mLastSyncedEnabledAttributesMask.test(attribIndex));
524
525 if (enableChanged)
526 {
527 setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_ENABLED);
528 }
529 else
530 {
531 clearDirtyAttribBit(attribIndex, DIRTY_ATTRIB_ENABLED);
532 }
533
534 mState.updateCachedMutableOrNonPersistentArrayBuffers(attribIndex);
535 mState.mCachedInvalidMappedArrayBuffer = mState.mCachedMappedArrayBuffers &
536 mState.mEnabledAttributesMask &
537 mState.mCachedMutableOrImpersistentArrayBuffers;
538 }
539
setVertexAttribPointerImpl(const Context * context,ComponentType componentType,bool pureInteger,size_t attribIndex,Buffer * boundBuffer,GLint size,VertexAttribType type,bool normalized,GLsizei stride,const void * pointer)540 ANGLE_INLINE void VertexArray::setVertexAttribPointerImpl(const Context *context,
541 ComponentType componentType,
542 bool pureInteger,
543 size_t attribIndex,
544 Buffer *boundBuffer,
545 GLint size,
546 VertexAttribType type,
547 bool normalized,
548 GLsizei stride,
549 const void *pointer)
550 {
551 ASSERT(attribIndex < getMaxAttribs());
552
553 VertexAttribute &attrib = mState.mVertexAttributes[attribIndex];
554
555 SetComponentTypeMask(componentType, attribIndex, &mState.mVertexAttributesTypeMask);
556
557 bool attribDirty = setVertexAttribFormatImpl(&attrib, size, type, normalized, pureInteger, 0);
558
559 if (attrib.bindingIndex != attribIndex)
560 {
561 setVertexAttribBinding(context, attribIndex, static_cast<GLuint>(attribIndex));
562 }
563
564 GLsizei effectiveStride =
565 stride == 0 ? static_cast<GLsizei>(ComputeVertexAttributeTypeSize(attrib)) : stride;
566
567 if (attrib.vertexAttribArrayStride != static_cast<GLuint>(stride))
568 {
569 attribDirty = true;
570 }
571 attrib.vertexAttribArrayStride = stride;
572
573 // If we switch from an array buffer to a client pointer(or vice-versa), we set the whole
574 // attribute dirty. This notifies the Vulkan back-end to update all its caches.
575 const VertexBinding &binding = mState.mVertexBindings[attribIndex];
576 if ((boundBuffer == nullptr) != (binding.getBuffer().get() == nullptr))
577 {
578 attribDirty = true;
579 }
580
581 // Change of attrib.pointer is not part of attribDirty. Pointer is actually the buffer offset
582 // which is handled within bindVertexBufferImpl and reflected in bufferDirty.
583 attrib.pointer = pointer;
584 GLintptr offset = boundBuffer ? reinterpret_cast<GLintptr>(pointer) : 0;
585 const bool bufferDirty =
586 bindVertexBufferImpl(context, attribIndex, boundBuffer, offset, effectiveStride);
587
588 if (attribDirty)
589 {
590 setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_POINTER);
591 }
592 else if (bufferDirty)
593 {
594 setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_POINTER_BUFFER);
595 }
596
597 mState.mNullPointerClientMemoryAttribsMask.set(attribIndex,
598 boundBuffer == nullptr && pointer == nullptr);
599 }
600
setVertexAttribPointer(const Context * context,size_t attribIndex,gl::Buffer * boundBuffer,GLint size,VertexAttribType type,bool normalized,GLsizei stride,const void * pointer)601 void VertexArray::setVertexAttribPointer(const Context *context,
602 size_t attribIndex,
603 gl::Buffer *boundBuffer,
604 GLint size,
605 VertexAttribType type,
606 bool normalized,
607 GLsizei stride,
608 const void *pointer)
609 {
610 setVertexAttribPointerImpl(context, ComponentType::Float, false, attribIndex, boundBuffer, size,
611 type, normalized, stride, pointer);
612 }
613
setVertexAttribIPointer(const Context * context,size_t attribIndex,gl::Buffer * boundBuffer,GLint size,VertexAttribType type,GLsizei stride,const void * pointer)614 void VertexArray::setVertexAttribIPointer(const Context *context,
615 size_t attribIndex,
616 gl::Buffer *boundBuffer,
617 GLint size,
618 VertexAttribType type,
619 GLsizei stride,
620 const void *pointer)
621 {
622 ComponentType componentType = GetVertexAttributeComponentType(true, type);
623 setVertexAttribPointerImpl(context, componentType, true, attribIndex, boundBuffer, size, type,
624 false, stride, pointer);
625 }
626
syncState(const Context * context)627 angle::Result VertexArray::syncState(const Context *context)
628 {
629 if (mDirtyBits.any())
630 {
631 mDirtyBitsGuard = mDirtyBits;
632 ANGLE_TRY(
633 mVertexArray->syncState(context, mDirtyBits, &mDirtyAttribBits, &mDirtyBindingBits));
634 mDirtyBits.reset();
635 mDirtyBitsGuard.reset();
636
637 // The dirty bits should be reset in the back-end. To simplify ASSERTs only check attrib 0.
638 ASSERT(mDirtyAttribBits[0].none());
639 ASSERT(mDirtyBindingBits[0].none());
640 mState.mLastSyncedEnabledAttributesMask = mState.mEnabledAttributesMask;
641 }
642 return angle::Result::Continue;
643 }
644
onBindingChanged(const Context * context,int incr)645 void VertexArray::onBindingChanged(const Context *context, int incr)
646 {
647 if (mState.mElementArrayBuffer.get())
648 mState.mElementArrayBuffer->onNonTFBindingChanged(incr);
649 for (auto &binding : mState.mVertexBindings)
650 {
651 binding.onContainerBindingChanged(context, incr);
652 }
653 }
654
getDirtyBitFromIndex(bool contentsChanged,angle::SubjectIndex index) const655 VertexArray::DirtyBitType VertexArray::getDirtyBitFromIndex(bool contentsChanged,
656 angle::SubjectIndex index) const
657 {
658 if (IsElementArrayBufferSubjectIndex(index))
659 {
660 mIndexRangeCache.invalidate();
661 return contentsChanged ? DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA
662 : DIRTY_BIT_ELEMENT_ARRAY_BUFFER;
663 }
664 else
665 {
666 // Note: this currently just gets the top-level dirty bit.
667 ASSERT(index < mArrayBufferObserverBindings.size());
668 return static_cast<DirtyBitType>(
669 (contentsChanged ? DIRTY_BIT_BUFFER_DATA_0 : DIRTY_BIT_BINDING_0) + index);
670 }
671 }
672
onSubjectStateChange(angle::SubjectIndex index,angle::SubjectMessage message)673 void VertexArray::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message)
674 {
675 switch (message)
676 {
677 case angle::SubjectMessage::ContentsChanged:
678 ASSERT(IsElementArrayBufferSubjectIndex(index));
679 setDependentDirtyBit(true, index);
680 break;
681
682 case angle::SubjectMessage::SubjectChanged:
683 if (!IsElementArrayBufferSubjectIndex(index))
684 {
685 updateCachedBufferBindingSize(&mState.mVertexBindings[index]);
686 }
687 setDependentDirtyBit(false, index);
688 break;
689
690 case angle::SubjectMessage::BindingChanged:
691 if (!IsElementArrayBufferSubjectIndex(index))
692 {
693 const Buffer *buffer = mState.mVertexBindings[index].getBuffer().get();
694 updateCachedTransformFeedbackBindingValidation(index, buffer);
695 }
696 break;
697
698 case angle::SubjectMessage::SubjectMapped:
699 if (!IsElementArrayBufferSubjectIndex(index))
700 {
701 updateCachedMappedArrayBuffersBinding(mState.mVertexBindings[index]);
702 }
703 onStateChange(angle::SubjectMessage::SubjectMapped);
704 break;
705
706 case angle::SubjectMessage::SubjectUnmapped:
707 setDependentDirtyBit(true, index);
708
709 if (!IsElementArrayBufferSubjectIndex(index))
710 {
711 updateCachedMappedArrayBuffersBinding(mState.mVertexBindings[index]);
712 }
713 onStateChange(angle::SubjectMessage::SubjectUnmapped);
714 break;
715
716 case angle::SubjectMessage::InternalMemoryAllocationChanged:
717 case angle::SubjectMessage::BufferVkStorageChanged:
718 setDependentDirtyBit(false, index);
719 break;
720
721 default:
722 UNREACHABLE();
723 break;
724 }
725 }
726
setDependentDirtyBit(bool contentsChanged,angle::SubjectIndex index)727 void VertexArray::setDependentDirtyBit(bool contentsChanged, angle::SubjectIndex index)
728 {
729 DirtyBitType dirtyBit = getDirtyBitFromIndex(contentsChanged, index);
730 ASSERT(!mDirtyBitsGuard.valid() || mDirtyBitsGuard.value().test(dirtyBit));
731 mDirtyBits.set(dirtyBit);
732 onStateChange(angle::SubjectMessage::ContentsChanged);
733 }
734
hasTransformFeedbackBindingConflict(const gl::Context * context) const735 bool VertexArray::hasTransformFeedbackBindingConflict(const gl::Context *context) const
736 {
737 // Fast check first.
738 if (!mCachedTransformFeedbackConflictedBindingsMask.any())
739 {
740 return false;
741 }
742
743 const AttributesMask &activeAttribues = context->getStateCache().getActiveBufferedAttribsMask();
744
745 // Slow check. We must ensure that the conflicting attributes are enabled/active.
746 for (size_t attribIndex : activeAttribues)
747 {
748 const VertexAttribute &attrib = mState.mVertexAttributes[attribIndex];
749 if (mCachedTransformFeedbackConflictedBindingsMask[attrib.bindingIndex])
750 {
751 return true;
752 }
753 }
754
755 return false;
756 }
757
getIndexRangeImpl(const Context * context,DrawElementsType type,GLsizei indexCount,const void * indices,IndexRange * indexRangeOut) const758 angle::Result VertexArray::getIndexRangeImpl(const Context *context,
759 DrawElementsType type,
760 GLsizei indexCount,
761 const void *indices,
762 IndexRange *indexRangeOut) const
763 {
764 Buffer *elementArrayBuffer = mState.mElementArrayBuffer.get();
765 if (!elementArrayBuffer)
766 {
767 *indexRangeOut = ComputeIndexRange(type, indices, indexCount,
768 context->getState().isPrimitiveRestartEnabled());
769 return angle::Result::Continue;
770 }
771
772 size_t offset = reinterpret_cast<uintptr_t>(indices);
773 ANGLE_TRY(elementArrayBuffer->getIndexRange(context, type, offset, indexCount,
774 context->getState().isPrimitiveRestartEnabled(),
775 indexRangeOut));
776
777 mIndexRangeCache.put(type, indexCount, offset, *indexRangeOut);
778 return angle::Result::Continue;
779 }
780
781 VertexArray::IndexRangeCache::IndexRangeCache() = default;
782
put(DrawElementsType type,GLsizei indexCount,size_t offset,const IndexRange & indexRange)783 void VertexArray::IndexRangeCache::put(DrawElementsType type,
784 GLsizei indexCount,
785 size_t offset,
786 const IndexRange &indexRange)
787 {
788 ASSERT(type != DrawElementsType::InvalidEnum);
789
790 mTypeKey = type;
791 mIndexCountKey = indexCount;
792 mOffsetKey = offset;
793 mPayload = indexRange;
794 }
795
onBufferContentsChange(uint32_t bufferIndex)796 void VertexArray::onBufferContentsChange(uint32_t bufferIndex)
797 {
798 setDependentDirtyBit(true, bufferIndex);
799 }
800
VertexArrayBufferContentsObservers(VertexArray * vertexArray)801 VertexArrayBufferContentsObservers::VertexArrayBufferContentsObservers(VertexArray *vertexArray)
802 : mVertexArray(vertexArray)
803 {}
804
enableForBuffer(Buffer * buffer,uint32_t bufferIndex)805 void VertexArrayBufferContentsObservers::enableForBuffer(Buffer *buffer, uint32_t bufferIndex)
806 {
807 buffer->addContentsObserver(mVertexArray, bufferIndex);
808 }
809
disableForBuffer(Buffer * buffer,uint32_t bufferIndex)810 void VertexArrayBufferContentsObservers::disableForBuffer(Buffer *buffer, uint32_t bufferIndex)
811 {
812 buffer->removeContentsObserver(mVertexArray, bufferIndex);
813 }
814 } // namespace gl
815