Repository navigation
fix(gpu): clamp the ADM contrast-masking far edge and finish the ssimulacra2 FMA unification - #1363
Merged
Merged
Conversation
…ulacra2 FMA unification Two cross-backend parity gates were red on any host with a GPU and green in CI, because every CUDA parity test skips when no CUDA device is visible. On an RTX 4090 the shipped suite was 16/18. ADR-1204 — float_adm GPU twins read the wrong far-edge sample. The CPU closed form `adm_cm_thresh3x3_s` is asymmetric: the near edge mirrors to index 1, the far edge clamps to the last index. All four twins mirrored both edges (`2 * half_w - x - 2`). It only diverges when the ADM border crop `(int)(dim * 0.1 - 0.5)` collapses to 0, i.e. band dimensions <= 14, since only then are the first and last row and column inside the sum. Isolated by sweep: scale-3 heights 9..14 diverge, 15+ are exactly 0.00; the predicted symmetric case (small width -> left crop 0) was then confirmed. adm_scale3 improves from 8.58e-04 to 4.00e-08 against the places=4 gate. ADR-1205 — ssimulacra2 scored differently with and without SIMD, and on every GPU backend. ADR-0891's FMA unification reached the four SIMD kernels and the SIMD test's own private scalar reference, but not the five shipped non-SIMD copies (the scalar fallback plus the CUDA, HIP, Metal and SYCL host conversions). Because the test compares kernels against its private reference rather than the shipped function, it asserted bit-exactness and passed. Forcing the CPU to scalar made CPU and CUDA bit-identical (delta exactly 0.0), which proves the GPU side was correct and the CPU SIMD path was the outlier. FP contraction was ruled out by measurement: rebuilding with `-ffp-contract=off` left the delta unchanged. The ~1 ULP seed is amplified by an ill-conditioned pipeline (edge-diff is a catastrophic cancellation, pooling is a 4-norm) into 2.62e-03; the fix takes it to ~2.8e-09. Scores on AVX2/AVX-512/NEON/SVE2 hosts are unchanged, so no snapshot moved. ADR-1206 — every CUDA parity test now also runs against a 960x540 fixture. All of them pinned one small size, which held the SSIM/MS-SSIM auto-scale at 1 and kept resolution-dependent branches unreachable; three defects have now hid behind that gap. Fixture macros are `#ifndef`-guarded so the second size comes from `-D` with no duplicated source. `float_ssim_cuda` is a documented v1 scale=1-only extractor, so its large variant asserts that contract as a skip rather than being dropped. Also opens T-CLI-GPUMASK-NEGATIVE-REJECTED-2026-09-06: the gpumask test script documents `--gpumask -1` as "use cpu" but the CLI rejects negatives. Found while running the full suite; unrelated to this change and not fixed here. Verified on an RTX 4090: shipped CUDA parity suite 18/18 (was 16/18), large fixture suite 16/16, `meson test` 179 ok / 1 pre-existing unrelated failure (test_vmaf_cuda_gpumask, the row above). SYCL/HIP/Metal carry the identical ADM and FMA changes but are verified by their own CI parity lanes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nts (ADR-1204, ADR-1205) Adds the ADR-0108 AGENTS.md invariant note for both fixes, and moves the ADR index rows into docs/adr/_index_fragments/ so docs/adr/README.md stays generated rather than hand-edited (ADR-0221). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tches it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…changelog The docs/state.md gate (ADR-0334) rejects `PR #TBD` placeholders, and the release-script contract requires CHANGELOG.md's Unreleased block to be rendered from changelog.d/. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Sep 6, 2026
Closed
This was referenced Sep 6, 2026
Closed
3 of 6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two cross-backend parity gates were red on any host with a GPU and green in CI, because every CUDA parity test skips when no CUDA device is visible and GitHub runners have none. On an RTX 4090 the shipped suite was 16/18. Both are fixed here, along with the test blind spot that hid them.
float_admCPU↔CUDA (adm_scale3)ssimulacra2CPU↔CUDAADR-1204 — the ADM contrast-masking far edge. The CPU closed form
adm_cm_thresh3x3_sis asymmetric: the near edge mirrors to index 1, the far edge clamps to the last index. All four GPU twins mirrored both edges (2 * half_w - x - 2, i.e.w - 2). It only diverges when the ADM border crop(int)(dim * 0.1 - 0.5)collapses to 0 — band dimensions ≤ 14 — because only then are the first and last row and column inside the sum. Isolated by sweep, not inspection: scale-3 heights 9..14 diverge and 15+ are exactly0.00; the predicted symmetric case (small width → left crop 0) was then confirmed independently.ADR-1205 — ssimulacra2 was not FMA-unified outside the SIMD kernels. ADR-0891 reached the AVX2 / AVX-512 / NEON / SVE2 kernels and the SIMD test's own private scalar reference, but not the five shipped non-SIMD copies: the scalar fallback plus the CUDA, HIP, Metal and SYCL host conversions. Because the test compares kernels against its private reference rather than the shipped function, it asserted bit-exactness and passed. This was also a CPU-only reproducibility bug — a host without AVX2 scored
ssimulacra2differently from one with it.Two things worth flagging about how this was diagnosed:
0.00e+00), proving the whole CUDA chain was already correct and the CPU SIMD path was the outlier. Fixing the kernel would have been fixing the wrong end.-ffp-contract=offleft the delta at 2.62e-03, unchanged. The cause was an explicitfmaf()five copies never received.A ~1 ULP seed becomes 1e-3 because the pipeline is ill-conditioned: the edge-diff term is
|img - blur(img)|(catastrophic cancellation) and pooling is a 4-norm. The 4-norm slots carried the entire error while every 1-norm slot matched — that signature is what located it.ADR-1206 — the blind spot. Every CUDA parity test pinned one small fixture, holding the SSIM/MS-SSIM auto-scale
max(1, round(min(w,h)/256))permanently at 1. Three defects have now hidden there (this pair plus the speed_chroma 4K bug). Fixture macros are#ifndef-guarded so a 960x540 variant is built from the same source via-D.Type
fix— bug fixsycl/cuda/simd— backend-specificChecklist
make format && make lintis green locally (pre-commit run --filesclean on all 40 changed files;assertion-density,check-copyright,check-state-md-rows,check-adr-numbering,check-dispatch-registryall pass).meson test -C build— 179 ok, 1 failure (test_vmaf_cuda_gpumask, pre-existing and unrelated; new open row below)./cross-backend-diffand the worst ULP is ≤ 2 — equivalent done directly: CPU↔CUDA agreement is 4.00e-08 (ADM) and 2.81e-09 (ssimulacra2), both far inside the places=4 gate..c/.cpp/.cu/.h/.hpp, it has the appropriate license header — no new source files added.docs/adr/_index_fragments/<NNNN-slug>.mdand the slug is appended to_order.txt—concat-adr-index.sh --checkis in sync.Bug-status hygiene (ADR-0165)
docs/state.mdupdated in this PR with a row — two closed (T-GPU-FLOAT-ADM-CM-EDGE-MIRROR-2026-09-06,T-SSIMULACRA2-FMA-SCALAR-GPU-DRIFT-2026-09-06) and one newly opened (T-CLI-GPUMASK-NEGATIVE-REJECTED-2026-09-06).Netflix golden-data gate (ADR-0024)
assertAlmostEqual(...)score in the Netflix golden Python tests.ssimulacra2is fork-added and not in the VMAF golden set, and scores on AVX2/AVX-512/NEON/SVE2 hosts are bit-unchanged, so no snapshot moved.Cross-backend numerical results
Measured on an RTX 4090 in the dev-mcp container. ADM
scale3delta by scale-3 band height, W=256:ssimulacra2CPU↔CUDA after the fix: 2.81e-09 @ 256x144, 1.12e-08 @ 512x288, 1.25e-09 @ 960x540, 1.14e-09 @ 1280x720.SYCL, HIP and Metal carry the identical ADM and FMA changes but are verified by their own CI parity lanes — this workstation only had a free CUDA device.
Deep-dive deliverables (ADR-0108)
docs/research/2032-gpu-parity-resolution-blind-spot.md, the measurement method and a reusable checklist.## Alternatives consideredin ADR-1204, ADR-1205 and ADR-1206 (including why fixing the GPU side, and why widening the tolerance, were both rejected).AGENTS.mdinvariant note — both invariants added tocore/src/feature/AGENTS.md.changelog.d/fixed/gpu-adm-cm-edge-clamp-and-ssimulacra2-fma.md.docs/rebase-notes.md, "ADR-1204 / ADR-1205 — ADM CM edge policy and the ssimulacra2 FMA contract".Reproducer
Needs a host with a CUDA device — without one the tests skip and report green, which is the whole point.
meson setup build -Denable_cuda=true -Denable_sycl=false ninja -C build meson test -C build test_cuda_float_adm_parity test_cuda_ssimulacra2_parity \ test_cuda_float_adm_parity_large test_cuda_ssimulacra2_parity_largeOn
masterthe first two fail (delta=4.78e-04anddelta=2.62e-03againsttol=1.00e-04); on this branch all four pass.Known follow-ups
test_ssimulacra2_simd.cshould assert against the shipped scalar functions rather than its own private references; until it does, this class of drift can recur silently. Not changed here to keep the diff reviewable._largevariants cover the CUDA family only, because that is the device this workstation had free. Extending the same pattern to the SYCL / HIP / Metal parity families is a follow-up.T-CLI-GPUMASK-NEGATIVE-REJECTED-2026-09-06(opened here, not fixed): the gpumask test script documents--gpumask -1as "use cpu", but the CLI rejects negatives, sotest_vmaf_cuda_gpumaskfails on any GPU host and is green only via its no-GPU skip. Needs a decision on which side is right, so it needs its own ADR.🤖 Generated with Claude Code