Skip to content

fix(denoise): meta=1 residual accumulators overflow uint32 at 4K/8K#42

Merged
lusoris merged 1 commit into
masterfrom
fix/denoise-meta-uint32-overflow
Jun 27, 2026
Merged

fix(denoise): meta=1 residual accumulators overflow uint32 at 4K/8K#42
lusoris merged 1 commit into
masterfrom
fix/denoise-meta-uint32-overflow

Conversation

@lusoris

@lusoris lusoris commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

What

Fixes a confirmed defect from an adversarial shader bug-hunt: the vf_pelorus_denoise_vulkan opt-in meta=1 residual-energy accumulators (abs_sum/sq_sum, uint32) silently overflow at 4K/8K.

The bug

The GLSL emits atomicAdd(abs_sum[slice], uint(|in-out| * GS)) with GS=1e6, and slice = wg & 15 spreads the whole frame across only 16 global accumulators → ~W·H/16 pixels/slice (518K at 4K, 2.07M at 8K). A realistic denoise residual of 0.02 at 4K sums to 518400·0.02·1e6 = 1.04e10, 2.4× over UINT32_MAX → wraps mod 2³² → residual_energy/sigma_estimate/psnr in the PEL_SEC_DENOISE telemetry are garbage. Overflow threshold: residual ≥0.008 (4K), ≥0.002 (8K) — i.e. virtually any real denoise at ≥4K.

The fix

Lowered GS to 1e3 (worst-case 8K sum 2.07e9 < UINT32_MAX, ~2× margin) and mirrored the host divisor. residual_energy (the mean) stays accurate; sigma_estimate is now a coarse-but-bounded estimate (a precise sigma would need a 64-bit atomic accumulator — noted as a follow-up). Opt-in telemetry only — zero effect on denoised pixel output, and no .comp lockstep (the accumulator is a filter-only host-readback path).

Provenance

Found by a 5-finder → adversarial-verify bug-hunt over the session's new shared-memory GLSL. The hunt's false positives were correctly refuted and not changed: a var/varr local-name divergence (cosmetic, identical SPIR-V) and the pel_load_tile leading barrier (intentional — load-bearing in the filter's multi-plane loop, mirrored in the standalone for CI parity).

Deliverables

Regenerated patch 0003 + changelog fragment. No ABI/surface change (same PEL_SEC_DENOISE fields, just correct values). Pure bugfix — no ADR.

🤖 Generated with Claude Code

The opt-in meta=1 residual-energy accumulators (abs_sum/sq_sum, uint32) summed
|in-out|*GS with GS=1e6 over ~W*H/16 pixels per slice. At 4K a slice is 518K
pixels, so a realistic residual of 0.02 sums to 1.04e10 -- 2.4x over UINT32_MAX
-- and wraps silently, corrupting residual_energy/sigma_estimate/psnr in the
PEL_SEC_DENOISE telemetry (overflow threshold: residual >=0.008 at 4K, >=0.002
at 8K). Lowered GS to 1e3 (worst-case 8K sum 2.07e9 < UINT32_MAX) and mirrored
the host divisor. residual_energy (the mean) stays accurate; sigma_estimate is
now coarse-but-bounded (a precise sigma would need a 64-bit atomic accumulator,
noted as a follow-up). Opt-in telemetry only -- no effect on denoised pixel
output. Found by an adversarial shader bug-hunt; the same hunt's false positives
(a var/varr naming nit, an intentional lockstep barrier) were adversarially
refuted and not changed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@lusoris
lusoris merged commit 71a2fbe into master Jun 27, 2026
3 checks passed
@lusoris
lusoris deleted the fix/denoise-meta-uint32-overflow branch June 27, 2026 18:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant