1 //
2 // Copyright 2013 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // Implementation of the state class for mananging GLES 3 Vertex Array Objects.
7 //
8
9 #include "libANGLE/VertexArray.h"
10
11 #include "common/utilities.h"
12 #include "libANGLE/Buffer.h"
13 #include "libANGLE/Context.h"
14 #include "libANGLE/renderer/BufferImpl.h"
15 #include "libANGLE/renderer/GLImplFactory.h"
16 #include "libANGLE/renderer/VertexArrayImpl.h"
17
18 namespace gl
19 {
20 namespace
21 {
IsElementArrayBufferSubjectIndex(angle::SubjectIndex subjectIndex)22 bool IsElementArrayBufferSubjectIndex(angle::SubjectIndex subjectIndex)
23 {
24 return (subjectIndex == kElementArrayBufferIndex);
25 }
26 } // namespace
27
28 // VertexArrayState implementation.
VertexArrayState(VertexArray * vertexArray,size_t maxAttribs,size_t maxAttribBindings)29 VertexArrayState::VertexArrayState(VertexArray *vertexArray,
30 size_t maxAttribs,
31 size_t maxAttribBindings)
32 : mElementArrayBuffer(vertexArray, kElementArrayBufferIndex)
33 {
34 ASSERT(maxAttribs <= maxAttribBindings);
35
36 for (size_t i = 0; i < maxAttribs; i++)
37 {
38 mVertexAttributes.emplace_back(static_cast<GLuint>(i));
39 mVertexBindings.emplace_back(static_cast<GLuint>(i));
40 }
41
42 // Initially all attributes start as "client" with no buffer bound.
43 mClientMemoryAttribsMask.set();
44 }
45
~VertexArrayState()46 VertexArrayState::~VertexArrayState() {}
47
hasEnabledNullPointerClientArray() const48 bool VertexArrayState::hasEnabledNullPointerClientArray() const
49 {
50 return (mNullPointerClientMemoryAttribsMask & mEnabledAttributesMask).any();
51 }
52
getBindingToAttributesMask(GLuint bindingIndex) const53 AttributesMask VertexArrayState::getBindingToAttributesMask(GLuint bindingIndex) const
54 {
55 ASSERT(bindingIndex < mVertexBindings.size());
56 return mVertexBindings[bindingIndex].getBoundAttributesMask();
57 }
58
59 // Set an attribute using a new binding.
setAttribBinding(const Context * context,size_t attribIndex,GLuint newBindingIndex)60 void VertexArrayState::setAttribBinding(const Context *context,
61 size_t attribIndex,
62 GLuint newBindingIndex)
63 {
64 ASSERT(attribIndex < mVertexAttributes.size() && newBindingIndex < mVertexBindings.size());
65
66 VertexAttribute &attrib = mVertexAttributes[attribIndex];
67
68 // Update the binding-attribute map.
69 const GLuint oldBindingIndex = attrib.bindingIndex;
70 ASSERT(oldBindingIndex != newBindingIndex);
71
72 VertexBinding &oldBinding = mVertexBindings[oldBindingIndex];
73 VertexBinding &newBinding = mVertexBindings[newBindingIndex];
74
75 ASSERT(oldBinding.getBoundAttributesMask().test(attribIndex) &&
76 !newBinding.getBoundAttributesMask().test(attribIndex));
77
78 oldBinding.resetBoundAttribute(attribIndex);
79 newBinding.setBoundAttribute(attribIndex);
80
81 // Set the attribute using the new binding.
82 attrib.bindingIndex = newBindingIndex;
83
84 if (context->isBufferAccessValidationEnabled())
85 {
86 attrib.updateCachedElementLimit(newBinding);
87 }
88
89 bool isMapped = newBinding.getBuffer().get() && newBinding.getBuffer()->isMapped();
90 mCachedMappedArrayBuffers.set(attribIndex, isMapped);
91 mEnabledAttributesMask.set(attribIndex, attrib.enabled);
92 updateCachedMutableOrNonPersistentArrayBuffers(attribIndex);
93 mCachedInvalidMappedArrayBuffer = mCachedMappedArrayBuffers & mEnabledAttributesMask &
94 mCachedMutableOrImpersistentArrayBuffers;
95 }
96
updateCachedMutableOrNonPersistentArrayBuffers(size_t index)97 void VertexArrayState::updateCachedMutableOrNonPersistentArrayBuffers(size_t index)
98 {
99 const VertexBinding &vertexBinding = mVertexBindings[index];
100 const BindingPointer<Buffer> &buffer = vertexBinding.getBuffer();
101 bool isMutableOrImpersistentArrayBuffer =
102 buffer.get() &&
103 (!buffer->isImmutable() || (buffer->getAccessFlags() & GL_MAP_PERSISTENT_BIT_EXT) == 0);
104 mCachedMutableOrImpersistentArrayBuffers.set(index, isMutableOrImpersistentArrayBuffer);
105 }
106
107 // VertexArray implementation.
VertexArray(rx::GLImplFactory * factory,VertexArrayID id,size_t maxAttribs,size_t maxAttribBindings)108 VertexArray::VertexArray(rx::GLImplFactory *factory,
109 VertexArrayID id,
110 size_t maxAttribs,
111 size_t maxAttribBindings)
112 : mId(id),
113 mState(this, maxAttribs, maxAttribBindings),
114 mVertexArray(factory->createVertexArray(mState)),
115 mBufferAccessValidationEnabled(false),
116 mContentsObservers(this)
117 {
118 for (size_t attribIndex = 0; attribIndex < maxAttribBindings; ++attribIndex)
119 {
120 mArrayBufferObserverBindings.emplace_back(this, attribIndex);
121 }
122
123 mVertexArray->setContentsObservers(&mContentsObservers);
124 }
125
onDestroy(const Context * context)126 void VertexArray::onDestroy(const Context *context)
127 {
128 bool isBound = context->isCurrentVertexArray(this);
129 for (size_t bindingIndex : mState.mBufferBindingMask)
130 {
131 VertexBinding &binding = mState.mVertexBindings[bindingIndex];
132 Buffer *buffer = binding.getBuffer().get();
133 ASSERT(buffer != nullptr);
134 if (isBound)
135 {
136 buffer->onNonTFBindingChanged(-1);
137 }
138 else
139 {
140 // un-assigning to avoid assertion, since it was already removed from buffer's observer
141 // list.
142 mArrayBufferObserverBindings[bindingIndex].assignSubject(nullptr);
143 }
144 // Note: the non-contents observer is unbound in the ObserverBinding destructor.
145 buffer->removeContentsObserver(this, static_cast<uint32_t>(bindingIndex));
146 binding.setBuffer(context, nullptr);
147 }
148 mState.mBufferBindingMask.reset();
149
150 if (mState.mElementArrayBuffer.get())
151 {
152 if (isBound)
153 {
154 mState.mElementArrayBuffer->onNonTFBindingChanged(-1);
155 }
156 mState.mElementArrayBuffer->removeContentsObserver(this, kElementArrayBufferIndex);
157 }
158 mState.mElementArrayBuffer.bind(context, nullptr);
159
160 mVertexArray->destroy(context);
161 SafeDelete(mVertexArray);
162 delete this;
163 }
164
~VertexArray()165 VertexArray::~VertexArray()
166 {
167 ASSERT(!mVertexArray);
168 }
169
setLabel(const Context * context,const std::string & label)170 angle::Result VertexArray::setLabel(const Context *context, const std::string &label)
171 {
172 mState.mLabel = label;
173
174 if (mVertexArray)
175 {
176 return mVertexArray->onLabelUpdate(context);
177 }
178 return angle::Result::Continue;
179 }
180
getLabel() const181 const std::string &VertexArray::getLabel() const
182 {
183 return mState.mLabel;
184 }
185
detachBuffer(const Context * context,BufferID bufferID)186 bool VertexArray::detachBuffer(const Context *context, BufferID bufferID)
187 {
188 bool isBound = context->isCurrentVertexArray(this);
189 bool anyBufferDetached = false;
190 for (size_t bindingIndex : mState.mBufferBindingMask)
191 {
192 VertexBinding &binding = mState.mVertexBindings[bindingIndex];
193 const BindingPointer<Buffer> &bufferBinding = binding.getBuffer();
194 if (bufferBinding.id() == bufferID)
195 {
196 if (isBound)
197 {
198 if (bufferBinding.get())
199 bufferBinding->onNonTFBindingChanged(-1);
200 }
201 bufferBinding->removeContentsObserver(this, static_cast<uint32_t>(bindingIndex));
202 binding.setBuffer(context, nullptr);
203 mArrayBufferObserverBindings[bindingIndex].reset();
204 mState.mBufferBindingMask.reset(bindingIndex);
205
206 if (context->getClientVersion() >= ES_3_1)
207 {
208 setDirtyBindingBit(bindingIndex, DIRTY_BINDING_BUFFER);
209 }
210 else
211 {
212 static_assert(MAX_VERTEX_ATTRIB_BINDINGS < 8 * sizeof(uint32_t),
213 "Not enough bits in bindingIndex");
214 // The redundant uint32_t cast here is required to avoid a warning on MSVC.
215 ASSERT(binding.getBoundAttributesMask() ==
216 AttributesMask(static_cast<uint32_t>(1 << bindingIndex)));
217 setDirtyAttribBit(bindingIndex, DIRTY_ATTRIB_POINTER);
218 }
219
220 anyBufferDetached = true;
221 mState.mClientMemoryAttribsMask |= binding.getBoundAttributesMask();
222 }
223 }
224
225 if (mState.mElementArrayBuffer.get() && mState.mElementArrayBuffer->id() == bufferID)
226 {
227 if (isBound && mState.mElementArrayBuffer.get())
228 mState.mElementArrayBuffer->onNonTFBindingChanged(-1);
229 mState.mElementArrayBuffer->removeContentsObserver(this, kElementArrayBufferIndex);
230 mState.mElementArrayBuffer.bind(context, nullptr);
231 mDirtyBits.set(DIRTY_BIT_ELEMENT_ARRAY_BUFFER);
232 anyBufferDetached = true;
233 }
234
235 return anyBufferDetached;
236 }
237
getVertexAttribute(size_t attribIndex) const238 const VertexAttribute &VertexArray::getVertexAttribute(size_t attribIndex) const
239 {
240 ASSERT(attribIndex < getMaxAttribs());
241 return mState.mVertexAttributes[attribIndex];
242 }
243
getVertexBinding(size_t bindingIndex) const244 const VertexBinding &VertexArray::getVertexBinding(size_t bindingIndex) const
245 {
246 ASSERT(bindingIndex < getMaxBindings());
247 return mState.mVertexBindings[bindingIndex];
248 }
249
GetVertexIndexFromDirtyBit(size_t dirtyBit)250 size_t VertexArray::GetVertexIndexFromDirtyBit(size_t dirtyBit)
251 {
252 static_assert(MAX_VERTEX_ATTRIBS == MAX_VERTEX_ATTRIB_BINDINGS,
253 "The stride of vertex attributes should equal to that of vertex bindings.");
254 ASSERT(dirtyBit > DIRTY_BIT_ELEMENT_ARRAY_BUFFER);
255 return (dirtyBit - DIRTY_BIT_ATTRIB_0) % MAX_VERTEX_ATTRIBS;
256 }
257
setDirtyAttribBit(size_t attribIndex,DirtyAttribBitType dirtyAttribBit)258 ANGLE_INLINE void VertexArray::setDirtyAttribBit(size_t attribIndex,
259 DirtyAttribBitType dirtyAttribBit)
260 {
261 mDirtyBits.set(DIRTY_BIT_ATTRIB_0 + attribIndex);
262 mDirtyAttribBits[attribIndex].set(dirtyAttribBit);
263 }
264
clearDirtyAttribBit(size_t attribIndex,DirtyAttribBitType dirtyAttribBit)265 ANGLE_INLINE void VertexArray::clearDirtyAttribBit(size_t attribIndex,
266 DirtyAttribBitType dirtyAttribBit)
267 {
268 mDirtyAttribBits[attribIndex].set(dirtyAttribBit, false);
269 if (mDirtyAttribBits[attribIndex].any())
270 {
271 return;
272 }
273 mDirtyBits.set(DIRTY_BIT_ATTRIB_0 + attribIndex, false);
274 }
275
setDirtyBindingBit(size_t bindingIndex,DirtyBindingBitType dirtyBindingBit)276 ANGLE_INLINE void VertexArray::setDirtyBindingBit(size_t bindingIndex,
277 DirtyBindingBitType dirtyBindingBit)
278 {
279 mDirtyBits.set(DIRTY_BIT_BINDING_0 + bindingIndex);
280 mDirtyBindingBits[bindingIndex].set(dirtyBindingBit);
281 }
282
updateCachedBufferBindingSize(VertexBinding * binding)283 ANGLE_INLINE void VertexArray::updateCachedBufferBindingSize(VertexBinding *binding)
284 {
285 if (!mBufferAccessValidationEnabled)
286 return;
287
288 for (size_t boundAttribute : binding->getBoundAttributesMask())
289 {
290 mState.mVertexAttributes[boundAttribute].updateCachedElementLimit(*binding);
291 }
292 }
293
updateCachedArrayBuffersMasks(bool isMapped,bool isImmutable,bool isPersistent,const AttributesMask & boundAttributesMask)294 ANGLE_INLINE void VertexArray::updateCachedArrayBuffersMasks(
295 bool isMapped,
296 bool isImmutable,
297 bool isPersistent,
298 const AttributesMask &boundAttributesMask)
299 {
300 if (isMapped)
301 {
302 mState.mCachedMappedArrayBuffers |= boundAttributesMask;
303 }
304 else
305 {
306 mState.mCachedMappedArrayBuffers &= ~boundAttributesMask;
307 }
308
309 if (!isImmutable || !isPersistent)
310 {
311 mState.mCachedMutableOrImpersistentArrayBuffers |= boundAttributesMask;
312 }
313 else
314 {
315 mState.mCachedMutableOrImpersistentArrayBuffers &= ~boundAttributesMask;
316 }
317
318 mState.mCachedInvalidMappedArrayBuffer = mState.mCachedMappedArrayBuffers &
319 mState.mEnabledAttributesMask &
320 mState.mCachedMutableOrImpersistentArrayBuffers;
321 }
322
updateCachedMappedArrayBuffersBinding(const VertexBinding & binding)323 ANGLE_INLINE void VertexArray::updateCachedMappedArrayBuffersBinding(const VertexBinding &binding)
324 {
325 const Buffer *buffer = binding.getBuffer().get();
326 bool isMapped = buffer && buffer->isMapped();
327 bool isImmutable = buffer && buffer->isImmutable();
328 bool isPersistent = buffer && (buffer->getAccessFlags() & GL_MAP_PERSISTENT_BIT_EXT) != 0;
329 return updateCachedArrayBuffersMasks(isMapped, isImmutable, isPersistent,
330 binding.getBoundAttributesMask());
331 }
332
updateCachedTransformFeedbackBindingValidation(size_t bindingIndex,const Buffer * buffer)333 ANGLE_INLINE void VertexArray::updateCachedTransformFeedbackBindingValidation(size_t bindingIndex,
334 const Buffer *buffer)
335 {
336 const bool hasConflict = buffer && buffer->hasWebGLXFBBindingConflict(true);
337 mCachedTransformFeedbackConflictedBindingsMask.set(bindingIndex, hasConflict);
338 }
339
bindVertexBufferImpl(const Context * context,size_t bindingIndex,Buffer * boundBuffer,GLintptr offset,GLsizei stride)340 VertexArray::DirtyBindingBits VertexArray::bindVertexBufferImpl(const Context *context,
341 size_t bindingIndex,
342 Buffer *boundBuffer,
343 GLintptr offset,
344 GLsizei stride)
345 {
346 ASSERT(bindingIndex < getMaxBindings());
347 ASSERT(context->isCurrentVertexArray(this));
348
349 VertexBinding *binding = &mState.mVertexBindings[bindingIndex];
350
351 Buffer *oldBuffer = binding->getBuffer().get();
352
353 DirtyBindingBits dirtyBindingBits;
354 dirtyBindingBits.set(DIRTY_BINDING_BUFFER, oldBuffer != boundBuffer);
355 dirtyBindingBits.set(DIRTY_BINDING_STRIDE, static_cast<GLuint>(stride) != binding->getStride());
356 dirtyBindingBits.set(DIRTY_BINDING_OFFSET, offset != binding->getOffset());
357
358 if (dirtyBindingBits.none())
359 {
360 return dirtyBindingBits;
361 }
362
363 angle::ObserverBinding *observer = &mArrayBufferObserverBindings[bindingIndex];
364 observer->assignSubject(boundBuffer);
365
366 // Several nullptr checks are combined here for optimization purposes.
367 if (oldBuffer)
368 {
369 oldBuffer->onNonTFBindingChanged(-1);
370 oldBuffer->removeObserver(observer);
371 oldBuffer->removeContentsObserver(this, static_cast<uint32_t>(bindingIndex));
372 oldBuffer->release(context);
373 mState.mBufferBindingMask.reset(bindingIndex);
374 }
375
376 binding->assignBuffer(boundBuffer);
377 binding->setOffset(offset);
378 binding->setStride(stride);
379 updateCachedBufferBindingSize(binding);
380
381 // Update client memory attribute pointers. Affects all bound attributes.
382 if (boundBuffer)
383 {
384 boundBuffer->addRef();
385 boundBuffer->onNonTFBindingChanged(1);
386 boundBuffer->addObserver(observer);
387 if (context->isWebGL())
388 {
389 mCachedTransformFeedbackConflictedBindingsMask.set(
390 bindingIndex, boundBuffer->hasWebGLXFBBindingConflict(true));
391 }
392 mState.mBufferBindingMask.set(bindingIndex);
393 mState.mClientMemoryAttribsMask &= ~binding->getBoundAttributesMask();
394
395 bool isMapped = boundBuffer->isMapped() == GL_TRUE;
396 bool isImmutable = boundBuffer->isImmutable() == GL_TRUE;
397 bool isPersistent = (boundBuffer->getAccessFlags() & GL_MAP_PERSISTENT_BIT_EXT) != 0;
398 updateCachedArrayBuffersMasks(isMapped, isImmutable, isPersistent,
399 binding->getBoundAttributesMask());
400 }
401 else
402 {
403 if (context->isWebGL())
404 {
405 mCachedTransformFeedbackConflictedBindingsMask.set(bindingIndex, false);
406 }
407 mState.mClientMemoryAttribsMask |= binding->getBoundAttributesMask();
408 updateCachedArrayBuffersMasks(false, false, false, binding->getBoundAttributesMask());
409 }
410
411 return dirtyBindingBits;
412 }
413
bindVertexBuffer(const Context * context,size_t bindingIndex,Buffer * boundBuffer,GLintptr offset,GLsizei stride)414 void VertexArray::bindVertexBuffer(const Context *context,
415 size_t bindingIndex,
416 Buffer *boundBuffer,
417 GLintptr offset,
418 GLsizei stride)
419 {
420 const VertexArray::DirtyBindingBits dirtyBindingBits =
421 bindVertexBufferImpl(context, bindingIndex, boundBuffer, offset, stride);
422 if (dirtyBindingBits.any())
423 {
424 mDirtyBits.set(DIRTY_BIT_BINDING_0 + bindingIndex);
425 mDirtyBindingBits[bindingIndex] |= dirtyBindingBits;
426 }
427 }
428
setVertexAttribBinding(const Context * context,size_t attribIndex,GLuint bindingIndex)429 void VertexArray::setVertexAttribBinding(const Context *context,
430 size_t attribIndex,
431 GLuint bindingIndex)
432 {
433 ASSERT(attribIndex < getMaxAttribs() && bindingIndex < getMaxBindings());
434
435 if (mState.mVertexAttributes[attribIndex].bindingIndex == bindingIndex)
436 {
437 return;
438 }
439
440 // In ES 3.0 contexts, the binding cannot change, hence the code below is unreachable.
441 ASSERT(context->getClientVersion() >= ES_3_1);
442
443 mState.setAttribBinding(context, attribIndex, bindingIndex);
444
445 setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_BINDING);
446
447 // Update client attribs mask.
448 bool hasBuffer = mState.mVertexBindings[bindingIndex].getBuffer().get() != nullptr;
449 mState.mClientMemoryAttribsMask.set(attribIndex, !hasBuffer);
450 }
451
setVertexBindingDivisor(const Context * context,size_t bindingIndex,GLuint divisor)452 void VertexArray::setVertexBindingDivisor(const Context *context,
453 size_t bindingIndex,
454 GLuint divisor)
455 {
456 ASSERT(bindingIndex < getMaxBindings());
457
458 VertexBinding &binding = mState.mVertexBindings[bindingIndex];
459
460 if (binding.getDivisor() == divisor)
461 {
462 return;
463 }
464
465 binding.setDivisor(divisor);
466 setDirtyBindingBit(bindingIndex, DIRTY_BINDING_DIVISOR);
467
468 // Trigger updates in all bound attributes.
469 if (context->isBufferAccessValidationEnabled())
470 {
471 for (size_t attribIndex : binding.getBoundAttributesMask())
472 {
473 mState.mVertexAttributes[attribIndex].updateCachedElementLimit(binding);
474 }
475 }
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 // Change of attrib.pointer is not part of attribDirty. Pointer is actually the buffer offset
600 // which is handled within bindVertexBufferImpl and reflected in bufferDirty.
601 attrib.pointer = pointer;
602 GLintptr offset = boundBuffer ? reinterpret_cast<GLintptr>(pointer) : 0;
603 const VertexArray::DirtyBindingBits dirtyBindingBits =
604 bindVertexBufferImpl(context, attribIndex, boundBuffer, offset, effectiveStride);
605
606 if (attribDirty)
607 {
608 setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_POINTER);
609 }
610 else if (dirtyBindingBits.any())
611 {
612 setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_POINTER_BUFFER);
613 }
614
615 mState.mNullPointerClientMemoryAttribsMask.set(attribIndex,
616 boundBuffer == nullptr && pointer == nullptr);
617 }
618
setVertexAttribPointer(const Context * context,size_t attribIndex,Buffer * boundBuffer,GLint size,VertexAttribType type,bool normalized,GLsizei stride,const void * pointer)619 void VertexArray::setVertexAttribPointer(const Context *context,
620 size_t attribIndex,
621 Buffer *boundBuffer,
622 GLint size,
623 VertexAttribType type,
624 bool normalized,
625 GLsizei stride,
626 const void *pointer)
627 {
628 setVertexAttribPointerImpl(context, ComponentType::Float, false, attribIndex, boundBuffer, size,
629 type, normalized, stride, pointer);
630 }
631
setVertexAttribIPointer(const Context * context,size_t attribIndex,Buffer * boundBuffer,GLint size,VertexAttribType type,GLsizei stride,const void * pointer)632 void VertexArray::setVertexAttribIPointer(const Context *context,
633 size_t attribIndex,
634 Buffer *boundBuffer,
635 GLint size,
636 VertexAttribType type,
637 GLsizei stride,
638 const void *pointer)
639 {
640 ComponentType componentType = GetVertexAttributeComponentType(true, type);
641 setVertexAttribPointerImpl(context, componentType, true, attribIndex, boundBuffer, size, type,
642 false, stride, pointer);
643 }
644
syncState(const Context * context)645 angle::Result VertexArray::syncState(const Context *context)
646 {
647 if (mDirtyBits.any())
648 {
649 mDirtyBitsGuard = mDirtyBits;
650 ANGLE_TRY(
651 mVertexArray->syncState(context, mDirtyBits, &mDirtyAttribBits, &mDirtyBindingBits));
652 mDirtyBits.reset();
653 mDirtyBitsGuard.reset();
654
655 // The dirty bits should be reset in the back-end. To simplify ASSERTs only check attrib 0.
656 ASSERT(mDirtyAttribBits[0].none());
657 ASSERT(mDirtyBindingBits[0].none());
658 mState.mLastSyncedEnabledAttributesMask = mState.mEnabledAttributesMask;
659 }
660 return angle::Result::Continue;
661 }
662
663 // This becomes current vertex array on the context
onBind(const Context * context)664 void VertexArray::onBind(const Context *context)
665 {
666 // This vertex array becoming current. Some of the bindings we may have removed from buffer's
667 // observer list. We need to add it back to the buffer's observer list and update dirty bits
668 // that we may have missed while we were not observing.
669 for (size_t bindingIndex : mState.getBufferBindingMask())
670 {
671 const VertexBinding &binding = mState.getVertexBindings()[bindingIndex];
672 Buffer *bufferGL = binding.getBuffer().get();
673 ASSERT(bufferGL != nullptr);
674
675 bufferGL->addObserver(&mArrayBufferObserverBindings[bindingIndex]);
676 updateCachedMappedArrayBuffersBinding(mState.mVertexBindings[bindingIndex]);
677
678 if (mBufferAccessValidationEnabled)
679 {
680 for (size_t boundAttribute :
681 mState.mVertexBindings[bindingIndex].getBoundAttributesMask())
682 {
683 mState.mVertexAttributes[boundAttribute].updateCachedElementLimit(
684 mState.mVertexBindings[bindingIndex]);
685 }
686 }
687
688 if (context->isWebGL())
689 {
690 updateCachedTransformFeedbackBindingValidation(bindingIndex, bufferGL);
691 }
692 }
693
694 mDirtyBits.set(DIRTY_BIT_LOST_OBSERVATION);
695 onStateChange(angle::SubjectMessage::ContentsChanged);
696 }
697
698 // This becomes non-current vertex array on the context
onUnbind(const Context * context)699 void VertexArray::onUnbind(const Context *context)
700 {
701 // This vertex array becoming non-current. For performance reason, we remove it from the
702 // buffers' observer list so that the cost of buffer sending signal to observers will not be too
703 // expensive.
704 for (size_t bindingIndex : mState.mBufferBindingMask)
705 {
706 const VertexBinding &binding = mState.getVertexBindings()[bindingIndex];
707 Buffer *bufferGL = binding.getBuffer().get();
708 ASSERT(bufferGL != nullptr);
709 bufferGL->removeObserver(&mArrayBufferObserverBindings[bindingIndex]);
710 }
711 }
712
onBindingChanged(const Context * context,int incr)713 void VertexArray::onBindingChanged(const Context *context, int incr)
714 {
715 // When vertex array gets unbound, we remove it from bound buffers' observer list so that when
716 // buffer changes, it wont has to loop over all these non-current vertex arrays and set dirty
717 // bit on them. To compensate for that, when we bind a vertex array, we have to check against
718 // each bound buffers and see if they have changed and needs to update vertex array's dirty bits
719 // accordingly
720 ASSERT(incr == 1 || incr == -1);
721 if (incr < 0)
722 {
723 onUnbind(context);
724 }
725 else
726 {
727 onBind(context);
728 }
729
730 if (context->isWebGL())
731 {
732 if (mState.mElementArrayBuffer.get())
733 {
734 mState.mElementArrayBuffer->onNonTFBindingChanged(incr);
735 }
736 for (size_t bindingIndex : mState.mBufferBindingMask)
737 {
738 mState.mVertexBindings[bindingIndex].onContainerBindingChanged(context, incr);
739 }
740 }
741 }
742
getDirtyBitFromIndex(bool contentsChanged,angle::SubjectIndex index) const743 VertexArray::DirtyBitType VertexArray::getDirtyBitFromIndex(bool contentsChanged,
744 angle::SubjectIndex index) const
745 {
746 if (IsElementArrayBufferSubjectIndex(index))
747 {
748 mIndexRangeCache.invalidate();
749 return contentsChanged ? DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA
750 : DIRTY_BIT_ELEMENT_ARRAY_BUFFER;
751 }
752 else
753 {
754 // Note: this currently just gets the top-level dirty bit.
755 ASSERT(index < mArrayBufferObserverBindings.size());
756 return static_cast<DirtyBitType>(
757 (contentsChanged ? DIRTY_BIT_BUFFER_DATA_0 : DIRTY_BIT_BINDING_0) + index);
758 }
759 }
760
onSubjectStateChange(angle::SubjectIndex index,angle::SubjectMessage message)761 void VertexArray::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message)
762 {
763 switch (message)
764 {
765 case angle::SubjectMessage::SubjectChanged:
766 if (!IsElementArrayBufferSubjectIndex(index))
767 {
768 updateCachedBufferBindingSize(&mState.mVertexBindings[index]);
769 }
770 setDependentDirtyBit(false, index);
771 break;
772
773 case angle::SubjectMessage::BindingChanged:
774 if (!IsElementArrayBufferSubjectIndex(index))
775 {
776 const Buffer *buffer = mState.mVertexBindings[index].getBuffer().get();
777 updateCachedTransformFeedbackBindingValidation(index, buffer);
778 }
779 break;
780
781 case angle::SubjectMessage::SubjectMapped:
782 if (!IsElementArrayBufferSubjectIndex(index))
783 {
784 updateCachedMappedArrayBuffersBinding(mState.mVertexBindings[index]);
785 }
786 onStateChange(angle::SubjectMessage::SubjectMapped);
787 break;
788
789 case angle::SubjectMessage::SubjectUnmapped:
790 setDependentDirtyBit(true, index);
791
792 if (!IsElementArrayBufferSubjectIndex(index))
793 {
794 updateCachedMappedArrayBuffersBinding(mState.mVertexBindings[index]);
795 }
796 onStateChange(angle::SubjectMessage::SubjectUnmapped);
797 break;
798
799 case angle::SubjectMessage::InternalMemoryAllocationChanged:
800 setDependentDirtyBit(false, index);
801 break;
802
803 default:
804 UNREACHABLE();
805 break;
806 }
807 }
808
setDependentDirtyBit(bool contentsChanged,angle::SubjectIndex index)809 void VertexArray::setDependentDirtyBit(bool contentsChanged, angle::SubjectIndex index)
810 {
811 DirtyBitType dirtyBit = getDirtyBitFromIndex(contentsChanged, index);
812 ASSERT(!mDirtyBitsGuard.valid() || mDirtyBitsGuard.value().test(dirtyBit));
813 mDirtyBits.set(dirtyBit);
814 onStateChange(angle::SubjectMessage::ContentsChanged);
815 }
816
hasTransformFeedbackBindingConflict(const Context * context) const817 bool VertexArray::hasTransformFeedbackBindingConflict(const Context *context) const
818 {
819 // Fast check first.
820 if (!mCachedTransformFeedbackConflictedBindingsMask.any())
821 {
822 return false;
823 }
824
825 const AttributesMask &activeAttribues = context->getStateCache().getActiveBufferedAttribsMask();
826
827 // Slow check. We must ensure that the conflicting attributes are enabled/active.
828 for (size_t attribIndex : activeAttribues)
829 {
830 const VertexAttribute &attrib = mState.mVertexAttributes[attribIndex];
831 if (mCachedTransformFeedbackConflictedBindingsMask[attrib.bindingIndex])
832 {
833 return true;
834 }
835 }
836
837 return false;
838 }
839
getIndexRangeImpl(const Context * context,DrawElementsType type,GLsizei indexCount,const void * indices,IndexRange * indexRangeOut) const840 angle::Result VertexArray::getIndexRangeImpl(const Context *context,
841 DrawElementsType type,
842 GLsizei indexCount,
843 const void *indices,
844 IndexRange *indexRangeOut) const
845 {
846 Buffer *elementArrayBuffer = mState.mElementArrayBuffer.get();
847 if (!elementArrayBuffer)
848 {
849 *indexRangeOut = ComputeIndexRange(type, indices, indexCount,
850 context->getState().isPrimitiveRestartEnabled());
851 return angle::Result::Continue;
852 }
853
854 size_t offset = reinterpret_cast<uintptr_t>(indices);
855 ANGLE_TRY(elementArrayBuffer->getIndexRange(context, type, offset, indexCount,
856 context->getState().isPrimitiveRestartEnabled(),
857 indexRangeOut));
858
859 mIndexRangeCache.put(type, indexCount, offset, *indexRangeOut);
860 return angle::Result::Continue;
861 }
862
863 VertexArray::IndexRangeCache::IndexRangeCache() = default;
864
put(DrawElementsType type,GLsizei indexCount,size_t offset,const IndexRange & indexRange)865 void VertexArray::IndexRangeCache::put(DrawElementsType type,
866 GLsizei indexCount,
867 size_t offset,
868 const IndexRange &indexRange)
869 {
870 ASSERT(type != DrawElementsType::InvalidEnum);
871
872 mTypeKey = type;
873 mIndexCountKey = indexCount;
874 mOffsetKey = offset;
875 mPayload = indexRange;
876 }
877
onBufferContentsChange(uint32_t bufferIndex)878 void VertexArray::onBufferContentsChange(uint32_t bufferIndex)
879 {
880 setDependentDirtyBit(true, bufferIndex);
881 }
882
VertexArrayBufferContentsObservers(VertexArray * vertexArray)883 VertexArrayBufferContentsObservers::VertexArrayBufferContentsObservers(VertexArray *vertexArray)
884 : mVertexArray(vertexArray)
885 {}
886
enableForBuffer(Buffer * buffer,uint32_t attribIndex)887 void VertexArrayBufferContentsObservers::enableForBuffer(Buffer *buffer, uint32_t attribIndex)
888 {
889 buffer->addContentsObserver(mVertexArray, attribIndex);
890 mBufferObserversBitMask.set(attribIndex);
891 }
892
disableForBuffer(Buffer * buffer,uint32_t attribIndex)893 void VertexArrayBufferContentsObservers::disableForBuffer(Buffer *buffer, uint32_t attribIndex)
894 {
895 buffer->removeContentsObserver(mVertexArray, attribIndex);
896 mBufferObserversBitMask.reset(attribIndex);
897 }
898 } // namespace gl
899