diff --git a/src/libANGLE/renderer/vulkan/spv_utils.cpp b/src/libANGLE/renderer/vulkan/spv_utils.cpp index 49bf7dd571..42cd2442f8 100644 --- a/src/libANGLE/renderer/vulkan/spv_utils.cpp +++ b/src/libANGLE/renderer/vulkan/spv_utils.cpp @@ -6164,6 +6164,73 @@ angle::Result SpvTransformSpirvCode(const SpvTransformOptions &options, ASSERT(spirv::Validate(*spirvBlobOut)); } + // --------------------------------------------------------------------------------- + // POC diagnostic instrumentation (not part of production code). + // + // In release builds the ASSERT(spirv::Validate(...)) above is a no-op, so the + // VUID-violating SPIR-V (two Input variables decorated BuiltIn FragCoord) would be + // handed straight to vkCreateShaderModule on the production driver. To make the bug + // observable on a Linux release build we unconditionally count BuiltIn FragCoord + // decorations in the transformed module and abort with a diagnostic if more than + // one is found. This does NOT change whether the bug fires; it only makes it visible. + // --------------------------------------------------------------------------------- + if (options.ditherControl != 0) + { + size_t off = spirv::kHeaderIndexInstructions; + int fragCoordBuiltIns = 0; + uint32_t fragCoordIds[8] = {}; + while (off < spirvBlobOut->size()) + { + const uint32_t word = (*spirvBlobOut)[off]; + const uint32_t wc = word >> 16; + const uint32_t opcode = word & 0xFFFFu; + if (wc == 0) + { + break; + } + // OpDecorate BuiltIn FragCoord -> [opcode=71 wc=4] 11 15 + if (opcode == spv::OpDecorate && wc == 4 && + (*spirvBlobOut)[off + 2] == spv::DecorationBuiltIn && + (*spirvBlobOut)[off + 3] == spv::BuiltInFragCoord) + { + if (fragCoordBuiltIns < 8) + { + fragCoordIds[fragCoordBuiltIns] = (*spirvBlobOut)[off + 1]; + } + ++fragCoordBuiltIns; + } + off += wc; + } + if (fragCoordBuiltIns > 1) + { + fprintf(stderr, + "\n========================================================================\n" + "POC: SpvTransformSpirvCode emitted %d 'OpDecorate BuiltIn FragCoord'\n" + "decorations on distinct Input variables. This violates\n" + "VUID-StandaloneSpirv-OpEntryPoint-08721. In production this blob would\n" + "be passed directly to vkCreateShaderModule on the unsandboxed Android\n" + "GPU process.\n" + "------------------------------------------------------------------------\n" + " OpDecorate %%%u BuiltIn FragCoord (reserved kIdFragCoord=%u)\n" + " OpDecorate %%%u BuiltIn FragCoord (translator-emitted generic id)\n" + "------------------------------------------------------------------------\n" + "Full disassembly of the transformed module follows:\n" + "========================================================================\n", + fragCoordBuiltIns, fragCoordIds[0], + static_cast(sh::vk::spirv::kIdFragCoord), fragCoordIds[1]); + // Dump the transformed binary so it can be inspected with spirv-dis / spirv-val. + if (FILE *f = fopen("/tmp/poc-transformed.spv", "wb")) + { + fwrite(spirvBlobOut->data(), sizeof(uint32_t), spirvBlobOut->size(), f); + fclose(f); + fprintf(stderr, "Transformed SPIR-V dumped to /tmp/poc-transformed.spv\n"); + } + spirv::Print(*spirvBlobOut); + fflush(stderr); + abort(); + } + } + return angle::Result::Continue; } } // namespace rx diff --git a/src/tests/gl_tests/SixteenBppTextureTest.cpp b/src/tests/gl_tests/SixteenBppTextureTest.cpp index 71a8baca57..d84c4988a3 100644 --- a/src/tests/gl_tests/SixteenBppTextureTest.cpp +++ b/src/tests/gl_tests/SixteenBppTextureTest.cpp @@ -933,12 +933,61 @@ TEST_P(SixteenBppTextureDitheringTestES3, SpvEmulation) spvEmulationTest(); } +// Regression test: a fragment shader that references gl_PointCoord lexically +// before gl_FragCoord must not cause the dither-emulation transform to emit a +// second BuiltIn FragCoord variable. +TEST_P(SixteenBppTextureDitheringTestES3, PointCoordBeforeFragCoord) +{ + ANGLE_SKIP_TEST_IF(!getEGLWindow()->isFeatureEnabled(Feature::EmulateDithering)); + + constexpr char kVS[] = R"(#version 300 es +void main() +{ + int bit0 = gl_VertexID & 1; + int bit1 = gl_VertexID >> 1; + gl_Position = vec4(bit0 * 2 - 1, bit1 * 2 - 1, 0, 1); + gl_PointSize = 1.0; +})"; + + // gl_PointCoord is referenced first; gl_FragCoord second. + constexpr char kFS[] = R"(#version 300 es +precision mediump float; +out vec4 colorOut; +void main() +{ + vec2 p = gl_PointCoord; + colorOut = vec4(p, gl_FragCoord.x * 0.001, 1.0); +})"; + + ANGLE_GL_PROGRAM(program, kVS, kFS); + + int w = getWindowWidth(); + int h = getWindowHeight(); + + GLFramebuffer fbo; + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + GLTexture tex4444; + glBindTexture(GL_TEXTURE_2D, tex4444); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA4, w, h); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex4444, 0); + ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); + + glViewport(0, 0, w, h); + glEnable(GL_DITHER); + glUseProgram(program); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + ASSERT_GL_NO_ERROR(); +} + ANGLE_INSTANTIATE_TEST_ES2(SixteenBppTextureTest); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SixteenBppTextureTestES3); ANGLE_INSTANTIATE_TEST_ES3(SixteenBppTextureTestES3); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SixteenBppTextureDitheringTestES3); -ANGLE_INSTANTIATE_TEST_ES3(SixteenBppTextureDitheringTestES3); +ANGLE_INSTANTIATE_TEST_ES3_AND(SixteenBppTextureDitheringTestES3, + ES3_VULKAN_SWIFTSHADER().enable(Feature::EmulateDithering), + ES3_VULKAN().enable(Feature::EmulateDithering)); } // namespace