| // |
| // Copyright 2022 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. |
| // |
| // FragDepthTest: |
| // Tests the correctness of gl_FragDepth usage. |
| // |
| |
| #include "test_utils/ANGLETest.h" |
| #include "test_utils/gl_raii.h" |
| |
| using namespace angle; |
| |
| class FragDepthTest : public ANGLETest<> |
| { |
| protected: |
| struct FragDepthTestResources |
| { |
| GLuint program; |
| GLint depthLocation; |
| GLTexture colorTexture; |
| GLTexture depthTexture; |
| GLFramebuffer framebuffer; |
| }; |
| |
| void setupResources(FragDepthTestResources &resources) |
| { |
| |
| // Writes a fixed depth value and green. |
| constexpr char kFS[] = |
| R"(#version 300 es |
| precision highp float; |
| layout(location = 0) out vec4 fragColor; |
| uniform float u_depth; |
| void main(){ |
| gl_FragDepth = u_depth; |
| fragColor = vec4(0.0, 1.0, 0.0, 1.0); |
| })"; |
| |
| resources.program = CompileProgram(essl3_shaders::vs::Simple(), kFS); |
| resources.depthLocation = glGetUniformLocation(resources.program, "u_depth"); |
| |
| glBindTexture(GL_TEXTURE_2D, resources.colorTexture); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| |
| glBindTexture(GL_TEXTURE_2D, resources.depthTexture); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH_COMPONENT32F, 1, 1); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, resources.framebuffer); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |
| resources.colorTexture, 0); |
| } |
| |
| void cleanupResources(FragDepthTestResources &resources) { glDeleteProgram(resources.program); } |
| |
| void checkDepthWritten(float expectedDepth, float fsDepth, bool bindDepthBuffer) |
| { |
| FragDepthTestResources resources; |
| setupResources(resources); |
| ASSERT_NE(0u, resources.program); |
| ASSERT_NE(-1, resources.depthLocation); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| ASSERT_GL_NO_ERROR(); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, resources.framebuffer); |
| if (bindDepthBuffer) |
| { |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, |
| resources.depthTexture, 0); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Clear to the expected depth so it will be compared to the FS depth with |
| // DepthFunc(GL_EQUAL) |
| glClearDepthf(expectedDepth); |
| glDepthFunc(GL_EQUAL); |
| glEnable(GL_DEPTH_TEST); |
| } |
| else |
| { |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| glDisable(GL_DEPTH_TEST); |
| } |
| glUseProgram(resources.program); |
| |
| // Clear to red, the FS will write green on success |
| glClearColor(1.0f, 0.0f, 0.0f, 0.0f); |
| // Clear to the expected depth so it will be compared to the FS depth with |
| // DepthFunc(GL_EQUAL) |
| |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| glUniform1f(resources.depthLocation, fsDepth); |
| |
| drawQuad(resources.program, "a_position", 0.0f); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| cleanupResources(resources); |
| } |
| }; |
| |
| // Test that writing to gl_FragDepth works |
| TEST_P(FragDepthTest, DepthBufferBound) |
| { |
| checkDepthWritten(0.5f, 0.5f, true); |
| } |
| |
| // Test that writing to gl_FragDepth with no depth buffer works. |
| TEST_P(FragDepthTest, DepthBufferUnbound) |
| { |
| // Depth test is disabled, so the expected depth should not matter. |
| checkDepthWritten(0.f, 0.5f, false); |
| } |
| |
| // Test that fragment shader depth writes to a no-depth framebuffer do not fail |
| // after using a depth-enabled framebuffer with the same program. |
| TEST_P(FragDepthTest, SwitchToNoDepthFramebuffer) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| out highp vec4 color; |
| void main() { |
| color = vec4(1.0, 0.0, 0.0, 1.0); |
| gl_FragDepth = 1.0; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| |
| // Draw to a depth-enabled framebuffer first |
| GLFramebuffer fbo1; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo1); |
| |
| GLRenderbuffer rb10; |
| glBindRenderbuffer(GL_RENDERBUFFER, rb10); |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 128, 128); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb10); |
| |
| GLRenderbuffer rb1d; |
| glBindRenderbuffer(GL_RENDERBUFFER, rb1d); |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32F, 128, 128); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rb1d); |
| |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| drawQuad(program, "a_position", 0.0f); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Draw to a no-depth framebuffer using the same program |
| GLFramebuffer fbo2; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo2); |
| |
| GLRenderbuffer rb20; |
| glBindRenderbuffer(GL_RENDERBUFFER, rb20); |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_R8, 64, 64); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb20); |
| |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| drawQuad(program, "a_position", 0.0f); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| class FragDepthRedeclarationTest : public ANGLETest<> |
| {}; |
| |
| // Test gl_FragDepth redeclared as vertex output |
| TEST_P(FragDepthRedeclarationTest, VSOutput) |
| { |
| constexpr char kVS[] = R"(#version 300 es |
| #extension GL_EXT_conservative_depth: enable |
| out highp float gl_FragDepth; |
| void main() { |
| gl_Position = vec4(0.0, 0.0, 0.0, 1.0); |
| gl_FragDepth = 1.0; |
| })"; |
| |
| GLProgram prg; |
| prg.makeRaster(kVS, essl3_shaders::fs::Red()); |
| EXPECT_FALSE(prg.valid()); |
| } |
| |
| // Test gl_FragDepth redeclared as fragment input |
| TEST_P(FragDepthRedeclarationTest, FSInput) |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| #extension GL_EXT_conservative_depth: enable |
| out highp vec4 color; |
| in highp float gl_FragDepth; |
| void main() { |
| color = vec4(gl_FragDepth, 0.0, 0.0, 1.0); |
| })"; |
| |
| GLProgram prg; |
| prg.makeRaster(essl3_shaders::vs::Simple(), kFS); |
| EXPECT_FALSE(prg.valid()); |
| } |
| |
| // Test gl_FragDepth redeclaration with no layout qualifier |
| TEST_P(FragDepthRedeclarationTest, NoLayout) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_conservative_depth")); |
| |
| constexpr char kFS[] = R"(#version 300 es |
| #extension GL_EXT_conservative_depth: require |
| out highp vec4 color; |
| out highp float gl_FragDepth; |
| void main() { |
| color = vec4(1.0, 0.0, 0.0, 1.0); |
| gl_FragDepth = 1.0; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.0f); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test gl_FragDepth redeclaration with depth_any layout qualifier |
| TEST_P(FragDepthRedeclarationTest, Any) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_conservative_depth")); |
| |
| constexpr char kFS[] = R"(#version 300 es |
| #extension GL_EXT_conservative_depth: require |
| out highp vec4 color; |
| layout (depth_any) out highp float gl_FragDepth; |
| void main() { |
| color = vec4(1.0, 0.0, 0.0, 1.0); |
| gl_FragDepth = 1.0; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.0f); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test gl_FragDepth redeclaration with depth_greater layout qualifier |
| TEST_P(FragDepthRedeclarationTest, Greater) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_conservative_depth")); |
| |
| constexpr char kFS[] = R"(#version 300 es |
| #extension GL_EXT_conservative_depth: require |
| out highp vec4 color; |
| layout (depth_greater) out highp float gl_FragDepth; |
| void main() { |
| color = vec4(1.0, 0.0, 0.0, 1.0); |
| gl_FragDepth = 1.0; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.0f); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test gl_FragDepth redeclaration with depth_less layout qualifier |
| TEST_P(FragDepthRedeclarationTest, Less) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_conservative_depth")); |
| |
| constexpr char kFS[] = R"(#version 300 es |
| #extension GL_EXT_conservative_depth: require |
| out highp vec4 color; |
| layout (depth_less) out highp float gl_FragDepth; |
| void main() { |
| color = vec4(1.0, 0.0, 0.0, 1.0); |
| gl_FragDepth = 1.0; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.0f); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test gl_FragDepth redeclaration with depth_unchanged layout qualifier |
| TEST_P(FragDepthRedeclarationTest, Unchanged) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_conservative_depth")); |
| |
| constexpr char kFS[] = R"(#version 300 es |
| #extension GL_EXT_conservative_depth: require |
| out highp vec4 color; |
| layout (depth_unchanged) out highp float gl_FragDepth; |
| void main() { |
| color = vec4(1.0, 0.0, 0.0, 1.0); |
| gl_FragDepth = 1.0; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.0f); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test gl_FragDepth redeclaration with depth_any layout qualifier and gl_FragCoord |
| TEST_P(FragDepthRedeclarationTest, AnyWithFragCoord) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_conservative_depth")); |
| |
| constexpr char kFS[] = R"(#version 300 es |
| #extension GL_EXT_conservative_depth: require |
| out highp vec4 color; |
| layout (depth_any) out highp float gl_FragDepth; |
| void main() { |
| color = vec4(1.0, 0.0, 0.0, 1.0); |
| gl_FragDepth = cos(gl_FragCoord.z); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.0f); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test gl_FragDepth redeclaration with depth_greater layout qualifier and gl_FragCoord |
| TEST_P(FragDepthRedeclarationTest, GreaterWithFragCoord) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_conservative_depth")); |
| |
| constexpr char kFS[] = R"(#version 300 es |
| #extension GL_EXT_conservative_depth: require |
| out highp vec4 color; |
| layout (depth_greater) out highp float gl_FragDepth; |
| void main() { |
| color = vec4(1.0, 0.0, 0.0, 1.0); |
| gl_FragDepth = gl_FragCoord.z + 0.5; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.0f); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test gl_FragDepth redeclaration with depth_less layout qualifier and gl_FragCoord |
| TEST_P(FragDepthRedeclarationTest, LessWithFragCoord) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_conservative_depth")); |
| |
| constexpr char kFS[] = R"(#version 300 es |
| #extension GL_EXT_conservative_depth: require |
| out highp vec4 color; |
| layout (depth_less) out highp float gl_FragDepth; |
| void main() { |
| color = vec4(1.0, 0.0, 0.0, 1.0); |
| gl_FragDepth = gl_FragCoord.z - 0.5; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.0f); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Test gl_FragDepth redeclaration with depth_unchanged layout qualifier and gl_FragCoord |
| TEST_P(FragDepthRedeclarationTest, UnchangedWithFragCoord) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_conservative_depth")); |
| |
| constexpr char kFS[] = R"(#version 300 es |
| #extension GL_EXT_conservative_depth: require |
| out highp vec4 color; |
| layout (depth_unchanged) out highp float gl_FragDepth; |
| void main() { |
| color = vec4(1.0, 0.0, 0.0, 1.0); |
| gl_FragDepth = gl_FragCoord.z; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.0f); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(FragDepthTest); |
| ANGLE_INSTANTIATE_TEST_ES3(FragDepthTest); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(FragDepthRedeclarationTest); |
| ANGLE_INSTANTIATE_TEST_ES3(FragDepthRedeclarationTest); |