From f35ddce87fcd7ba08e051d2143dce9db2333c046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 9 Oct 2026 18:39:39 +0200 Subject: [PATCH] Vulkan: Do the GE-exact FIXA/FIXB blend math in highp The fragment shader's default precision is lowp, which NVIDIA's Vulkan driver runs as fp16, and the product (2c+1)(2f+1) reaches 511*511, past fp16's range. Split/Second's bloom saturated whole channels; its two bloom dumps go from MSE 2354/9305 to 80/153, the same as OpenGL. Co-Authored-By: Claude Opus 5.5 (1M context) --- GPU/Common/FragmentShaderGenerator.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index 36305007c3..76838076da 100644 --- a/GPU/Common/FragmentShaderGenerator.cpp +++ b/GPU/Common/FragmentShaderGenerator.cpp @@ -1018,10 +1018,15 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu // Sampler), and rounding the GPU's finer filtered value instead makes each texel about a level // brighter. That doubles God Eater Burst's faint bloom, which feeds back into itself pass after pass. // The 0.01 keeps an unfiltered texel's exact integer from flooring a level down on float error. - WRITE(p, " vec3 fixA2 = floor(u_blendFixA * 510.0 + 0.5) + 1.0;\n"); - WRITE(p, " vec3 fixB2 = floor(u_blendFixB * 510.0 + 0.5) + 1.0;\n"); - WRITE(p, " v.rgb = floor((floor(v.rgb * 255.0 + 0.01) * 2.0 + 1.0) * fixA2 / 1024.0);\n"); - WRITE(p, " v.rgb = (v.rgb - 0.5 + fixB2 / 1024.0) / 255.0;\n"); + // This needs highp: the product reaches 511*511, past fp16's range, and the default precision is lowp, + // which NVIDIA's Vulkan driver runs as fp16. + WRITE(p, " highp vec3 fixA2 = u_blendFixA;\n"); + WRITE(p, " highp vec3 fixB2 = u_blendFixB;\n"); + WRITE(p, " highp vec3 srcTerm = v.rgb;\n"); + WRITE(p, " fixA2 = floor(fixA2 * 510.0 + 0.5) + 1.0;\n"); + WRITE(p, " fixB2 = floor(fixB2 * 510.0 + 0.5) + 1.0;\n"); + WRITE(p, " srcTerm = floor((floor(srcTerm * 255.0 + 0.01) * 2.0 + 1.0) * fixA2 / 1024.0);\n"); + WRITE(p, " v.rgb = (srcTerm - 0.5 + fixB2 / 1024.0) / 255.0;\n"); } else { WRITE(p, " v.rgb = v.rgb * %s;\n", srcFactor); }