From 80ea44573eec0b307f114737fb2e547f392851c7 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Fri, 21 Aug 2026 03:34:27 -0400 Subject: [PATCH] [Test] (ShaderTranspiler): pin the ESSL a widened image module emits, on both SPIRV-Cross failure modes --- .../WidenImageFormatsTest.cpp | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/MobileGL/MG_Test/ShaderTranspiler/WidenImageFormatsTest.cpp b/MobileGL/MG_Test/ShaderTranspiler/WidenImageFormatsTest.cpp index 294edd6a..29e143cc 100644 --- a/MobileGL/MG_Test/ShaderTranspiler/WidenImageFormatsTest.cpp +++ b/MobileGL/MG_Test/ShaderTranspiler/WidenImageFormatsTest.cpp @@ -31,6 +31,7 @@ #include "Includes.h" #include "Init.h" #include +#include #include #include @@ -163,6 +164,35 @@ namespace { return nullptr; } + // The ESSL SPIRV-Cross emits for a module, or the error it refused with - which is the whole + // point for the formats in its is_desktop_only_format set: it THROWS rather than printing a + // token, and the throw takes the stage with it. + struct EsslAttempt { + Bool succeeded = false; + String text; + String error; + }; + + EsslAttempt EmitEssl(const Vector& spirv) { + using namespace MobileGL::MG_Util::ShaderTranspiler; + EsslAttempt attempt; + SpvcSession session(spirv, SessionUsageBit::Transpile); + spvc_compiler_options options; + if (session.CreateOptions(&options) != SPVC_SUCCESS) return attempt; + spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_GLSL_VERSION, 320); + spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_ES, SPVC_TRUE); + spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS, SPVC_FALSE); + if (session.SetOptions(options) != SPVC_SUCCESS) return attempt; + auto essl = ShaderCompiler::DecompileShader(session); + if (!essl) { + attempt.error = essl.error().log; + return attempt; + } + attempt.succeeded = true; + attempt.text = *essl; + return attempt; + } + // rg32f: two float channels, and the entry the four CTS allFormats walkers abort on. Both an // imageLoad and an imageStore, so both masks are exercised on one image. const char* const kRg32fLoadStore = R"(#version 430 core @@ -357,6 +387,54 @@ TEST(WidenImageFormats, SingleChannelUnsignedImageBecomesRgba8uiWithBothAccesses EXPECT_TRUE(HasComponents(*loadMask, {0u, 5u, 6u, 7u})); } +// The point of the whole exercise, end to end: what reaches the ES driver. +// +// r8ui is in SPIRV-Cross's is_desktop_only_format set, so for an ESSL target it THROWS instead of +// printing a token and no text is produced at all - which is what +// KHR-GL43.shader_image_load_store.single-byte_data_alignment hit ("Attempting to use image format +// not supported in ES profile"), leaving a program that linked and drew nothing. rg32f is the +// other failure mode: SPIRV-Cross prints it happily and the DRIVER rejects it ("'rg32f' : not a +// legal layout qualifier id"). After the widening both come out naming a core format, which is the +// only thing on either side that makes the stage compilable. +TEST(WidenImageFormats, WidenedModulesEmitEsslNamingTheCoreCarrier) { + { + const Vector spirv = CompileFragment(kR8uiLoadStore); + ASSERT_FALSE(spirv.empty()); + const EsslAttempt before = EmitEssl(spirv); + EXPECT_FALSE(before.succeeded) + << "SPIRV-Cross printed r8ui for an ES target; the widening's premise has changed:\n" + << before.text; + + Vector widened; + ASSERT_TRUE(ShaderCompiler::WidenImageFormatsForEssl(spirv, widened, true)); + const EsslAttempt after = EmitEssl(widened); + ASSERT_TRUE(after.succeeded) << after.error; + EXPECT_NE(after.text.find("rgba8ui"), String::npos) << after.text; + // "rgba8ui" does not contain "r8ui", so this is a clean negative. + EXPECT_EQ(after.text.find("r8ui"), String::npos) << after.text; + // GL reads a one-channel image as (r, 0, 0, 1) and drops everything past r on a store, so + // both accesses have to be spelled that way whatever the carrier holds. + EXPECT_NE(after.text.find("uvec4(0u, 0u, 0u, 1u)"), String::npos) << after.text; + } + { + const Vector spirv = CompileFragment(kRg32fLoadStore); + ASSERT_FALSE(spirv.empty()); + // This one SPIRV-Cross does print - the token is simply not one GLSL ES has. + const EsslAttempt before = EmitEssl(spirv); + ASSERT_TRUE(before.succeeded) << before.error; + EXPECT_NE(before.text.find("rg32f"), String::npos) << before.text; + + Vector widened; + ASSERT_TRUE(ShaderCompiler::WidenImageFormatsForEssl(spirv, widened, true)); + const EsslAttempt after = EmitEssl(widened); + ASSERT_TRUE(after.succeeded) << after.error; + EXPECT_NE(after.text.find("rgba32f"), String::npos) << after.text; + EXPECT_EQ(after.text.find("rg32f"), String::npos) + << "the token no ES driver accepts is still in the emitted source:\n" + << after.text; + } +} + TEST(WidenImageFormats, CoreFormatModuleIsHandedBackUntouched) { const Vector spirv = CompileFragment(kCoreFormatLoadStore); ASSERT_FALSE(spirv.empty());