[Fix] (ShaderTranspiler): keep declared modern GLSL versions strict

Normalization rewrote every desktop core #version below 400 to 330 (and
400+ to 460), and a failed parse was retried at 460. Together these erased
the declared version's rules: KHR-GL33 negative-compile cases (reserved
names, parenthesized layout-qualifier values in a declared-420 shader,
GLSL 4.5 mix() overloads at 330, precise in struct members) all compiled.

Explicitly declared core versions >= 330 now keep their number, and the
460 retry only fires for sources whose directive carries the normalizer's
own legacy marker - i.e. shaders that declared 110-150 (or nothing), which
is the shader-pack compatibility case the retry exists for. Replaces the
narrower arrays-of-arrays special case.
This commit is contained in:
BZLZHH
2026-07-31 16:43:27 -04:00
parent 45d506545e
commit 94a8f1e3f3
2 changed files with 39 additions and 13 deletions
@@ -220,18 +220,14 @@ namespace MobileGL {
auto result = ParseShaderSource(lang, shaderType, source, attrib.flags);
if (result) return result;
// Legacy desktop sources are normalized to "#version 330 core", which parses under
// stricter rules than the 460 they used to be forced to: a shader declaring 330 while
// using e.g. layout(binding=...) without the matching #extension line compiles on real
// drivers but is rejected here. Retry once at 460 before reporting failure; a genuinely
// broken shader fails both attempts and keeps its original diagnostics.
//
// Arrays of arrays are the exception: every desktop driver rejects them below 430
// (the GL33 CTS requires the compile failure), so re-legalizing them at 460 would
// trade a conformance failure for no real-world shader gain.
if (result.error().log.find("arrays of arrays") != String::npos) {
return result;
}
// Legacy desktop sources are normalized to "#version 330 core" (with a marker on the
// directive), which parses under stricter rules than the 460 they used to be forced
// to: a shader declaring 110-150 while using e.g. layout(binding=...) without the
// matching #extension line compiles on real drivers but is rejected here. Retry once
// at 460 before reporting failure; a genuinely broken shader fails both attempts and
// keeps its original diagnostics. Application-declared 330+ sources carry no marker
// and keep their declared version's strict rules (the GL CTS negative-compile cases
// depend on that).
String retrySource = source;
if (!MG_Util::ShaderTranspiler::RetargetLegacyVersionDirectiveTo460(retrySource)) {
return result;
@@ -688,6 +688,10 @@ namespace {
return info;
}
// Stamped onto the normalized directive when a legacy (or absent) desktop
// version was rewritten to 330; consumed by RetargetLegacyVersionDirectiveTo460.
constexpr const char* kNormalizedLegacyMarker = "/*mobilegl-normalized-legacy*/";
MobileGL::String GetNormalizedVersionDirective(const ShaderLanguageInfo& info) {
if (info.profile == MobileGL::ShaderProfile::ES) {
// Preserve the pre-existing behavior for standard lowercase "es" directives. MobileGL's Vulkan
@@ -702,9 +706,23 @@ namespace {
return "#version 460 compatibility\n";
}
// An explicitly declared modern core version keeps its number: the GL CTS
// negative-compile cases (reserved names, layout-qualifier forms, missing
// overloads) rely on the declared version's rules, and raising it would
// silently legalize them. gpu_shader5 opt-ins keep the 460 escalation -
// Vulkan glslang's ARB_gpu_shader5 support is not complete enough alone.
if (info.hasValidVersionDirective && info.version >= 330 && !info.enablesGpuShader5) {
return "#version " + std::to_string(info.version) + " core\n";
}
const bool useLegacyDesktopVersion =
info.version < 400 && !info.enablesGpuShader5;
return useLegacyDesktopVersion ? "#version 330 core\n" : "#version 460 core\n";
// The trailing marker records that this 330 came from a legacy declaration
// (or none at all), so the compile-failure retry may re-raise it to 460.
// An application's own "#version 330" never carries it and keeps strict
// 3.30 semantics.
return useLegacyDesktopVersion ? MobileGL::String("#version 330 core ") + kNormalizedLegacyMarker + "\n"
: "#version 460 core\n";
}
void NormalizeVersionDirective(MobileGL::String& source, const ShaderLanguageInfo& info) {
@@ -1306,6 +1324,18 @@ namespace MobileGL {
// Only the set NormalizeVersionDirective downgraded: desktop core below 400. ES and
// compatibility shaders keep whatever they declared.
if (info.profile != ShaderProfile::Core || info.version >= 400) return false;
// Only rescue MobileGL's own legacy normalization (marked on the directive line).
// An application-declared "#version 330" keeps strict 3.30 semantics: raising it
// would re-legalize the CTS negative-compile cases (reserved names, arrays of
// arrays, missing overloads).
SizeT lineEnd = source.find('\n', info.versionDirectiveStart);
if (lineEnd == MobileGL::String::npos) {
lineEnd = source.size();
}
const SizeT markerPos = source.find(kNormalizedLegacyMarker, info.versionDirectiveStart);
if (markerPos == MobileGL::String::npos || markerPos > lineEnd) {
return false;
}
source.replace(info.versionDirectiveStart, info.versionDirectiveEnd - info.versionDirectiveStart,
"#version 460 core\n");