From eef6e3b4e9f6f28e98ef662ad97dc42f8b6da005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 8 Oct 2026 23:39:14 +0200 Subject: [PATCH] Truncate the color in the GE-exact FIXA/FIXB blend, like the GE's filter The GE's bilinear filter truncates (4-bit weights, twice), and rounding the GPU's finer filtered value made each texel about a level brighter. God Eater Burst's bloom is faint and feeds back into itself pass after pass, so that roughly doubled it: its dumps go from 15-31k px off the PSP to 0.4-3k, the #17033 ones from 79k/107k to 25k/47k. Split/Second and Call of Duty: Road to Victory improve too. Co-Authored-By: Claude Opus 5.5 --- GPU/Common/FragmentShaderGenerator.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index bf5689c8b7..65954fc2ec 100644 --- a/GPU/Common/FragmentShaderGenerator.cpp +++ b/GPU/Common/FragmentShaderGenerator.cpp @@ -1012,9 +1012,13 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu // The GE's product of a color and a factor is floor((2c+1)(2f+1)/1024), and the hardware blend multiplies // the dst by (2B+1)/512 (see ConvertBlendState). Lowering the src term by 0.5 - (2B+1)/1024 turns the // rounding of the sum into the GE's floor, except where that would go below zero. + // The color is truncated too: the GE's bilinear filter truncates (4-bit weights, see the software + // 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.5) * 2.0 + 1.0) * fixA2 / 1024.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"); } else { WRITE(p, " v.rgb = v.rgb * %s;\n", srcFactor);