Split VAO dirty bits to speed iteration.

Using > 64 bits (we had over 90) would use a much slower dirty bit
iteration. Speed this up by splitting the dirty bits into two levels.
The first top level only has a single dirty bit per attrib, per
binding, and one bit for the element array buffer. The next level has
separate dirty bits for attribs and bindings.

The D3D11 back-end doesn't actually care about individual dirty bits
of attribs or bindings, since it resets entire attributes at a time,
but the GL back-end only refreshes the necessary info.

Improves the score of a simple state change microbenchmark by 15% on
the D3D11 and GL back-ends with a no-op driver. Real-world impact will
be smaller.

Also includes a test suppression for an NVIDIA bug that surfaced when
we changed the order of that GL commands were sent to the driver.

BUG=angleproject:2389

Change-Id: If8d5e5eb0b27e2a77e20535e33626183d372d311
Reviewed-on: https://chromium-review.googlesource.com/556799
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Yuly Novikov <ynovikov@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/renderer/gl/VertexArrayGL.cpp b/src/libANGLE/renderer/gl/VertexArrayGL.cpp
index 09eccf4..7496733 100644
--- a/src/libANGLE/renderer/gl/VertexArrayGL.cpp
+++ b/src/libANGLE/renderer/gl/VertexArrayGL.cpp
@@ -640,56 +640,99 @@
     mAppliedBindings[bindingIndex].setDivisor(adjustedDivisor);
 }
 
-void VertexArrayGL::syncState(const gl::Context *context, const VertexArray::DirtyBits &dirtyBits)
+void VertexArrayGL::syncDirtyAttrib(const gl::Context *context,
+                                    size_t attribIndex,
+                                    const gl::VertexArray::DirtyAttribBits &dirtyAttribBits)
+{
+    ASSERT(dirtyAttribBits.any());
+
+    for (size_t dirtyBit : dirtyAttribBits)
+    {
+        switch (dirtyBit)
+        {
+            case VertexArray::DIRTY_ATTRIB_ENABLED:
+                updateAttribEnabled(attribIndex);
+                break;
+
+            case VertexArray::DIRTY_ATTRIB_POINTER:
+                updateAttribPointer(context, attribIndex);
+                break;
+
+            case VertexArray::DIRTY_ATTRIB_FORMAT:
+                ASSERT(supportVertexAttribBinding());
+                updateAttribFormat(attribIndex);
+                break;
+
+            case VertexArray::DIRTY_ATTRIB_BINDING:
+                ASSERT(supportVertexAttribBinding());
+                updateAttribBinding(attribIndex);
+                break;
+
+            default:
+                UNREACHABLE();
+                break;
+        }
+    }
+}
+
+void VertexArrayGL::syncDirtyBinding(const gl::Context *context,
+                                     size_t bindingIndex,
+                                     const gl::VertexArray::DirtyBindingBits &dirtyBindingBits)
+{
+    ASSERT(dirtyBindingBits.any());
+
+    for (size_t dirtyBit : dirtyBindingBits)
+    {
+        switch (dirtyBit)
+        {
+            case VertexArray::DIRTY_BINDING_BUFFER:
+                ASSERT(supportVertexAttribBinding());
+                updateBindingBuffer(context, bindingIndex);
+                break;
+
+            case VertexArray::DIRTY_BINDING_DIVISOR:
+                updateBindingDivisor(bindingIndex);
+                break;
+
+            default:
+                UNREACHABLE();
+                break;
+        }
+    }
+}
+
+void VertexArrayGL::syncState(const gl::Context *context,
+                              const VertexArray::DirtyBits &dirtyBits,
+                              const gl::VertexArray::DirtyAttribBitsArray &attribBits,
+                              const gl::VertexArray::DirtyBindingBitsArray &bindingBits)
 {
     mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID());
 
     for (size_t dirtyBit : dirtyBits)
     {
-        if (dirtyBit == VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER)
+        switch (dirtyBit)
         {
-            updateElementArrayBufferBinding(context);
-            continue;
-        }
+            case VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER:
+                updateElementArrayBufferBinding(context);
+                break;
 
-        size_t index = VertexArray::GetVertexIndexFromDirtyBit(dirtyBit);
-        if (dirtyBit >= VertexArray::DIRTY_BIT_ATTRIB_0_ENABLED &&
-            dirtyBit < VertexArray::DIRTY_BIT_ATTRIB_MAX_ENABLED)
-        {
-            updateAttribEnabled(index);
+            default:
+            {
+                ASSERT(dirtyBit >= VertexArray::DIRTY_BIT_ATTRIB_0);
+                size_t index = VertexArray::GetVertexIndexFromDirtyBit(dirtyBit);
+                if (dirtyBit < VertexArray::DIRTY_BIT_ATTRIB_MAX)
+                {
+                    syncDirtyAttrib(context, index, attribBits[index]);
+                }
+                else
+                {
+                    ASSERT(dirtyBit >= VertexArray::DIRTY_BIT_BINDING_0 &&
+                           dirtyBit < VertexArray::DIRTY_BIT_BINDING_MAX);
+                    syncDirtyBinding(context, index, bindingBits[index]);
+                }
+                break;
+            }
         }
-        else if (dirtyBit >= VertexArray::DIRTY_BIT_ATTRIB_0_POINTER &&
-                 dirtyBit < VertexArray::DIRTY_BIT_ATTRIB_MAX_POINTER)
-        {
-            updateAttribPointer(context, index);
-        }
-
-        else if (dirtyBit >= VertexArray::DIRTY_BIT_ATTRIB_0_FORMAT &&
-                 dirtyBit < VertexArray::DIRTY_BIT_ATTRIB_MAX_FORMAT)
-        {
-            ASSERT(supportVertexAttribBinding());
-            updateAttribFormat(index);
-        }
-        else if (dirtyBit >= VertexArray::DIRTY_BIT_ATTRIB_0_BINDING &&
-                 dirtyBit < VertexArray::DIRTY_BIT_ATTRIB_MAX_BINDING)
-        {
-            ASSERT(supportVertexAttribBinding());
-            updateAttribBinding(index);
-        }
-        else if (dirtyBit >= VertexArray::DIRTY_BIT_BINDING_0_BUFFER &&
-                 dirtyBit < VertexArray::DIRTY_BIT_BINDING_MAX_BUFFER)
-        {
-            ASSERT(supportVertexAttribBinding());
-            updateBindingBuffer(context, index);
-        }
-
-        else if (dirtyBit >= VertexArray::DIRTY_BIT_BINDING_0_DIVISOR &&
-                 dirtyBit < VertexArray::DIRTY_BIT_BINDING_MAX_DIVISOR)
-        {
-            updateBindingDivisor(index);
-        }
-        else
-            UNREACHABLE();
     }
 }