blob: e4fa7a3424ba11fc4958e1d1622814d86872fe04 [file] [log] [blame]
//
// 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);