// // Copyright 2024 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. // // Tests for shader interpolation qualifiers // #include "test_utils/ANGLETest.h" #include "test_utils/gl_raii.h" using namespace angle; constexpr int kPixelColorThreshhold = 8; class ShaderOpTest : public ANGLETest<> { protected: ShaderOpTest() : ANGLETest() { setWindowWidth(256); setWindowHeight(256); setConfigRedBits(8); setConfigGreenBits(8); setConfigBlueBits(8); setConfigAlphaBits(8); setConfigDepthBits(24); setConfigStencilBits(8); setMultisampleEnabled(0); } }; // Simplified test from dEQP-GLES2.functional.fragment_ops.interaction.basic_shader.22 // Test that vulkan drivers correctly handle the constant expression spirv generated by ANGLE TEST_P(ShaderOpTest, ConstantExpression) { const char *vertSrc = R"( attribute vec4 pos; void main() { gl_Position = pos; } )"; // ANGLE and glslang translate below expression in fragment shader differently // vec4 c = vec4(1.0, 0.5, 0.5, 0.75) // ANGLE: // %constVec4 = OpConstantComposite %vec4 %const1 %const2 %const2 %const3 // %var = OpVariable %PrivateVec4Pointer Private %constVec4 // %loadedVal = OpLoad %vec4 %var // OpStore %gl_fragColor %loadedVal // glslang: // %constVec4 = OpConstantComposite %vec4 %const1 %const2 %const2 %const3 // %var = OpVariable %PrivateVec4Pointer Private // OpStore %var %constVec4 // %loadedVal = OpLoad %vec4 %var // OpStore %gl_fragColor %loadedVal // Both are valid spirv instructions. // Tests should pass with spirv generated by ANGLE. // Note: setting forceDeferNonConstGlobalInitializers to true will make ANGLE generate the same // spirv instruction for the constant expression as glslang. const char *fragSrc = R"( precision mediump float; vec4 c = vec4(1.0, 0.5, 0.5, 0.75); void main() { gl_FragColor = c; } )"; ANGLE_GL_PROGRAM(program, vertSrc, fragSrc); glUseProgram(program); std::array attribPosData = {1, 1, 0.5, 1, -1, 1, 0.5, 1, 1, -1, 0.5, 1, -1, -1, 0.5, 1}; GLint attribPosLoc = glGetAttribLocation(1, "pos"); ASSERT(attribPosLoc >= 0); glEnableVertexAttribArray(attribPosLoc); glVertexAttribPointer(attribPosLoc, 4, GL_FLOAT, GL_FALSE, 0, attribPosData.data()); const uint16_t indices[] = {0, 1, 2, 2, 1, 3}; glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, &indices[0]); EXPECT_PIXEL_COLOR_NEAR(64, 64, GLColor(255, 125, 125, 190), kPixelColorThreshhold); } // Simplified test from dEQP-GLES2.functional.shaders.random.all_features.fragment.12 // Test that vulkan drivers correctly handle the constant expression spirv generated by ANGLE // Difference beween this test and ConstantExpression test is there is a uniform in fragment shader // in this test, and with this extra element the ANGLE spirv can trigger an error in // vkCreateGraphicsPipelines on some drivers. TEST_P(ShaderOpTest, ConstantExpressionComparedWithUniform) { const char *vertSrc = R"( attribute vec4 pos; void main() { gl_Position = pos; } )"; // ANGLE and glslang translate below expression in fragment shader differently // int k = (5) * (1) // ANGLE: // %k = OpVariable %_ptr_Private_int_0 Private %int_5 // declare Private OpVariable with // initializer // ... some other instructions // %main = OpFunction %void None %62 // %67 = OpLoad %int %k // ... some other instructions // glslang: // %k = OpVariable %_ptr_Private_int_0 Private // declare Private OpVariable without initializer // ... some other instructions // %main = OpFunction %void None %62 // OpStore %k %int_5 // Assign the value to the Private OpVariable in the main function // %67 = OpLoad %int %k // ... some other instructions // Both are valid spirv instructions. // Tests should pass with spirv generated by ANGLE // Note: setting forceDeferNonConstGlobalInitializers to true will make ANGLE generate the same // spirv instruction for the constant expression as glslang. const char *fragSrc = R"( precision mediump float; int k = (5) * (1); uniform mediump int o; void main() { gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); if (o >= k) gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0); } )"; ANGLE_GL_PROGRAM(program, vertSrc, fragSrc); glUseProgram(program); EXPECT_GL_NO_ERROR(); std::array attribPosData = {1, 1, 0.5, 1, -1, 1, 0.5, 1, 1, -1, 0.5, 1, -1, -1, 0.5, 1}; GLint attribPosLoc = glGetAttribLocation(1, "pos"); ASSERT(attribPosLoc >= 0); glEnableVertexAttribArray(attribPosLoc); glVertexAttribPointer(attribPosLoc, 4, GL_FLOAT, GL_FALSE, 0, attribPosData.data()); EXPECT_GL_NO_ERROR(); GLint uniformOLocation = glGetUniformLocation(program, "o"); ASSERT(uniformOLocation >= 0); GLint uniformOValue = 4; glUniform1i(uniformOLocation, uniformOValue); const uint16_t indices[] = {0, 1, 2, 2, 1, 3}; glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, &indices[0]); EXPECT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_NEAR(64, 64, GLColor(255, 255, 255, 255), kPixelColorThreshhold); } GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ShaderOpTest); ANGLE_INSTANTIATE_TEST_ES2(ShaderOpTest);