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

Merged
lusoris merged 9 commits into
masterfrom
fix/float-adm-csf-scale-mode0-and-aliases
Sep 20, 2026
Merged

lusoris merged 9 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 fragment — changelog.d/fixed/float-adm-csf-scale-watson-mode-and-aliases.md.
  • Rebase note — docs/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


Rewritten 2026-09-20 — the code already merged; this is now the record and the test

The implementation is on master as 64ea351be ("fix(gpu): stop the float_adm twins applying adm_csf_scale in Watson mode, and align its aliases with the CPU") — this branch's own commit, landed through an earlier train. What it merged without is everything the same-PR rules ask for:

Piece On master before this PR
ADR-1214 no
core/src/feature/AGENTS.md invariant no
docs/state.md row no
A regression test no

So master has carried the fix with no decision record and nothing that would catch a regression. This PR closes that.

The new test. test_float_adm_csf_scale_is_a_watson_mode_noop, added to the CUDA, HIP and SYCL float-ADM parity tests: it sets adm_csf_scale=2.0 on both sides and asserts the twin still matches the CPU — true only while the twin ignores a Barten-mode argument in the Watson-97 mode it implements. The derived key table follows ADR-1183 (alias base plus _<alias>_<%g value>, hence _scf_2).

Verified against real gfx1036 kernels, not just compiled:

test_float_adm_hip_registered: pass
test_float_adm_cpu_hip_parity: pass
test_float_adm_p_norm_reaches_kernel: pass
test_float_adm_csf_scale_is_a_watson_mode_noop: pass
4 tests run, 4 passed

and under the default scaffold posture it skips with [skip: HIP scaffold ENOSYS on feed], per ADR-1264.

The conflict that deferred this PR is resolved by dropping this branch's test files. #1379 restructured the three parity tests around per-variant key tables after this branch was cut; the branch had restructured them around runtime suffix derivation. Those were two answers to the same question, and #1379's is on master, so master's files are taken whole and the new case is written in each file's own existing idiom rather than importing a fourth one. Nothing the branch's version tested is lost: its csf_scale case is the case added here.

CPU build and meson test --suite=fast: 140 of 140, zero warnings. praetorctl audit passes, baseline 1414 → 1414.

lusoris added a commit that referenced this pull request Sep 6, 2026
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
lusoris added 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 added 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 added 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 added 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 added 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 9bc52d0 to db53586 Compare September 7, 2026 11:13
lusoris added 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 db53586 to 3853091 Compare September 7, 2026 13:15
lusoris added 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 3853091 to c7c4990 Compare September 7, 2026 16:38
lusoris added 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 c7c4990 to cb8d3f9 Compare September 7, 2026 21:23
lusoris added a commit that referenced this pull request Sep 15, 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 cb8d3f9 to f4e76ba Compare September 15, 2026 20:02
@lusoris

lusoris commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

Left out of the pre-rc.1 fixing train in #1425, deliberately, and it needs a decision.

This PR and #1379 both refactored core/test/test_cuda_float_adm_parity.c's run_cpu / run_cuda helpers, in incompatible directions:

  • fix(gpu): honour adm_p_norm, adm_bypass_cm and adm_skip_scale0 in the float-ADM twins #1379, now on the train: run_cpu(opt_name, opt_val, keys, out_scores) builds the option dictionary internally and takes an explicit key array per option, because each option derives a different published feature name (ADM_FEATURES, ADM_FEATURES_APN, ADM_FEATURES_BCM).
  • This PR: run_cpu(out_scores, opts, suffix) takes a prebuilt dictionary and derives every key as ADM_FEATURES[m] + suffix.

Both are reasonable; neither is a superset. Merging the text produces a helper that is half of each, so this is a redesign of the test rather than a conflict resolution, and it is better done once against the merged train than twice against a moving one.

Nothing else in this PR conflicts. Suggested path: after #1425 lands, rebase this branch and port the adm_csf_scale parity case onto #1379's shape by giving it its own key array, which keeps one design in the file.

lusoris added a commit that referenced this pull request Sep 20, 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 f4e76ba to 77dfa4f Compare September 20, 2026 08:12
@github-actions github-actions Bot added the type:bug Something isn't working label Sep 20, 2026
lusoris added a commit that referenced this pull request Sep 20, 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 77dfa4f to 8d879c4 Compare September 20, 2026 08:37
@lusoris
lusoris marked this pull request as ready for review September 20, 2026 08:47
lusoris and others added 3 commits September 20, 2026 11:38
…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>
…without

64ea351 stopped the float_adm twins applying adm_csf_scale in the
Watson-97 mode and aligned their aliases with the CPU's scf / scfd, but
it merged while this branch still held its ADR, its AGENTS invariant,
its state row and its test. master therefore carries the fix with no
record of the decision and nothing that would catch a regression.

The three GPU float-ADM parity tests gain
test_float_adm_csf_scale_is_a_watson_mode_noop: it sets
adm_csf_scale=2.0 on both sides and asserts the twin still matches the
CPU, which holds only while the twin ignores a Barten-mode argument in
Watson mode. The derived key table follows ADR-1183 — alias base plus
_<alias>_<%g value>, so _scf_2 — and each test reuses its own file's
existing variant idiom rather than importing a fourth one.

The branch's own versions of these three files are dropped in favour of
master's: #1379 restructured them around per-variant key tables after
this branch was cut, and that design already covers what the branch's
suffix-derivation was for.
The row carried the 'PR #TBD' placeholder that ADR-0334's gate rejects:
inserted state.md rows must name the merged PR or commit, because a row
that cites nothing is what makes the register go stale.
@lusoris
lusoris force-pushed the fix/float-adm-csf-scale-mode0-and-aliases branch from 9f0e994 to 3b207d4 Compare September 20, 2026 09:46
@lusoris
lusoris merged commit 8d0cdd7 into master Sep 20, 2026
81 of 82 checks passed
@lusoris
lusoris deleted the fix/float-adm-csf-scale-mode0-and-aliases branch September 20, 2026 10:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type:bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant