• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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