From a4dcdf989ea5058ac072ef3a50374fa016324a18 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Fri, 28 Aug 2026 06:06:11 -0400 Subject: [PATCH] [Fix] (ShaderTranspiler): stop the point-size carrier landing on a location a live varying owns, and decline the evaluation stage a synthesized pass-through control stage cannot feed - an i64vec4 counted as one location, and a located input carrier trips both backends' pass-through guard --- .../ShaderTranspiler/ShaderCompiler.cpp | 30 +++++++++++++++++-- .../MG_Util/ShaderTranspiler/ShaderCompiler.h | 8 +++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp index 17a13cd5..49d8877a 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp +++ b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp @@ -832,8 +832,17 @@ namespace MobileGL { return 1u; } if (const auto* vector = type->AsVector()) { - const auto* elementFloat = vector->element_type()->AsFloat(); - const Bool is64Bit = elementFloat != nullptr && elementFloat->width() == 64; + // 64-bit INTEGER elements count exactly like 64-bit floats: + // ARB_gpu_shader_int64 extends 11.1.2.1's double-precision rule + // verbatim to i64/u64, and DirectVulkan advertises that extension + // unconditionally - so answering "one location" for an i64vec4 would + // place the carrier on the SECOND location that varying already owns, + // which is the underestimate this function's header forbids. + const auto* element = vector->element_type(); + const auto* elementFloat = element->AsFloat(); + const auto* elementInteger = element->AsInteger(); + const Bool is64Bit = (elementFloat != nullptr && elementFloat->width() == 64) || + (elementInteger != nullptr && elementInteger->width() == 64); return (is64Bit && vector->element_count() > 2) ? 2u : 1u; } if (const auto* matrix = type->AsMatrix()) { @@ -1096,6 +1105,23 @@ namespace MobileGL { outcome.declineDetail = Move(report.declineReason); return true; // byte-identical decline; the existing refusals stay armed } + // AN EVALUATION STAGE WITH NO CONTROL STAGE THAT NOW READS A LOCATED + // INPUT. GL lets the evaluation stage sit straight on the vertex stage, + // and both backends stand a SYNTHESIZED pass-through control stage in + // between - one that forwards gl_Position and nothing else. Their guard + // for that is literally "does this module read a located input" + // (ModuleReadsLocatedInput / ReflectPassthroughTessControlNeed), so the + // carrier this pass just created would turn the very program the demotion + // exists to rescue into a declined one, reported against a varying name + // the application never wrote. Declining here keeps the modules + // byte-identical and leaves the honest built-in refusal in charge; only + // teaching the synthesized stage to forward the carrier could do better. + if (stage == 2 && stageIndex[1] < 0 && report.createdInputCarrier) { + outcome.declineDetail = + "an evaluation stage reads gl_in point size with no control stage to " + "carry it; the synthesized pass-through cannot forward the carrier"; + return true; + } rewrote[stage] = true; if (report.createdInputCarrier && producer >= 0) { forceOutput[producer] = true; diff --git a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.h b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.h index d921022f..efe3225e 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.h +++ b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.h @@ -603,6 +603,14 @@ namespace MobileGL { // the optimizer itself failed (modules untouched); a shape decline is // reported through `outcome` and also leaves the modules untouched. See // DemotePointSizePass for the per-module rewrite and its honest residue. + // + // Two declines are PROGRAM-shaped and therefore live here rather than in the + // pass: a carrier that would land past the minimum-spec varying budget, and + // an evaluation stage reading gl_in point size with NO control stage - the + // synthesized pass-through control stage both backends stand in that gap + // forwards gl_Position alone, so the input carrier would strand the value and + // trip the backends' own "reads a located input" refusal against a name the + // application never wrote. static Bool DemoteTessellationGeometryPointSizeForProgram( Vector>& modules, const Vector& shaderTypes, Bool demoteTessellation, Bool demoteGeometry, Bool captureRequestsPointSize,