Context: Use Observer pattern for container objects.

Container objects in this case are the Vertex Array and Read and Draw
Framebuffers. Instead of using a mutable dirty bit state with a const
function to notify of dirty bits we can use the Observer/Subject
pattern. This more cleanly allows us to do cache updates.

Bug: angleproject:1391
Change-Id: I88f863894ec3efa00322038f323a84850166107d
Reviewed-on: https://chromium-review.googlesource.com/1153399
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Frank Henigman <fjhenigman@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index 4c9d49d..578ee3d 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -298,6 +298,9 @@
 static_assert(static_cast<gl::PrimitiveMode>(11) == gl::PrimitiveMode::EnumCount,
               "gl::PrimitiveMode enum values have changed, update kMinimumPrimitiveCounts.");
 
+constexpr angle::SubjectIndex kVertexArraySubjectIndex     = 0;
+constexpr angle::SubjectIndex kReadFramebufferSubjectIndex = 1;
+constexpr angle::SubjectIndex kDrawFramebufferSubjectIndex = 2;
 }  // anonymous namespace
 
 namespace gl
@@ -347,6 +350,9 @@
       mWebGLContext(GetWebGLContext(attribs)),
       mExtensionsEnabled(GetExtensionsEnabled(attribs, mWebGLContext)),
       mMemoryProgramCache(memoryProgramCache),
+      mVertexArrayObserverBinding(this, kVertexArraySubjectIndex),
+      mDrawFramebufferObserverBinding(this, kDrawFramebufferSubjectIndex),
+      mReadFramebufferObserverBinding(this, kReadFramebufferSubjectIndex),
       mScratchBuffer(1000u),
       mZeroFilledBuffer(1000u)
 {
@@ -634,15 +640,15 @@
     // Update default framebuffer, the binding of the previous default
     // framebuffer (or lack of) will have a nullptr.
     {
+        mState.mFramebuffers->setDefaultFramebuffer(newDefault);
         if (mGLState.getReadFramebuffer() == nullptr)
         {
-            mGLState.setReadFramebufferBinding(newDefault);
+            bindReadFramebuffer(0);
         }
         if (mGLState.getDrawFramebuffer() == nullptr)
         {
-            mGLState.setDrawFramebufferBinding(newDefault);
+            bindDrawFramebuffer(0);
         }
-        mState.mFramebuffers->setDefaultFramebuffer(newDefault);
     }
 
     // Notify the renderer of a context switch
@@ -658,11 +664,13 @@
     if (mGLState.getReadFramebuffer() == defaultFramebuffer)
     {
         mGLState.setReadFramebufferBinding(nullptr);
+        mReadFramebufferObserverBinding.bind(nullptr);
     }
 
     if (mGLState.getDrawFramebuffer() == defaultFramebuffer)
     {
         mGLState.setDrawFramebufferBinding(nullptr);
+        mDrawFramebufferObserverBinding.bind(nullptr);
     }
 
     if (defaultFramebuffer)
@@ -1083,6 +1091,7 @@
     Framebuffer *framebuffer = mState.mFramebuffers->checkFramebufferAllocation(
         mImplementation.get(), mCaps, framebufferHandle);
     mGLState.setReadFramebufferBinding(framebuffer);
+    mReadFramebufferObserverBinding.bind(framebuffer);
 }
 
 void Context::bindDrawFramebuffer(GLuint framebufferHandle)
@@ -1090,12 +1099,14 @@
     Framebuffer *framebuffer = mState.mFramebuffers->checkFramebufferAllocation(
         mImplementation.get(), mCaps, framebufferHandle);
     mGLState.setDrawFramebufferBinding(framebuffer);
+    mDrawFramebufferObserverBinding.bind(framebuffer);
 }
 
 void Context::bindVertexArray(GLuint vertexArrayHandle)
 {
     VertexArray *vertexArray = checkVertexArrayAllocation(vertexArrayHandle);
     mGLState.setVertexArrayBinding(this, vertexArray);
+    mVertexArrayObserverBinding.bind(vertexArray);
     mStateCache.updateActiveAttribsMask(this);
 }
 
@@ -7547,6 +7558,31 @@
     return mState.getClientVersion() < Version(2, 0);
 }
 
+void Context::onSubjectStateChange(const Context *context,
+                                   angle::SubjectIndex index,
+                                   angle::SubjectMessage message)
+{
+    ASSERT(message == angle::SubjectMessage::CONTENTS_CHANGED);
+    switch (index)
+    {
+        case kVertexArraySubjectIndex:
+            mGLState.setObjectDirty(GL_VERTEX_ARRAY);
+            break;
+
+        case kReadFramebufferSubjectIndex:
+            mGLState.setObjectDirty(GL_READ_FRAMEBUFFER);
+            break;
+
+        case kDrawFramebufferSubjectIndex:
+            mGLState.setObjectDirty(GL_DRAW_FRAMEBUFFER);
+            break;
+
+        default:
+            UNREACHABLE();
+            break;
+    }
+}
+
 // ErrorSet implementation.
 ErrorSet::ErrorSet(Context *context) : mContext(context)
 {