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