| // |
| // 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. |
| // |
| // StateChangeTest: |
| // Specifically designed for an ANGLE implementation of GL, these tests validate that |
| // ANGLE's dirty bits systems don't get confused by certain sequences of state changes. |
| // |
| |
| #include "test_utils/ANGLETest.h" |
| |
| using namespace angle; |
| |
| namespace |
| { |
| |
| class StateChangeTest : public ANGLETest |
| { |
| protected: |
| StateChangeTest() : mFramebuffer(0) |
| { |
| setWindowWidth(64); |
| setWindowHeight(64); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| |
| // Enable the no error extension to avoid syncing the FBO state on validation. |
| setNoErrorEnabled(true); |
| } |
| |
| void SetUp() override |
| { |
| ANGLETest::SetUp(); |
| |
| glGenFramebuffers(1, &mFramebuffer); |
| |
| mTextures.resize(2, 0); |
| glGenTextures(2, mTextures.data()); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void TearDown() override |
| { |
| if (mFramebuffer != 0) |
| { |
| glDeleteFramebuffers(1, &mFramebuffer); |
| mFramebuffer = 0; |
| } |
| |
| if (!mTextures.empty()) |
| { |
| glDeleteTextures(static_cast<GLsizei>(mTextures.size()), mTextures.data()); |
| mTextures.clear(); |
| } |
| |
| ANGLETest::TearDown(); |
| } |
| |
| GLuint mFramebuffer; |
| std::vector<GLuint> mTextures; |
| }; |
| |
| class StateChangeTestES3 : public StateChangeTest |
| { |
| protected: |
| StateChangeTestES3() {} |
| }; |
| |
| } // anonymous namespace |
| |
| // Ensure that CopyTexImage2D syncs framebuffer changes. |
| TEST_P(StateChangeTest, CopyTexImage2DSync) |
| { |
| if (IsAMD() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE) |
| { |
| // TODO(geofflang): Fix on Linux AMD drivers (http://anglebug.com/1291) |
| std::cout << "Test disabled on AMD OpenGL." << std::endl; |
| return; |
| } |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| |
| // Init first texture to red |
| 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); |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); |
| |
| // Init second texture to green |
| glBindTexture(GL_TEXTURE_2D, mTextures[1]); |
| 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[1], 0); |
| glClearColor(0.0f, 1.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); |
| |
| // Copy in the red texture to the green one. |
| // CopyTexImage should sync the framebuffer attachment change. |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); |
| glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 16, 16, 0); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], 0); |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Ensure that CopyTexSubImage2D syncs framebuffer changes. |
| TEST_P(StateChangeTest, CopyTexSubImage2DSync) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| |
| // Init first texture to red |
| 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); |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); |
| |
| // Init second texture to green |
| glBindTexture(GL_TEXTURE_2D, mTextures[1]); |
| 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[1], 0); |
| glClearColor(0.0f, 1.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); |
| |
| // Copy in the red texture to the green one. |
| // CopyTexImage should sync the framebuffer attachment change. |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0); |
| glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 16, 16); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], 0); |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Ensure that CopyTexSubImage3D syncs framebuffer changes. |
| TEST_P(StateChangeTestES3, CopyTexSubImage3DSync) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| |
| // Init first texture to red |
| glBindTexture(GL_TEXTURE_3D, mTextures[0]); |
| glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 16, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTextures[0], 0, 0); |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); |
| |
| // Init second texture to green |
| glBindTexture(GL_TEXTURE_3D, mTextures[1]); |
| glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 16, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTextures[1], 0, 0); |
| glClearColor(0.0f, 1.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); |
| |
| // Copy in the red texture to the green one. |
| // CopyTexImage should sync the framebuffer attachment change. |
| glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTextures[0], 0, 0); |
| glCopyTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 0, 0, 16, 16); |
| glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTextures[1], 0, 0); |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Ensure that BlitFramebuffer syncs framebuffer changes. |
| TEST_P(StateChangeTestES3, BlitFramebufferSync) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| |
| // Init first texture to red |
| 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); |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); |
| |
| // Init second texture to green |
| glBindTexture(GL_TEXTURE_2D, mTextures[1]); |
| 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[1], 0); |
| glClearColor(0.0f, 1.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); |
| |
| // Change to the red textures and blit. |
| // BlitFramebuffer should sync the framebuffer attachment change. |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); |
| glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], |
| 0); |
| glBlitFramebuffer(0, 0, 16, 16, 0, 0, 16, 16, GL_COLOR_BUFFER_BIT, GL_NEAREST); |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Ensure that ReadBuffer and DrawBuffers sync framebuffer changes. |
| TEST_P(StateChangeTestES3, ReadBufferAndDrawBuffersSync) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| |
| // Initialize two FBO attachments |
| 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); |
| glBindTexture(GL_TEXTURE_2D, mTextures[1]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, mTextures[1], 0); |
| |
| // Clear first attachment to red |
| GLenum bufs1[] = {GL_COLOR_ATTACHMENT0, GL_NONE}; |
| glDrawBuffers(2, bufs1); |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Clear second texture to green |
| GLenum bufs2[] = {GL_NONE, GL_COLOR_ATTACHMENT1}; |
| glDrawBuffers(2, bufs2); |
| glClearColor(0.0f, 1.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Verify first attachment is red and second is green |
| glReadBuffer(GL_COLOR_ATTACHMENT1); |
| EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255); |
| |
| glReadBuffer(GL_COLOR_ATTACHMENT0); |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255); |
| |
| 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()); |