diff --git a/crates/gpu/src/shaders/fx_tone.wgsl b/crates/gpu/src/shaders/fx_tone.wgsl index bcd23baa7..a47d5df59 100644 --- a/crates/gpu/src/shaders/fx_tone.wgsl +++ b/crates/gpu/src/shaders/fx_tone.wgsl @@ -40,8 +40,12 @@ fn fxt_lv(i: u32, v: f32, clip_b: bool, clip_w: bool) -> f32 { if (clip_w) { t = min(t, 1.0); } - // color2::LEVELS_IC_CURVE_LIMIT keeps the curve finite. - let curve = clamp(fxt_gamma(t, max(data[o + 2u], 0.01)), -1e30, 1e30); + // color2::LEVELS_IC_CURVE_LIMIT keeps the curve finite. WGSL leaves an overflowing pow + // indeterminate, so a curve past the bound saturates before pow: |t|^(1/g) > 1e30 once + // log2|t| / g > 99.66; between 1e30 and 2^99.7 the clamp gives the same 1e30. + let g = max(data[o + 2u], 0.01); + let saturate = abs(t) > 1.0 && log2(abs(t)) / g > 99.7; + let curve = select(clamp(fxt_gamma(t, g), -1e30, 1e30), sign(t) * 1e30, saturate); return data[o + 3u] + (data[o + 4u] - data[o + 3u]) * curve; }