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/StateChangeTest.cpp b/src/tests/gl_tests/StateChangeTest.cpp
index 7f03271..e96cc05 100644
--- a/src/tests/gl_tests/StateChangeTest.cpp
+++ b/src/tests/gl_tests/StateChangeTest.cpp
@@ -240,5 +240,148 @@
ASSERT_GL_NO_ERROR();
}
+class StateChangeRenderTest : public StateChangeTest
+{
+ protected:
+ StateChangeRenderTest() : mProgram(0), mRenderbuffer(0) {}
+
+ void SetUp() override
+ {
+ StateChangeTest::SetUp();
+
+ const std::string vertexShaderSource =
+ "attribute vec2 position;\n"
+ "void main() {\n"
+ " gl_Position = vec4(position, 0, 1);\n"
+ "}";
+ const std::string fragmentShaderSource =
+ "uniform highp vec4 uniformColor;\n"
+ "void main() {\n"
+ " gl_FragColor = uniformColor;\n"
+ "}";
+
+ mProgram = CompileProgram(vertexShaderSource, fragmentShaderSource);
+ ASSERT_NE(0u, mProgram);
+
+ glGenRenderbuffers(1, &mRenderbuffer);
+ }
+
+ void TearDown() override
+ {
+ glDeleteProgram(mProgram);
+ glDeleteRenderbuffers(1, &mRenderbuffer);
+
+ StateChangeTest::TearDown();
+ }
+
+ void setUniformColor(const GLColor &color)
+ {
+ glUseProgram(mProgram);
+ const Vector4 &normalizedColor = color.toNormalizedVector();
+ GLint uniformLocation = glGetUniformLocation(mProgram, "uniformColor");
+ ASSERT_NE(-1, uniformLocation);
+ glUniform4fv(uniformLocation, 1, normalizedColor.data());
+ }
+
+ GLuint mProgram;
+ GLuint mRenderbuffer;
+};
+
+// Test that re-creating a currently attached texture works as expected.
+TEST_P(StateChangeRenderTest, RecreateTexture)
+{
+ glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
+
+ glBindTexture(GL_TEXTURE_2D, mTextures[0]);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0);
+
+ // Draw with red to the FBO.
+ GLColor red(255, 0, 0, 255);
+ setUniformColor(red);
+ drawQuad(mProgram, "position", 0.5f);
+ EXPECT_PIXEL_COLOR_EQ(0, 0, red);
+
+ // Recreate the texture with green.
+ GLColor green(0, 255, 0, 255);
+ std::vector<GLColor> greenPixels(32 * 32, green);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ greenPixels.data());
+ EXPECT_PIXEL_COLOR_EQ(0, 0, green);
+
+ // Verify drawing blue gives blue. This covers the FBO sync with D3D dirty bits.
+ GLColor blue(0, 0, 255, 255);
+ setUniformColor(blue);
+ drawQuad(mProgram, "position", 0.5f);
+ EXPECT_PIXEL_COLOR_EQ(0, 0, blue);
+
+ EXPECT_GL_NO_ERROR();
+}
+
+// Test that re-creating a currently attached renderbuffer works as expected.
+TEST_P(StateChangeRenderTest, RecreateRenderbuffer)
+{
+ glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
+
+ glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 16, 16);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mRenderbuffer);
+
+ // Draw with red to the FBO.
+ GLColor red(255, 0, 0, 255);
+ setUniformColor(red);
+ drawQuad(mProgram, "position", 0.5f);
+ EXPECT_PIXEL_COLOR_EQ(0, 0, red);
+
+ // Recreate the renderbuffer and clear to green.
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 32, 32);
+ glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ GLColor green(0, 255, 0, 255);
+ EXPECT_PIXEL_COLOR_EQ(0, 0, green);
+
+ // Verify drawing blue gives blue. This covers the FBO sync with D3D dirty bits.
+ GLColor blue(0, 0, 255, 255);
+ setUniformColor(blue);
+ drawQuad(mProgram, "position", 0.5f);
+ EXPECT_PIXEL_COLOR_EQ(0, 0, blue);
+
+ EXPECT_GL_NO_ERROR();
+}
+
+// Test that recreating a texture with GenerateMipmaps signals the FBO is dirty.
+TEST_P(StateChangeRenderTest, GenerateMipmap)
+{
+ glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
+
+ glBindTexture(GL_TEXTURE_2D, mTextures[0]);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0);
+
+ // Draw once to set the RenderTarget in D3D11
+ GLColor red(255, 0, 0, 255);
+ setUniformColor(red);
+ drawQuad(mProgram, "position", 0.5f);
+ EXPECT_PIXEL_COLOR_EQ(0, 0, red);
+
+ // This will trigger the texture to be re-created on FL9_3.
+ glGenerateMipmap(GL_TEXTURE_2D);
+
+ // Now ensure we don't have a stale render target.
+ GLColor blue(0, 0, 255, 255);
+ setUniformColor(blue);
+ drawQuad(mProgram, "position", 0.5f);
+ EXPECT_PIXEL_COLOR_EQ(0, 0, blue);
+
+ EXPECT_GL_NO_ERROR();
+}
+
ANGLE_INSTANTIATE_TEST(StateChangeTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL());
+ANGLE_INSTANTIATE_TEST(StateChangeRenderTest,
+ ES2_D3D9(),
+ ES2_D3D11(),
+ ES2_OPENGL(),
+ ES2_D3D11_FL9_3());
ANGLE_INSTANTIATE_TEST(StateChangeTestES3, ES3_D3D11(), ES3_OPENGL());