From c6b38f8d4427f5908fdb58294512871040f9bda9 Mon Sep 17 00:00:00 2001 From: luisgonzaleznf Date: Mon, 3 Aug 2026 15:14:04 +0200 Subject: [PATCH] fix water shader under LOVE 12: make effect()'s parameter precision a define The float params were pinned to mediump to match LOVE 11's forward declaration of effect(), because a definition whose precisions differ from the prototype's reads as a second function to some compilers. LOVE 12 declares it under a different precision, so the pin became the mismatch there and the shader stopped compiling -- lakes drew flat on any LOVE 12 + Metal build, iOS included. The qualifier is a define now, and Water.shader compiles the pinned form first and the bare one only if that is refused, so whichever prototype a runtime brought, one of the two agrees with it. The warning fires only when both are refused. --- lib/Water.lua | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/lib/Water.lua b/lib/Water.lua index 45ecd26..ed761df 100644 --- a/lib/Water.lua +++ b/lib/Water.lua @@ -940,7 +940,16 @@ vec2 waveUV(vec2 tc, vec2 col) { // can afford it -- the colour is a colour, and tc/sc arrived through // LOVE's mediump plumbing whatever this signature says -- and the maths // below runs on the stage default the moment the values touch a local. -vec4 effect(mediump vec4 color, Image tex, mediump vec2 tc, mediump vec2 sc) { +// +// Which precision that has to BE is not ours to know: LOVE 12 forward- +// declares effect() under a different one, and pins that matched 11's +// prototype are the mismatch there -- the same refusal, from the other +// side, with the water falling back to flat. So the qualifier is a define +// the Lua side fills in, and Water.shader compiles the pinned form first +// and the bare one only if that is refused. Whichever prototype a runtime +// brought, one of the two agrees with it. +vec4 effect(EFFECT_PREC vec4 color, Image tex, EFFECT_PREC vec2 tc, + EFFECT_PREC vec2 sc) { // THE DEPTH TEST, done here because the buffer that would have done it is // detached for the length of this pass so it can be READ (see the header). // Same comparison, same buffer, same result: a building in front of a pond @@ -1133,7 +1142,7 @@ end Water._trainSource = trainSource -- named for the suite -local function source(grid) +local function source(grid, bare) local src = SHADER_SRC:gsub("//@CRATERS", (craterSource():gsub("%%", "%%%%"))) src = src:gsub("//@TRAINS", (trainSource():gsub("%%", "%%%%"))) local head = ("#define RAY_STEPS %d\n#define RAY_REFINE %d\n" @@ -1141,6 +1150,12 @@ local function source(grid) :format(Water.RAY_STEPS, Water.RAY_REFINE, Water.WAVE_STEPS, Water.WAVE_STRIDE) if grid then head = head .. "#define VOXEL_GRID 1\n" end + -- effect()'s parameter precision -- see the signature for why it cannot + -- simply be spelled there. Empty is a define all the same: the params + -- then carry the stage default, which is what a prototype declared + -- without qualifiers wants. + head = head .. (bare and "#define EFFECT_PREC\n" + or "#define EFFECT_PREC mediump\n") return head .. src end @@ -1159,6 +1174,13 @@ function Water.shader(grid) shaders[grid] = false else local ok, sh = pcall(love.graphics.newShader, source(grid)) + if not ok then + -- the pinned prototype was the wrong one for this runtime; the bare + -- one is the only other shape there is, and a driver that refuses + -- both was never going to draw this water anyway + local bareOk, bareSh = pcall(love.graphics.newShader, source(grid, true)) + if bareOk then ok, sh = bareOk, bareSh end + end if not ok and V and V.mod and V.mod.log then -- once, where it can be read: the fallback is flat water, which is -- easy to look at and impossible to diagnose without this line