| // |
| // Copyright 2021 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. |
| // |
| // RobustFragmentShaderOutputTest: Tests for the custom ANGLE extension. |
| |
| #include "test_utils/ANGLETest.h" |
| #include "test_utils/gl_raii.h" |
| |
| #include "util/random_utils.h" |
| |
| #include <stdint.h> |
| |
| using namespace angle; |
| |
| namespace |
| { |
| bool ExtEnabled() |
| { |
| return IsGLExtensionEnabled("GL_ANGLE_robust_fragment_shader_output"); |
| } |
| } // namespace |
| |
| class RobustFragmentShaderOutputTest : public ANGLETest<> |
| { |
| public: |
| RobustFragmentShaderOutputTest() {} |
| }; |
| |
| // Basic behaviour from the extension. |
| TEST_P(RobustFragmentShaderOutputTest, Basic) |
| { |
| ANGLE_SKIP_TEST_IF(!ExtEnabled()); |
| |
| const char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 outvar; |
| void main() { |
| outvar = vec4(0.0, 1.0, 0.0, 1.0); |
| })"; |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| |
| GLFramebuffer framebuffer; |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); |
| |
| constexpr GLsizei kSize = 2; |
| std::vector<GLColor> bluePixels(kSize * kSize, GLColor::blue); |
| |
| GLTexture texA; |
| glBindTexture(GL_TEXTURE_2D, texA); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| bluePixels.data()); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texA, 0); |
| |
| GLTexture texB; |
| glBindTexture(GL_TEXTURE_2D, texB); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| bluePixels.data()); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, texB, 0); |
| |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Verify initial attachment colors (blue). |
| glReadBuffer(GL_COLOR_ATTACHMENT0); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); |
| |
| glReadBuffer(GL_COLOR_ATTACHMENT1); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); |
| |
| constexpr std::array<GLenum, 2> kDrawBuffers = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}; |
| glDrawBuffers(2, kDrawBuffers.data()); |
| glViewport(0, 0, kSize, kSize); |
| glUseProgram(program); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw, verify first attachment is updated (green) and second is unchanged (blue). |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f, 1.0f, true); |
| ASSERT_GL_NO_ERROR(); |
| |
| glReadBuffer(GL_COLOR_ATTACHMENT0); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| |
| glReadBuffer(GL_COLOR_ATTACHMENT1); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); |
| } |
| |
| // Test that changing framebuffer attachments work |
| TEST_P(RobustFragmentShaderOutputTest, ChangingFramebufferAttachments) |
| { |
| ANGLE_SKIP_TEST_IF(!ExtEnabled()); |
| |
| const char kFS[] = R"(#version 300 es |
| precision mediump float; |
| layout(location = 1) out vec4 color1; |
| layout(location = 3) out vec4 color2; |
| |
| uniform vec4 color1In; |
| uniform vec4 color2In; |
| |
| void main() { |
| color1 = color1In; |
| color2 = color2In; |
| })"; |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| glUseProgram(program); |
| |
| GLint color1Loc = glGetUniformLocation(program, "color1In"); |
| ASSERT_NE(color1Loc, -1); |
| GLint color2Loc = glGetUniformLocation(program, "color2In"); |
| ASSERT_NE(color2Loc, -1); |
| |
| constexpr GLsizei kSize = 2; |
| const std::vector<GLColor> initColor(kSize * kSize, GLColor::transparentBlack); |
| |
| GLTexture color1; |
| glBindTexture(GL_TEXTURE_2D, color1); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| initColor.data()); |
| |
| GLTexture color2; |
| glBindTexture(GL_TEXTURE_2D, color2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| initColor.data()); |
| |
| GLTexture color3; |
| glBindTexture(GL_TEXTURE_2D, color3); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| initColor.data()); |
| |
| // Draw with matching framebuffer attachments |
| GLFramebuffer framebuffer; |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, color1, 0); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, color2, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| std::array<GLenum, 4> drawBuffers = {GL_NONE, GL_COLOR_ATTACHMENT1, GL_NONE, |
| GL_COLOR_ATTACHMENT3}; |
| glDrawBuffers(4, drawBuffers.data()); |
| glViewport(0, 0, kSize, kSize); |
| ASSERT_GL_NO_ERROR(); |
| |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_ONE, GL_ONE); |
| |
| // Draw once, should update color1 and color2 |
| glUniform4f(color1Loc, 1, 0, 0, 0); |
| glUniform4f(color2Loc, 0, 1, 0, 0); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| |
| // Change the framebuffer attachments, this time adding an attachment that's not written to. |
| // This attachment should be unmodified. |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color3, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| drawBuffers[0] = GL_COLOR_ATTACHMENT0; |
| glDrawBuffers(4, drawBuffers.data()); |
| |
| glUniform4f(color1Loc, 0, 0, 1, 1); |
| glUniform4f(color2Loc, 0, 0, 0, 1); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| |
| // Shuffle attachments. |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color1, 0); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, color2, 0); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, color3, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| glUniform4f(color1Loc, 0, 0, 1, 0); |
| glUniform4f(color2Loc, 1, 0, 0, 1); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| |
| // Switch back to matching attachment count |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| drawBuffers[0] = GL_NONE; |
| glDrawBuffers(4, drawBuffers.data()); |
| |
| glUniform4f(color1Loc, 1, 0, 0, 0); |
| glUniform4f(color2Loc, 0, 1, 0, 0); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| |
| // Verify results |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color1, 0); |
| glReadBuffer(GL_COLOR_ATTACHMENT0); |
| EXPECT_PIXEL_RECT_EQ(0, 0, kSize, kSize, GLColor::magenta); |
| |
| glReadBuffer(GL_COLOR_ATTACHMENT1); |
| EXPECT_PIXEL_RECT_EQ(0, 0, kSize, kSize, GLColor::white); |
| |
| glReadBuffer(GL_COLOR_ATTACHMENT3); |
| EXPECT_PIXEL_RECT_EQ(0, 0, kSize, kSize, GLColor::yellow); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test that changing framebuffers work |
| TEST_P(RobustFragmentShaderOutputTest, ChangingFramebuffers) |
| { |
| ANGLE_SKIP_TEST_IF(!ExtEnabled()); |
| |
| const char kFS[] = R"(#version 300 es |
| precision mediump float; |
| layout(location = 1) out vec4 color1; |
| layout(location = 3) out vec4 color2; |
| |
| uniform vec4 color1In; |
| uniform vec4 color2In; |
| |
| void main() { |
| color1 = color1In; |
| color2 = color2In; |
| })"; |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| glUseProgram(program); |
| |
| GLint color1Loc = glGetUniformLocation(program, "color1In"); |
| ASSERT_NE(color1Loc, -1); |
| GLint color2Loc = glGetUniformLocation(program, "color2In"); |
| ASSERT_NE(color2Loc, -1); |
| |
| constexpr GLsizei kSize = 2; |
| const std::vector<GLColor> initColor(kSize * kSize, GLColor::transparentBlack); |
| |
| GLTexture color1; |
| glBindTexture(GL_TEXTURE_2D, color1); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| initColor.data()); |
| |
| GLTexture color2; |
| glBindTexture(GL_TEXTURE_2D, color2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| initColor.data()); |
| |
| GLTexture color3; |
| glBindTexture(GL_TEXTURE_2D, color3); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| initColor.data()); |
| |
| GLFramebuffer framebuffer1; |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer1); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, color1, 0); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, color2, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| GLFramebuffer framebuffer2; |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer2); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, color1, 0); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, color2, 0); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color3, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| GLFramebuffer framebuffer3; |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer3); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color1, 0); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, color2, 0); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, color3, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| GLFramebuffer framebuffer4; |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer4); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, color2, 0); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, color3, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Draw with matching framebuffer attachments |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer1); |
| |
| std::array<GLenum, 4> drawBuffers = {GL_NONE, GL_COLOR_ATTACHMENT1, GL_NONE, |
| GL_COLOR_ATTACHMENT3}; |
| glDrawBuffers(4, drawBuffers.data()); |
| glViewport(0, 0, kSize, kSize); |
| ASSERT_GL_NO_ERROR(); |
| |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_ONE, GL_ONE); |
| |
| // Draw once, should update color1 and color2 |
| glUniform4f(color1Loc, 1, 0, 0, 0); |
| glUniform4f(color2Loc, 0, 1, 0, 0); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| |
| // Change the framebuffer attachments, this time adding an attachment that's not written to. |
| // This attachment should be unmodified. |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer2); |
| |
| drawBuffers[0] = GL_COLOR_ATTACHMENT0; |
| glDrawBuffers(4, drawBuffers.data()); |
| |
| glUniform4f(color1Loc, 0, 0, 1, 1); |
| glUniform4f(color2Loc, 0, 0, 0, 1); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| |
| // Shuffle attachments. |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer3); |
| glDrawBuffers(4, drawBuffers.data()); |
| |
| glUniform4f(color1Loc, 0, 0, 1, 0); |
| glUniform4f(color2Loc, 1, 0, 0, 1); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| |
| // Switch back to matching attachment count |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer4); |
| |
| drawBuffers[0] = GL_NONE; |
| glDrawBuffers(4, drawBuffers.data()); |
| |
| glUniform4f(color1Loc, 1, 0, 0, 0); |
| glUniform4f(color2Loc, 0, 1, 0, 0); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| |
| // Verify results |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer3); |
| glReadBuffer(GL_COLOR_ATTACHMENT0); |
| EXPECT_PIXEL_RECT_EQ(0, 0, kSize, kSize, GLColor::magenta); |
| |
| glReadBuffer(GL_COLOR_ATTACHMENT1); |
| EXPECT_PIXEL_RECT_EQ(0, 0, kSize, kSize, GLColor::white); |
| |
| glReadBuffer(GL_COLOR_ATTACHMENT3); |
| EXPECT_PIXEL_RECT_EQ(0, 0, kSize, kSize, GLColor::yellow); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test that changing program outputs work |
| TEST_P(RobustFragmentShaderOutputTest, ChangingProgramOutputs) |
| { |
| ANGLE_SKIP_TEST_IF(!ExtEnabled()); |
| |
| const char kFS1[] = R"(#version 300 es |
| precision mediump float; |
| layout(location = 1) out vec4 color1; |
| |
| uniform vec4 color1In; |
| |
| void main() { |
| color1 = color1In; |
| })"; |
| |
| const char kFS2[] = R"(#version 300 es |
| precision mediump float; |
| layout(location = 1) out vec4 color1; |
| layout(location = 3) out vec4 color2; |
| |
| uniform vec4 color1In; |
| uniform vec4 color2In; |
| |
| void main() { |
| color1 = color1In; |
| color2 = color2In; |
| })"; |
| |
| const char kFS3[] = R"(#version 300 es |
| precision mediump float; |
| layout(location = 0) out vec4 color1; |
| layout(location = 1) out vec4 color2; |
| layout(location = 3) out vec4 color3; |
| |
| uniform vec4 color1In; |
| uniform vec4 color2In; |
| uniform vec4 color3In; |
| |
| void main() { |
| color1 = color1In; |
| color2 = color2In; |
| color3 = color3In; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program1, essl3_shaders::vs::Simple(), kFS1); |
| glUseProgram(program1); |
| GLint program1Color1Loc = glGetUniformLocation(program1, "color1In"); |
| ASSERT_NE(program1Color1Loc, -1); |
| |
| ANGLE_GL_PROGRAM(program2, essl3_shaders::vs::Simple(), kFS2); |
| glUseProgram(program2); |
| GLint program2Color1Loc = glGetUniformLocation(program2, "color1In"); |
| ASSERT_NE(program2Color1Loc, -1); |
| GLint program2Color2Loc = glGetUniformLocation(program2, "color2In"); |
| ASSERT_NE(program2Color2Loc, -1); |
| |
| ANGLE_GL_PROGRAM(program3, essl3_shaders::vs::Simple(), kFS3); |
| glUseProgram(program3); |
| GLint program3Color1Loc = glGetUniformLocation(program3, "color1In"); |
| ASSERT_NE(program2Color1Loc, -1); |
| GLint program3Color2Loc = glGetUniformLocation(program3, "color2In"); |
| ASSERT_NE(program2Color2Loc, -1); |
| GLint program3Color3Loc = glGetUniformLocation(program3, "color3In"); |
| ASSERT_NE(program3Color3Loc, -1); |
| |
| constexpr GLsizei kSize = 2; |
| const std::vector<GLColor> initColor(kSize * kSize, GLColor::transparentBlack); |
| |
| GLTexture color1; |
| glBindTexture(GL_TEXTURE_2D, color1); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| initColor.data()); |
| |
| GLTexture color2; |
| glBindTexture(GL_TEXTURE_2D, color2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| initColor.data()); |
| |
| GLTexture color3; |
| glBindTexture(GL_TEXTURE_2D, color3); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| initColor.data()); |
| |
| // Draw with matching framebuffer attachments |
| GLFramebuffer framebuffer; |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color1, 0); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, color2, 0); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, color3, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| std::array<GLenum, 4> drawBuffers = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_NONE, |
| GL_COLOR_ATTACHMENT3}; |
| glDrawBuffers(4, drawBuffers.data()); |
| glViewport(0, 0, kSize, kSize); |
| ASSERT_GL_NO_ERROR(); |
| |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_ONE, GL_ONE); |
| |
| // Draw with 2 outputs, should update color2 and color3 |
| glUseProgram(program2); |
| glUniform4f(program2Color1Loc, 0.6, 0, 0, 0); |
| glUniform4f(program2Color2Loc, 0, 0.7, 0, 0); |
| drawQuad(program2, essl3_shaders::PositionAttrib(), 0.5f); |
| |
| // Draw with 1 output, should update color2 |
| glUseProgram(program1); |
| glUniform4f(program1Color1Loc, 0.5, 0, 0, 0); |
| drawQuad(program1, essl3_shaders::PositionAttrib(), 0.5f); |
| |
| // Draw with 3 outputs, should update all |
| glUseProgram(program3); |
| glUniform4f(program3Color1Loc, 0, 0, 1, 1); |
| glUniform4f(program3Color2Loc, 0, 1, 0, 0); |
| glUniform4f(program3Color3Loc, 0, 0.4, 0, 0); |
| drawQuad(program3, essl3_shaders::PositionAttrib(), 0.5f); |
| |
| // Draw with 2 outputs again, should update color2 and color3 |
| glUseProgram(program2); |
| glUniform4f(program2Color1Loc, 0, 0, 0, 1); |
| glUniform4f(program2Color2Loc, 0, 0, 0, 1); |
| drawQuad(program2, essl3_shaders::PositionAttrib(), 0.5f); |
| |
| // Verify results |
| glReadBuffer(GL_COLOR_ATTACHMENT0); |
| EXPECT_PIXEL_RECT_EQ(0, 0, kSize, kSize, GLColor::blue); |
| |
| glReadBuffer(GL_COLOR_ATTACHMENT1); |
| EXPECT_PIXEL_RECT_EQ(0, 0, kSize, kSize, GLColor::yellow); |
| |
| glReadBuffer(GL_COLOR_ATTACHMENT3); |
| EXPECT_PIXEL_RECT_EQ(0, 0, kSize, kSize, GLColor::green); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| ANGLE_INSTANTIATE_TEST_ES3(RobustFragmentShaderOutputTest); |