tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

VertexArray.cpp (33340B)


      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 {
     22 constexpr size_t kMaxObserverCountToTriggerUnobserve = 20;
     23 
     24 bool IsElementArrayBufferSubjectIndex(angle::SubjectIndex subjectIndex)
     25 {
     26    return (subjectIndex == kElementArrayBufferIndex);
     27 }
     28 }  // namespace
     29 
     30 // VertexArrayState implementation.
     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 
     48 VertexArrayState::~VertexArrayState() {}
     49 
     50 bool VertexArrayState::hasEnabledNullPointerClientArray() const
     51 {
     52    return (mNullPointerClientMemoryAttribsMask & mEnabledAttributesMask).any();
     53 }
     54 
     55 AttributesMask VertexArrayState::getBindingToAttributesMask(GLuint bindingIndex) const
     56 {
     57    ASSERT(bindingIndex < mVertexBindings.size());
     58    return mVertexBindings[bindingIndex].getBoundAttributesMask();
     59 }
     60 
     61 // Set an attribute using a new binding.
     62 void VertexArrayState::setAttribBinding(const Context *context,
     63                                        size_t attribIndex,
     64                                        GLuint newBindingIndex)
     65 {
     66    ASSERT(attribIndex < mVertexAttributes.size() && newBindingIndex < mVertexBindings.size());
     67 
     68    VertexAttribute &attrib = mVertexAttributes[attribIndex];
     69 
     70    // Update the binding-attribute map.
     71    const GLuint oldBindingIndex = attrib.bindingIndex;
     72    ASSERT(oldBindingIndex != newBindingIndex);
     73 
     74    VertexBinding &oldBinding = mVertexBindings[oldBindingIndex];
     75    VertexBinding &newBinding = mVertexBindings[newBindingIndex];
     76 
     77    ASSERT(oldBinding.getBoundAttributesMask().test(attribIndex) &&
     78           !newBinding.getBoundAttributesMask().test(attribIndex));
     79 
     80    oldBinding.resetBoundAttribute(attribIndex);
     81    newBinding.setBoundAttribute(attribIndex);
     82 
     83    // Set the attribute using the new binding.
     84    attrib.bindingIndex = newBindingIndex;
     85 
     86    if (context->isBufferAccessValidationEnabled())
     87    {
     88        attrib.updateCachedElementLimit(newBinding);
     89    }
     90 
     91    bool isMapped = newBinding.getBuffer().get() && newBinding.getBuffer()->isMapped();
     92    mCachedMappedArrayBuffers.set(attribIndex, isMapped);
     93    mEnabledAttributesMask.set(attribIndex, attrib.enabled);
     94    updateCachedMutableOrNonPersistentArrayBuffers(attribIndex);
     95    mCachedInvalidMappedArrayBuffer = mCachedMappedArrayBuffers & mEnabledAttributesMask &
     96                                      mCachedMutableOrImpersistentArrayBuffers;
     97 }
     98 
     99 void VertexArrayState::updateCachedMutableOrNonPersistentArrayBuffers(size_t index)
    100 {
    101    const VertexBinding &vertexBinding   = mVertexBindings[index];
    102    const BindingPointer<Buffer> &buffer = vertexBinding.getBuffer();
    103    bool isMutableOrImpersistentArrayBuffer =
    104        buffer.get() &&
    105        (!buffer->isImmutable() || (buffer->getAccessFlags() & GL_MAP_PERSISTENT_BIT_EXT) == 0);
    106    mCachedMutableOrImpersistentArrayBuffers.set(index, isMutableOrImpersistentArrayBuffer);
    107 }
    108 
    109 // VertexArray implementation.
    110 VertexArray::VertexArray(rx::GLImplFactory *factory,
    111                         VertexArrayID id,
    112                         size_t maxAttribs,
    113                         size_t maxAttribBindings)
    114    : mId(id),
    115      mState(this, maxAttribs, maxAttribBindings),
    116      mVertexArray(factory->createVertexArray(mState)),
    117      mBufferAccessValidationEnabled(false),
    118      mContentsObservers(this)
    119 {
    120    for (size_t attribIndex = 0; attribIndex < maxAttribBindings; ++attribIndex)
    121    {
    122        mArrayBufferObserverBindings.emplace_back(this, attribIndex);
    123    }
    124 
    125    mVertexArray->setContentsObservers(&mContentsObservers);
    126 }
    127 
    128 void VertexArray::onDestroy(const Context *context)
    129 {
    130    bool isBound = context->isCurrentVertexArray(this);
    131    for (uint32_t bindingIndex = 0; bindingIndex < mState.mVertexBindings.size(); ++bindingIndex)
    132    {
    133        VertexBinding &binding = mState.mVertexBindings[bindingIndex];
    134        Buffer *buffer         = binding.getBuffer().get();
    135        if (isBound)
    136        {
    137            if (buffer)
    138            {
    139                buffer->onNonTFBindingChanged(-1);
    140            }
    141        }
    142        if (buffer)
    143        {
    144            // Note: the non-contents observer is unbound in the ObserverBinding destructor.
    145            buffer->removeContentsObserver(this, bindingIndex);
    146        }
    147        binding.setBuffer(context, nullptr);
    148    }
    149    if (mState.mElementArrayBuffer.get())
    150    {
    151        if (isBound)
    152        {
    153            mState.mElementArrayBuffer->onNonTFBindingChanged(-1);
    154        }
    155        mState.mElementArrayBuffer->removeContentsObserver(this, kElementArrayBufferIndex);
    156    }
    157    mState.mElementArrayBuffer.bind(context, nullptr);
    158 
    159    // If mDirtyObserverBindingBits is set, it means we have removed it from the buffer's observer
    160    // list. We should unassign subject to avoid assertion.
    161    for (size_t bindingIndex : mDirtyObserverBindingBits)
    162    {
    163        angle::ObserverBinding *observer = &mArrayBufferObserverBindings[bindingIndex];
    164        observer->assignSubject(nullptr);
    165    }
    166 
    167    mVertexArray->destroy(context);
    168    SafeDelete(mVertexArray);
    169    delete this;
    170 }
    171 
    172 VertexArray::~VertexArray()
    173 {
    174    ASSERT(!mVertexArray);
    175 }
    176 
    177 angle::Result VertexArray::setLabel(const Context *context, const std::string &label)
    178 {
    179    mState.mLabel = label;
    180 
    181    if (mVertexArray)
    182    {
    183        return mVertexArray->onLabelUpdate(context);
    184    }
    185    return angle::Result::Continue;
    186 }
    187 
    188 const std::string &VertexArray::getLabel() const
    189 {
    190    return mState.mLabel;
    191 }
    192 
    193 bool VertexArray::detachBuffer(const Context *context, BufferID bufferID)
    194 {
    195    bool isBound           = context->isCurrentVertexArray(this);
    196    bool anyBufferDetached = false;
    197    for (uint32_t bindingIndex = 0; bindingIndex < mState.mVertexBindings.size(); ++bindingIndex)
    198    {
    199        VertexBinding &binding                      = mState.mVertexBindings[bindingIndex];
    200        const BindingPointer<Buffer> &bufferBinding = binding.getBuffer();
    201        if (bufferBinding.id() == bufferID)
    202        {
    203            if (isBound)
    204            {
    205                if (bufferBinding.get())
    206                    bufferBinding->onNonTFBindingChanged(-1);
    207            }
    208            bufferBinding->removeContentsObserver(this, bindingIndex);
    209            binding.setBuffer(context, nullptr);
    210            mArrayBufferObserverBindings[bindingIndex].reset();
    211 
    212            if (context->getClientVersion() >= ES_3_1)
    213            {
    214                setDirtyBindingBit(bindingIndex, DIRTY_BINDING_BUFFER);
    215            }
    216            else
    217            {
    218                static_assert(gl::MAX_VERTEX_ATTRIB_BINDINGS < 8 * sizeof(uint32_t),
    219                              "Not enough bits in bindingIndex");
    220                // The redundant uint32_t cast here is required to avoid a warning on MSVC.
    221                ASSERT(binding.getBoundAttributesMask() ==
    222                       AttributesMask(static_cast<uint32_t>(1 << bindingIndex)));
    223                setDirtyAttribBit(bindingIndex, DIRTY_ATTRIB_POINTER);
    224            }
    225 
    226            anyBufferDetached = true;
    227            mState.mClientMemoryAttribsMask |= binding.getBoundAttributesMask();
    228        }
    229    }
    230 
    231    if (mState.mElementArrayBuffer.get() && mState.mElementArrayBuffer->id() == bufferID)
    232    {
    233        if (isBound && mState.mElementArrayBuffer.get())
    234            mState.mElementArrayBuffer->onNonTFBindingChanged(-1);
    235        mState.mElementArrayBuffer->removeContentsObserver(this, kElementArrayBufferIndex);
    236        mState.mElementArrayBuffer.bind(context, nullptr);
    237        mDirtyBits.set(DIRTY_BIT_ELEMENT_ARRAY_BUFFER);
    238        anyBufferDetached = true;
    239    }
    240 
    241    return anyBufferDetached;
    242 }
    243 
    244 const VertexAttribute &VertexArray::getVertexAttribute(size_t attribIndex) const
    245 {
    246    ASSERT(attribIndex < getMaxAttribs());
    247    return mState.mVertexAttributes[attribIndex];
    248 }
    249 
    250 const VertexBinding &VertexArray::getVertexBinding(size_t bindingIndex) const
    251 {
    252    ASSERT(bindingIndex < getMaxBindings());
    253    return mState.mVertexBindings[bindingIndex];
    254 }
    255 
    256 size_t VertexArray::GetVertexIndexFromDirtyBit(size_t dirtyBit)
    257 {
    258    static_assert(gl::MAX_VERTEX_ATTRIBS == gl::MAX_VERTEX_ATTRIB_BINDINGS,
    259                  "The stride of vertex attributes should equal to that of vertex bindings.");
    260    ASSERT(dirtyBit > DIRTY_BIT_ELEMENT_ARRAY_BUFFER);
    261    return (dirtyBit - DIRTY_BIT_ATTRIB_0) % gl::MAX_VERTEX_ATTRIBS;
    262 }
    263 
    264 ANGLE_INLINE void VertexArray::setDirtyAttribBit(size_t attribIndex,
    265                                                 DirtyAttribBitType dirtyAttribBit)
    266 {
    267    mDirtyBits.set(DIRTY_BIT_ATTRIB_0 + attribIndex);
    268    mDirtyAttribBits[attribIndex].set(dirtyAttribBit);
    269 }
    270 
    271 ANGLE_INLINE void VertexArray::clearDirtyAttribBit(size_t attribIndex,
    272                                                   DirtyAttribBitType dirtyAttribBit)
    273 {
    274    mDirtyAttribBits[attribIndex].set(dirtyAttribBit, false);
    275    if (mDirtyAttribBits[attribIndex].any())
    276    {
    277        return;
    278    }
    279    mDirtyBits.set(DIRTY_BIT_ATTRIB_0 + attribIndex, false);
    280 }
    281 
    282 ANGLE_INLINE void VertexArray::setDirtyBindingBit(size_t bindingIndex,
    283                                                  DirtyBindingBitType dirtyBindingBit)
    284 {
    285    mDirtyBits.set(DIRTY_BIT_BINDING_0 + bindingIndex);
    286    mDirtyBindingBits[bindingIndex].set(dirtyBindingBit);
    287 }
    288 
    289 ANGLE_INLINE void VertexArray::updateCachedBufferBindingSize(VertexBinding *binding)
    290 {
    291    if (!mBufferAccessValidationEnabled)
    292        return;
    293 
    294    for (size_t boundAttribute : binding->getBoundAttributesMask())
    295    {
    296        mState.mVertexAttributes[boundAttribute].updateCachedElementLimit(*binding);
    297    }
    298 }
    299 
    300 ANGLE_INLINE void VertexArray::updateCachedArrayBuffersMasks(
    301    bool isMapped,
    302    bool isImmutable,
    303    bool isPersistent,
    304    const AttributesMask &boundAttributesMask)
    305 {
    306    if (isMapped)
    307    {
    308        mState.mCachedMappedArrayBuffers |= boundAttributesMask;
    309    }
    310    else
    311    {
    312        mState.mCachedMappedArrayBuffers &= ~boundAttributesMask;
    313    }
    314 
    315    if (!isImmutable || !isPersistent)
    316    {
    317        mState.mCachedMutableOrImpersistentArrayBuffers |= boundAttributesMask;
    318    }
    319    else
    320    {
    321        mState.mCachedMutableOrImpersistentArrayBuffers &= ~boundAttributesMask;
    322    }
    323 
    324    mState.mCachedInvalidMappedArrayBuffer = mState.mCachedMappedArrayBuffers &
    325                                             mState.mEnabledAttributesMask &
    326                                             mState.mCachedMutableOrImpersistentArrayBuffers;
    327 }
    328 
    329 ANGLE_INLINE void VertexArray::updateCachedMappedArrayBuffersBinding(const VertexBinding &binding)
    330 {
    331    const Buffer *buffer = binding.getBuffer().get();
    332    bool isMapped        = buffer && buffer->isMapped();
    333    bool isImmutable     = buffer && buffer->isImmutable();
    334    bool isPersistent    = buffer && (buffer->getAccessFlags() & GL_MAP_PERSISTENT_BIT_EXT) != 0;
    335    return updateCachedArrayBuffersMasks(isMapped, isImmutable, isPersistent,
    336                                         binding.getBoundAttributesMask());
    337 }
    338 
    339 ANGLE_INLINE void VertexArray::updateCachedTransformFeedbackBindingValidation(size_t bindingIndex,
    340                                                                              const Buffer *buffer)
    341 {
    342    const bool hasConflict = buffer && buffer->hasWebGLXFBBindingConflict(true);
    343    mCachedTransformFeedbackConflictedBindingsMask.set(bindingIndex, hasConflict);
    344 }
    345 
    346 bool VertexArray::bindVertexBufferImpl(const Context *context,
    347                                       size_t bindingIndex,
    348                                       Buffer *boundBuffer,
    349                                       GLintptr offset,
    350                                       GLsizei stride)
    351 {
    352    ASSERT(bindingIndex < getMaxBindings());
    353    ASSERT(context->isCurrentVertexArray(this));
    354 
    355    VertexBinding *binding = &mState.mVertexBindings[bindingIndex];
    356 
    357    Buffer *oldBuffer = binding->getBuffer().get();
    358 
    359    const bool sameBuffer = oldBuffer == boundBuffer;
    360    const bool sameStride = static_cast<GLuint>(stride) == binding->getStride();
    361    const bool sameOffset = offset == binding->getOffset();
    362 
    363    if (sameBuffer && sameStride && sameOffset)
    364    {
    365        return false;
    366    }
    367 
    368    angle::ObserverBinding *observer = &mArrayBufferObserverBindings[bindingIndex];
    369    observer->assignSubject(boundBuffer);
    370 
    371    // Several nullptr checks are combined here for optimization purposes.
    372    if (oldBuffer)
    373    {
    374        oldBuffer->onNonTFBindingChanged(-1);
    375        oldBuffer->removeObserver(observer);
    376        oldBuffer->removeContentsObserver(this, static_cast<uint32_t>(bindingIndex));
    377        oldBuffer->release(context);
    378    }
    379 
    380    binding->assignBuffer(boundBuffer);
    381    binding->setOffset(offset);
    382    binding->setStride(stride);
    383    updateCachedBufferBindingSize(binding);
    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.mClientMemoryAttribsMask &= ~binding->getBoundAttributesMask();
    397 
    398        bool isMapped     = boundBuffer->isMapped() == GL_TRUE;
    399        bool isImmutable  = boundBuffer->isImmutable() == GL_TRUE;
    400        bool isPersistent = (boundBuffer->getAccessFlags() & GL_MAP_PERSISTENT_BIT_EXT) != 0;
    401        updateCachedArrayBuffersMasks(isMapped, isImmutable, isPersistent,
    402                                      binding->getBoundAttributesMask());
    403    }
    404    else
    405    {
    406        if (context->isWebGL())
    407        {
    408            mCachedTransformFeedbackConflictedBindingsMask.set(bindingIndex, false);
    409        }
    410        mState.mClientMemoryAttribsMask |= binding->getBoundAttributesMask();
    411        updateCachedArrayBuffersMasks(false, false, false, binding->getBoundAttributesMask());
    412    }
    413 
    414    return true;
    415 }
    416 
    417 void VertexArray::bindVertexBuffer(const Context *context,
    418                                   size_t bindingIndex,
    419                                   Buffer *boundBuffer,
    420                                   GLintptr offset,
    421                                   GLsizei stride)
    422 {
    423    if (bindVertexBufferImpl(context, bindingIndex, boundBuffer, offset, stride))
    424    {
    425        setDirtyBindingBit(bindingIndex, DIRTY_BINDING_BUFFER);
    426    }
    427 }
    428 
    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 
    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 
    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 = gl::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 
    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 
    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 
    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 
    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 bool bufferDirty =
    604        bindVertexBufferImpl(context, attribIndex, boundBuffer, offset, effectiveStride);
    605 
    606    if (attribDirty)
    607    {
    608        setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_POINTER);
    609    }
    610    else if (bufferDirty)
    611    {
    612        setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_POINTER_BUFFER);
    613    }
    614 
    615    mState.mNullPointerClientMemoryAttribsMask.set(attribIndex,
    616                                                   boundBuffer == nullptr && pointer == nullptr);
    617 }
    618 
    619 void VertexArray::setVertexAttribPointer(const Context *context,
    620                                         size_t attribIndex,
    621                                         gl::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 
    632 void VertexArray::setVertexAttribIPointer(const Context *context,
    633                                          size_t attribIndex,
    634                                          gl::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 
    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
    664 void VertexArray::onBind(const Context *context)
    665 {
    666    if (mDirtyObserverBindingBits.none())
    667    {
    668        return;
    669    }
    670 
    671    // This vertex array becoming current. Some of the bindings we may have removed from buffer's
    672    // observer list. We need to add it back to the buffer's observer list and update dirty bits
    673    // that we may have missed while we were not observing.
    674    for (size_t bindingIndex : mDirtyObserverBindingBits)
    675    {
    676        const gl::VertexBinding &binding = mState.getVertexBindings()[bindingIndex];
    677        gl::Buffer *bufferGL             = binding.getBuffer().get();
    678        ASSERT(bufferGL != nullptr);
    679 
    680        bufferGL->addObserver(&mArrayBufferObserverBindings[bindingIndex]);
    681        updateCachedMappedArrayBuffersBinding(mState.mVertexBindings[bindingIndex]);
    682 
    683        // Assume both data and internal storage has been dirtied.
    684        mDirtyBits.set(DIRTY_BIT_BINDING_0 + bindingIndex);
    685 
    686        if (mBufferAccessValidationEnabled)
    687        {
    688            for (size_t boundAttribute :
    689                 mState.mVertexBindings[bindingIndex].getBoundAttributesMask())
    690            {
    691                mState.mVertexAttributes[boundAttribute].updateCachedElementLimit(
    692                    mState.mVertexBindings[bindingIndex]);
    693            }
    694        }
    695 
    696        if (context->isWebGL())
    697        {
    698            updateCachedTransformFeedbackBindingValidation(bindingIndex, bufferGL);
    699        }
    700    }
    701    mDirtyObserverBindingBits.reset();
    702 
    703    onStateChange(angle::SubjectMessage::ContentsChanged);
    704 }
    705 
    706 // This becomes non-current vertex array on the context
    707 void VertexArray::onUnbind(const Context *context)
    708 {
    709    // This vertex array becoming non-current. For performance reason, if there are too many
    710    // observers in the buffer, we remove it from the buffers' observer list so that the cost of
    711    // buffer sending signal to observers will be too expensive.
    712    for (uint32_t bindingIndex = 0; bindingIndex < mArrayBufferObserverBindings.size();
    713         ++bindingIndex)
    714    {
    715        const gl::VertexBinding &binding = mState.getVertexBindings()[bindingIndex];
    716        gl::Buffer *bufferGL             = binding.getBuffer().get();
    717        if (bufferGL && bufferGL->getObserversCount() > kMaxObserverCountToTriggerUnobserve)
    718        {
    719            bufferGL->removeObserver(&mArrayBufferObserverBindings[bindingIndex]);
    720            mDirtyObserverBindingBits.set(bindingIndex);
    721        }
    722    }
    723 }
    724 
    725 void VertexArray::onBindingChanged(const Context *context, int incr)
    726 {
    727    // When vertex array gets unbound, we remove it from bound buffers' observer list so that when
    728    // buffer changes, it wont has to loop over all these non-current vertex arrays and set dirty
    729    // bit on them. To compensate for that, when we bind a vertex array, we have to check against
    730    // each bound buffers and see if they have changed and needs to update vertex array's dirty bits
    731    // accordingly
    732    ASSERT(incr == 1 || incr == -1);
    733    if (incr < 0)
    734    {
    735        onUnbind(context);
    736    }
    737    else
    738    {
    739        onBind(context);
    740    }
    741 
    742    if (context->isWebGL())
    743    {
    744        if (mState.mElementArrayBuffer.get())
    745            mState.mElementArrayBuffer->onNonTFBindingChanged(incr);
    746        for (auto &binding : mState.mVertexBindings)
    747        {
    748            binding.onContainerBindingChanged(context, incr);
    749        }
    750    }
    751 }
    752 
    753 VertexArray::DirtyBitType VertexArray::getDirtyBitFromIndex(bool contentsChanged,
    754                                                            angle::SubjectIndex index) const
    755 {
    756    if (IsElementArrayBufferSubjectIndex(index))
    757    {
    758        mIndexRangeCache.invalidate();
    759        return contentsChanged ? DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA
    760                               : DIRTY_BIT_ELEMENT_ARRAY_BUFFER;
    761    }
    762    else
    763    {
    764        // Note: this currently just gets the top-level dirty bit.
    765        ASSERT(index < mArrayBufferObserverBindings.size());
    766        return static_cast<DirtyBitType>(
    767            (contentsChanged ? DIRTY_BIT_BUFFER_DATA_0 : DIRTY_BIT_BINDING_0) + index);
    768    }
    769 }
    770 
    771 void VertexArray::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message)
    772 {
    773    switch (message)
    774    {
    775        case angle::SubjectMessage::SubjectChanged:
    776            if (!IsElementArrayBufferSubjectIndex(index))
    777            {
    778                updateCachedBufferBindingSize(&mState.mVertexBindings[index]);
    779            }
    780            setDependentDirtyBit(false, index);
    781            break;
    782 
    783        case angle::SubjectMessage::BindingChanged:
    784            if (!IsElementArrayBufferSubjectIndex(index))
    785            {
    786                const Buffer *buffer = mState.mVertexBindings[index].getBuffer().get();
    787                updateCachedTransformFeedbackBindingValidation(index, buffer);
    788            }
    789            break;
    790 
    791        case angle::SubjectMessage::SubjectMapped:
    792            if (!IsElementArrayBufferSubjectIndex(index))
    793            {
    794                updateCachedMappedArrayBuffersBinding(mState.mVertexBindings[index]);
    795            }
    796            onStateChange(angle::SubjectMessage::SubjectMapped);
    797            break;
    798 
    799        case angle::SubjectMessage::SubjectUnmapped:
    800            setDependentDirtyBit(true, index);
    801 
    802            if (!IsElementArrayBufferSubjectIndex(index))
    803            {
    804                updateCachedMappedArrayBuffersBinding(mState.mVertexBindings[index]);
    805            }
    806            onStateChange(angle::SubjectMessage::SubjectUnmapped);
    807            break;
    808 
    809        case angle::SubjectMessage::InternalMemoryAllocationChanged:
    810            setDependentDirtyBit(false, index);
    811            break;
    812 
    813        default:
    814            UNREACHABLE();
    815            break;
    816    }
    817 }
    818 
    819 void VertexArray::setDependentDirtyBit(bool contentsChanged, angle::SubjectIndex index)
    820 {
    821    DirtyBitType dirtyBit = getDirtyBitFromIndex(contentsChanged, index);
    822    ASSERT(!mDirtyBitsGuard.valid() || mDirtyBitsGuard.value().test(dirtyBit));
    823    mDirtyBits.set(dirtyBit);
    824    onStateChange(angle::SubjectMessage::ContentsChanged);
    825 }
    826 
    827 bool VertexArray::hasTransformFeedbackBindingConflict(const gl::Context *context) const
    828 {
    829    // Fast check first.
    830    if (!mCachedTransformFeedbackConflictedBindingsMask.any())
    831    {
    832        return false;
    833    }
    834 
    835    const AttributesMask &activeAttribues = context->getStateCache().getActiveBufferedAttribsMask();
    836 
    837    // Slow check. We must ensure that the conflicting attributes are enabled/active.
    838    for (size_t attribIndex : activeAttribues)
    839    {
    840        const VertexAttribute &attrib = mState.mVertexAttributes[attribIndex];
    841        if (mCachedTransformFeedbackConflictedBindingsMask[attrib.bindingIndex])
    842        {
    843            return true;
    844        }
    845    }
    846 
    847    return false;
    848 }
    849 
    850 angle::Result VertexArray::getIndexRangeImpl(const Context *context,
    851                                             DrawElementsType type,
    852                                             GLsizei indexCount,
    853                                             const void *indices,
    854                                             IndexRange *indexRangeOut) const
    855 {
    856    Buffer *elementArrayBuffer = mState.mElementArrayBuffer.get();
    857    if (!elementArrayBuffer)
    858    {
    859        *indexRangeOut = ComputeIndexRange(type, indices, indexCount,
    860                                           context->getState().isPrimitiveRestartEnabled());
    861        return angle::Result::Continue;
    862    }
    863 
    864    size_t offset = reinterpret_cast<uintptr_t>(indices);
    865    ANGLE_TRY(elementArrayBuffer->getIndexRange(context, type, offset, indexCount,
    866                                                context->getState().isPrimitiveRestartEnabled(),
    867                                                indexRangeOut));
    868 
    869    mIndexRangeCache.put(type, indexCount, offset, *indexRangeOut);
    870    return angle::Result::Continue;
    871 }
    872 
    873 VertexArray::IndexRangeCache::IndexRangeCache() = default;
    874 
    875 void VertexArray::IndexRangeCache::put(DrawElementsType type,
    876                                       GLsizei indexCount,
    877                                       size_t offset,
    878                                       const IndexRange &indexRange)
    879 {
    880    ASSERT(type != DrawElementsType::InvalidEnum);
    881 
    882    mTypeKey       = type;
    883    mIndexCountKey = indexCount;
    884    mOffsetKey     = offset;
    885    mPayload       = indexRange;
    886 }
    887 
    888 void VertexArray::onBufferContentsChange(uint32_t bufferIndex)
    889 {
    890    setDependentDirtyBit(true, bufferIndex);
    891 }
    892 
    893 VertexArrayBufferContentsObservers::VertexArrayBufferContentsObservers(VertexArray *vertexArray)
    894    : mVertexArray(vertexArray)
    895 {}
    896 
    897 void VertexArrayBufferContentsObservers::enableForBuffer(Buffer *buffer, uint32_t bufferIndex)
    898 {
    899    buffer->addContentsObserver(mVertexArray, bufferIndex);
    900 }
    901 
    902 void VertexArrayBufferContentsObservers::disableForBuffer(Buffer *buffer, uint32_t bufferIndex)
    903 {
    904    buffer->removeContentsObserver(mVertexArray, bufferIndex);
    905 }
    906 }  // namespace gl