| // |
| // 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. |
| // |
| |
| // ShadingRateEXTTest.cpp : Tests of the GL_EXT_fragment_shading_rate extension. |
| |
| #ifdef UNSAFE_BUFFERS_BUILD |
| # pragma allow_unsafe_buffers |
| #endif |
| |
| #include "test_utils/ANGLETest.h" |
| |
| #include "test_utils/gl_raii.h" |
| |
| namespace angle |
| { |
| |
| class ShadingRateEXTTest : public ANGLETest<> |
| { |
| protected: |
| ShadingRateEXTTest() |
| { |
| setWindowWidth(256); |
| setWindowHeight(256); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| } |
| }; |
| |
| constexpr char kSimpleShadingRateVS[] = R"(#version 310 es |
| in vec4 a_position; |
| void main() |
| { |
| gl_Position = a_position; |
| })"; |
| |
| constexpr char kSimplePrimitiveShadingRateVS[] = R"(#version 310 es |
| #extension GL_EXT_fragment_shading_rate : require |
| in vec4 a_position; |
| void main() |
| { |
| gl_Position = a_position; |
| gl_PrimitiveShadingRateEXT = gl_ShadingRateFlag2VerticalPixelsEXT | gl_ShadingRateFlag2HorizontalPixelsEXT; |
| })"; |
| |
| constexpr char kSimplePrimitiveShadingRateGS[] = R"(#version 310 es |
| #extension GL_EXT_geometry_shader : require |
| #extension GL_EXT_fragment_shading_rate : require |
| layout (triangles) in; |
| layout (triangle_strip, max_vertices = 3) out; |
| void main() |
| { |
| gl_PrimitiveShadingRateEXT = gl_ShadingRateFlag2VerticalPixelsEXT | gl_ShadingRateFlag2HorizontalPixelsEXT; |
| for (int i = 0; i < 3; i++) |
| { |
| gl_Position = gl_in[i].gl_Position; |
| EmitVertex(); |
| } |
| EndPrimitive(); |
| })"; |
| |
| constexpr char kSimpleShadingRateFS[] = R"(#version 310 es |
| #extension GL_EXT_fragment_shading_rate : require |
| precision highp float; |
| layout(location = 0) out vec4 fragColor; |
| uniform mediump vec4 u_color; |
| void main() |
| { |
| // Emit red color if ShadingRateEXT == gl_ShadingRateFlag2VerticalPixelsEXT | gl_ShadingRateFlag2HorizontalPixelsEXT |
| if (gl_ShadingRateEXT == 5) { |
| fragColor = vec4(1.0, 0.0, 0.0, 1.0); // red |
| } else if (gl_ShadingRateEXT == 0) { |
| fragColor = u_color; |
| }else { |
| fragColor = vec4(0.0, 1.0, 0.0, 1.0); |
| } |
| })"; |
| |
| constexpr char kSimpleShadingRateUniformColorFS[] = R"(#version 310 es |
| #extension GL_EXT_fragment_shading_rate : require |
| precision highp float; |
| uniform mediump vec4 u_color; |
| layout(location = 0) out vec4 fragColor; |
| void main() |
| { |
| // Emit uniform color if ShadingRateEXT == gl_ShadingRateFlag2VerticalPixelsEXT | gl_ShadingRateFlag2HorizontalPixelsEXT |
| if (gl_ShadingRateEXT == 5) { |
| fragColor = u_color; |
| } else { |
| fragColor = vec4(0.0, 0.0, 0.0, 1.0); |
| } |
| })"; |
| |
| // Test basic functionality of EXT_fragment_shading_rate |
| TEST_P(ShadingRateEXTTest, FragmentShadingRate) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_fragment_shading_rate")); |
| |
| // Verify validate shading rate. |
| GLsizei count = 0; |
| const int maxNum = 9; |
| GLenum shadingRates[maxNum]; |
| glGetFragmentShadingRatesEXT(1, maxNum, &count, shadingRates); |
| ASSERT_GL_NO_ERROR(); |
| |
| for (int i = 0; i < count; i++) |
| { |
| glShadingRateEXT(shadingRates[i]); |
| } |
| ASSERT_GL_NO_ERROR(); |
| |
| glShadingRateEXT(GL_SHADING_RATE_1X1_PIXELS_EXT); |
| glClearColor(0.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| ANGLE_GL_PROGRAM(drawShadingRateProgram, kSimpleShadingRateVS, kSimpleShadingRateFS); |
| glUseProgram(drawShadingRateProgram); |
| |
| // Set and query shading rate. |
| glShadingRateEXT(GL_SHADING_RATE_2X2_PIXELS_EXT); |
| glShadingRateCombinerOpsEXT(GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT, |
| GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT); |
| GLint shadingRate = 0; |
| glGetIntegerv(GL_SHADING_RATE_EXT, &shadingRate); |
| ASSERT(shadingRate == GL_SHADING_RATE_2X2_PIXELS_EXT); |
| |
| // Verify draw call with 2x2 shading rate. |
| drawQuad(drawShadingRateProgram, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| } |
| |
| // Test EXT_fragment_shading_rate and EXT_fragment_shading_rate_primitive state change with Blend |
| TEST_P(ShadingRateEXTTest, FragmentShadingRateBlend) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_fragment_shading_rate")); |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_fragment_shading_rate_primitive")); |
| |
| glClearColor(0.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| // Render red quad with 2x2 shading rate. |
| ANGLE_GL_PROGRAM(drawShadingRateProgram, kSimpleShadingRateVS, |
| kSimpleShadingRateUniformColorFS); |
| glUseProgram(drawShadingRateProgram); |
| GLint colorUniformLocation = glGetUniformLocation(drawShadingRateProgram, "u_color"); |
| ASSERT_NE(colorUniformLocation, -1); |
| glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f); |
| |
| glShadingRateEXT(GL_SHADING_RATE_2X2_PIXELS_EXT); |
| glShadingRateCombinerOpsEXT(GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT, |
| GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT); |
| drawQuad(drawShadingRateProgram, essl1_shaders::PositionAttrib(), 0.5f); |
| |
| // Enable blend |
| glEnable(GL_BLEND); |
| glBlendEquation(GL_FUNC_ADD); |
| glBlendFunc(GL_ONE, GL_ONE); |
| |
| // Render green quad with 2x2 shading rate. |
| ANGLE_GL_PROGRAM(primitiveShadingRateVSProgram, kSimplePrimitiveShadingRateVS, |
| kSimpleShadingRateUniformColorFS); |
| glUseProgram(primitiveShadingRateVSProgram); |
| colorUniformLocation = glGetUniformLocation(primitiveShadingRateVSProgram, "u_color"); |
| ASSERT_NE(colorUniformLocation, -1); |
| glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 0.0f); |
| |
| glShadingRateEXT(GL_SHADING_RATE_1X1_PIXELS_EXT); |
| glShadingRateCombinerOpsEXT(GL_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_EXT, |
| GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT); |
| drawQuad(primitiveShadingRateVSProgram, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Verify if render a yellow quad. |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor(255u, 255u, 0u, 255u)); |
| } |
| |
| // Test basic functionality of EXT_fragment_shading_rate_primitive |
| TEST_P(ShadingRateEXTTest, FragmentShadingRatePrimitive) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_fragment_shading_rate_primitive")); |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| glClearColor(0.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| ANGLE_GL_PROGRAM(drawShadingRateProgram, kSimpleShadingRateVS, kSimpleShadingRateFS); |
| glUseProgram(drawShadingRateProgram); |
| // Set 1x1 shading rate and KEEP combinerOps. |
| glShadingRateEXT(GL_SHADING_RATE_1X1_PIXELS_EXT); |
| glShadingRateCombinerOpsEXT(GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT, |
| GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT); |
| // Verify draw call with 1x1 shading rate. |
| drawQuad(drawShadingRateProgram, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| |
| // Compile PrimitiveShadingRateVS + FS and use this program |
| ANGLE_GL_PROGRAM(primitiveShadingRateVSProgram, kSimplePrimitiveShadingRateVS, |
| kSimpleShadingRateFS); |
| glUseProgram(primitiveShadingRateVSProgram); |
| // Set REPLACE combinerOp0. |
| glShadingRateCombinerOpsEXT(GL_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_EXT, |
| GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT); |
| // Verify draw call with 2x2 primitive shading rate. |
| drawQuad(primitiveShadingRateVSProgram, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| glClear(GL_COLOR_BUFFER_BIT); |
| // Compile VS + PrimitiveShadingRateGS + FS and use this program |
| ANGLE_GL_PROGRAM_WITH_GS(primitiveShadingRateGSProgram, kSimpleShadingRateVS, |
| kSimplePrimitiveShadingRateGS, kSimpleShadingRateFS); |
| glUseProgram(primitiveShadingRateGSProgram); |
| |
| // Verify draw call with 2x2 primitive shading rate with GS. |
| drawQuad(primitiveShadingRateGSProgram, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| // Recover 1x1 shading rate and KEEP combinerOps to verify. |
| glShadingRateEXT(GL_SHADING_RATE_1X1_PIXELS_EXT); |
| glShadingRateCombinerOpsEXT(GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT, |
| GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT); |
| drawQuad(drawShadingRateProgram, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // The negative test of EXT_fragment_shading_rate |
| TEST_P(ShadingRateEXTTest, Error) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_fragment_shading_rate")); |
| |
| glShadingRateEXT(GL_SAMPLE_SHADING); |
| EXPECT_GL_ERROR(GL_INVALID_ENUM); |
| |
| glShadingRateCombinerOpsEXT(GL_SHADING_RATE_EXT, |
| GL_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_EXT); |
| EXPECT_GL_ERROR(GL_INVALID_ENUM); |
| glShadingRateCombinerOpsEXT(GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT, |
| GL_MIN_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_WIDTH_EXT); |
| EXPECT_GL_ERROR(GL_INVALID_ENUM); |
| |
| GLboolean supportNonTrivialCombiner = false; |
| glGetBooleanv(GL_FRAGMENT_SHADING_RATE_NON_TRIVIAL_COMBINERS_SUPPORTED_EXT, |
| &supportNonTrivialCombiner); |
| |
| if (!supportNonTrivialCombiner) |
| { |
| glShadingRateCombinerOpsEXT(GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MIN_EXT, |
| GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| |
| glShadingRateCombinerOpsEXT(GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT, |
| GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_EXT); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| } |
| |
| if (!IsGLExtensionEnabled("GL_EXT_fragment_shading_rate_primitive")) |
| { |
| glShadingRateCombinerOpsEXT(GL_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_EXT, |
| GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| } |
| |
| if (!IsGLExtensionEnabled("GL_EXT_fragment_shading_rate_attachment")) |
| { |
| glShadingRateCombinerOpsEXT(GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT, |
| GL_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_EXT); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| } |
| } |
| |
| // Test that when FETCH_PER_SAMPLE_ARM is enabled, the fragment shading rate |
| // is set to GL_SHADING_RATE_1X1_PIXELS_EXT. |
| TEST_P(ShadingRateEXTTest, ShadingRateFramebufferFetchPerSample) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_fragment_shading_rate")); |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch")); |
| |
| glShadingRateEXT(GL_SHADING_RATE_2X2_PIXELS_EXT); |
| ASSERT_GL_NO_ERROR(); |
| |
| ANGLE_GL_PROGRAM(program, kSimpleShadingRateVS, kSimpleShadingRateFS); |
| glUseProgram(program); |
| |
| GLint colorLoc = glGetUniformLocation(program, "u_color"); |
| ASSERT_NE(colorLoc, -1); |
| |
| glClearColor(0, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_ONE, GL_ONE); |
| |
| glEnable(GL_FETCH_PER_SAMPLE_ARM); |
| glUniform4f(colorLoc, 0, 0, 1, 0); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| |
| glDisable(GL_FETCH_PER_SAMPLE_ARM); |
| glUniform4f(colorLoc, 0, 0, 0, 1); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| |
| glEnable(GL_FETCH_PER_SAMPLE_ARM); |
| glUniform4f(colorLoc, 0, 0.5, 0, 0); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(255, 128, 255, 255), 1); |
| } |
| |
| // Use this to select which configurations (e.g. which renderer, which GLES major version) these |
| // tests should be run against. |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ShadingRateEXTTest); |
| ANGLE_INSTANTIATE_TEST_ES31(ShadingRateEXTTest); |
| |
| } // namespace angle |