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