From 3a40778c4b6680101b3beb05f23af1713ef511c8 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Thu, 16 Jul 2026 01:50:33 -0400 Subject: [PATCH] [Fix] (MG_Util/ShaderTranspiler): stop stripping precision qualifiers - the strip corrupted "precision highp float;" into invalid syntax; glslang accepts and ignores them natively in 460 core (unblocks ~2000 GL CTS cases per backend) --- MobileGL/MG_Test/Program/ProgramUtilTest.cpp | 80 +++++++++++++++++-- .../ShaderSourceProcessor.cpp | 11 +-- 2 files changed, 77 insertions(+), 14 deletions(-) diff --git a/MobileGL/MG_Test/Program/ProgramUtilTest.cpp b/MobileGL/MG_Test/Program/ProgramUtilTest.cpp index ef645cac..652826f6 100644 --- a/MobileGL/MG_Test/Program/ProgramUtilTest.cpp +++ b/MobileGL/MG_Test/Program/ProgramUtilTest.cpp @@ -112,8 +112,14 @@ void main() { EXPECT_NE(source.find("out vec2 uv;"), String::npos); EXPECT_EQ(source.find("attribute"), String::npos); EXPECT_EQ(source.find("varying"), String::npos); - EXPECT_EQ(source.find("HIGHP_OR_DEFAULT"), String::npos); - EXPECT_EQ(source.find("#define"), String::npos); + // Precision-qualifier macros are left for glslang's own preprocessor to expand. + EXPECT_NE(source.find("#define HIGHP_OR_DEFAULT highp"), String::npos); + + ShaderAttrib attrib{.shaderType = GL_VERTEX_SHADER, .sourceStr = source}; + auto res = ShaderCompiler::CompileShader(attrib); + if (!res) { + FAIL() << "errc: " << res.error().errc << "\nlog: " << res.error().log << "\nsource:\n" << source; + } } TEST_F(ProgramUtilTest, PreprocessLegacyFragmentShaderModernizesGlmarkStyleSource) { @@ -137,9 +143,14 @@ void main() { EXPECT_NE(source.find("mg_FragColor = color;"), String::npos); EXPECT_EQ(source.find("gl_FragColor"), String::npos); EXPECT_EQ(source.find("texture2D"), String::npos); - EXPECT_EQ(source.find("MEDIUMP_OR_DEFAULT"), String::npos); - EXPECT_EQ(source.find("mediump"), String::npos); - EXPECT_EQ(source.find("#define"), String::npos); + // Precision-qualifier macros are left for glslang's own preprocessor to expand. + EXPECT_NE(source.find("#define MEDIUMP_OR_DEFAULT mediump"), String::npos); + + ShaderAttrib attrib{.shaderType = GL_FRAGMENT_SHADER, .sourceStr = source}; + auto res = ShaderCompiler::CompileShader(attrib); + if (!res) { + FAIL() << "errc: " << res.error().errc << "\nlog: " << res.error().log << "\nsource:\n" << source; + } } TEST_F(ProgramUtilTest, PreprocessLegacyFragmentShaderModernizesFragData) { @@ -166,6 +177,65 @@ void main() { } } +TEST_F(ProgramUtilTest, PreprocessKeepsDefaultPrecisionStatements) { + using namespace MG_Util::ShaderTranspiler; + + // Mirrors the GL CTS helper shaders (e.g. glcPixelStorageModesTests): the old qualifier strip + // turned "precision highp float;" into invalid "precision float;". Precision qualifiers are + // legal (and ignored) in the forced 460 core profile, so they now pass through untouched. + String source = R"(#version 330 +precision highp float; +precision mediump int; +out vec4 fragColor; +uniform highp sampler2D tex; + +void main() { + highp vec2 uv = vec2(0.5); + fragColor = texture(tex, uv); +})"; + + PreprocessShaderSource(ShaderStage::Fragment, source); + + EXPECT_NE(source.find("precision highp float;"), String::npos); + EXPECT_NE(source.find("precision mediump int;"), String::npos); + EXPECT_NE(source.find("uniform highp sampler2D tex;"), String::npos); + EXPECT_NE(source.find("fragColor = texture(tex, uv);"), String::npos); + + ShaderAttrib attrib{.shaderType = GL_FRAGMENT_SHADER, .sourceStr = source}; + auto res = ShaderCompiler::CompileShader(attrib); + if (!res) { + FAIL() << "errc: " << res.error().errc << "\nlog: " << res.error().log << "\nsource:\n" << source; + } +} + +TEST_F(ProgramUtilTest, PreprocessKeepsPrecisionInLegacyShaderForGlslang) { + using namespace MG_Util::ShaderTranspiler; + + // Legacy ES-style shader: precision statements and qualifier macros are left for glslang + // (its preprocessor expands the #define; the 460 core parse ignores the qualifiers). + String source = R"(#define HIGHP_OR_DEFAULT highp +precision HIGHP_OR_DEFAULT float; +precision mediump int; +varying vec2 uv; + +void main() { + mediump float shade = uv.x; + gl_FragColor = vec4(uv, shade, 1.0); +})"; + + PreprocessShaderSource(ShaderStage::Fragment, source); + + EXPECT_NE(source.find("precision HIGHP_OR_DEFAULT float;"), String::npos); + EXPECT_NE(source.find("precision mediump int;"), String::npos); + EXPECT_NE(source.find("in vec2 uv;"), String::npos); + + ShaderAttrib attrib{.shaderType = GL_FRAGMENT_SHADER, .sourceStr = source}; + auto res = ShaderCompiler::CompileShader(attrib); + if (!res) { + FAIL() << "errc: " << res.error().errc << "\nlog: " << res.error().log << "\nsource:\n" << source; + } +} + TEST_F(ProgramUtilTest, PreprocessFragmentShaderInjectsDepthRangeShim) { using namespace MG_Util::ShaderTranspiler; diff --git a/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp b/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp index 2cf8dcb2..43c2d5aa 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp +++ b/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp @@ -262,15 +262,8 @@ namespace { } void ModernizeLegacyGLSL(MobileGL::ShaderStage stage, MobileGL::String& source) { - RemoveDefineForIdentifier(source, "HIGHP_OR_DEFAULT"); - RemoveDefineForIdentifier(source, "MEDIUMP_OR_DEFAULT"); - RemoveDefineForIdentifier(source, "LOWP_OR_DEFAULT"); - ReplaceIdentifier(source, "HIGHP_OR_DEFAULT", ""); - ReplaceIdentifier(source, "MEDIUMP_OR_DEFAULT", ""); - ReplaceIdentifier(source, "LOWP_OR_DEFAULT", ""); - ReplaceIdentifier(source, "highp", ""); - ReplaceIdentifier(source, "mediump", ""); - ReplaceIdentifier(source, "lowp", ""); + // Precision qualifiers (highp/mediump/lowp and default-precision statements) are legal and + // ignored in the forced "#version 460 core" profile, so glslang handles them natively. ReplaceIdentifier(source, "texture2D", "texture"); ReplaceIdentifier(source, "texture2DProj", "textureProj");