Sync dirty objects at the GL layer.

The dirty bit system currently puts dirty objects in line with the
rest of the dirty state. This means at the Renderer level, the sync
manager code would call back to the GL layer to sync the specific
objects that are dirty. This is a bit of a layering violation (impl
layer mutating top-level objects) and also a bit of repeated boiler-
plate code.

Fix this by treating dirty objects in a separate dirty bit set, called
the dirty objects. This also has the benefit of allowing us to re-
implement the dirty object set at a later date, if we want to store
them in a list, or other structure.

Also don't skip the state sync at the GL level if there are no GL
dirty bits. The Impl might have some dirty bits locally, and it's
better to call syncState and do the no-op check in the Impl than it
is to also sync local state at every sync point (draw call, read, etc)
in each Impl.

BUG=angleproject:1260

Change-Id: Id5d4beb2a1c4e3ad351edf54e3f32e828d5f5da3
Reviewed-on: https://chromium-review.googlesource.com/318790
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Tested-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index 7e596a8..daba660 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -1919,21 +1919,19 @@
 void Context::syncRendererState()
 {
     const State::DirtyBits &dirtyBits = mState.getDirtyBits();
-    if (dirtyBits.any())
-    {
-        mRenderer->syncState(mState, dirtyBits);
-        mState.clearDirtyBits();
-    }
+    mRenderer->syncState(mState, dirtyBits);
+    mState.clearDirtyBits();
+    mState.syncDirtyObjects();
 }
 
 void Context::syncRendererState(const State::DirtyBits &bitMask)
 {
     const State::DirtyBits &dirtyBits = (mState.getDirtyBits() & bitMask);
-    if (dirtyBits.any())
-    {
-        mRenderer->syncState(mState, dirtyBits);
-        mState.clearDirtyBits(dirtyBits);
-    }
+    mRenderer->syncState(mState, dirtyBits);
+    mState.clearDirtyBits(dirtyBits);
+
+    // TODO(jmadill): Filter objects by bitMask somehow?
+    mState.syncDirtyObjects();
 }
 
 void Context::blitFramebuffer(GLint srcX0,
diff --git a/src/libANGLE/State.cpp b/src/libANGLE/State.cpp
index 015382d..b9c9f4b 100644
--- a/src/libANGLE/State.cpp
+++ b/src/libANGLE/State.cpp
@@ -8,6 +8,7 @@
 
 #include "libANGLE/State.h"
 
+#include "common/BitSetIterator.h"
 #include "libANGLE/Context.h"
 #include "libANGLE/Caps.h"
 #include "libANGLE/Debug.h"
@@ -901,7 +902,11 @@
 {
     mVertexArray = vertexArray;
     mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_BINDING);
-    mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_OBJECT);
+
+    if (mVertexArray && mVertexArray->hasAnyDirtyBit())
+    {
+        mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY);
+    }
 }
 
 GLuint State::getVertexArrayId() const
@@ -922,7 +927,7 @@
     {
         mVertexArray = NULL;
         mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_BINDING);
-        mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_OBJECT);
+        mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY);
         return true;
     }
 
@@ -1098,7 +1103,7 @@
 void State::setEnableVertexAttribArray(unsigned int attribNum, bool enabled)
 {
     getVertexArray()->enableAttribute(attribNum, enabled);
-    mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_OBJECT);
+    mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY);
 }
 
 void State::setVertexAttribf(GLuint index, const GLfloat values[4])
@@ -1132,13 +1137,13 @@
                                  const void *pointer)
 {
     getVertexArray()->setAttributeState(attribNum, boundBuffer, size, type, normalized, pureInteger, stride, pointer);
-    mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_OBJECT);
+    mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY);
 }
 
 void State::setVertexAttribDivisor(GLuint index, GLuint divisor)
 {
     getVertexArray()->setVertexAttribDivisor(index, divisor);
-    mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_OBJECT);
+    mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY);
 }
 
 const VertexAttribCurrentValueData &State::getVertexAttribCurrentValue(unsigned int attribNum) const
@@ -1710,4 +1715,34 @@
     }
 }
 
+void State::syncDirtyObjects()
+{
+    if (!mDirtyObjects.any())
+        return;
+
+    for (auto dirtyObject : angle::IterateBitSet(mDirtyObjects))
+    {
+        switch (dirtyObject)
+        {
+            case DIRTY_OBJECT_READ_FRAMEBUFFER:
+                // TODO(jmadill): implement this
+                break;
+            case DIRTY_OBJECT_DRAW_FRAMEBUFFER:
+                // TODO(jmadill): implement this
+                break;
+            case DIRTY_OBJECT_VERTEX_ARRAY:
+                mVertexArray->syncImplState();
+                break;
+            case DIRTY_OBJECT_PROGRAM:
+                // TODO(jmadill): implement this
+                break;
+            default:
+                UNREACHABLE();
+                break;
+        }
+    }
+
+    mDirtyObjects.reset();
 }
+
+}  // namespace gl
diff --git a/src/libANGLE/State.h b/src/libANGLE/State.h
index 8fcf3f8..96458d6 100644
--- a/src/libANGLE/State.h
+++ b/src/libANGLE/State.h
@@ -328,26 +328,38 @@
         DIRTY_BIT_GENERATE_MIPMAP_HINT,
         DIRTY_BIT_SHADER_DERIVATIVE_HINT,
         DIRTY_BIT_READ_FRAMEBUFFER_BINDING,
-        DIRTY_BIT_READ_FRAMEBUFFER_OBJECT,
         DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING,
-        DIRTY_BIT_DRAW_FRAMEBUFFER_OBJECT,
         DIRTY_BIT_RENDERBUFFER_BINDING,
         DIRTY_BIT_VERTEX_ARRAY_BINDING,
-        DIRTY_BIT_VERTEX_ARRAY_OBJECT,
         DIRTY_BIT_PROGRAM_BINDING,
-        DIRTY_BIT_PROGRAM_OBJECT,
         DIRTY_BIT_CURRENT_VALUE_0,
         DIRTY_BIT_CURRENT_VALUE_MAX = DIRTY_BIT_CURRENT_VALUE_0 + MAX_VERTEX_ATTRIBS,
         DIRTY_BIT_INVALID           = DIRTY_BIT_CURRENT_VALUE_MAX,
         DIRTY_BIT_MAX               = DIRTY_BIT_INVALID,
     };
 
+    // TODO(jmadill): Consider storing dirty objects in a list instead of by binding.
+    enum DirtyObjectType
+    {
+        DIRTY_OBJECT_READ_FRAMEBUFFER,
+        DIRTY_OBJECT_DRAW_FRAMEBUFFER,
+        DIRTY_OBJECT_VERTEX_ARRAY,
+        DIRTY_OBJECT_PROGRAM,
+        DIRTY_OBJECT_UNKNOWN,
+        DIRTY_OBJECT_MAX = DIRTY_OBJECT_UNKNOWN,
+    };
+
     typedef std::bitset<DIRTY_BIT_MAX> DirtyBits;
     const DirtyBits &getDirtyBits() const { return mDirtyBits; }
     void clearDirtyBits() { mDirtyBits.reset(); }
     void clearDirtyBits(const DirtyBits &bitset) { mDirtyBits &= ~bitset; }
     void setAllDirtyBits() { mDirtyBits.set(); }
 
+    typedef std::bitset<DIRTY_OBJECT_MAX> DirtyObjects;
+    void clearDirtyObjects() { mDirtyObjects.reset(); }
+    void setAllDirtyObjects() { mDirtyObjects.set(); }
+    void syncDirtyObjects();
+
     // Dirty bit masks
     const DirtyBits &unpackStateBitMask() const { return mUnpackStateBitMask; }
     const DirtyBits &packStateBitMask() const { return mPackStateBitMask; }
@@ -430,6 +442,8 @@
     DirtyBits mPackStateBitMask;
     DirtyBits mClearStateBitMask;
     DirtyBits mBlitStateBitMask;
+
+    DirtyObjects mDirtyObjects;
 };
 
 }
diff --git a/src/libANGLE/VertexArray.h b/src/libANGLE/VertexArray.h
index 27b020b..6bc267d 100644
--- a/src/libANGLE/VertexArray.h
+++ b/src/libANGLE/VertexArray.h
@@ -109,6 +109,7 @@
     typedef std::bitset<DIRTY_BIT_MAX> DirtyBits;
 
     void syncImplState();
+    bool hasAnyDirtyBit() const { return mDirtyBits.any(); }
 
   private:
     GLuint mId;
diff --git a/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp b/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
index 7351c2ad..4295188 100644
--- a/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
@@ -111,6 +111,11 @@
 
 void StateManager11::syncState(const gl::State &state, const gl::State::DirtyBits &dirtyBits)
 {
+    if (!dirtyBits.any())
+    {
+        return;
+    }
+
     for (unsigned int dirtyBit : angle::IterateBitSet(dirtyBits))
     {
         switch (dirtyBit)
diff --git a/src/libANGLE/renderer/d3d/d3d9/StateManager9.cpp b/src/libANGLE/renderer/d3d/d3d9/StateManager9.cpp
index 1ae98f7..440a776 100644
--- a/src/libANGLE/renderer/d3d/d3d9/StateManager9.cpp
+++ b/src/libANGLE/renderer/d3d/d3d9/StateManager9.cpp
@@ -109,6 +109,11 @@
 
 void StateManager9::syncState(const gl::State &state, const gl::State::DirtyBits &dirtyBits)
 {
+    if (!dirtyBits.any())
+    {
+        return;
+    }
+
     for (auto dirtyBit : angle::IterateBitSet(dirtyBits))
     {
         switch (dirtyBit)
diff --git a/src/libANGLE/renderer/gl/StateManagerGL.cpp b/src/libANGLE/renderer/gl/StateManagerGL.cpp
index 037f5d5..2894206 100644
--- a/src/libANGLE/renderer/gl/StateManagerGL.cpp
+++ b/src/libANGLE/renderer/gl/StateManagerGL.cpp
@@ -1274,10 +1274,17 @@
     }
 }
 
-void StateManagerGL::syncState(const gl::State &state, const gl::State::DirtyBits &dirtyBits)
+void StateManagerGL::syncState(const gl::State &state, const gl::State::DirtyBits &glDirtyBits)
 {
+    const auto &glAndLocalDirtyBits = (glDirtyBits | mLocalDirtyBits);
+
+    if (!glAndLocalDirtyBits.any())
+    {
+        return;
+    }
+
     // TODO(jmadill): Investigate only syncing vertex state for active attributes
-    for (auto dirtyBit : angle::IterateBitSet(dirtyBits | mLocalDirtyBits))
+    for (auto dirtyBit : angle::IterateBitSet(glAndLocalDirtyBits))
     {
         switch (dirtyBit)
         {
@@ -1469,30 +1476,18 @@
             case gl::State::DIRTY_BIT_READ_FRAMEBUFFER_BINDING:
                 // TODO(jmadill): implement this
                 break;
-            case gl::State::DIRTY_BIT_READ_FRAMEBUFFER_OBJECT:
-                // TODO(jmadill): implement this
-                break;
             case gl::State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING:
                 // TODO(jmadill): implement this
                 break;
-            case gl::State::DIRTY_BIT_DRAW_FRAMEBUFFER_OBJECT:
-                // TODO(jmadill): implement this
-                break;
             case gl::State::DIRTY_BIT_RENDERBUFFER_BINDING:
                 // TODO(jmadill): implement this
                 break;
             case gl::State::DIRTY_BIT_VERTEX_ARRAY_BINDING:
                 // TODO(jmadill): implement this
                 break;
-            case gl::State::DIRTY_BIT_VERTEX_ARRAY_OBJECT:
-                state.getVertexArray()->syncImplState();
-                break;
             case gl::State::DIRTY_BIT_PROGRAM_BINDING:
                 // TODO(jmadill): implement this
                 break;
-            case gl::State::DIRTY_BIT_PROGRAM_OBJECT:
-                // TODO(jmadill): implement this
-                break;
             default:
             {
                 ASSERT(dirtyBit >= gl::State::DIRTY_BIT_CURRENT_VALUE_0 &&
diff --git a/src/libANGLE/renderer/gl/StateManagerGL.h b/src/libANGLE/renderer/gl/StateManagerGL.h
index b430208..ad26c58 100644
--- a/src/libANGLE/renderer/gl/StateManagerGL.h
+++ b/src/libANGLE/renderer/gl/StateManagerGL.h
@@ -135,7 +135,7 @@
                                    GLsizei instanceCount,
                                    const GLvoid **outIndices);
 
-    void syncState(const gl::State &state, const gl::State::DirtyBits &dirtyBits);
+    void syncState(const gl::State &state, const gl::State::DirtyBits &glDirtyBits);
 
   private:
     gl::Error setGenericDrawState(const gl::Data &data);