Implement dirty bits acceleration for VertexArrayGL.

BUG=angleproject:1040
TEST=angle_end2end_tests,angle_perftests,WebGL

Change-Id: I91d9aea5eefb58ecaf5b1cc95926fddb2aa846ea
Reviewed-on: https://chromium-review.googlesource.com/289570
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Tested-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index 6c70a25..260713f 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -1481,7 +1481,7 @@
 
 void Context::setVertexAttribDivisor(GLuint index, GLuint divisor)
 {
-    mState.getVertexArray()->setVertexAttribDivisor(index, divisor);
+    mState.setVertexAttribDivisor(index, divisor);
 }
 
 void Context::samplerParameteri(GLuint sampler, GLenum pname, GLint param)
diff --git a/src/libANGLE/State.cpp b/src/libANGLE/State.cpp
index 5791cb9..752f995 100644
--- a/src/libANGLE/State.cpp
+++ b/src/libANGLE/State.cpp
@@ -846,6 +846,8 @@
 void State::setVertexArrayBinding(VertexArray *vertexArray)
 {
     mVertexArray = vertexArray;
+    mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_BINDING);
+    mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_OBJECT);
 }
 
 GLuint State::getVertexArrayId() const
@@ -865,6 +867,8 @@
     if (mVertexArray->id() == vertexArray)
     {
         mVertexArray = NULL;
+        mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_BINDING);
+        mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_OBJECT);
         return true;
     }
 
@@ -1051,6 +1055,7 @@
 void State::setEnableVertexAttribArray(unsigned int attribNum, bool enabled)
 {
     getVertexArray()->enableAttribute(attribNum, enabled);
+    mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_OBJECT);
 }
 
 void State::setVertexAttribf(GLuint index, const GLfloat values[4])
@@ -1074,10 +1079,23 @@
     mDirtyBits.set(DIRTY_BIT_CURRENT_VALUE_0 + index);
 }
 
-void State::setVertexAttribState(unsigned int attribNum, Buffer *boundBuffer, GLint size, GLenum type, bool normalized,
-    bool pureInteger, GLsizei stride, const void *pointer)
+void State::setVertexAttribState(unsigned int attribNum,
+                                 Buffer *boundBuffer,
+                                 GLint size,
+                                 GLenum type,
+                                 bool normalized,
+                                 bool pureInteger,
+                                 GLsizei stride,
+                                 const void *pointer)
 {
     getVertexArray()->setAttributeState(attribNum, boundBuffer, size, type, normalized, pureInteger, stride, pointer);
+    mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_OBJECT);
+}
+
+void State::setVertexAttribDivisor(GLuint index, GLuint divisor)
+{
+    getVertexArray()->setVertexAttribDivisor(index, divisor);
+    mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_OBJECT);
 }
 
 const VertexAttribCurrentValueData &State::getVertexAttribCurrentValue(unsigned int attribNum) const
diff --git a/src/libANGLE/State.h b/src/libANGLE/State.h
index a7c4afe..177c5ab 100644
--- a/src/libANGLE/State.h
+++ b/src/libANGLE/State.h
@@ -226,6 +226,7 @@
     void setVertexAttribi(GLuint index, const GLint values[4]);
     void setVertexAttribState(unsigned int attribNum, Buffer *boundBuffer, GLint size, GLenum type,
                               bool normalized, bool pureInteger, GLsizei stride, const void *pointer);
+    void setVertexAttribDivisor(GLuint index, GLuint divisor);
     const VertexAttribCurrentValueData &getVertexAttribCurrentValue(unsigned int attribNum) const;
     const void *getVertexAttribPointer(unsigned int attribNum) const;
 
diff --git a/src/libANGLE/VertexArray.cpp b/src/libANGLE/VertexArray.cpp
index 3489a31..4afc361 100644
--- a/src/libANGLE/VertexArray.cpp
+++ b/src/libANGLE/VertexArray.cpp
@@ -73,12 +73,14 @@
 {
     ASSERT(index < getMaxAttribs());
     mData.mVertexAttributes[index].divisor = divisor;
+    mDirtyBits.set(DIRTY_BIT_ATTRIB_0_DIVISOR + index);
 }
 
 void VertexArray::enableAttribute(size_t attributeIndex, bool enabledState)
 {
     ASSERT(attributeIndex < getMaxAttribs());
     mData.mVertexAttributes[attributeIndex].enabled = enabledState;
+    mDirtyBits.set(DIRTY_BIT_ATTRIB_0_ENABLED + attributeIndex);
 
     // Update state cache
     if (enabledState)
@@ -109,11 +111,22 @@
     attrib->pureInteger = pureInteger;
     attrib->stride = stride;
     attrib->pointer = pointer;
+    mDirtyBits.set(DIRTY_BIT_ATTRIB_0_POINTER + attributeIndex);
 }
 
 void VertexArray::setElementArrayBuffer(Buffer *buffer)
 {
     mData.mElementArrayBuffer.set(buffer);
+    mDirtyBits.set(DIRTY_BIT_ELEMENT_ARRAY_BUFFER);
+}
+
+void VertexArray::syncImplState()
+{
+    if (mDirtyBits.any())
+    {
+        mVertexArray->syncState(mDirtyBits);
+        mDirtyBits.reset();
+    }
 }
 
 }
diff --git a/src/libANGLE/VertexArray.h b/src/libANGLE/VertexArray.h
index 1c75db5..bf0c922 100644
--- a/src/libANGLE/VertexArray.h
+++ b/src/libANGLE/VertexArray.h
@@ -15,6 +15,7 @@
 
 #include "libANGLE/RefCountObject.h"
 #include "libANGLE/Constants.h"
+#include "libANGLE/State.h"
 #include "libANGLE/VertexAttribute.h"
 
 #include <vector>
@@ -29,6 +30,9 @@
 {
 class Buffer;
 
+// Used in other places.
+typedef std::bitset<MAX_VERTEX_ATTRIBS> AttributesMask;
+
 class VertexArray
 {
   public:
@@ -66,6 +70,10 @@
         size_t getMaxAttribs() const { return mVertexAttributes.size(); }
         size_t getMaxEnabledAttribute() const { return mMaxEnabledAttribute; }
         const std::vector<VertexAttribute> &getVertexAttributes() const { return mVertexAttributes; }
+        const VertexAttribute &getVertexAttribute(size_t index) const
+        {
+            return mVertexAttributes[index];
+        }
 
       private:
         friend class VertexArray;
@@ -74,12 +82,37 @@
         size_t mMaxEnabledAttribute;
     };
 
+    enum DirtyBitType
+    {
+        DIRTY_BIT_ELEMENT_ARRAY_BUFFER,
+
+        // Reserve bits for enabled flags
+        DIRTY_BIT_ATTRIB_0_ENABLED,
+        DIRTY_BIT_ATTRIB_MAX_ENABLED = DIRTY_BIT_ATTRIB_0_ENABLED + gl::MAX_VERTEX_ATTRIBS,
+
+        // Reserve bits for attrib pointers
+        DIRTY_BIT_ATTRIB_0_POINTER   = DIRTY_BIT_ATTRIB_MAX_ENABLED,
+        DIRTY_BIT_ATTRIB_MAX_POINTER = DIRTY_BIT_ATTRIB_0_POINTER + gl::MAX_VERTEX_ATTRIBS,
+
+        // Reserve bits for divisors
+        DIRTY_BIT_ATTRIB_0_DIVISOR   = DIRTY_BIT_ATTRIB_MAX_POINTER,
+        DIRTY_BIT_ATTRIB_MAX_DIVISOR = DIRTY_BIT_ATTRIB_0_DIVISOR + gl::MAX_VERTEX_ATTRIBS,
+
+        DIRTY_BIT_UNKNOWN = DIRTY_BIT_ATTRIB_MAX_DIVISOR,
+        DIRTY_BIT_MAX     = DIRTY_BIT_UNKNOWN,
+    };
+
+    typedef std::bitset<DIRTY_BIT_MAX> DirtyBits;
+
+    void syncImplState();
+
   private:
     GLuint mId;
 
     rx::VertexArrayImpl *mVertexArray;
 
     Data mData;
+    DirtyBits mDirtyBits;
 };
 
 }
diff --git a/src/libANGLE/renderer/VertexArrayImpl.h b/src/libANGLE/renderer/VertexArrayImpl.h
index 8381676..13617c7 100644
--- a/src/libANGLE/renderer/VertexArrayImpl.h
+++ b/src/libANGLE/renderer/VertexArrayImpl.h
@@ -21,7 +21,7 @@
   public:
     VertexArrayImpl(const gl::VertexArray::Data &data) : mData(data) { }
     virtual ~VertexArrayImpl() { }
-
+    virtual void syncState(const gl::VertexArray::DirtyBits &dirtyBits) {}
   protected:
     const gl::VertexArray::Data &mData;
 };
diff --git a/src/libANGLE/renderer/gl/ProgramGL.cpp b/src/libANGLE/renderer/gl/ProgramGL.cpp
index bb3571d..ddf7216 100644
--- a/src/libANGLE/renderer/gl/ProgramGL.cpp
+++ b/src/libANGLE/renderer/gl/ProgramGL.cpp
@@ -207,7 +207,7 @@
         // TODO: determine attribute precision
         setShaderAttribute(static_cast<size_t>(i), attributeType, GL_NONE, attributeName, attributeSize, location);
 
-        mActiveAttributeLocations.push_back(location);
+        mActiveAttributesMask.set(location);
     }
 
     return LinkResult(true, gl::Error(GL_NO_ERROR));
@@ -445,7 +445,7 @@
 
     mSamplerUniformMap.clear();
     mSamplerBindings.clear();
-    mActiveAttributeLocations.clear();
+    mActiveAttributesMask.reset();
 }
 
 GLuint ProgramGL::getProgramID() const
@@ -458,9 +458,9 @@
     return mSamplerBindings;
 }
 
-const std::vector<GLuint> &ProgramGL::getActiveAttributeLocations() const
+const gl::AttributesMask &ProgramGL::getActiveAttributesMask() const
 {
-    return mActiveAttributeLocations;
+    return mActiveAttributesMask;
 }
 
 }
diff --git a/src/libANGLE/renderer/gl/ProgramGL.h b/src/libANGLE/renderer/gl/ProgramGL.h
index 785654b..b6e030a 100644
--- a/src/libANGLE/renderer/gl/ProgramGL.h
+++ b/src/libANGLE/renderer/gl/ProgramGL.h
@@ -95,7 +95,7 @@
 
     GLuint getProgramID() const;
     const std::vector<SamplerBindingGL> &getAppliedSamplerUniforms() const;
-    const std::vector<GLuint> &getActiveAttributeLocations() const;
+    const gl::AttributesMask &getActiveAttributesMask() const;
 
   private:
     const FunctionsGL *mFunctions;
@@ -113,7 +113,7 @@
     std::vector<SamplerBindingGL> mSamplerBindings;
 
     // Array of attribute locations used by this program
-    std::vector<GLuint> mActiveAttributeLocations;
+    gl::AttributesMask mActiveAttributesMask;
 
     GLuint mProgramID;
 };
diff --git a/src/libANGLE/renderer/gl/StateManagerGL.cpp b/src/libANGLE/renderer/gl/StateManagerGL.cpp
index 2f07074..e851c92 100644
--- a/src/libANGLE/renderer/gl/StateManagerGL.cpp
+++ b/src/libANGLE/renderer/gl/StateManagerGL.cpp
@@ -383,7 +383,8 @@
     const gl::VertexArray *vao = state.getVertexArray();
     const VertexArrayGL *vaoGL = GetImplAs<VertexArrayGL>(vao);
 
-    gl::Error error = vaoGL->syncDrawArraysState(programGL->getActiveAttributeLocations(), first, count);
+    gl::Error error =
+        vaoGL->syncDrawArraysState(programGL->getActiveAttributesMask(), first, count);
     if (error.isError())
     {
         return error;
@@ -405,7 +406,8 @@
     const gl::VertexArray *vao = state.getVertexArray();
     const VertexArrayGL *vaoGL = GetImplAs<VertexArrayGL>(vao);
 
-    gl::Error error = vaoGL->syncDrawElementsState(programGL->getActiveAttributeLocations(), count, type, indices, outIndices);
+    gl::Error error = vaoGL->syncDrawElementsState(programGL->getActiveAttributesMask(), count,
+                                                   type, indices, outIndices);
     if (error.isError())
     {
         return error;
@@ -1079,7 +1081,7 @@
                 // TODO(jmadill): implement this
                 break;
             case gl::State::DIRTY_BIT_VERTEX_ARRAY_OBJECT:
-                // TODO(jmadill): implement this
+                state.getVertexArray()->syncImplState();
                 break;
             case gl::State::DIRTY_BIT_PROGRAM_BINDING:
                 // TODO(jmadill): implement this
diff --git a/src/libANGLE/renderer/gl/VertexArrayGL.cpp b/src/libANGLE/renderer/gl/VertexArrayGL.cpp
index 9e7cce8..0da7375 100644
--- a/src/libANGLE/renderer/gl/VertexArrayGL.cpp
+++ b/src/libANGLE/renderer/gl/VertexArrayGL.cpp
@@ -8,6 +8,7 @@
 
 #include "libANGLE/renderer/gl/VertexArrayGL.h"
 
+#include "common/BitSetIterator.h"
 #include "common/debug.h"
 #include "common/mathutil.h"
 #include "common/utilities.h"
@@ -18,10 +19,20 @@
 #include "libANGLE/renderer/gl/FunctionsGL.h"
 #include "libANGLE/renderer/gl/StateManagerGL.h"
 
+using namespace gl;
+
 namespace rx
 {
+namespace
+{
+bool AttributeNeedsStreaming(const VertexAttribute &attribute)
+{
+    return (attribute.enabled && attribute.buffer.get() == nullptr);
+}
 
-VertexArrayGL::VertexArrayGL(const gl::VertexArray::Data &data,
+}  // anonymous namespace
+
+VertexArrayGL::VertexArrayGL(const VertexArray::Data &data,
                              const FunctionsGL *functions,
                              StateManagerGL *stateManager)
     : VertexArrayImpl(data),
@@ -64,29 +75,40 @@
     }
 }
 
-gl::Error VertexArrayGL::syncDrawArraysState(const std::vector<GLuint> &activeAttribLocations, GLint first, GLsizei count) const
+gl::Error VertexArrayGL::syncDrawArraysState(const gl::AttributesMask &activeAttributesMask,
+                                             GLint first,
+                                             GLsizei count) const
 {
-    return syncDrawState(activeAttribLocations, first, count, GL_NONE, nullptr, nullptr);
+    return syncDrawState(activeAttributesMask, first, count, GL_NONE, nullptr, nullptr);
 }
 
-gl::Error VertexArrayGL::syncDrawElementsState(const std::vector<GLuint> &activeAttribLocations, GLsizei count,
-                                               GLenum type, const GLvoid *indices, const GLvoid **outIndices) const
+gl::Error VertexArrayGL::syncDrawElementsState(const gl::AttributesMask &activeAttributesMask,
+                                               GLsizei count,
+                                               GLenum type,
+                                               const GLvoid *indices,
+                                               const GLvoid **outIndices) const
 {
-    return syncDrawState(activeAttribLocations, 0, count, type, indices, outIndices);
+    return syncDrawState(activeAttributesMask, 0, count, type, indices, outIndices);
 }
 
-gl::Error VertexArrayGL::syncDrawState(const std::vector<GLuint> &activeAttribLocations, GLint first, GLsizei count, GLenum type, const GLvoid *indices, const GLvoid **outIndices) const
+gl::Error VertexArrayGL::syncDrawState(const gl::AttributesMask &activeAttributesMask,
+                                       GLint first,
+                                       GLsizei count,
+                                       GLenum type,
+                                       const GLvoid *indices,
+                                       const GLvoid **outIndices) const
 {
     mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID());
 
     // Check if any attributes need to be streamed, determines if the index range needs to be computed
-    bool attributesNeedStreaming = doAttributesNeedStreaming(activeAttribLocations);
+    bool attributesNeedStreaming = mAttributesNeedStreaming.any();
 
     // Determine if an index buffer needs to be streamed and the range of vertices that need to be copied
-    gl::RangeUI indexRange(0, 0);
+    RangeUI indexRange(0, 0);
     if (type != GL_NONE)
     {
-        gl::Error error = syncIndexData(count, type, indices, attributesNeedStreaming, &indexRange, outIndices);
+        Error error =
+            syncIndexData(count, type, indices, attributesNeedStreaming, &indexRange, outIndices);
         if (error.isError())
         {
             return error;
@@ -99,130 +121,24 @@
         indexRange.end = first + count;
     }
 
-    // Sync the vertex attribute state and track what data needs to be streamed
-    size_t streamingDataSize = 0;
-    size_t maxAttributeDataSize = 0;
-    gl::Error error = syncAttributeState(activeAttribLocations, attributesNeedStreaming, indexRange,
-                                         &streamingDataSize, &maxAttributeDataSize);
-    if (error.isError())
+    if (attributesNeedStreaming)
     {
-        return error;
-    }
-
-    if (streamingDataSize > 0)
-    {
-        ASSERT(attributesNeedStreaming);
-
-        error = streamAttributes(activeAttribLocations, streamingDataSize, maxAttributeDataSize,
-                                 indexRange);
+        Error error = streamAttributes(activeAttributesMask, indexRange);
         if (error.isError())
         {
             return error;
         }
     }
 
-    return gl::Error(GL_NO_ERROR);
+    return Error(GL_NO_ERROR);
 }
 
-bool VertexArrayGL::doAttributesNeedStreaming(const std::vector<GLuint> &activeAttribLocations) const
-{
-    // TODO: if GLES, nothing needs to be streamed
-    const auto &attribs = mData.getVertexAttributes();
-    for (size_t activeAttrib = 0; activeAttrib < activeAttribLocations.size(); activeAttrib++)
-    {
-        GLuint idx = activeAttribLocations[activeAttrib];
-        if (attribs[idx].enabled && attribs[idx].buffer.get() == nullptr)
-        {
-            return true;
-        }
-    }
-
-    return false;
-}
-
-gl::Error VertexArrayGL::syncAttributeState(const std::vector<GLuint> &activeAttribLocations, bool attributesNeedStreaming,
-                                            const gl::RangeUI &indexRange,  size_t *outStreamingDataSize, size_t *outMaxAttributeDataSize) const
-{
-    *outStreamingDataSize = 0;
-    *outMaxAttributeDataSize = 0;
-
-    const auto &attribs = mData.getVertexAttributes();
-    for (size_t activeAttrib = 0; activeAttrib < activeAttribLocations.size(); activeAttrib++)
-    {
-        GLuint idx = activeAttribLocations[activeAttrib];
-        const auto &attrib = attribs[idx];
-
-        // Always sync the enabled and divisor state, they are required for both streaming and buffered
-        // attributes
-        if (mAppliedAttributes[idx].enabled != attrib.enabled)
-        {
-            if (attrib.enabled)
-            {
-                mFunctions->enableVertexAttribArray(idx);
-            }
-            else
-            {
-                mFunctions->disableVertexAttribArray(idx);
-            }
-            mAppliedAttributes[idx].enabled = attrib.enabled;
-        }
-        if (mAppliedAttributes[idx].divisor != attrib.divisor)
-        {
-            mFunctions->vertexAttribDivisor(idx, attrib.divisor);
-            mAppliedAttributes[idx].divisor = attrib.divisor;
-        }
-
-        if (attribs[idx].enabled && attrib.buffer.get() == nullptr)
-        {
-            ASSERT(attributesNeedStreaming);
-
-            const size_t streamedVertexCount = indexRange.end - indexRange.start + 1;
-
-            // If streaming is going to be required, compute the size of the required buffer
-            // and how much slack space at the beginning of the buffer will be required by determining
-            // the attribute with the largest data size.
-            size_t typeSize = ComputeVertexAttributeTypeSize(attrib);
-            *outStreamingDataSize += typeSize * streamedVertexCount;
-            *outMaxAttributeDataSize = std::max(*outMaxAttributeDataSize, typeSize);
-        }
-        else
-        {
-            // Sync the attribute with no translation
-            if (mAppliedAttributes[idx] != attrib)
-            {
-                const gl::Buffer *arrayBuffer = attrib.buffer.get();
-                if (arrayBuffer != nullptr)
-                {
-                    const BufferGL *arrayBufferGL = GetImplAs<BufferGL>(arrayBuffer);
-                    mStateManager->bindBuffer(GL_ARRAY_BUFFER, arrayBufferGL->getBufferID());
-                }
-                else
-                {
-                    mStateManager->bindBuffer(GL_ARRAY_BUFFER, 0);
-                }
-
-                if (attrib.pureInteger)
-                {
-                    mFunctions->vertexAttribIPointer(idx, attrib.size, attrib.type,
-                                                     attrib.stride, attrib.pointer);
-                }
-                else
-                {
-                    mFunctions->vertexAttribPointer(idx, attrib.size, attrib.type,
-                                                    attrib.normalized, attrib.stride,
-                                                    attrib.pointer);
-                }
-
-                mAppliedAttributes[idx] = attrib;
-            }
-        }
-    }
-
-    return gl::Error(GL_NO_ERROR);
-}
-
-gl::Error VertexArrayGL::syncIndexData(GLsizei count, GLenum type, const GLvoid *indices, bool attributesNeedStreaming,
-                                       gl::RangeUI *outIndexRange, const GLvoid **outIndices) const
+Error VertexArrayGL::syncIndexData(GLsizei count,
+                                   GLenum type,
+                                   const GLvoid *indices,
+                                   bool attributesNeedStreaming,
+                                   RangeUI *outIndexRange,
+                                   const GLvoid **outIndices) const
 {
     ASSERT(outIndices);
 
@@ -242,7 +158,8 @@
         if (attributesNeedStreaming)
         {
             ptrdiff_t elementArrayBufferOffset = reinterpret_cast<ptrdiff_t>(indices);
-            gl::Error error = mData.getElementArrayBuffer()->getIndexRange(type, static_cast<size_t>(elementArrayBufferOffset), count, outIndexRange);
+            Error error = mData.getElementArrayBuffer()->getIndexRange(
+                type, static_cast<size_t>(elementArrayBufferOffset), count, outIndexRange);
             if (error.isError())
             {
                 return error;
@@ -260,7 +177,7 @@
         // Only compute the index range if the attributes also need to be streamed
         if (attributesNeedStreaming)
         {
-            *outIndexRange = gl::ComputeIndexRange(type, indices, count);
+            *outIndexRange = ComputeIndexRange(type, indices, count);
         }
 
         // Allocate the streaming element array buffer
@@ -274,7 +191,7 @@
         mAppliedElementArrayBuffer.set(nullptr);
 
         // Make sure the element array buffer is large enough
-        const gl::Type &indexTypeInfo = gl::GetTypeInfo(type);
+        const Type &indexTypeInfo          = GetTypeInfo(type);
         size_t requiredStreamingBufferSize = indexTypeInfo.bytes * count;
         if (requiredStreamingBufferSize > mStreamingElementArrayBufferSize)
         {
@@ -292,12 +209,51 @@
         *outIndices = nullptr;
     }
 
-    return gl::Error(GL_NO_ERROR);
+    return Error(GL_NO_ERROR);
 }
 
-gl::Error VertexArrayGL::streamAttributes(const std::vector<GLuint> &activeAttribLocations, size_t streamingDataSize,
-                                          size_t maxAttributeDataSize, const gl::RangeUI &indexRange) const
+void VertexArrayGL::computeStreamingAttributeSizes(const gl::AttributesMask &activeAttributesMask,
+                                                   const gl::RangeUI &indexRange,
+                                                   size_t *outStreamingDataSize,
+                                                   size_t *outMaxAttributeDataSize) const
 {
+    *outStreamingDataSize    = 0;
+    *outMaxAttributeDataSize = 0;
+
+    ASSERT(mAttributesNeedStreaming.any());
+
+    const auto &attribs = mData.getVertexAttributes();
+    for (unsigned int idx : angle::IterateBitSet(mAttributesNeedStreaming & activeAttributesMask))
+    {
+        const auto &attrib = attribs[idx];
+        ASSERT(AttributeNeedsStreaming(attrib));
+
+        const size_t streamedVertexCount = indexRange.end - indexRange.start + 1;
+
+        // If streaming is going to be required, compute the size of the required buffer
+        // and how much slack space at the beginning of the buffer will be required by determining
+        // the attribute with the largest data size.
+        size_t typeSize = ComputeVertexAttributeTypeSize(attrib);
+        *outStreamingDataSize += typeSize * streamedVertexCount;
+        *outMaxAttributeDataSize = std::max(*outMaxAttributeDataSize, typeSize);
+    }
+}
+
+gl::Error VertexArrayGL::streamAttributes(const gl::AttributesMask &activeAttributesMask,
+                                          const gl::RangeUI &indexRange) const
+{
+    // Sync the vertex attribute state and track what data needs to be streamed
+    size_t streamingDataSize    = 0;
+    size_t maxAttributeDataSize = 0;
+
+    computeStreamingAttributeSizes(activeAttributesMask, indexRange, &streamingDataSize,
+                                   &maxAttributeDataSize);
+
+    if (streamingDataSize == 0)
+    {
+        return gl::Error(GL_NO_ERROR);
+    }
+
     if (mStreamingArrayBuffer == 0)
     {
         mFunctions->genBuffers(1, &mStreamingArrayBuffer);
@@ -329,52 +285,48 @@
         const size_t streamedVertexCount = indexRange.end - indexRange.start + 1;
 
         const auto &attribs = mData.getVertexAttributes();
-        for (size_t activeAttrib = 0; activeAttrib < activeAttribLocations.size(); activeAttrib++)
+        for (unsigned int idx :
+             angle::IterateBitSet(mAttributesNeedStreaming & activeAttributesMask))
         {
-            GLuint idx = activeAttribLocations[activeAttrib];
             const auto &attrib = attribs[idx];
+            ASSERT(AttributeNeedsStreaming(attrib));
 
-            if (attrib.enabled && attrib.buffer.get() == nullptr)
+            const size_t sourceStride = ComputeVertexAttributeStride(attrib);
+            const size_t destStride   = ComputeVertexAttributeTypeSize(attrib);
+
+            const uint8_t *inputPointer = reinterpret_cast<const uint8_t *>(attrib.pointer);
+
+            // Pack the data when copying it, user could have supplied a very large stride that
+            // would cause the buffer to be much larger than needed.
+            if (destStride == sourceStride)
             {
-                const size_t sourceStride = ComputeVertexAttributeStride(attrib);
-                const size_t destStride = ComputeVertexAttributeTypeSize(attrib);
-
-                const uint8_t *inputPointer = reinterpret_cast<const uint8_t*>(attrib.pointer);
-
-                // Pack the data when copying it, user could have supplied a very large stride that would
-                // cause the buffer to be much larger than needed.
-                if (destStride == sourceStride)
-                {
-                    // Can copy in one go, the data is packed
-                    memcpy(bufferPointer + curBufferOffset,
-                           inputPointer + (sourceStride * indexRange.start),
-                           destStride * streamedVertexCount);
-                }
-                else
-                {
-                    // Copy each vertex individually
-                    for (size_t vertexIdx = indexRange.start; vertexIdx <= indexRange.end; vertexIdx++)
-                    {
-                        memcpy(bufferPointer + curBufferOffset + (destStride * vertexIdx),
-                               inputPointer + (sourceStride * vertexIdx),
-                               destStride);
-                    }
-                }
-
-                // Compute where the 0-index vertex would be.
-                const size_t vertexStartOffset = curBufferOffset - (indexRange.start * destStride);
-
-                mFunctions->vertexAttribPointer(
-                    idx, attrib.size, attrib.type, attrib.normalized,
-                    static_cast<GLsizei>(destStride),
-                    reinterpret_cast<const GLvoid *>(vertexStartOffset));
-
-                curBufferOffset += destStride * streamedVertexCount;
-
-                // Mark the applied attribute as dirty by setting an invalid size so that if it doesn't
-                // need to be streamed later, there is no chance that the caching will skip it.
-                mAppliedAttributes[idx].size = static_cast<GLuint>(-1);
+                // Can copy in one go, the data is packed
+                memcpy(bufferPointer + curBufferOffset,
+                       inputPointer + (sourceStride * indexRange.start),
+                       destStride * streamedVertexCount);
             }
+            else
+            {
+                // Copy each vertex individually
+                for (size_t vertexIdx = indexRange.start; vertexIdx <= indexRange.end; vertexIdx++)
+                {
+                    memcpy(bufferPointer + curBufferOffset + (destStride * vertexIdx),
+                           inputPointer + (sourceStride * vertexIdx), destStride);
+                }
+            }
+
+            // Compute where the 0-index vertex would be.
+            const size_t vertexStartOffset = curBufferOffset - (indexRange.start * destStride);
+
+            mFunctions->vertexAttribPointer(idx, attrib.size, attrib.type, attrib.normalized,
+                                            static_cast<GLsizei>(destStride),
+                                            reinterpret_cast<const GLvoid *>(vertexStartOffset));
+
+            curBufferOffset += destStride * streamedVertexCount;
+
+            // Mark the applied attribute as dirty by setting an invalid size so that if it doesn't
+            // need to be streamed later, there is no chance that the caching will skip it.
+            mAppliedAttributes[idx].size = static_cast<GLuint>(-1);
         }
 
         unmapResult = mFunctions->unmapBuffer(GL_ARRAY_BUFFER);
@@ -382,10 +334,10 @@
 
     if (unmapResult != GL_TRUE)
     {
-        return gl::Error(GL_OUT_OF_MEMORY, "Failed to unmap the client data streaming buffer.");
+        return Error(GL_OUT_OF_MEMORY, "Failed to unmap the client data streaming buffer.");
     }
 
-    return gl::Error(GL_NO_ERROR);
+    return Error(GL_NO_ERROR);
 }
 
 GLuint VertexArrayGL::getVertexArrayID() const
@@ -403,4 +355,114 @@
     return GetImplAs<BufferGL>(mAppliedElementArrayBuffer.get())->getBufferID();
 }
 
+void VertexArrayGL::updateNeedsStreaming(size_t attribIndex)
+{
+    const VertexAttribute &attrib = mData.getVertexAttribute(attribIndex);
+    mAttributesNeedStreaming.set(attribIndex, AttributeNeedsStreaming(attrib));
 }
+
+void VertexArrayGL::updateAttribEnabled(size_t attribIndex)
+{
+    const VertexAttribute &attrib = mData.getVertexAttribute(attribIndex);
+    if (mAppliedAttributes[attribIndex].enabled == attrib.enabled)
+    {
+        return;
+    }
+
+    updateNeedsStreaming(attribIndex);
+
+    mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID());
+    if (attrib.enabled)
+    {
+        mFunctions->enableVertexAttribArray(static_cast<GLuint>(attribIndex));
+    }
+    else
+    {
+        mFunctions->disableVertexAttribArray(static_cast<GLuint>(attribIndex));
+    }
+    mAppliedAttributes[attribIndex].enabled = attrib.enabled;
+}
+
+void VertexArrayGL::updateAttribPointer(size_t attribIndex)
+{
+    const VertexAttribute &attrib = mData.getVertexAttribute(attribIndex);
+    if (mAppliedAttributes[attribIndex] == attrib)
+    {
+        return;
+    }
+
+    updateNeedsStreaming(attribIndex);
+    mAppliedAttributes[attribIndex] = attrib;
+
+    // If we need to stream, defer the attribPointer to the draw call.
+    if (mAttributesNeedStreaming[attribIndex])
+    {
+        return;
+    }
+
+    mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID());
+    const Buffer *arrayBuffer = attrib.buffer.get();
+    if (arrayBuffer != nullptr)
+    {
+        const BufferGL *arrayBufferGL = GetImplAs<BufferGL>(arrayBuffer);
+        mStateManager->bindBuffer(GL_ARRAY_BUFFER, arrayBufferGL->getBufferID());
+    }
+    else
+    {
+        mStateManager->bindBuffer(GL_ARRAY_BUFFER, 0);
+    }
+
+    if (attrib.pureInteger)
+    {
+        mFunctions->vertexAttribIPointer(static_cast<GLuint>(attribIndex), attrib.size, attrib.type,
+                                         attrib.stride, attrib.pointer);
+    }
+    else
+    {
+        mFunctions->vertexAttribPointer(static_cast<GLuint>(attribIndex), attrib.size, attrib.type,
+                                        attrib.normalized, attrib.stride, attrib.pointer);
+    }
+}
+
+void VertexArrayGL::syncState(const VertexArray::DirtyBits &dirtyBits)
+{
+    for (unsigned long dirtyBit : angle::IterateBitSet(dirtyBits))
+    {
+        if (dirtyBit == VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER)
+        {
+            // TODO(jmadill): Element array buffer bindings
+        }
+        else if (dirtyBit >= VertexArray::DIRTY_BIT_ATTRIB_0_ENABLED &&
+                 dirtyBit < VertexArray::DIRTY_BIT_ATTRIB_MAX_ENABLED)
+        {
+            size_t attribIndex =
+                static_cast<size_t>(dirtyBit) - VertexArray::DIRTY_BIT_ATTRIB_0_ENABLED;
+            updateAttribEnabled(attribIndex);
+        }
+        else if (dirtyBit >= VertexArray::DIRTY_BIT_ATTRIB_0_POINTER &&
+                 dirtyBit < VertexArray::DIRTY_BIT_ATTRIB_MAX_POINTER)
+        {
+            size_t attribIndex =
+                static_cast<size_t>(dirtyBit) - VertexArray::DIRTY_BIT_ATTRIB_0_POINTER;
+            updateAttribPointer(attribIndex);
+        }
+        else if (dirtyBit >= VertexArray::DIRTY_BIT_ATTRIB_0_DIVISOR &&
+                 dirtyBit < VertexArray::DIRTY_BIT_ATTRIB_MAX_DIVISOR)
+        {
+            size_t attribIndex =
+                static_cast<size_t>(dirtyBit) - VertexArray::DIRTY_BIT_ATTRIB_0_DIVISOR;
+            const VertexAttribute &attrib = mData.getVertexAttribute(attribIndex);
+
+            if (mAppliedAttributes[attribIndex].divisor != attrib.divisor)
+            {
+                mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID());
+                mFunctions->vertexAttribDivisor(static_cast<GLuint>(attribIndex), attrib.divisor);
+                mAppliedAttributes[attribIndex].divisor = attrib.divisor;
+            }
+        }
+        else
+            UNREACHABLE();
+    }
+}
+
+}  // rx
diff --git a/src/libANGLE/renderer/gl/VertexArrayGL.h b/src/libANGLE/renderer/gl/VertexArrayGL.h
index f277bdb..52c8f4e 100644
--- a/src/libANGLE/renderer/gl/VertexArrayGL.h
+++ b/src/libANGLE/renderer/gl/VertexArrayGL.h
@@ -23,33 +23,46 @@
     VertexArrayGL(const gl::VertexArray::Data &data, const FunctionsGL *functions, StateManagerGL *stateManager);
     ~VertexArrayGL() override;
 
-    gl::Error syncDrawArraysState(const std::vector<GLuint> &activeAttribLoations, GLint first, GLsizei count) const;
-    gl::Error syncDrawElementsState(const std::vector<GLuint> &activeAttribLoations, GLsizei count, GLenum type,
-                                    const GLvoid *indices, const GLvoid **outIndices) const;
+    gl::Error syncDrawArraysState(const gl::AttributesMask &activeAttributesMask,
+                                  GLint first,
+                                  GLsizei count) const;
+    gl::Error syncDrawElementsState(const gl::AttributesMask &activeAttributesMask,
+                                    GLsizei count,
+                                    GLenum type,
+                                    const GLvoid *indices,
+                                    const GLvoid **outIndices) const;
 
     GLuint getVertexArrayID() const;
     GLuint getAppliedElementArrayBufferID() const;
 
+    void syncState(const gl::VertexArray::DirtyBits &dirtyBits) override;
+
   private:
-    gl::Error syncDrawState(const std::vector<GLuint> &activeAttribLoations, GLint first, GLsizei count,
-                            GLenum type, const GLvoid *indices, const GLvoid **outIndices) const;
-
-    // Check if any vertex attributes need to be streamed
-    bool doAttributesNeedStreaming(const std::vector<GLuint> &activeAttribLoations) const;
-
-    // Apply attribute state, returns the amount of space needed to stream all attributes that need streaming
-    // and the data size of the largest attribute
-    gl::Error syncAttributeState(const std::vector<GLuint> &activeAttribLoations, bool attributesNeedStreaming,
-                                 const gl::RangeUI &indexRange, size_t *outStreamingDataSize,
-                                 size_t *outMaxAttributeDataSize) const;
+    gl::Error syncDrawState(const gl::AttributesMask &activeAttributesMask,
+                            GLint first,
+                            GLsizei count,
+                            GLenum type,
+                            const GLvoid *indices,
+                            const GLvoid **outIndices) const;
 
     // Apply index data, only sets outIndexRange if attributesNeedStreaming is true
     gl::Error syncIndexData(GLsizei count, GLenum type, const GLvoid *indices, bool attributesNeedStreaming,
                             gl::RangeUI *outIndexRange, const GLvoid **outIndices) const;
 
+    // Returns the amount of space needed to stream all attributes that need streaming
+    // and the data size of the largest attribute
+    void computeStreamingAttributeSizes(const gl::AttributesMask &activeAttributesMask,
+                                        const gl::RangeUI &indexRange,
+                                        size_t *outStreamingDataSize,
+                                        size_t *outMaxAttributeDataSize) const;
+
     // Stream attributes that have client data
-    gl::Error streamAttributes(const std::vector<GLuint> &activeAttribLoations, size_t streamingDataSize,
-                               size_t maxAttributeDataSize, const gl::RangeUI &indexRange) const;
+    gl::Error streamAttributes(const gl::AttributesMask &activeAttributesMask,
+                               const gl::RangeUI &indexRange) const;
+
+    void updateNeedsStreaming(size_t attribIndex);
+    void updateAttribEnabled(size_t attribIndex);
+    void updateAttribPointer(size_t attribIndex);
 
     const FunctionsGL *mFunctions;
     StateManagerGL *mStateManager;
@@ -64,6 +77,8 @@
 
     mutable size_t mStreamingArrayBufferSize;
     mutable GLuint mStreamingArrayBuffer;
+
+    gl::AttributesMask mAttributesNeedStreaming;
 };
 
 }