GS/VK: emit the driver-workaround shader wrappers as defines when unused

LEGO Batman (SLUS-21785) crashed on the Vulkan renderer moments after the
BIOS, on an Adreno 740 running driver 512.676.53. Qualcomm's SPIR-V compiler
took a SIGSEGV inside CreateQGLCProgram building a TFX pipeline; our signal
handler turned that into a SIGABRT on the GS thread, so it presented as an
emulator abort rather than a driver fault. The same build ran the game
correctly on OpenGL, which never goes through SPIR-V.

gpu_bitwise_and and gpu_matrix_element were emitted as real functions on
every driver. Where no workaround applies their bodies are just 'a & b' and
'value[column][row]', so the shader means exactly what it did before -- but
each of the ~12 call sites, including one inside the texture loop and three
in the region-clamp path, now costs an OpFunctionCall in the SPIR-V, and
that shape is what the compiler falls over on.

They are #defines now unless their workaround is active, so a driver the
database has no rule for gets the same SPIR-V it had at 108. That is what
introducing the wrappers claimed -- 'the generated SPIR-V is unchanged on any
driver the database has no rule for' -- and it was not true, because the
wrapper went out unconditionally. Mali and PowerVR keep the real bodies.

Every macro argument is parenthesised: the dither lookup passes
'fpos.y & 3', which needs the parens to survive the subscript.

SHADER_CACHE_VERSION 109 -> 110; the shader text changed again.

Diagnosed with jpolo1224 from an on-device tombstone.
This commit is contained in:
jpolo1224
2026-07-27 01:05:23 -04:00
parent 88bc78bc13
commit bce46f874f
2 changed files with 25 additions and 21 deletions
+20 -20
View File
@@ -4919,46 +4919,46 @@ static void AddShaderHeader(std::stringstream& ss)
dev->UsesMobileDriverWorkaround(DriverWorkaround::ScalarizeVectorBitwiseAnd) ? 1 : 0);
AddMacro(ss, "DRIVER_REWRITE_UNIFORM_INDEXING",
dev->UsesMobileDriverWorkaround(DriverWorkaround::RewriteUniformIndexing) ? 1 : 0);
// When no workaround is active these MUST expand to the bare operator, not to a function that
// happens to return it. Overloads cost an OpFunctionCall in the SPIR-V at every call site --
// including inside the texture loop in tfx.glsl and the region-clamp path -- and Qualcomm's
// SPIR-V compiler segfaults building a TFX pipeline from that shape (LEGO Batman, Adreno 740,
// driver 512.676.53: SIGSEGV inside CreateQGLCProgram, chained to SIGABRT on the GS thread).
// OpenGL is unaffected because it hands GLSL straight to the driver and never goes through
// SPIR-V, which is why the same build renders that game fine on the GL renderer.
//
// This also makes good on what the wrappers were introduced promising -- that a driver the
// database has no rule for gets unchanged SPIR-V. It did not hold: the function wrapper was
// emitted unconditionally, so EVERY driver got new shader structure to please the two that
// needed it.
ss << R"(
#if DRIVER_SCALARIZE_VECTOR_BITWISE_AND
uvec2 gpu_bitwise_and(uvec2 a, uvec2 b)
{
#if DRIVER_SCALARIZE_VECTOR_BITWISE_AND
return uvec2(a.x & b.x, a.y & b.y);
#else
return a & b;
#endif
}
uvec3 gpu_bitwise_and(uvec3 a, uvec3 b)
{
#if DRIVER_SCALARIZE_VECTOR_BITWISE_AND
return uvec3(a.x & b.x, a.y & b.y, a.z & b.z);
#else
return a & b;
#endif
}
uvec4 gpu_bitwise_and(uvec4 a, uvec4 b)
{
#if DRIVER_SCALARIZE_VECTOR_BITWISE_AND
return uvec4(a.x & b.x, a.y & b.y, a.z & b.z, a.w & b.w);
#else
return a & b;
#endif
}
ivec3 gpu_bitwise_and(ivec3 a, ivec3 b)
{
#if DRIVER_SCALARIZE_VECTOR_BITWISE_AND
return ivec3(a.x & b.x, a.y & b.y, a.z & b.z);
#else
return a & b;
#endif
}
#else
#define gpu_bitwise_and(a, b) ((a) & (b))
#endif
#if DRIVER_REWRITE_UNIFORM_INDEXING
float gpu_matrix_element(mat4 value, int column, int row)
{
#if DRIVER_REWRITE_UNIFORM_INDEXING
vec4 selected_column;
if (column == 0)
selected_column = value[0];
@@ -4976,10 +4976,10 @@ float gpu_matrix_element(mat4 value, int column, int row)
if (row == 2)
return selected_column[2];
return selected_column[3];
#else
return value[column][row];
#endif
}
#else
#define gpu_matrix_element(value, column, row) ((value)[(column)][(row)])
#endif
)";
}
+5 -1
View File
@@ -7,4 +7,8 @@
// gpu_matrix_element). Every TFX and convert shader's source text changed, so a cached blob from
// 108 no longer matches the source that produced it — leaving this alone hands users stale
// binaries and garbage rendering after the update.
static constexpr u32 SHADER_CACHE_VERSION = 109; // 108 was upstream PR 14688
// 110: Vulkan emits the gpu_bitwise_and / gpu_matrix_element wrappers as bare #defines when no
// driver workaround is active, so unaffected drivers get the same SPIR-V they had at 108. 109 wrapped
// them in real functions on EVERY driver, and Qualcomm's SPIR-V compiler segfaults compiling a TFX
// pipeline containing those calls.
static constexpr u32 SHADER_CACHE_VERSION = 110; // 108 was upstream PR 14688