D3D11: Enable dirty bits for Framebuffer11.

This patch works using a notification scheme - whenever a Texture or
Renderbuffer changes in such a way as to recreate its RenderTarget, we
pass a signal to the Framebuffer to invalidate some internal state.
Everything is entirely tracked in the Renderer11 layer, and the GL
layer is left untouched.

A RenderTarget11 now tracks points to which it is bound, and the
Framebuffer11 is mostly responsible for managing those links.

The three locations where we notify a Framebuffer when its bound
RenderTargets might be dirty are:

 1) RenderTarget11::~RenderTarget
 2) EGLImageD3D::copyToLocalRendertarget
 3) TextureStorage11_2D::useLevelZeroWorkaroundTexture

This patch gives about a 10% score increase in the D3D11 draw call
benchmark on my system.

BUG=angleproject:1260

Change-Id: Ide38aeadff4a2681bf5bd685e8ca3c9e2612a380
Reviewed-on: https://chromium-review.googlesource.com/327255
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/tests/gl_tests/FramebufferTest.cpp b/src/tests/gl_tests/FramebufferTest.cpp
new file mode 100644
index 0000000..7b0c45c
--- /dev/null
+++ b/src/tests/gl_tests/FramebufferTest.cpp
@@ -0,0 +1,357 @@
+//
+// Copyright 2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Framebuffer tests:
+//   Various tests related for Frambuffers.
+//
+
+#include "test_utils/ANGLETest.h"
+
+using namespace angle;
+
+class FramebufferFormatsTest : public ANGLETest
+{
+  protected:
+    FramebufferFormatsTest() : mFramebuffer(0), mTexture(0), mRenderbuffer(0), mProgram(0)
+    {
+        setWindowWidth(128);
+        setWindowHeight(128);
+        setConfigRedBits(8);
+        setConfigGreenBits(8);
+        setConfigBlueBits(8);
+        setConfigAlphaBits(8);
+    }
+
+    void checkBitCount(GLuint fbo, GLenum channel, GLint minBits)
+    {
+        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+        GLint bits = 0;
+        glGetIntegerv(channel, &bits);
+
+        if (minBits == 0)
+        {
+            EXPECT_EQ(minBits, bits);
+        }
+        else
+        {
+            EXPECT_GE(bits, minBits);
+        }
+    }
+
+    void testBitCounts(GLuint fbo,
+                       GLint minRedBits,
+                       GLint minGreenBits,
+                       GLint minBlueBits,
+                       GLint minAlphaBits,
+                       GLint minDepthBits,
+                       GLint minStencilBits)
+    {
+        checkBitCount(fbo, GL_RED_BITS, minRedBits);
+        checkBitCount(fbo, GL_GREEN_BITS, minGreenBits);
+        checkBitCount(fbo, GL_BLUE_BITS, minBlueBits);
+        checkBitCount(fbo, GL_ALPHA_BITS, minAlphaBits);
+        checkBitCount(fbo, GL_DEPTH_BITS, minDepthBits);
+        checkBitCount(fbo, GL_STENCIL_BITS, minStencilBits);
+    }
+
+    void testTextureFormat(GLenum internalFormat,
+                           GLint minRedBits,
+                           GLint minGreenBits,
+                           GLint minBlueBits,
+                           GLint minAlphaBits)
+    {
+        glGenTextures(1, &mTexture);
+        glBindTexture(GL_TEXTURE_2D, mTexture);
+        glTexStorage2DEXT(GL_TEXTURE_2D, 1, internalFormat, 1, 1);
+
+        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0);
+
+        testBitCounts(mFramebuffer, minRedBits, minGreenBits, minBlueBits, minAlphaBits, 0, 0);
+    }
+
+    void testRenderbufferMultisampleFormat(int minESVersion,
+                                           GLenum attachmentType,
+                                           GLenum internalFormat)
+    {
+        // TODO(geofflang): Figure out why this is broken on Intel OpenGL
+        if (isIntel() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
+        {
+            std::cout << "Test skipped on Intel OpenGL." << std::endl;
+            return;
+        }
+
+        int clientVersion = getClientVersion();
+        if (clientVersion < minESVersion)
+        {
+            return;
+        }
+
+        // Check that multisample is supported with at least two samples (minimum required is 1)
+        bool supports2Samples = false;
+
+        if (clientVersion == 2)
+        {
+            if (extensionEnabled("ANGLE_framebuffer_multisample"))
+            {
+                int maxSamples;
+                glGetIntegerv(GL_MAX_SAMPLES_ANGLE, &maxSamples);
+                supports2Samples = maxSamples >= 2;
+            }
+        }
+        else
+        {
+            assert(clientVersion >= 3);
+            int maxSamples;
+            glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
+            supports2Samples = maxSamples >= 2;
+        }
+
+        if (!supports2Samples)
+        {
+            return;
+        }
+
+        glGenRenderbuffers(1, &mRenderbuffer);
+        glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
+
+        EXPECT_GL_NO_ERROR();
+        glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER, 2, internalFormat, 128, 128);
+        EXPECT_GL_NO_ERROR();
+        glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachmentType, GL_RENDERBUFFER, mRenderbuffer);
+        EXPECT_GL_NO_ERROR();
+    }
+
+    void SetUp() override
+    {
+        ANGLETest::SetUp();
+
+        glGenFramebuffers(1, &mFramebuffer);
+        glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
+    }
+
+    void TearDown() override
+    {
+        ANGLETest::TearDown();
+
+        if (mTexture != 0)
+        {
+            glDeleteTextures(1, &mTexture);
+            mTexture = 0;
+        }
+
+        if (mRenderbuffer != 0)
+        {
+            glDeleteRenderbuffers(1, &mRenderbuffer);
+            mRenderbuffer = 0;
+        }
+
+        if (mFramebuffer != 0)
+        {
+            glDeleteFramebuffers(1, &mFramebuffer);
+            mFramebuffer = 0;
+        }
+
+        if (mProgram != 0)
+        {
+            glDeleteProgram(mProgram);
+            mProgram = 0;
+        }
+    }
+
+    GLuint mFramebuffer;
+    GLuint mTexture;
+    GLuint mRenderbuffer;
+    GLuint mProgram;
+};
+
+TEST_P(FramebufferFormatsTest, RGBA4)
+{
+    testTextureFormat(GL_RGBA4, 4, 4, 4, 4);
+}
+
+TEST_P(FramebufferFormatsTest, RGB565)
+{
+    testTextureFormat(GL_RGB565, 5, 6, 5, 0);
+}
+
+TEST_P(FramebufferFormatsTest, RGB8)
+{
+    if (getClientVersion() < 3 && !extensionEnabled("GL_OES_rgb8_rgba8"))
+    {
+        std::cout << "Test skipped due to missing ES3 or GL_OES_rgb8_rgba8." << std::endl;
+        return;
+    }
+
+    testTextureFormat(GL_RGB8_OES, 8, 8, 8, 0);
+}
+
+TEST_P(FramebufferFormatsTest, BGRA8)
+{
+    if (!extensionEnabled("GL_EXT_texture_format_BGRA8888"))
+    {
+        std::cout << "Test skipped due to missing GL_EXT_texture_format_BGRA8888." << std::endl;
+        return;
+    }
+
+    testTextureFormat(GL_BGRA8_EXT, 8, 8, 8, 8);
+}
+
+TEST_P(FramebufferFormatsTest, RGBA8)
+{
+    if (getClientVersion() < 3 && !extensionEnabled("GL_OES_rgb8_rgba8"))
+    {
+        std::cout << "Test skipped due to missing ES3 or GL_OES_rgb8_rgba8." << std::endl;
+        return;
+    }
+
+    testTextureFormat(GL_RGBA8_OES, 8, 8, 8, 8);
+}
+
+TEST_P(FramebufferFormatsTest, RenderbufferMultisample_DEPTH16)
+{
+    testRenderbufferMultisampleFormat(2, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT16);
+}
+
+TEST_P(FramebufferFormatsTest, RenderbufferMultisample_DEPTH24)
+{
+    testRenderbufferMultisampleFormat(3, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT24);
+}
+
+TEST_P(FramebufferFormatsTest, RenderbufferMultisample_DEPTH32F)
+{
+    if (getClientVersion() < 3)
+    {
+        std::cout << "Test skipped due to missing ES3." << std::endl;
+        return;
+    }
+
+    testRenderbufferMultisampleFormat(3, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT32F);
+}
+
+TEST_P(FramebufferFormatsTest, RenderbufferMultisample_DEPTH24_STENCIL8)
+{
+    testRenderbufferMultisampleFormat(3, GL_DEPTH_STENCIL_ATTACHMENT, GL_DEPTH24_STENCIL8);
+}
+
+TEST_P(FramebufferFormatsTest, RenderbufferMultisample_DEPTH32F_STENCIL8)
+{
+    if (getClientVersion() < 3)
+    {
+        std::cout << "Test skipped due to missing ES3." << std::endl;
+        return;
+    }
+
+    testRenderbufferMultisampleFormat(3, GL_DEPTH_STENCIL_ATTACHMENT, GL_DEPTH32F_STENCIL8);
+}
+
+TEST_P(FramebufferFormatsTest, RenderbufferMultisample_STENCIL_INDEX8)
+{
+    // TODO(geofflang): Figure out how to support GLSTENCIL_INDEX8 on desktop GL
+    if (GetParam().getRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
+    {
+        std::cout << "Test skipped on Desktop OpenGL." << std::endl;
+        return;
+    }
+
+    testRenderbufferMultisampleFormat(2, GL_STENCIL_ATTACHMENT, GL_STENCIL_INDEX8);
+}
+
+// Test that binding an incomplete cube map is rejected by ANGLE.
+TEST_P(FramebufferFormatsTest, IncompleteCubeMap)
+{
+    // First make a complete CubeMap.
+    glGenTextures(1, &mTexture);
+    glBindTexture(GL_TEXTURE_CUBE_MAP, mTexture);
+    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+                 nullptr);
+    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+                 nullptr);
+    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+                 nullptr);
+    glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+                 nullptr);
+    glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+                 nullptr);
+    glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+                 nullptr);
+    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X,
+                           mTexture, 0);
+
+    // Verify the framebuffer is complete.
+    ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
+
+    // Make the CubeMap cube-incomplete.
+    glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+                 nullptr);
+
+    // Verify the framebuffer is incomplete.
+    ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
+                     glCheckFramebufferStatus(GL_FRAMEBUFFER));
+
+    // Verify drawing with the incomplete framebuffer produces a GL error
+    const std::string &vs = "attribute vec4 position; void main() { gl_Position = position; }";
+    const std::string &ps = "void main() { gl_FragColor = vec4(1, 0, 0, 1); }";
+    mProgram = CompileProgram(vs, ps);
+    ASSERT_NE(0u, mProgram);
+    drawQuad(mProgram, "position", 0.5f);
+    ASSERT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
+}
+
+// Use this to select which configurations (e.g. which renderer, which GLES major version) these
+// tests should be run against.
+ANGLE_INSTANTIATE_TEST(FramebufferFormatsTest,
+                       ES2_D3D9(),
+                       ES2_D3D11(),
+                       ES3_D3D11(),
+                       ES2_OPENGL(),
+                       ES3_OPENGL(),
+                       ES2_OPENGLES(),
+                       ES3_OPENGLES());
+
+class FramebufferInvalidateTest : public ANGLETest
+{
+  protected:
+    FramebufferInvalidateTest() : mFramebuffer(0), mRenderbuffer(0) {}
+
+    void SetUp() override
+    {
+        ANGLETest::SetUp();
+
+        glGenFramebuffers(1, &mFramebuffer);
+        glGenRenderbuffers(1, &mRenderbuffer);
+    }
+
+    void TearDown() override
+    {
+        glDeleteFramebuffers(1, &mFramebuffer);
+        glDeleteRenderbuffers(1, &mRenderbuffer);
+        ANGLETest::TearDown();
+    }
+
+    GLuint mFramebuffer;
+    GLuint mRenderbuffer;
+};
+
+// Covers invalidating an incomplete framebuffer. This should be a no-op, but should not error.
+TEST_P(FramebufferInvalidateTest, Incomplete)
+{
+    glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
+    glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mRenderbuffer);
+    EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
+                     glCheckFramebufferStatus(GL_FRAMEBUFFER));
+
+    std::vector<GLenum> attachments;
+    attachments.push_back(GL_COLOR_ATTACHMENT0);
+
+    glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, attachments.data());
+    EXPECT_GL_NO_ERROR();
+}
+
+ANGLE_INSTANTIATE_TEST(FramebufferInvalidateTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());