From 5047d8b5e70aafe1f41e72aab0394afe40fb0ab3 Mon Sep 17 00:00:00 2001 From: FredApps <153173854+FredApps@users.noreply.github.com> Date: Fri, 6 Nov 2026 20:04:21 +0100 Subject: [PATCH] Posterize on the GPU: divide by the levels exactly, as the CPU does (#3179) Native division isn't correctly rounded on real GPUs, so posterize_matches_the_cpu_on_every_8bpc_pair failed on NVIDIA and Apple GPUs while passing on the software renderer hosted CI uses. Co-authored-by: Claude Opus 5.5 --- crates/gpu/src/shaders/fx_stylize.wgsl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/gpu/src/shaders/fx_stylize.wgsl b/crates/gpu/src/shaders/fx_stylize.wgsl index 1fa8134fc..c9eb9a50e 100644 --- a/crates/gpu/src/shaders/fx_stylize.wgsl +++ b/crates/gpu/src/shaders/fx_stylize.wgsl @@ -127,7 +127,9 @@ fn fxs_point(@builtin(global_invocation_id) gid: vec3) { case 1u: { let lv = P.f[0].x; let c = vec3(unit_div(px.x, a), unit_div(px.y, a), unit_div(px.z, a)); - let o = vec3(fxs_posterize_bin(c.x, u32(lv)), fxs_posterize_bin(c.y, u32(lv)), fxs_posterize_bin(c.z, u32(lv))) / lv; + // (`unit_div`: a native division by the levels is off by an ulp on real GPUs) + let bins = vec3(fxs_posterize_bin(c.x, u32(lv)), fxs_posterize_bin(c.y, u32(lv)), fxs_posterize_bin(c.z, u32(lv))); + let o = vec3(unit_div(bins.x, lv), unit_div(bins.y, lv), unit_div(bins.z, lv)); textureStore(out, p, vec4(o * a, a)); } case 2u: {