Skip to content

fix(gpu): stop the float_adm twins applying adm_csf_scale in Watson mode, and align its aliases with the CPU - #1373

Draft
lusoris wants to merge 6 commits into
masterfrom
fix/float-adm-csf-scale-mode0-and-aliases
Draft

fix(gpu): stop the float_adm twins applying adm_csf_scale in Watson mode, and align its aliases with the CPU#1373
lusoris wants to merge 6 commits into
masterfrom
fix/float-adm-csf-scale-mode0-and-aliases

Conversation

@lusoris

@lusoris lusoris commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Two related drifts in the CUDA/SYCL/HIP/Metal float_adm twins, found by the twin-drift sweep (ids 58–61, 67, 94–97) and confirmed against the source before any code was touched.

Semantics (ADR-1214). The only CSF mode the twins support is adm_csf_mode == 0 (Watson-97). In that mode the CPU reference adm_tools.c::adm_csf_rfactor_s computes rfactor = 1 / dwt_quant_step(...) and never reads adm_csf_scale / adm_csf_diag_scale — they're arguments of the Barten branch (mode 1) only. All four twins multiplied them into every rfactor:

s->rfactor[scale * 3 + 0] = (float)s->adm_csf_scale / f1;   // twin
factor1 = 1.0f / dwt_quant_step(...);                        // CPU, mode 0

The CUDA comment beside it claimed this "matches the CPU Watson-mode path" — the opposite of what adm_tools.c does. So adm_csf_scale=2.0 doubled the GPU's CSF weights while the CPU ignored it.

Naming. CUDA/SYCL/HIP aliased the options cs / cds with max 100; the CPU (and Metal) use scf / scfd, max 50. ADR-1183 derives feature names from aliases, so one request produced adm2_scf_2 on the CPU and adm2_cs_2 on the GPU — two keys for one feature.

Verified, Netflix 576x324 pair, adm_csf_scale=2.0:

backend default adm2 with scf=2 key emitted
CPU 0.962085811 0.962085811 adm2_scf_2
CUDA 0.962085756 0.962085756 adm2_scf_2 (was adm2_cs_2)
SYCL 0.962090577 0.962090577 adm2_scf_2
HIP 0.962090577 0.962090577 adm2_scf_2

i.e. the option is now a no-op on the twins exactly as on the CPU, under the CPU's key.

Gates added: each backend's float_adm parity test gains a _csf_scale case that sets adm_csf_scale=2.0, adm_csf_diag_scale=0.5 and reads the scores back under the derived key adm2_scfd_0.5_scf_2 (options sorted by name, %g values) — so both the arithmetic and the naming are asserted. CUDA 2/2, SYCL 2/2, HIP 3/3 on RTX 4090 / Arc A380 / gfx1030.

Type

  • fix — bug fix
  • test — test-only (new gates)
  • sycl / cuda / simd — backend-specific

Checklist

  • Commits follow Conventional Commits.
  • make format && make lint is green locally (pre-commit run --files clean; check-state-md-rows OK; ADR index in sync).
  • Unit tests pass: meson test -C build — the touched targets on all three local backends, listed above.
  • If I touched any SIMD/GPU code path, I ran /cross-backend-diff and the worst ULP is ≤ 2 — equivalent done directly: CPU vs each twin at the option value is identical to the default-path delta each twin already had.
  • If I touched a feature extractor with SIMD/GPU twins, I either updated every twin or listed the gap — all four twins fixed; Metal unverified here (no Apple hardware), its aliases were already correct.
  • If I added a new .c / .h — no new source files.
  • If this is a breaking change — a model file that used the GPU-only aliases cs= / cds= now gets an unknown-option error; those aliases never matched the CPU, so no model that worked on both backends can be affected.
  • If this PR adds an ADR, the row lives in docs/adr/_index_fragments/ — in sync.

Bug-status hygiene (ADR-0165)

  • docs/state.md updated — T-GPU-FLOAT-ADM-CSF-SCALE-WATSON-MODE-2026-09-07 closed.

Netflix golden-data gate (ADR-0024)

  • I did not modify any assertAlmostEqual(...) score in the Netflix golden Python tests.
  • If I believe a golden value must change — none does; the CPU is untouched and the golden gate is CPU-only.

Deep-dive deliverables (ADR-0108)

  • Research digest — no digest needed: the evidence is two source reads (the CPU branch and the twins' rfactor block) recorded verbatim in ADR-1214's Context.
  • Decision matrix## Alternatives considered in ADR-1214 (including changing the CPU instead, dropping the options from the twins, and porting Barten).
  • AGENTS.md invariant note — added to core/src/feature/AGENTS.md.
  • Reproducer / smoke-test command — below.
  • CHANGELOG fragmentchangelog.d/fixed/float-adm-csf-scale-watson-mode-and-aliases.md.
  • Rebase notedocs/rebase-notes.md, "ADR-1214 — Watson-mode CSF rfactors and the float-ADM option aliases".

Reproducer

Y=python/test/resource/yuv
for f in float_adm float_adm_cuda; do
  vmaf -r $Y/src01_hrc00_576x324.yuv -d $Y/src01_hrc01_576x324.yuv --width 576 --height 324 \
       --pixel_format 420 --bitdepth 8 --frame_cnt 1 --no_prediction \
       --feature "$f=adm_csf_scale=2.0" --output /dev/stdout --json --precision=max | grep -oE '"adm2[^"]*": *[0-9.]+'
done
meson test -C build test_cuda_float_adm_parity   # includes the _csf_scale case

On master the CUDA line prints a different value than its default under the key adm2_cs_2; on this branch it prints its default value under adm2_scf_2, matching the CPU's key.

Known follow-ups

  • The sweep also flagged, in the same twins, that adm_p_norm is accepted but hard-coded to 3 in the kernels, and that adm_bypass_cm / adm_skip_scale0 are advertised but not honoured (ids 98–104). Separate defects, separate PR.

🤖 Generated with Claude Code

lusoris pushed a commit that referenced this pull request Sep 6, 2026
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
lusoris pushed a commit that referenced this pull request Sep 7, 2026
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@lusoris
lusoris force-pushed the fix/float-adm-csf-scale-mode0-and-aliases branch from 4c50030 to da88e82 Compare September 7, 2026 06:54
lusoris pushed a commit that referenced this pull request Sep 7, 2026
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@lusoris
lusoris force-pushed the fix/float-adm-csf-scale-mode0-and-aliases branch from da88e82 to f0658ff Compare September 7, 2026 09:14
lusoris pushed a commit that referenced this pull request Sep 7, 2026
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@lusoris
lusoris force-pushed the fix/float-adm-csf-scale-mode0-and-aliases branch from f0658ff to a023cac Compare September 7, 2026 09:17
lusoris pushed a commit that referenced this pull request Sep 7, 2026
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@lusoris
lusoris force-pushed the fix/float-adm-csf-scale-mode0-and-aliases branch from a023cac to 9bc52d0 Compare September 7, 2026 10:18
Lusoris and others added 6 commits September 7, 2026 13:13
…ode, and align its aliases with the CPU

In the only CSF mode the CUDA/SYCL/HIP/Metal float_adm twins support
(adm_csf_mode == 0, Watson-97), the CPU reference
adm_tools.c::adm_csf_rfactor_s sets rfactor = 1 / dwt_quant_step(...) and does
not consult adm_csf_scale / adm_csf_diag_scale at all — those two options only
enter the Barten branch (mode 1). All four twins multiplied them into the
rfactor anyway, so a non-default scale changed the GPU score while the CPU
ignored it; the CUDA comment asserted the opposite of what adm_tools.c does.

The CUDA, SYCL and HIP option tables also declared the two options under the
aliases "cs" / "cds" with max 100, where the CPU float_adm (and Metal) use
"scf" / "scfd" with max 50. Because ADR-1183 derives feature names from the
alias when an option is non-default, the CPU emitted *_scf_2 while the GPU
emitted *_cs_2 for the same request — different keys for one feature.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…derived feature name

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s base

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…the derived feature name

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…lias invariant

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@lusoris
lusoris force-pushed the fix/float-adm-csf-scale-mode0-and-aliases branch from 9bc52d0 to db53586 Compare September 7, 2026 11:13
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