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 : mId(vertexArray->id()), 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
isDefault() const107 bool VertexArrayState::isDefault() const
108 {
109 return mId.value == 0;
110 }
111
112 // VertexArray implementation.
VertexArray(rx::GLImplFactory * factory,VertexArrayID id,size_t maxAttribs,size_t maxAttribBindings)113 VertexArray::VertexArray(rx::GLImplFactory *factory,
114 VertexArrayID id,
115 size_t maxAttribs,
116 size_t maxAttribBindings)
117 : mId(id),
118 mState(this, maxAttribs, maxAttribBindings),
119 mVertexArray(factory->createVertexArray(mState)),
120 mBufferAccessValidationEnabled(false),
121 mContentsObservers(this)
122 {
123 for (size_t attribIndex = 0; attribIndex < maxAttribBindings; ++attribIndex)
124 {
125 mArrayBufferObserverBindings.emplace_back(this, attribIndex);
126 }
127
128 mVertexArray->setContentsObservers(&mContentsObservers);
129 }
130
onDestroy(const Context * context)131 void VertexArray::onDestroy(const Context *context)
132 {
133 bool isBound = context->isCurrentVertexArray(this);
134 for (size_t bindingIndex : mState.mBufferBindingMask)
135 {
136 VertexBinding &binding = mState.mVertexBindings[bindingIndex];
137 Buffer *buffer = binding.getBuffer().get();
138 ASSERT(buffer != nullptr);
139 if (isBound)
140 {
141 buffer->onNonTFBindingChanged(-1);
142 }
143 else
144 {
145 // un-assigning to avoid assertion, since it was already removed from buffer's observer
146 // list.
147 mArrayBufferObserverBindings[bindingIndex].assignSubject(nullptr);
148 }
149 // Note: the non-contents observer is unbound in the ObserverBinding destructor.
150 buffer->removeContentsObserver(this, static_cast<uint32_t>(bindingIndex));
151 binding.setBuffer(context, nullptr);
152 }
153 mState.mBufferBindingMask.reset();
154
155 if (mState.mElementArrayBuffer.get())
156 {
157 if (isBound)
158 {
159 mState.mElementArrayBuffer->onNonTFBindingChanged(-1);
160 }
161 mState.mElementArrayBuffer->removeContentsObserver(this, kElementArrayBufferIndex);
162 }
163 mState.mElementArrayBuffer.bind(context, nullptr);
164
165 mVertexArray->destroy(context);
166 SafeDelete(mVertexArray);
167 delete this;
168 }
169
~VertexArray()170 VertexArray::~VertexArray()
171 {
172 ASSERT(!mVertexArray);
173 }
174
setLabel(const Context * context,const std::string & label)175 angle::Result VertexArray::setLabel(const Context *context, const std::string &label)
176 {
177 mState.mLabel = label;
178
179 if (mVertexArray)
180 {
181 return mVertexArray->onLabelUpdate(context);
182 }
183 return angle::Result::Continue;
184 }
185
getLabel() const186 const std::string &VertexArray::getLabel() const
187 {
188 return mState.mLabel;
189 }
190
detachBuffer(const Context * context,BufferID bufferID)191 bool VertexArray::detachBuffer(const Context *context, BufferID bufferID)
192 {
193 bool isBound = context->isCurrentVertexArray(this);
194 bool anyBufferDetached = false;
195 for (size_t bindingIndex : mState.mBufferBindingMask)
196 {
197 VertexBinding &binding = mState.mVertexBindings[bindingIndex];
198 const BindingPointer<Buffer> &bufferBinding = binding.getBuffer();
199 if (bufferBinding.id() == bufferID)
200 {
201 if (isBound)
202 {
203 if (bufferBinding.get())
204 bufferBinding->onNonTFBindingChanged(-1);
205 }
206 bufferBinding->removeContentsObserver(this, static_cast<uint32_t>(bindingIndex));
207 binding.setBuffer(context, nullptr);
208 mArrayBufferObserverBindings[bindingIndex].reset();
209 mState.mBufferBindingMask.reset(bindingIndex);
210
211 if (context->getClientVersion() >= ES_3_1 && !mState.isDefault())
212 {
213 setDirtyBindingBit(bindingIndex, DIRTY_BINDING_BUFFER);
214 }
215 else
216 {
217 static_assert(MAX_VERTEX_ATTRIB_BINDINGS < 8 * sizeof(uint32_t),
218 "Not enough bits in bindingIndex");
219 // The redundant uint32_t cast here is required to avoid a warning on MSVC.
220 ASSERT(binding.getBoundAttributesMask() ==
221 AttributesMask(static_cast<uint32_t>(1 << bindingIndex)));
222 setDirtyAttribBit(bindingIndex, DIRTY_ATTRIB_POINTER);
223 }
224
225 anyBufferDetached = true;
226 mState.mClientMemoryAttribsMask |= binding.getBoundAttributesMask();
227 }
228 }
229
230 if (mState.mElementArrayBuffer.get() && mState.mElementArrayBuffer->id() == bufferID)
231 {
232 if (isBound && mState.mElementArrayBuffer.get())
233 mState.mElementArrayBuffer->onNonTFBindingChanged(-1);
234 mState.mElementArrayBuffer->removeContentsObserver(this, kElementArrayBufferIndex);
235 mState.mElementArrayBuffer.bind(context, nullptr);
236 mDirtyBits.set(DIRTY_BIT_ELEMENT_ARRAY_BUFFER);
237 anyBufferDetached = true;
238 }
239
240 return anyBufferDetached;
241 }
242
getVertexAttribute(size_t attribIndex) const243 const VertexAttribute &VertexArray::getVertexAttribute(size_t attribIndex) const
244 {
245 ASSERT(attribIndex < getMaxAttribs());
246 return mState.mVertexAttributes[attribIndex];
247 }
248
getVertexBinding(size_t bindingIndex) const249 const VertexBinding &VertexArray::getVertexBinding(size_t bindingIndex) const
250 {
251 ASSERT(bindingIndex < getMaxBindings());
252 return mState.mVertexBindings[bindingIndex];
253 }
254
GetVertexIndexFromDirtyBit(size_t dirtyBit)255 size_t VertexArray::GetVertexIndexFromDirtyBit(size_t dirtyBit)
256 {
257 static_assert(MAX_VERTEX_ATTRIBS == MAX_VERTEX_ATTRIB_BINDINGS,
258 "The stride of vertex attributes should equal to that of vertex bindings.");
259 ASSERT(dirtyBit > DIRTY_BIT_ELEMENT_ARRAY_BUFFER);
260 return (dirtyBit - DIRTY_BIT_ATTRIB_0) % MAX_VERTEX_ATTRIBS;
261 }
262
setDirtyAttribBit(size_t attribIndex,DirtyAttribBitType dirtyAttribBit)263 ANGLE_INLINE void VertexArray::setDirtyAttribBit(size_t attribIndex,
264 DirtyAttribBitType dirtyAttribBit)
265 {
266 mDirtyBits.set(DIRTY_BIT_ATTRIB_0 + attribIndex);
267 mDirtyAttribBits[attribIndex].set(dirtyAttribBit);
268 }
269
clearDirtyAttribBit(size_t attribIndex,DirtyAttribBitType dirtyAttribBit)270 ANGLE_INLINE void VertexArray::clearDirtyAttribBit(size_t attribIndex,
271 DirtyAttribBitType dirtyAttribBit)
272 {
273 mDirtyAttribBits[attribIndex].set(dirtyAttribBit, false);
274 if (mDirtyAttribBits[attribIndex].any())
275 {
276 return;
277 }
278 mDirtyBits.set(DIRTY_BIT_ATTRIB_0 + attribIndex, false);
279 }
280
setDirtyBindingBit(size_t bindingIndex,DirtyBindingBitType dirtyBindingBit)281 ANGLE_INLINE void VertexArray::setDirtyBindingBit(size_t bindingIndex,
282 DirtyBindingBitType dirtyBindingBit)
283 {
284 mDirtyBits.set(DIRTY_BIT_BINDING_0 + bindingIndex);
285 mDirtyBindingBits[bindingIndex].set(dirtyBindingBit);
286 }
287
updateCachedBufferBindingSize(VertexBinding * binding)288 ANGLE_INLINE void VertexArray::updateCachedBufferBindingSize(VertexBinding *binding)
289 {
290 if (!mBufferAccessValidationEnabled)
291 return;
292
293 for (size_t boundAttribute : binding->getBoundAttributesMask())
294 {
295 mState.mVertexAttributes[boundAttribute].updateCachedElementLimit(*binding);
296 }
297 }
298
updateCachedArrayBuffersMasks(bool isMapped,bool isImmutable,bool isPersistent,const AttributesMask & boundAttributesMask)299 ANGLE_INLINE void VertexArray::updateCachedArrayBuffersMasks(
300 bool isMapped,
301 bool isImmutable,
302 bool isPersistent,
303 const AttributesMask &boundAttributesMask)
304 {
305 if (isMapped)
306 {
307 mState.mCachedMappedArrayBuffers |= boundAttributesMask;
308 }
309 else
310 {
311 mState.mCachedMappedArrayBuffers &= ~boundAttributesMask;
312 }
313
314 if (!isImmutable || !isPersistent)
315 {
316 mState.mCachedMutableOrImpersistentArrayBuffers |= boundAttributesMask;
317 }
318 else
319 {
320 mState.mCachedMutableOrImpersistentArrayBuffers &= ~boundAttributesMask;
321 }
322
323 mState.mCachedInvalidMappedArrayBuffer = mState.mCachedMappedArrayBuffers &
324 mState.mEnabledAttributesMask &
325 mState.mCachedMutableOrImpersistentArrayBuffers;
326 }
327
updateCachedMappedArrayBuffersBinding(const VertexBinding & binding)328 ANGLE_INLINE void VertexArray::updateCachedMappedArrayBuffersBinding(const VertexBinding &binding)
329 {
330 const Buffer *buffer = binding.getBuffer().get();
331 bool isMapped = buffer && buffer->isMapped();
332 bool isImmutable = buffer && buffer->isImmutable();
333 bool isPersistent = buffer && (buffer->getAccessFlags() & GL_MAP_PERSISTENT_BIT_EXT) != 0;
334 return updateCachedArrayBuffersMasks(isMapped, isImmutable, isPersistent,
335 binding.getBoundAttributesMask());
336 }
337
updateCachedTransformFeedbackBindingValidation(size_t bindingIndex,const Buffer * buffer)338 ANGLE_INLINE void VertexArray::updateCachedTransformFeedbackBindingValidation(size_t bindingIndex,
339 const Buffer *buffer)
340 {
341 const bool hasConflict = buffer && buffer->hasWebGLXFBBindingConflict(true);
342 mCachedTransformFeedbackConflictedBindingsMask.set(bindingIndex, hasConflict);
343 }
344
bindVertexBufferImpl(const Context * context,size_t bindingIndex,Buffer * boundBuffer,GLintptr offset,GLsizei stride)345 VertexArray::DirtyBindingBits VertexArray::bindVertexBufferImpl(const Context *context,
346 size_t bindingIndex,
347 Buffer *boundBuffer,
348 GLintptr offset,
349 GLsizei stride)
350 {
351 ASSERT(bindingIndex < getMaxBindings());
352 ASSERT(context->isCurrentVertexArray(this));
353
354 VertexBinding *binding = &mState.mVertexBindings[bindingIndex];
355
356 Buffer *oldBuffer = binding->getBuffer().get();
357
358 DirtyBindingBits dirtyBindingBits;
359 dirtyBindingBits.set(DIRTY_BINDING_BUFFER, oldBuffer != boundBuffer);
360 dirtyBindingBits.set(DIRTY_BINDING_STRIDE, static_cast<GLuint>(stride) != binding->getStride());
361 dirtyBindingBits.set(DIRTY_BINDING_OFFSET, offset != binding->getOffset());
362
363 if (dirtyBindingBits.none())
364 {
365 return dirtyBindingBits;
366 }
367
368 if (boundBuffer != oldBuffer)
369 {
370 angle::ObserverBinding *observer = &mArrayBufferObserverBindings[bindingIndex];
371 observer->assignSubject(boundBuffer);
372
373 // Several nullptr checks are combined here for optimization purposes.
374 if (oldBuffer)
375 {
376 oldBuffer->onNonTFBindingChanged(-1);
377 oldBuffer->removeObserver(observer);
378 oldBuffer->removeContentsObserver(this, static_cast<uint32_t>(bindingIndex));
379 oldBuffer->release(context);
380 mState.mBufferBindingMask.reset(bindingIndex);
381 }
382
383 binding->assignBuffer(boundBuffer);
384
385 // Update client memory attribute pointers. Affects all bound attributes.
386 if (boundBuffer)
387 {
388 boundBuffer->addRef();
389 boundBuffer->onNonTFBindingChanged(1);
390 boundBuffer->addObserver(observer);
391 if (context->isWebGL())
392 {
393 mCachedTransformFeedbackConflictedBindingsMask.set(
394 bindingIndex, boundBuffer->hasWebGLXFBBindingConflict(true));
395 }
396 mState.mBufferBindingMask.set(bindingIndex);
397 mState.mClientMemoryAttribsMask &= ~binding->getBoundAttributesMask();
398
399 bool isMapped = boundBuffer->isMapped() == GL_TRUE;
400 bool isImmutable = boundBuffer->isImmutable() == GL_TRUE;
401 bool isPersistent = (boundBuffer->getAccessFlags() & GL_MAP_PERSISTENT_BIT_EXT) != 0;
402 updateCachedArrayBuffersMasks(isMapped, isImmutable, isPersistent,
403 binding->getBoundAttributesMask());
404 }
405 else
406 {
407 if (context->isWebGL())
408 {
409 mCachedTransformFeedbackConflictedBindingsMask.set(bindingIndex, false);
410 }
411 mState.mClientMemoryAttribsMask |= binding->getBoundAttributesMask();
412 updateCachedArrayBuffersMasks(false, false, false, binding->getBoundAttributesMask());
413 }
414 }
415
416 binding->setOffset(offset);
417 binding->setStride(stride);
418 updateCachedBufferBindingSize(binding);
419
420 return dirtyBindingBits;
421 }
422
bindVertexBuffer(const Context * context,size_t bindingIndex,Buffer * boundBuffer,GLintptr offset,GLsizei stride)423 void VertexArray::bindVertexBuffer(const Context *context,
424 size_t bindingIndex,
425 Buffer *boundBuffer,
426 GLintptr offset,
427 GLsizei stride)
428 {
429 const VertexArray::DirtyBindingBits dirtyBindingBits =
430 bindVertexBufferImpl(context, bindingIndex, boundBuffer, offset, stride);
431 if (dirtyBindingBits.any())
432 {
433 mDirtyBits.set(DIRTY_BIT_BINDING_0 + bindingIndex);
434 mDirtyBindingBits[bindingIndex] |= dirtyBindingBits;
435 }
436 }
437
setVertexAttribBinding(const Context * context,size_t attribIndex,GLuint bindingIndex)438 void VertexArray::setVertexAttribBinding(const Context *context,
439 size_t attribIndex,
440 GLuint bindingIndex)
441 {
442 ASSERT(attribIndex < getMaxAttribs() && bindingIndex < getMaxBindings());
443
444 if (mState.mVertexAttributes[attribIndex].bindingIndex == bindingIndex)
445 {
446 return;
447 }
448
449 // In ES 3.0 contexts, the binding cannot change, hence the code below is unreachable.
450 ASSERT(context->getClientVersion() >= ES_3_1 && !mState.isDefault());
451
452 mState.setAttribBinding(context, attribIndex, bindingIndex);
453
454 setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_BINDING);
455
456 // Update client attribs mask.
457 bool hasBuffer = mState.mVertexBindings[bindingIndex].getBuffer().get() != nullptr;
458 mState.mClientMemoryAttribsMask.set(attribIndex, !hasBuffer);
459 }
460
setVertexBindingDivisor(const Context * context,size_t bindingIndex,GLuint divisor)461 void VertexArray::setVertexBindingDivisor(const Context *context,
462 size_t bindingIndex,
463 GLuint divisor)
464 {
465 ASSERT(bindingIndex < getMaxBindings());
466
467 VertexBinding &binding = mState.mVertexBindings[bindingIndex];
468
469 if (binding.getDivisor() == divisor)
470 {
471 return;
472 }
473
474 binding.setDivisor(divisor);
475 setDirtyBindingBit(bindingIndex, DIRTY_BINDING_DIVISOR);
476 }
477
setVertexAttribFormatImpl(VertexAttribute * attrib,GLint size,VertexAttribType type,bool normalized,bool pureInteger,GLuint relativeOffset)478 ANGLE_INLINE bool VertexArray::setVertexAttribFormatImpl(VertexAttribute *attrib,
479 GLint size,
480 VertexAttribType type,
481 bool normalized,
482 bool pureInteger,
483 GLuint relativeOffset)
484 {
485 angle::FormatID formatID = GetVertexFormatID(type, normalized, size, pureInteger);
486
487 if (formatID != attrib->format->id || attrib->relativeOffset != relativeOffset)
488 {
489 attrib->relativeOffset = relativeOffset;
490 attrib->format = &angle::Format::Get(formatID);
491 return true;
492 }
493
494 return false;
495 }
496
setVertexAttribFormat(size_t attribIndex,GLint size,VertexAttribType type,bool normalized,bool pureInteger,GLuint relativeOffset)497 void VertexArray::setVertexAttribFormat(size_t attribIndex,
498 GLint size,
499 VertexAttribType type,
500 bool normalized,
501 bool pureInteger,
502 GLuint relativeOffset)
503 {
504 VertexAttribute &attrib = mState.mVertexAttributes[attribIndex];
505
506 ComponentType componentType = GetVertexAttributeComponentType(pureInteger, type);
507 SetComponentTypeMask(componentType, attribIndex, &mState.mVertexAttributesTypeMask);
508
509 if (setVertexAttribFormatImpl(&attrib, size, type, normalized, pureInteger, relativeOffset))
510 {
511 setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_FORMAT);
512 }
513
514 attrib.updateCachedElementLimit(mState.mVertexBindings[attrib.bindingIndex]);
515 }
516
setVertexAttribDivisor(const Context * context,size_t attribIndex,GLuint divisor)517 void VertexArray::setVertexAttribDivisor(const Context *context, size_t attribIndex, GLuint divisor)
518 {
519 ASSERT(attribIndex < getMaxAttribs());
520
521 setVertexAttribBinding(context, attribIndex, static_cast<GLuint>(attribIndex));
522 setVertexBindingDivisor(context, attribIndex, divisor);
523 }
524
enableAttribute(size_t attribIndex,bool enabledState)525 void VertexArray::enableAttribute(size_t attribIndex, bool enabledState)
526 {
527 ASSERT(attribIndex < getMaxAttribs());
528
529 VertexAttribute &attrib = mState.mVertexAttributes[attribIndex];
530
531 if (mState.mEnabledAttributesMask.test(attribIndex) == enabledState)
532 {
533 return;
534 }
535
536 attrib.enabled = enabledState;
537
538 // Update state cache
539 mState.mEnabledAttributesMask.set(attribIndex, enabledState);
540 bool enableChanged = (mState.mEnabledAttributesMask.test(attribIndex) !=
541 mState.mLastSyncedEnabledAttributesMask.test(attribIndex));
542
543 if (enableChanged)
544 {
545 setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_ENABLED);
546 }
547 else
548 {
549 clearDirtyAttribBit(attribIndex, DIRTY_ATTRIB_ENABLED);
550 }
551
552 mState.updateCachedMutableOrNonPersistentArrayBuffers(attribIndex);
553 mState.mCachedInvalidMappedArrayBuffer = mState.mCachedMappedArrayBuffers &
554 mState.mEnabledAttributesMask &
555 mState.mCachedMutableOrImpersistentArrayBuffers;
556 }
557
setVertexAttribPointerImpl(const Context * context,ComponentType componentType,bool pureInteger,size_t attribIndex,Buffer * boundBuffer,GLint size,VertexAttribType type,bool normalized,GLsizei stride,const void * pointer)558 ANGLE_INLINE void VertexArray::setVertexAttribPointerImpl(const Context *context,
559 ComponentType componentType,
560 bool pureInteger,
561 size_t attribIndex,
562 Buffer *boundBuffer,
563 GLint size,
564 VertexAttribType type,
565 bool normalized,
566 GLsizei stride,
567 const void *pointer)
568 {
569 ASSERT(attribIndex < getMaxAttribs());
570
571 VertexAttribute &attrib = mState.mVertexAttributes[attribIndex];
572
573 SetComponentTypeMask(componentType, attribIndex, &mState.mVertexAttributesTypeMask);
574
575 bool attribDirty = setVertexAttribFormatImpl(&attrib, size, type, normalized, pureInteger, 0);
576
577 if (attrib.bindingIndex != attribIndex)
578 {
579 setVertexAttribBinding(context, attribIndex, static_cast<GLuint>(attribIndex));
580 }
581
582 GLsizei effectiveStride =
583 stride == 0 ? static_cast<GLsizei>(ComputeVertexAttributeTypeSize(attrib)) : stride;
584
585 if (attrib.vertexAttribArrayStride != static_cast<GLuint>(stride))
586 {
587 attribDirty = true;
588 }
589 attrib.vertexAttribArrayStride = stride;
590
591 // If we switch from an array buffer to a client pointer(or vice-versa), we set the whole
592 // attribute dirty. This notifies the Vulkan back-end to update all its caches.
593 const VertexBinding &binding = mState.mVertexBindings[attribIndex];
594 if ((boundBuffer == nullptr) != (binding.getBuffer().get() == nullptr))
595 {
596 attribDirty = true;
597 }
598
599 // If using client arrays and the pointer changes, set the attribute as dirty
600 if (boundBuffer == nullptr && attrib.pointer != pointer)
601 {
602 attribDirty = true;
603 }
604
605 // Change of attrib.pointer is not part of attribDirty. Pointer is actually the buffer offset
606 // which is handled within bindVertexBufferImpl and reflected in bufferDirty.
607 attrib.pointer = pointer;
608 GLintptr offset = boundBuffer ? reinterpret_cast<GLintptr>(pointer) : 0;
609 const VertexArray::DirtyBindingBits dirtyBindingBits =
610 bindVertexBufferImpl(context, attribIndex, boundBuffer, offset, effectiveStride);
611
612 if (attribDirty)
613 {
614 setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_POINTER);
615 }
616 else if (dirtyBindingBits.any())
617 {
618 setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_POINTER_BUFFER);
619 }
620
621 mState.mNullPointerClientMemoryAttribsMask.set(attribIndex,
622 boundBuffer == nullptr && pointer == nullptr);
623 }
624
setVertexAttribPointer(const Context * context,size_t attribIndex,Buffer * boundBuffer,GLint size,VertexAttribType type,bool normalized,GLsizei stride,const void * pointer)625 void VertexArray::setVertexAttribPointer(const Context *context,
626 size_t attribIndex,
627 Buffer *boundBuffer,
628 GLint size,
629 VertexAttribType type,
630 bool normalized,
631 GLsizei stride,
632 const void *pointer)
633 {
634 setVertexAttribPointerImpl(context, ComponentType::Float, false, attribIndex, boundBuffer, size,
635 type, normalized, stride, pointer);
636 }
637
setVertexAttribIPointer(const Context * context,size_t attribIndex,Buffer * boundBuffer,GLint size,VertexAttribType type,GLsizei stride,const void * pointer)638 void VertexArray::setVertexAttribIPointer(const Context *context,
639 size_t attribIndex,
640 Buffer *boundBuffer,
641 GLint size,
642 VertexAttribType type,
643 GLsizei stride,
644 const void *pointer)
645 {
646 ComponentType componentType = GetVertexAttributeComponentType(true, type);
647 setVertexAttribPointerImpl(context, componentType, true, attribIndex, boundBuffer, size, type,
648 false, stride, pointer);
649 }
650
syncState(const Context * context)651 angle::Result VertexArray::syncState(const Context *context)
652 {
653 if (mDirtyBits.any())
654 {
655 mDirtyBitsGuard = mDirtyBits;
656 ANGLE_TRY(
657 mVertexArray->syncState(context, mDirtyBits, &mDirtyAttribBits, &mDirtyBindingBits));
658 mDirtyBits.reset();
659 mDirtyBitsGuard.reset();
660
661 // The dirty bits should be reset in the back-end. To simplify ASSERTs only check attrib 0.
662 ASSERT(mDirtyAttribBits[0].none());
663 ASSERT(mDirtyBindingBits[0].none());
664 mState.mLastSyncedEnabledAttributesMask = mState.mEnabledAttributesMask;
665 }
666 return angle::Result::Continue;
667 }
668
669 // This becomes current vertex array on the context
onBind(const Context * context)670 void VertexArray::onBind(const Context *context)
671 {
672 // This vertex array becoming current. Some of the bindings we may have removed from buffer's
673 // observer list. We need to add it back to the buffer's observer list and update dirty bits
674 // that we may have missed while we were not observing.
675 for (size_t bindingIndex : mState.getBufferBindingMask())
676 {
677 const VertexBinding &binding = mState.getVertexBindings()[bindingIndex];
678 Buffer *bufferGL = binding.getBuffer().get();
679 ASSERT(bufferGL != nullptr);
680
681 bufferGL->addObserver(&mArrayBufferObserverBindings[bindingIndex]);
682 updateCachedMappedArrayBuffersBinding(mState.mVertexBindings[bindingIndex]);
683
684 if (mBufferAccessValidationEnabled)
685 {
686 for (size_t boundAttribute :
687 mState.mVertexBindings[bindingIndex].getBoundAttributesMask())
688 {
689 mState.mVertexAttributes[boundAttribute].updateCachedElementLimit(
690 mState.mVertexBindings[bindingIndex]);
691 }
692 }
693
694 if (context->isWebGL())
695 {
696 updateCachedTransformFeedbackBindingValidation(bindingIndex, bufferGL);
697 }
698 }
699
700 mDirtyBits.set(DIRTY_BIT_LOST_OBSERVATION);
701 onStateChange(angle::SubjectMessage::ContentsChanged);
702 }
703
704 // This becomes non-current vertex array on the context
onUnbind(const Context * context)705 void VertexArray::onUnbind(const Context *context)
706 {
707 // This vertex array becoming non-current. For performance reason, we remove it from the
708 // buffers' observer list so that the cost of buffer sending signal to observers will not be too
709 // expensive.
710 for (size_t bindingIndex : mState.mBufferBindingMask)
711 {
712 const VertexBinding &binding = mState.getVertexBindings()[bindingIndex];
713 Buffer *bufferGL = binding.getBuffer().get();
714 ASSERT(bufferGL != nullptr);
715 bufferGL->removeObserver(&mArrayBufferObserverBindings[bindingIndex]);
716 }
717 }
718
onBindingChanged(const Context * context,int incr)719 void VertexArray::onBindingChanged(const Context *context, int incr)
720 {
721 // When vertex array gets unbound, we remove it from bound buffers' observer list so that when
722 // buffer changes, it wont has to loop over all these non-current vertex arrays and set dirty
723 // bit on them. To compensate for that, when we bind a vertex array, we have to check against
724 // each bound buffers and see if they have changed and needs to update vertex array's dirty bits
725 // accordingly
726 ASSERT(incr == 1 || incr == -1);
727 if (incr < 0)
728 {
729 onUnbind(context);
730 }
731 else
732 {
733 onBind(context);
734 }
735
736 if (context->isWebGL())
737 {
738 if (mState.mElementArrayBuffer.get())
739 {
740 mState.mElementArrayBuffer->onNonTFBindingChanged(incr);
741 }
742 for (size_t bindingIndex : mState.mBufferBindingMask)
743 {
744 mState.mVertexBindings[bindingIndex].onContainerBindingChanged(context, incr);
745 }
746 }
747 }
748
getDirtyBitFromIndex(bool contentsChanged,angle::SubjectIndex index) const749 VertexArray::DirtyBitType VertexArray::getDirtyBitFromIndex(bool contentsChanged,
750 angle::SubjectIndex index) const
751 {
752 if (IsElementArrayBufferSubjectIndex(index))
753 {
754 mIndexRangeCache.invalidate();
755 return contentsChanged ? DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA
756 : DIRTY_BIT_ELEMENT_ARRAY_BUFFER;
757 }
758 else
759 {
760 // Note: this currently just gets the top-level dirty bit.
761 ASSERT(index < mArrayBufferObserverBindings.size());
762 return static_cast<DirtyBitType>(
763 (contentsChanged ? DIRTY_BIT_BUFFER_DATA_0 : DIRTY_BIT_BINDING_0) + index);
764 }
765 }
766
onSubjectStateChange(angle::SubjectIndex index,angle::SubjectMessage message)767 void VertexArray::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message)
768 {
769 switch (message)
770 {
771 case angle::SubjectMessage::SubjectChanged:
772 if (!IsElementArrayBufferSubjectIndex(index))
773 {
774 updateCachedBufferBindingSize(&mState.mVertexBindings[index]);
775 }
776 setDependentDirtyBit(false, index);
777 break;
778
779 case angle::SubjectMessage::BindingChanged:
780 if (!IsElementArrayBufferSubjectIndex(index))
781 {
782 const Buffer *buffer = mState.mVertexBindings[index].getBuffer().get();
783 updateCachedTransformFeedbackBindingValidation(index, buffer);
784 }
785 break;
786
787 case angle::SubjectMessage::SubjectMapped:
788 if (!IsElementArrayBufferSubjectIndex(index))
789 {
790 updateCachedMappedArrayBuffersBinding(mState.mVertexBindings[index]);
791 }
792 onStateChange(angle::SubjectMessage::SubjectMapped);
793 break;
794
795 case angle::SubjectMessage::SubjectUnmapped:
796 setDependentDirtyBit(true, index);
797
798 if (!IsElementArrayBufferSubjectIndex(index))
799 {
800 updateCachedMappedArrayBuffersBinding(mState.mVertexBindings[index]);
801 }
802 onStateChange(angle::SubjectMessage::SubjectUnmapped);
803 break;
804
805 case angle::SubjectMessage::InternalMemoryAllocationChanged:
806 setDependentDirtyBit(false, index);
807 break;
808
809 default:
810 UNREACHABLE();
811 break;
812 }
813 }
814
setDependentDirtyBit(bool contentsChanged,angle::SubjectIndex index)815 void VertexArray::setDependentDirtyBit(bool contentsChanged, angle::SubjectIndex index)
816 {
817 DirtyBitType dirtyBit = getDirtyBitFromIndex(contentsChanged, index);
818 ASSERT(!mDirtyBitsGuard.valid() || mDirtyBitsGuard.value().test(dirtyBit));
819 mDirtyBits.set(dirtyBit);
820 onStateChange(angle::SubjectMessage::ContentsChanged);
821 }
822
hasTransformFeedbackBindingConflict(const Context * context) const823 bool VertexArray::hasTransformFeedbackBindingConflict(const Context *context) const
824 {
825 // Fast check first.
826 if (!mCachedTransformFeedbackConflictedBindingsMask.any())
827 {
828 return false;
829 }
830
831 const AttributesMask &activeAttribues = context->getStateCache().getActiveBufferedAttribsMask();
832
833 // Slow check. We must ensure that the conflicting attributes are enabled/active.
834 for (size_t attribIndex : activeAttribues)
835 {
836 const VertexAttribute &attrib = mState.mVertexAttributes[attribIndex];
837 if (mCachedTransformFeedbackConflictedBindingsMask[attrib.bindingIndex])
838 {
839 return true;
840 }
841 }
842
843 return false;
844 }
845
getIndexRangeImpl(const Context * context,DrawElementsType type,GLsizei indexCount,const void * indices,IndexRange * indexRangeOut) const846 angle::Result VertexArray::getIndexRangeImpl(const Context *context,
847 DrawElementsType type,
848 GLsizei indexCount,
849 const void *indices,
850 IndexRange *indexRangeOut) const
851 {
852 Buffer *elementArrayBuffer = mState.mElementArrayBuffer.get();
853 if (!elementArrayBuffer)
854 {
855 *indexRangeOut = ComputeIndexRange(type, indices, indexCount,
856 context->getState().isPrimitiveRestartEnabled());
857 return angle::Result::Continue;
858 }
859
860 size_t offset = reinterpret_cast<uintptr_t>(indices);
861 ANGLE_TRY(elementArrayBuffer->getIndexRange(context, type, offset, indexCount,
862 context->getState().isPrimitiveRestartEnabled(),
863 indexRangeOut));
864
865 mIndexRangeCache.put(type, indexCount, offset, *indexRangeOut);
866 return angle::Result::Continue;
867 }
868
869 VertexArray::IndexRangeCache::IndexRangeCache() = default;
870
put(DrawElementsType type,GLsizei indexCount,size_t offset,const IndexRange & indexRange)871 void VertexArray::IndexRangeCache::put(DrawElementsType type,
872 GLsizei indexCount,
873 size_t offset,
874 const IndexRange &indexRange)
875 {
876 ASSERT(type != DrawElementsType::InvalidEnum);
877
878 mTypeKey = type;
879 mIndexCountKey = indexCount;
880 mOffsetKey = offset;
881 mPayload = indexRange;
882 }
883
onBufferContentsChange(uint32_t bufferIndex)884 void VertexArray::onBufferContentsChange(uint32_t bufferIndex)
885 {
886 setDependentDirtyBit(true, bufferIndex);
887 }
888
VertexArrayBufferContentsObservers(VertexArray * vertexArray)889 VertexArrayBufferContentsObservers::VertexArrayBufferContentsObservers(VertexArray *vertexArray)
890 : mVertexArray(vertexArray)
891 {}
892
enableForBuffer(Buffer * buffer,uint32_t attribIndex)893 void VertexArrayBufferContentsObservers::enableForBuffer(Buffer *buffer, uint32_t attribIndex)
894 {
895 buffer->addContentsObserver(mVertexArray, attribIndex);
896 mBufferObserversBitMask.set(attribIndex);
897 }
898
disableForBuffer(Buffer * buffer,uint32_t attribIndex)899 void VertexArrayBufferContentsObservers::disableForBuffer(Buffer *buffer, uint32_t attribIndex)
900 {
901 if (mBufferObserversBitMask.test(attribIndex))
902 {
903 buffer->removeContentsObserver(mVertexArray, attribIndex);
904 mBufferObserversBitMask.reset(attribIndex);
905 }
906 }
907 } // namespace gl
908