Edit

kc3-lang/angle/src/libANGLE/VertexArray.cpp

Branch :

  • Show log

    Commit

  • Author : Stuart Morgan
    Date : 2019-08-14 12:25:12
    Hash : 9d737966
    Message : Standardize copyright notices to project style For all "ANGLE Project" copyrights, standardize to the format specified by the style guide. Changes: - "Copyright (c)" and "Copyright(c)" changed to just "Copyright". - Removed the second half of date ranges ("Y1Y1-Y2Y2"->"Y1Y1"). - Fixed a small number of files that had no copyright date using the initial commit year from the version control history. - Fixed one instance of copyright being "The ANGLE Project" rather than "The ANGLE Project Authors" These changes are applied both to the copyright of source file, and where applicable to copyright statements that are generated by templates. BUG=angleproject:3811 Change-Id: I973dd65e4ef9deeba232d5be74c768256a0eb2e5 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1754397 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org>

  • src/libANGLE/VertexArray.cpp
  • //
    // Copyright 2013 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    // Implementation of the state class for mananging GLES 3 Vertex Array Objects.
    //
    
    #include "libANGLE/VertexArray.h"
    
    #include "common/utilities.h"
    #include "libANGLE/Buffer.h"
    #include "libANGLE/Context.h"
    #include "libANGLE/renderer/BufferImpl.h"
    #include "libANGLE/renderer/GLImplFactory.h"
    #include "libANGLE/renderer/VertexArrayImpl.h"
    
    namespace gl
    {
    namespace
    {
    bool IsElementArrayBufferSubjectIndex(angle::SubjectIndex subjectIndex)
    {
        return (subjectIndex == MAX_VERTEX_ATTRIBS);
    }
    
    constexpr angle::SubjectIndex kElementArrayBufferIndex = MAX_VERTEX_ATTRIBS;
    }  // namespace
    
    // VertexArrayState implementation.
    VertexArrayState::VertexArrayState(VertexArray *vertexArray,
                                       size_t maxAttribs,
                                       size_t maxAttribBindings)
        : mElementArrayBuffer(vertexArray, kElementArrayBufferIndex)
    {
        ASSERT(maxAttribs <= maxAttribBindings);
    
        for (size_t i = 0; i < maxAttribs; i++)
        {
            mVertexAttributes.emplace_back(static_cast<GLuint>(i));
            mVertexBindings.emplace_back(static_cast<GLuint>(i));
        }
    
        // Initially all attributes start as "client" with no buffer bound.
        mClientMemoryAttribsMask.set();
    }
    
    VertexArrayState::~VertexArrayState() {}
    
    bool VertexArrayState::hasEnabledNullPointerClientArray() const
    {
        return (mNullPointerClientMemoryAttribsMask & mEnabledAttributesMask).any();
    }
    
    AttributesMask VertexArrayState::getBindingToAttributesMask(GLuint bindingIndex) const
    {
        ASSERT(bindingIndex < MAX_VERTEX_ATTRIB_BINDINGS);
        return mVertexBindings[bindingIndex].getBoundAttributesMask();
    }
    
    // Set an attribute using a new binding.
    void VertexArrayState::setAttribBinding(const Context *context,
                                            size_t attribIndex,
                                            GLuint newBindingIndex)
    {
        ASSERT(attribIndex < MAX_VERTEX_ATTRIBS && newBindingIndex < MAX_VERTEX_ATTRIB_BINDINGS);
    
        VertexAttribute &attrib = mVertexAttributes[attribIndex];
    
        // Update the binding-attribute map.
        const GLuint oldBindingIndex = attrib.bindingIndex;
        ASSERT(oldBindingIndex != newBindingIndex);
    
        VertexBinding &oldBinding = mVertexBindings[oldBindingIndex];
        VertexBinding &newBinding = mVertexBindings[newBindingIndex];
    
        ASSERT(oldBinding.getBoundAttributesMask().test(attribIndex) &&
               !newBinding.getBoundAttributesMask().test(attribIndex));
    
        oldBinding.resetBoundAttribute(attribIndex);
        newBinding.setBoundAttribute(attribIndex);
    
        // Set the attribute using the new binding.
        attrib.bindingIndex = newBindingIndex;
    
        if (context->isBufferAccessValidationEnabled())
        {
            attrib.updateCachedElementLimit(newBinding);
        }
    
        bool isMapped = newBinding.getBuffer().get() && newBinding.getBuffer()->isMapped();
        mCachedMappedArrayBuffers.set(attribIndex, isMapped);
        mCachedEnabledMappedArrayBuffers.set(attribIndex, isMapped && attrib.enabled);
    }
    
    // VertexArray implementation.
    VertexArray::VertexArray(rx::GLImplFactory *factory,
                             GLuint id,
                             size_t maxAttribs,
                             size_t maxAttribBindings)
        : mId(id),
          mState(this, maxAttribs, maxAttribBindings),
          mVertexArray(factory->createVertexArray(mState)),
          mBufferAccessValidationEnabled(false)
    {
        for (size_t attribIndex = 0; attribIndex < maxAttribBindings; ++attribIndex)
        {
            mArrayBufferObserverBindings.emplace_back(this, attribIndex);
        }
    }
    
    void VertexArray::onDestroy(const Context *context)
    {
        bool isBound = context->isCurrentVertexArray(this);
        for (VertexBinding &binding : mState.mVertexBindings)
        {
            if (isBound)
            {
                if (binding.getBuffer().get())
                    binding.getBuffer()->onNonTFBindingChanged(-1);
            }
            binding.setBuffer(context, nullptr);
        }
        if (isBound && mState.mElementArrayBuffer.get())
            mState.mElementArrayBuffer->onNonTFBindingChanged(-1);
        mState.mElementArrayBuffer.bind(context, nullptr);
        mVertexArray->destroy(context);
        SafeDelete(mVertexArray);
        delete this;
    }
    
    VertexArray::~VertexArray()
    {
        ASSERT(!mVertexArray);
    }
    
    void VertexArray::setLabel(const Context *context, const std::string &label)
    {
        mState.mLabel = label;
    }
    
    const std::string &VertexArray::getLabel() const
    {
        return mState.mLabel;
    }
    
    bool VertexArray::detachBuffer(const Context *context, GLuint bufferName)
    {
        bool isBound           = context->isCurrentVertexArray(this);
        bool anyBufferDetached = false;
        for (size_t bindingIndex = 0; bindingIndex < gl::MAX_VERTEX_ATTRIB_BINDINGS; ++bindingIndex)
        {
            VertexBinding &binding = mState.mVertexBindings[bindingIndex];
            if (binding.getBuffer().id() == bufferName)
            {
                if (isBound)
                {
                    if (binding.getBuffer().get())
                        binding.getBuffer()->onNonTFBindingChanged(-1);
                }
                binding.setBuffer(context, nullptr);
                mArrayBufferObserverBindings[bindingIndex].reset();
    
                if (context->getClientVersion() >= ES_3_1)
                {
                    setDirtyBindingBit(bindingIndex, DIRTY_BINDING_BUFFER);
                }
                else
                {
                    ASSERT(binding.getBoundAttributesMask() ==
                           AttributesMask(static_cast<size_t>(1) << bindingIndex));
                    setDirtyAttribBit(bindingIndex, DIRTY_ATTRIB_POINTER);
                }
    
                anyBufferDetached = true;
                mState.mClientMemoryAttribsMask |= binding.getBoundAttributesMask();
            }
        }
    
        if (mState.mElementArrayBuffer.get() && mState.mElementArrayBuffer->id() == bufferName)
        {
            if (isBound && mState.mElementArrayBuffer.get())
                mState.mElementArrayBuffer->onNonTFBindingChanged(-1);
            mState.mElementArrayBuffer.bind(context, nullptr);
            mDirtyBits.set(DIRTY_BIT_ELEMENT_ARRAY_BUFFER);
            anyBufferDetached = true;
        }
    
        return anyBufferDetached;
    }
    
    const VertexAttribute &VertexArray::getVertexAttribute(size_t attribIndex) const
    {
        ASSERT(attribIndex < getMaxAttribs());
        return mState.mVertexAttributes[attribIndex];
    }
    
    const VertexBinding &VertexArray::getVertexBinding(size_t bindingIndex) const
    {
        ASSERT(bindingIndex < getMaxBindings());
        return mState.mVertexBindings[bindingIndex];
    }
    
    size_t VertexArray::GetVertexIndexFromDirtyBit(size_t dirtyBit)
    {
        static_assert(gl::MAX_VERTEX_ATTRIBS == gl::MAX_VERTEX_ATTRIB_BINDINGS,
                      "The stride of vertex attributes should equal to that of vertex bindings.");
        ASSERT(dirtyBit > DIRTY_BIT_ELEMENT_ARRAY_BUFFER);
        return (dirtyBit - DIRTY_BIT_ATTRIB_0) % gl::MAX_VERTEX_ATTRIBS;
    }
    
    ANGLE_INLINE void VertexArray::setDirtyAttribBit(size_t attribIndex,
                                                     DirtyAttribBitType dirtyAttribBit)
    {
        mDirtyBits.set(DIRTY_BIT_ATTRIB_0 + attribIndex);
        mDirtyAttribBits[attribIndex].set(dirtyAttribBit);
    }
    
    ANGLE_INLINE void VertexArray::setDirtyBindingBit(size_t bindingIndex,
                                                      DirtyBindingBitType dirtyBindingBit)
    {
        mDirtyBits.set(DIRTY_BIT_BINDING_0 + bindingIndex);
        mDirtyBindingBits[bindingIndex].set(dirtyBindingBit);
    }
    
    ANGLE_INLINE void VertexArray::updateCachedBufferBindingSize(VertexBinding *binding)
    {
        if (!mBufferAccessValidationEnabled)
            return;
    
        for (size_t boundAttribute : binding->getBoundAttributesMask())
        {
            mState.mVertexAttributes[boundAttribute].updateCachedElementLimit(*binding);
        }
    }
    
    ANGLE_INLINE void VertexArray::updateCachedMappedArrayBuffers(
        bool isMapped,
        const AttributesMask &boundAttributesMask)
    {
        if (isMapped)
        {
            mState.mCachedMappedArrayBuffers |= boundAttributesMask;
        }
        else
        {
            mState.mCachedMappedArrayBuffers &= ~boundAttributesMask;
        }
    
        mState.mCachedEnabledMappedArrayBuffers =
            mState.mCachedMappedArrayBuffers & mState.mEnabledAttributesMask;
    }
    
    ANGLE_INLINE void VertexArray::updateCachedMappedArrayBuffersBinding(const VertexBinding &binding)
    {
        const Buffer *buffer = binding.getBuffer().get();
        return updateCachedMappedArrayBuffers(buffer && buffer->isMapped(),
                                              binding.getBoundAttributesMask());
    }
    
    ANGLE_INLINE void VertexArray::updateCachedTransformFeedbackBindingValidation(size_t bindingIndex,
                                                                                  const Buffer *buffer)
    {
        const bool hasConflict = buffer && buffer->isBoundForTransformFeedbackAndOtherUse();
        mCachedTransformFeedbackConflictedBindingsMask.set(bindingIndex, hasConflict);
    }
    
    bool VertexArray::bindVertexBufferImpl(const Context *context,
                                           size_t bindingIndex,
                                           Buffer *boundBuffer,
                                           GLintptr offset,
                                           GLsizei stride)
    {
        ASSERT(bindingIndex < getMaxBindings());
        ASSERT(context->isCurrentVertexArray(this));
    
        VertexBinding *binding = &mState.mVertexBindings[bindingIndex];
    
        Buffer *oldBuffer = binding->getBuffer().get();
    
        const bool sameBuffer = oldBuffer == boundBuffer;
        const bool sameStride = static_cast<GLuint>(stride) == binding->getStride();
        const bool sameOffset = offset == binding->getOffset();
    
        if (sameBuffer && sameStride && sameOffset)
        {
            return false;
        }
    
        angle::ObserverBinding *observer = &mArrayBufferObserverBindings[bindingIndex];
        observer->assignSubject(boundBuffer);
    
        // Several nullptr checks are combined here for optimization purposes.
        if (oldBuffer)
        {
            oldBuffer->onNonTFBindingChanged(-1);
            oldBuffer->removeObserver(observer);
            oldBuffer->release(context);
        }
    
        binding->assignBuffer(boundBuffer);
        binding->setOffset(offset);
        binding->setStride(stride);
        updateCachedBufferBindingSize(binding);
    
        // Update client memory attribute pointers. Affects all bound attributes.
        if (boundBuffer)
        {
            boundBuffer->addRef();
            boundBuffer->onNonTFBindingChanged(1);
            boundBuffer->addObserver(observer);
            mCachedTransformFeedbackConflictedBindingsMask.set(
                bindingIndex, boundBuffer->isBoundForTransformFeedbackAndOtherUse());
            mState.mClientMemoryAttribsMask &= ~binding->getBoundAttributesMask();
            updateCachedMappedArrayBuffers(boundBuffer->isMapped(), binding->getBoundAttributesMask());
        }
        else
        {
            mCachedTransformFeedbackConflictedBindingsMask.set(bindingIndex, false);
            mState.mClientMemoryAttribsMask |= binding->getBoundAttributesMask();
            updateCachedMappedArrayBuffers(false, binding->getBoundAttributesMask());
        }
    
        return true;
    }
    
    void VertexArray::bindVertexBuffer(const Context *context,
                                       size_t bindingIndex,
                                       Buffer *boundBuffer,
                                       GLintptr offset,
                                       GLsizei stride)
    {
        if (bindVertexBufferImpl(context, bindingIndex, boundBuffer, offset, stride))
        {
            setDirtyBindingBit(bindingIndex, DIRTY_BINDING_BUFFER);
        }
    }
    
    void VertexArray::setVertexAttribBinding(const Context *context,
                                             size_t attribIndex,
                                             GLuint bindingIndex)
    {
        ASSERT(attribIndex < getMaxAttribs() && bindingIndex < getMaxBindings());
    
        if (mState.mVertexAttributes[attribIndex].bindingIndex != bindingIndex)
        {
            // In ES 3.0 contexts, the binding cannot change, hence the code below is unreachable.
            ASSERT(context->getClientVersion() >= ES_3_1);
    
            mState.setAttribBinding(context, attribIndex, bindingIndex);
    
            setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_BINDING);
    
            // Update client attribs mask.
            bool hasBuffer = mState.mVertexBindings[bindingIndex].getBuffer().get() != nullptr;
            mState.mClientMemoryAttribsMask.set(attribIndex, !hasBuffer);
        }
    }
    
    void VertexArray::setVertexBindingDivisor(size_t bindingIndex, GLuint divisor)
    {
        ASSERT(bindingIndex < getMaxBindings());
    
        VertexBinding &binding = mState.mVertexBindings[bindingIndex];
    
        binding.setDivisor(divisor);
        setDirtyBindingBit(bindingIndex, DIRTY_BINDING_DIVISOR);
    
        // Trigger updates in all bound attributes.
        for (size_t attribIndex : binding.getBoundAttributesMask())
        {
            mState.mVertexAttributes[attribIndex].updateCachedElementLimit(binding);
        }
    }
    
    ANGLE_INLINE bool VertexArray::setVertexAttribFormatImpl(VertexAttribute *attrib,
                                                             GLint size,
                                                             VertexAttribType type,
                                                             bool normalized,
                                                             bool pureInteger,
                                                             GLuint relativeOffset)
    {
        angle::FormatID formatID = gl::GetVertexFormatID(type, normalized, size, pureInteger);
    
        if (formatID != attrib->format->id || attrib->relativeOffset != relativeOffset)
        {
            attrib->relativeOffset = relativeOffset;
            attrib->format         = &angle::Format::Get(formatID);
            return true;
        }
    
        return false;
    }
    
    void VertexArray::setVertexAttribFormat(size_t attribIndex,
                                            GLint size,
                                            VertexAttribType type,
                                            bool normalized,
                                            bool pureInteger,
                                            GLuint relativeOffset)
    {
        VertexAttribute &attrib = mState.mVertexAttributes[attribIndex];
    
        ComponentType componentType = GetVertexAttributeComponentType(pureInteger, type);
        SetComponentTypeMask(componentType, attribIndex, &mState.mVertexAttributesTypeMask);
    
        if (setVertexAttribFormatImpl(&attrib, size, type, normalized, pureInteger, relativeOffset))
        {
            setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_FORMAT);
        }
    
        attrib.updateCachedElementLimit(mState.mVertexBindings[attrib.bindingIndex]);
    }
    
    void VertexArray::setVertexAttribDivisor(const Context *context, size_t attribIndex, GLuint divisor)
    {
        ASSERT(attribIndex < getMaxAttribs());
    
        setVertexAttribBinding(context, attribIndex, static_cast<GLuint>(attribIndex));
        setVertexBindingDivisor(attribIndex, divisor);
    }
    
    void VertexArray::enableAttribute(size_t attribIndex, bool enabledState)
    {
        ASSERT(attribIndex < getMaxAttribs());
    
        VertexAttribute &attrib = mState.mVertexAttributes[attribIndex];
    
        if (mState.mEnabledAttributesMask.test(attribIndex) == enabledState)
        {
            return;
        }
    
        attrib.enabled = enabledState;
    
        setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_ENABLED);
    
        // Update state cache
        mState.mEnabledAttributesMask.set(attribIndex, enabledState);
        mState.mCachedEnabledMappedArrayBuffers =
            mState.mCachedMappedArrayBuffers & mState.mEnabledAttributesMask;
    }
    
    ANGLE_INLINE void VertexArray::setVertexAttribPointerImpl(const Context *context,
                                                              ComponentType componentType,
                                                              bool pureInteger,
                                                              size_t attribIndex,
                                                              Buffer *boundBuffer,
                                                              GLint size,
                                                              VertexAttribType type,
                                                              bool normalized,
                                                              GLsizei stride,
                                                              const void *pointer)
    {
        ASSERT(attribIndex < getMaxAttribs());
    
        GLintptr offset = boundBuffer ? reinterpret_cast<GLintptr>(pointer) : 0;
    
        VertexAttribute &attrib = mState.mVertexAttributes[attribIndex];
    
        SetComponentTypeMask(componentType, attribIndex, &mState.mVertexAttributesTypeMask);
    
        bool attribDirty = setVertexAttribFormatImpl(&attrib, size, type, normalized, pureInteger, 0);
    
        if (attrib.bindingIndex != attribIndex)
        {
            setVertexAttribBinding(context, attribIndex, static_cast<GLuint>(attribIndex));
        }
    
        GLsizei effectiveStride =
            stride != 0 ? stride : static_cast<GLsizei>(ComputeVertexAttributeTypeSize(attrib));
    
        if (pointer != attrib.pointer || attrib.vertexAttribArrayStride != static_cast<GLuint>(stride))
        {
            attribDirty = true;
        }
    
        attrib.pointer                 = pointer;
        attrib.vertexAttribArrayStride = stride;
    
        // "Pointer buffer" dirty bit disabled because of a bug. http://anglebug.com/3256
        bindVertexBufferImpl(context, attribIndex, boundBuffer, offset, effectiveStride);
        setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_POINTER);
        ANGLE_UNUSED_VARIABLE(attribDirty);
    
        // if (bindVertexBufferImpl(context, attribIndex, boundBuffer, offset, effectiveStride) &&
        //    !attribDirty)
        //{
        //    setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_POINTER_BUFFER);
        //}
        // else if (attribDirty)
        //{
        //    setDirtyAttribBit(attribIndex, DIRTY_ATTRIB_POINTER);
        //}
    
        mState.mNullPointerClientMemoryAttribsMask.set(attribIndex,
                                                       boundBuffer == nullptr && pointer == nullptr);
    }
    
    void VertexArray::setVertexAttribPointer(const Context *context,
                                             size_t attribIndex,
                                             gl::Buffer *boundBuffer,
                                             GLint size,
                                             VertexAttribType type,
                                             bool normalized,
                                             GLsizei stride,
                                             const void *pointer)
    {
        setVertexAttribPointerImpl(context, ComponentType::Float, false, attribIndex, boundBuffer, size,
                                   type, normalized, stride, pointer);
    }
    
    void VertexArray::setVertexAttribIPointer(const Context *context,
                                              size_t attribIndex,
                                              gl::Buffer *boundBuffer,
                                              GLint size,
                                              VertexAttribType type,
                                              GLsizei stride,
                                              const void *pointer)
    {
        ComponentType componentType = GetVertexAttributeComponentType(true, type);
        setVertexAttribPointerImpl(context, componentType, true, attribIndex, boundBuffer, size, type,
                                   false, stride, pointer);
    }
    
    angle::Result VertexArray::syncState(const Context *context)
    {
        if (mDirtyBits.any())
        {
            mDirtyBitsGuard = mDirtyBits;
            ANGLE_TRY(
                mVertexArray->syncState(context, mDirtyBits, &mDirtyAttribBits, &mDirtyBindingBits));
            mDirtyBits.reset();
            mDirtyBitsGuard.reset();
    
            // The dirty bits should be reset in the back-end. To simplify ASSERTs only check attrib 0.
            ASSERT(mDirtyAttribBits[0].none());
            ASSERT(mDirtyBindingBits[0].none());
        }
        return angle::Result::Continue;
    }
    
    void VertexArray::onBindingChanged(const Context *context, int incr)
    {
        if (mState.mElementArrayBuffer.get())
            mState.mElementArrayBuffer->onNonTFBindingChanged(incr);
        for (auto &binding : mState.mVertexBindings)
        {
            binding.onContainerBindingChanged(context, incr);
        }
    }
    
    VertexArray::DirtyBitType VertexArray::getDirtyBitFromIndex(bool contentsChanged,
                                                                angle::SubjectIndex index) const
    {
        if (IsElementArrayBufferSubjectIndex(index))
        {
            mIndexRangeCache.invalidate();
            return contentsChanged ? DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA
                                   : DIRTY_BIT_ELEMENT_ARRAY_BUFFER;
        }
        else
        {
            // Note: this currently just gets the top-level dirty bit.
            ASSERT(index < mArrayBufferObserverBindings.size());
            return static_cast<DirtyBitType>(
                (contentsChanged ? DIRTY_BIT_BUFFER_DATA_0 : DIRTY_BIT_BINDING_0) + index);
        }
    }
    
    void VertexArray::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message)
    {
        switch (message)
        {
            case angle::SubjectMessage::ContentsChanged:
                setDependentDirtyBit(true, index);
                break;
    
            case angle::SubjectMessage::SubjectChanged:
                if (!IsElementArrayBufferSubjectIndex(index))
                {
                    updateCachedBufferBindingSize(&mState.mVertexBindings[index]);
                }
                setDependentDirtyBit(false, index);
                break;
    
            case angle::SubjectMessage::BindingChanged:
                if (!IsElementArrayBufferSubjectIndex(index))
                {
                    const Buffer *buffer = mState.mVertexBindings[index].getBuffer().get();
                    updateCachedTransformFeedbackBindingValidation(index, buffer);
                }
                break;
    
            case angle::SubjectMessage::SubjectMapped:
                if (!IsElementArrayBufferSubjectIndex(index))
                {
                    updateCachedMappedArrayBuffersBinding(mState.mVertexBindings[index]);
                }
                onStateChange(angle::SubjectMessage::SubjectMapped);
                break;
    
            case angle::SubjectMessage::SubjectUnmapped:
                setDependentDirtyBit(true, index);
    
                if (!IsElementArrayBufferSubjectIndex(index))
                {
                    updateCachedMappedArrayBuffersBinding(mState.mVertexBindings[index]);
                }
                onStateChange(angle::SubjectMessage::SubjectUnmapped);
                break;
    
            default:
                UNREACHABLE();
                break;
        }
    }
    
    void VertexArray::setDependentDirtyBit(bool contentsChanged, angle::SubjectIndex index)
    {
        DirtyBitType dirtyBit = getDirtyBitFromIndex(contentsChanged, index);
        ASSERT(!mDirtyBitsGuard.valid() || mDirtyBitsGuard.value().test(dirtyBit));
        mDirtyBits.set(dirtyBit);
        onStateChange(angle::SubjectMessage::ContentsChanged);
    }
    
    bool VertexArray::hasTransformFeedbackBindingConflict(const gl::Context *context) const
    {
        // Fast check first.
        if (!mCachedTransformFeedbackConflictedBindingsMask.any())
        {
            return false;
        }
    
        const AttributesMask &activeAttribues = context->getStateCache().getActiveBufferedAttribsMask();
    
        // Slow check. We must ensure that the conflicting attributes are enabled/active.
        for (size_t attribIndex : activeAttribues)
        {
            const VertexAttribute &attrib = mState.mVertexAttributes[attribIndex];
            if (mCachedTransformFeedbackConflictedBindingsMask[attrib.bindingIndex])
            {
                return true;
            }
        }
    
        return false;
    }
    
    angle::Result VertexArray::getIndexRangeImpl(const Context *context,
                                                 DrawElementsType type,
                                                 GLsizei indexCount,
                                                 const void *indices,
                                                 IndexRange *indexRangeOut) const
    {
        Buffer *elementArrayBuffer = mState.mElementArrayBuffer.get();
        if (!elementArrayBuffer)
        {
            *indexRangeOut = ComputeIndexRange(type, indices, indexCount,
                                               context->getState().isPrimitiveRestartEnabled());
            return angle::Result::Continue;
        }
    
        size_t offset = reinterpret_cast<uintptr_t>(indices);
        ANGLE_TRY(elementArrayBuffer->getIndexRange(context, type, offset, indexCount,
                                                    context->getState().isPrimitiveRestartEnabled(),
                                                    indexRangeOut));
    
        mIndexRangeCache.put(type, indexCount, offset, *indexRangeOut);
        return angle::Result::Continue;
    }
    
    VertexArray::IndexRangeCache::IndexRangeCache() = default;
    
    void VertexArray::IndexRangeCache::put(DrawElementsType type,
                                           GLsizei indexCount,
                                           size_t offset,
                                           const IndexRange &indexRange)
    {
        ASSERT(type != DrawElementsType::InvalidEnum);
    
        mTypeKey       = type;
        mIndexCountKey = indexCount;
        mOffsetKey     = offset;
        mPayload       = indexRange;
    }
    }  // namespace gl