StateManager11: Defer RenderTarget invalidation to draw.

Although this adds a boolean check (and state flag maintenance) cost
to each draw call, it makes ANGLE's internal life a lot simpler
because it doesn't have to process a framebuffer change until the
draw call. It turns out there are a few dependent checks of the
Framebuffer that aren't always easy to do. In one test, Context
destruction was triggering RenderTarget invalidation, after the
Context had already freed the Framebuffer manager.

This also fixes the problem in feature level 9_3 with framebuffer
invalidation affecting the internal dirty bit set. (Note that it
still dirties the Framebuffer for the next frame).

BUG=chromium:767279
BUG=angleproject:2151

Change-Id: I74d61bddf9926004a04f712a9f9eb1205d5df0e1
Reviewed-on: https://chromium-review.googlesource.com/676657
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.cpp b/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.cpp
index 8fb55af..8b5ec4b 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.cpp
@@ -440,7 +440,7 @@
     FramebufferD3D::syncState(context, dirtyBits);
 
     // Call this last to allow the state manager to take advantage of the cached render targets.
-    mRenderer->getStateManager()->invalidateRenderTarget(context);
+    mRenderer->getStateManager()->invalidateRenderTarget();
 
     // Call this to syncViewport for framebuffer default parameters.
     if (mState.getDefaultWidth() != 0 || mState.getDefaultHeight() != 0)
@@ -465,7 +465,7 @@
 
     // Notify the context we need to re-validate the RenderTarget.
     // TODO(jmadill): Check that we're the active draw framebuffer.
-    mRenderer->getStateManager()->invalidateRenderTarget(context);
+    mRenderer->getStateManager()->invalidateRenderTarget();
 }
 
 gl::Error Framebuffer11::getSamplePosition(size_t index, GLfloat *xy) const
diff --git a/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp b/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
index 84a2e7c..60f62ce 100644
--- a/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
@@ -536,6 +536,7 @@
       mCurNear(0.0f),
       mCurFar(0.0f),
       mViewportBounds(),
+      mRenderTargetIsDirty(true),
       mCurPresentPathFastEnabled(false),
       mCurPresentPathFastColorBufferHeight(0),
       mDirtyCurrentValueAttribs(),
@@ -928,7 +929,7 @@
                 }
                 break;
             case gl::State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING:
-                invalidateRenderTarget(context);
+                invalidateRenderTarget();
                 if (mIsMultiviewEnabled)
                 {
                     handleMultiviewDrawFramebufferChange(context);
@@ -951,7 +952,7 @@
             {
                 mInternalDirtyBits.set(DIRTY_BIT_SHADERS);
                 invalidateVertexBuffer();
-                invalidateRenderTarget(context);
+                invalidateRenderTarget();
                 invalidateTexturesAndSamplers();
                 invalidateProgramUniforms();
                 invalidateProgramUniformBuffers();
@@ -1290,8 +1291,21 @@
     mShaderConstants.onViewportChange(viewport, adjustViewport, is9_3, mCurPresentPathFastEnabled);
 }
 
-void StateManager11::invalidateRenderTarget(const gl::Context *context)
+void StateManager11::invalidateRenderTarget()
 {
+    mRenderTargetIsDirty = true;
+}
+
+void StateManager11::processFramebufferInvalidation(const gl::Context *context)
+{
+    if (!mRenderTargetIsDirty)
+    {
+        return;
+    }
+
+    ASSERT(context);
+
+    mRenderTargetIsDirty = false;
     mInternalDirtyBits.set(DIRTY_BIT_RENDER_TARGET);
 
     // The pixel shader is dependent on the output layout.
@@ -1300,19 +1314,8 @@
     // The D3D11 blend state is heavily dependent on the current render target.
     mInternalDirtyBits.set(DIRTY_BIT_BLEND_STATE);
 
-    // nullptr only on display initialization.
-    if (!context)
-    {
-        return;
-    }
-
     gl::Framebuffer *fbo = context->getGLState().getDrawFramebuffer();
-
-    // nullptr fbo can occur in some egl events like display initialization.
-    if (!fbo)
-    {
-        return;
-    }
+    ASSERT(fbo);
 
     // Disable the depth test/depth write if we are using a stencil-only attachment.
     // This is because ANGLE emulates stencil-only with D24S8 on D3D11 - we should neither read
@@ -1354,12 +1357,12 @@
     }
 }
 
-void StateManager11::invalidateBoundViews(const gl::Context *context)
+void StateManager11::invalidateBoundViews()
 {
     mCurVertexSRVs.clear();
     mCurPixelSRVs.clear();
 
-    invalidateRenderTarget(context);
+    invalidateRenderTarget();
 }
 
 void StateManager11::invalidateVertexBuffer()
@@ -1843,6 +1846,9 @@
     auto *programD3D    = GetImplAs<ProgramD3D>(glState.getProgram());
 
     // TODO(jmadill): Use dirty bits.
+    processFramebufferInvalidation(context);
+
+    // TODO(jmadill): Use dirty bits.
     if (programD3D->updateSamplerMapping() == ProgramD3D::SamplerMapping::WasDirty)
     {
         invalidateTexturesAndSamplers();
@@ -1960,9 +1966,7 @@
     ANGLE_TRY(syncTransformFeedbackBuffers(context));
 
     // Check that we haven't set any dirty bits in the flushing of the dirty bits loop.
-    // TODO(jmadill): Fix FL 9_3 RenderTarget dirtying in call to syncTextures.
-    ASSERT(mInternalDirtyBits.none() ||
-           mRenderer->getRenderer11DeviceCaps().featureLevel <= D3D_FEATURE_LEVEL_9_3);
+    ASSERT(mInternalDirtyBits.none());
 
     return gl::NoError();
 }
diff --git a/src/libANGLE/renderer/d3d/d3d11/StateManager11.h b/src/libANGLE/renderer/d3d/d3d11/StateManager11.h
index 1abbf6a..9309242 100644
--- a/src/libANGLE/renderer/d3d/d3d11/StateManager11.h
+++ b/src/libANGLE/renderer/d3d/d3d11/StateManager11.h
@@ -161,14 +161,14 @@
     // These invalidations methods are called externally.
 
     // Called from TextureStorage11.
-    void invalidateBoundViews(const gl::Context *context);
+    void invalidateBoundViews();
 
     // Called from VertexArray11::updateVertexAttribStorage.
     void invalidateCurrentValueAttrib(size_t attribIndex);
 
     // Checks are done on a framebuffer state change to trigger other state changes.
     // The Context is allowed to be nullptr for these methods, when called in EGL init code.
-    void invalidateRenderTarget(const gl::Context *context);
+    void invalidateRenderTarget();
 
     // Called by instanced point sprite emulation.
     void invalidateVertexBuffer();
@@ -331,7 +331,7 @@
     void invalidateConstantBuffer(unsigned int slot);
 
     // Called by the Framebuffer11 directly.
-    void dirtyDrawFramebuffer();
+    void processFramebufferInvalidation(const gl::Context *context);
 
     enum DirtyBitType
     {
@@ -393,6 +393,7 @@
 
     // Render target variables
     gl::Extents mViewportBounds;
+    bool mRenderTargetIsDirty;
 
     // EGL_ANGLE_experimental_present_path variables
     bool mCurPresentPathFastEnabled;
diff --git a/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp b/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp
index 2f60440..fcac8a6 100644
--- a/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp
@@ -898,7 +898,7 @@
 
     // Some swapping mechanisms such as DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL unbind the current render
     // target. Mark it dirty. Use the proxy context in display since there is none available.
-    mRenderer->getStateManager()->invalidateRenderTarget(context);
+    mRenderer->getStateManager()->invalidateRenderTarget();
 
     if (result == DXGI_ERROR_DEVICE_REMOVED)
     {
diff --git a/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp b/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
index 6a5538d..8ffe443 100644
--- a/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
@@ -818,7 +818,7 @@
     {
         // If the keyed mutex is released that will unbind it and cause the state cache to become
         // desynchronized.
-        mRenderer->getStateManager()->invalidateBoundViews(context);
+        mRenderer->getStateManager()->invalidateBoundViews();
     }
 
     // Invalidate RenderTargets.
@@ -1312,7 +1312,7 @@
     {
         // If the keyed mutex is released that will unbind it and cause the state cache to become
         // desynchronized.
-        mRenderer->getStateManager()->invalidateBoundViews(context);
+        mRenderer->getStateManager()->invalidateBoundViews();
     }
 
     return gl::NoError();