fix(gpu): honour adm_p_norm, adm_bypass_cm and adm_skip_scale0 in the float-ADM twins - #1379
Draft
lusoris wants to merge 1 commit into
Draft
fix(gpu): honour adm_p_norm, adm_bypass_cm and adm_skip_scale0 in the float-ADM twins#1379lusoris wants to merge 1 commit into
lusoris wants to merge 1 commit into
Conversation
lusoris
force-pushed
the
fix/gpu-float-adm-options
branch
5 times, most recently
from
September 7, 2026 11:13
d5edf21 to
f79c01a
Compare
… float-ADM twins
Three VMAF_OPT_FLAG_FEATURE_PARAM options that the GPU float_adm twins declare
with the CPU's names, aliases, defaults and ranges -- and did not implement. The
framework accepts each one, range-checks it, and folds it into the derived
feature name, so a score filed under `adm2_apn_2` asserted a setting that was
never applied.
1. adm_p_norm has FOUR application points in adm_tools.c: the DLM numerator sum
and the CSF denominator sum (each branching on p == 3 to a literal cube), the
pooling root powf(accum, 1.0f / adm_p_norm), and get_noise_constant(), which
is powf(w * h * weight, 1.0f / p). All four twins hardcoded the cube in the
kernel and 1.0f / 3.0f in the host pooling, and used adm_p_norm for the AIM
exponent alone -- producing a sum of cubes raised to 1/p, with adm2 and every
adm_scaleN left at p = 3.
Measured at apn=2.0 against the 1e-4 ADR-0214 gate:
CUDA (RTX 4090) adm2_apn_2 cpu=0.43097075 gpu=0.45416959 2.32e-02
SYCL (Arc A380) adm_scale0_apn_2 cpu=0.90092957 gpu=0.89784085 3.09e-03
HIP (gfx1030) adm2_apn_2 cpu=0.99818595 gpu=0.99833104 1.45e-04
2. adm_bypass_cm was declared and stored by the CUDA and Metal twins and read by
nothing: `grep` over each returns only the struct field and the option-table
entry, and the 3x3 masking threshold was always subtracted. It now gates both
the DLM and the AIM CM kernel, as adm.c passes it to both adm_cm() calls.
SYCL and HIP do not declare it and reject it; that is unchanged.
3. Metal's adm_skip_scale0 zeroed only the REPORTED adm_scale0 sub-score while
still folding the full scale-0 numerator and denominator into the pooled
adm2 / aim. adm.c sets num_scale = 0 and den_scale = 1e-10 instead. No kernel
change is needed: adm_dwt2_lo_s writes only band_a, which adm_dwt2 computes
identically, so scales 1..3 are unaffected.
The kernels keep the CPU's own `p == 3` literal-cube fast path
(fadm_pnorm_term). Device powf(x, 3.0f) is not guaranteed to equal x * x * x,
and p = 3 is what every shipped model uses -- the pre-existing default-options
parity tests stay green as the proof.
No test could see any of this: every float_adm parity test ran with NULL
options, where p = 3 IS the hardcoded exponent and bypass = 0 IS the hardcoded
behaviour. Each backend now carries a variant per option it declares, reading
the ADR-1183-derived key. The SYCL test also had to be widened from adm2 alone
to all five ADM features -- on its fixture the aggregate does not move past the
gate, and only adm_scale0 exposes the defect.
Metal gets all three fixes by construction; there is no Apple hardware here to
measure on.
ADR-1220.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
lusoris
force-pushed
the
fix/gpu-float-adm-options
branch
from
September 7, 2026 13:15
f79c01a to
a667649
Compare
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
Three
VMAF_OPT_FLAG_FEATURE_PARAMoptions that the GPUfloat_admtwinsdeclare with the CPU's names, aliases, defaults and ranges — and did not
implement. The framework accepts each, range-checks it, and folds it into the
derived feature name (ADR-1183), so a score filed under
adm2_apn_2asserteda setting that was never applied.
1.
adm_p_norm— four application points, one honoured (all four twins)adm_tools.capplies the p-norm in four places:All four twins hardcoded the cube in the kernel and
1.0f / 3.0fin the hostpooling, and used
adm_p_normfor the AIM exponent alone — producing a sumof cubes raised to
1/p, withadm2and everyadm_scaleNleft atp = 3.2.
adm_bypass_cm— declared, stored, read by nothing (CUDA + Metal)grep -n adm_bypass_cmover either twin returns exactly two hits: the statefield and the option-table entry. The 3x3 masking threshold was always
subtracted. It now gates both the DLM and the AIM CM kernel, as
adm.cpasses it to both
adm_cm()calls. SYCL and HIP do not declare it and rejectit — unchanged.
3.
adm_skip_scale0— a pooling rule mistaken for a reporting rule (Metal)adm.csetsnum_scale = 0,den_scale = 1e-10for scale 0, so it drops outof the pooled
adm2/aim. The Metal twin zeroed only the reportedadm_scale0sub-score while still folding the full scale-0 numerator anddenominator into the pooled score. No kernel change was needed:
adm_dwt2_lo_swrites only
band_a, whichadm_dwt2computes identically.Reproducer
meson setup build-cuda core -Denable_cuda=true -Denable_sycl=false -Db_lto=false ninja -C build-cuda meson test -C build-cuda test_cuda_float_adm_parity --print-errorlogs -vRevert
core/src/feature/cuda/and the new variants fail. Measured atadm_p_norm = 2.0, against the1e-4ADR-0214 gate:adm2_apn_20.430970750.454169592.32e-02adm_scale0_apn_20.900929570.897840853.09e-03adm2_apn_20.998185950.998331041.45e-04Each variant fails with the fix reverted and passes with it applied, on all
three local devices. SYCL and HIP legs:
The default path does not move
The kernels keep the CPU's own
p == 3literal-cube fast path(
fadm_pnorm_term). Devicepowf(x, 3.0f)is not guaranteed to equalx * x * x, andp = 3is what every shipped model uses. The pre-existingdefault-options
test_cuda_float_adm_parityandtest_cuda_adm_paritystaygreen as the proof.
Why no test caught it
Every
float_admparity test ran withNULLoptions, wherep = 3is thehardcoded exponent and
bypass = 0is the hardcoded behaviour.The SYCL test also had to be widened from
adm2alone to all five ADM features:on its fixture the aggregate never moves past the gate, and the variant passed
against the unfixed code until
adm_scale0was added. The per-scalesub-scores are where a kernel-vs-CPU divergence surfaces first — the CUDA test's
own comment says so, and the SYCL one had not followed it.
Scope
Metal gets all three fixes by construction — there is no Apple hardware here to
measure on, and that is stated in the ADR rather than implied. Its CM uniform's
two former padding slots now carry
p_normandbypass_cm, soFadmCsfandFadmCsfHostkeep the same size and alignment.Deep-dive deliverables (ADR-0108)
docs/research/2037-advertised-but-unimplemented-gpu-options.md: the third instance of this pattern in a week, the one-grep mechanical check that finds it, and why an aggregate-only parity test could not see it.docs/adr/1220-gpu-float-adm-options-reach-kernels.md## Alternatives considered(five options; the runner-up "always callpowf, nop == 3branch" would move the default path for every shipped model).core/src/feature/cuda/AGENTS.md, cross-referenced fromcore/src/feature/sycl/AGENTS.md, with full sections incore/src/feature/hip/AGENTS.mdandcore/src/feature/metal/AGENTS.md.changelog.d/fixed/1220-gpu-float-adm-options.md.docs/rebase-notes.md— entryADR-1220 — float-ADM options must reach the GPU kernels (2026-09-07).Docs (rule 10)
docs/metrics/features.md: theadm_p_normandadm_skip_scale0rows now state which backends honour them and what thepre-fix behaviour was, and
adm_bypass_cmgains a row it never had — includingthat SYCL and HIP reject it.
Bug status (rule 13 / ADR-0165)
docs/state.md— closesT-GPU-FLOAT-ADM-OPTIONS-IGNORED-2026-09-07, and opensT-GAP-FLOAT-ADM-BYPASS-CM-SYCL-HIP-2026-09-07for the option SYCL and HIP still do not declare.🤖 Generated with Claude Code