Skip to content

feat(sycl): evaluate two ADM viewing distances in one adm_sycl instance - #2657

Merged
lusoris merged 2 commits into
masterfrom
port/upstream-cffd5b77-sycl
Oct 9, 2026
Merged

lusoris merged 2 commits into
masterfrom
port/upstream-cffd5b77-sycl

Conversation

@lusoris

@lusoris lusoris commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The SYCL twin of integer ADM evaluates two viewing distances in one instance, the third step of the Q-298 stack (ADR-2795). This covers adm_norm_view_dist_extra and two models such as vmaf_v1.0.16_3d0h and _5d0h on --backend sycl. The wavelet transform runs once per scale. The decouple + CSF pass and the denominator / contrast-masking reductions run once per distance. Both distances return the CPU's scores bit for bit on the Arc A380.

adm_sycl uses the shared merge callback and name hook of adm_view_dist.c (#2647), so there is still one implementation of the merge and naming rules (HISS-19).

Builds on #2646 (C + Rust) and #2647 (CUDA), both landed; this branch is one commit on master.

Maintainer decision Q-298 (2026-10-08): "D4 nvde, cffd5b77d + 33e5f0aca: full scope — C + Rust mirror (ADR-1713) AND bit-identical on CUDA, SYCL, HIP and Metal now (no DEFAULT_ONLY). Stack it as needed (C+Rust first, then one PR per backend is fine), each with == parity tests and exact_twins entries; device runs under the locks; Metal evidence is outside hardware — mark pending with the tester path if no Mac is available."

What changed

  • core/src/feature/sycl/integer_adm_sycl.cpp:
    • Option. adm_norm_view_dist_extra (nvde), the same table entry as the CPU's.
    • Per-distance weights. rfactor, i_rfactor and csf_normalization_shift are indexed by distance; adm_init_view_rfactors() fills each, after adm_csf_config_check() has checked every distance in init.
    • Per-frame graph. For each scale, enqueue_adm_dwt() once, then enqueue_adm_reductions() once per distance, with that distance's fixed-point weights and into its own ADM_ACCUM_SLOTS block of d_accum. The queue is in order, and a distance's kernels write only csf_f, csf_f_aim and their own block.
    • One clear and one readback. The pre-graph memset and the post-graph copy cover ADM_ACCUM_BYTES * adm_sycl_views().
    • Host conclusion. adm_scale_cpu(), adm_terms() and the new collect_view() take the distance. The second distance is filed under <base>:nvde, without debug scores. adm_init_names() builds the name dictionary and extends it through the shared hook.
    • The descriptor sets .merge and .extend_name_dict to the shared helpers.
  • Tests:
    • test_sycl_adm_parity.c:
      • The recorded option gap is gone.
      • One runner (run_adm_keys()) serves every case, so run_adm_with_model_opts() drops its NOLINT. The fixture now takes the bit depth.
      • New test_adm_two_views_exact covers 25 keys at 8 and 10 bits with debug, and 14 keys under the model options at 3H/5H.
      • New test_adm_merged_registrations_exact registers adm_sycl twice, as two models do, against one CPU context.
      • All compare with ==.
    • test_adm_view_merge.c: the adm_sycl row flips to merging.
    • test_adm_view_dist_contract.py: the SYCL option table joins MERGING_TABLES, and the table parser reads C++ tables.
  • Docs and notes: docs/metrics/adm.md, core/src/feature/sycl/AGENTS.d/adm.md, core/src/feature/AGENTS.d/adm-view-distance-merge.md, the changelog fragment and the rebase note.

Evidence

All device runs are on the Arc A380 under sycl-a380.lock, built and run in the pinned dev image (oneAPI 2026.1, CC=icx CXX=icpx, AOT dg2-g11).

Check Result
test_sycl_adm_parity 9 of 9. The existing 7 still hold, the bit-exact case included. test_adm_two_views_exact compares 25 values at 8 bits, 25 at 10 bits and 14 under the model options; test_adm_merged_registrations_exact compares 14. All use ==
CLI, --precision max, vmaf_v1.0.16_3d0h + _5d0h together, 576x324 pair, 48 frames: SYCL against CPU 1352 of 1352 identical; each model alone, 780 of 780
Same SYCL two-model run against each model alone on SYCL 1440 of 1440 identical
Run receipt (feature_backends) of the two-model run one adm_sycl
Planted defects (4): every distance with the first distance's fixed-point weights; second distance into the first block of d_accum; host conclusion with the first distance's weights; adm_sycl without .merge the first three fail test_adm_two_views_exact (the _nvd_5 or first-distance scores differ); the last fails test_adm_view_merge
test_sycl_kernel_scratch 3 of 3 (no kernel uses scratch memory)
test_adm_view_merge, test_integer_adm_view_dist, test_adm_view_dist_contract.py 11 of 11, 3 of 3, 4 of 4
Compiler warnings in the touched translation units (icx / icpx 2026.1) 0
tidy, sycl lane: integer_adm_sycl.cpp, test_sycl_adm_parity.c 0 findings (a braces and a function-size finding in the test fixed)
tidy, cpu lane: test_adm_view_merge.c 0 findings
scripts/dev/preflight.sh --stage msvcism pass
Re-run after the restack on master 4007c9801 + #2647 10d0013d6 (this head, 252c00ea7) test_sycl_adm_parity 9 of 9, test_sycl_kernel_scratch 3 of 3, test_adm_view_merge 11 of 11
Netflix golden gate (GOLDEN_NINJA_JOBS=4) on the top of the stack (the #2660 head before this rebase, 2a934db95, which contained this commit) 280 passed, 3 skipped

Type

  • feat — new feature
  • fix — bug fix
  • perf — performance improvement
  • refactor — no behavior change
  • docs — documentation only
  • test — test-only
  • build / ci — tooling / infra
  • port — cherry-pick from upstream Netflix/vmaf
  • sycl / cuda / simd — backend-specific

Checklist

  • Commits follow Conventional Commits (the commit-msg hook enforces this).
  • Every commit is signed off (git commit -s; fix a branch with git rebase --signoff origin/master). See DCO sign-off.
  • make format && make lint is green locally. Not run as one target. These are green: the commit hooks and tidy above.
  • Unit tests pass: python3 scripts/ci/run_meson_test.py -- -C build. See Evidence.
  • If I touched any SIMD/GPU code path, I ran /cross-backend-diff and the worst ULP is ≤ 2. 0 ULP: the twin's outputs equal the CPU's (== tests).
  • If I touched a feature extractor with SIMD/GPU twins, I either updated every twin or listed the gap under "Known follow-ups" below.
  • If I added a new .c / .cpp / .cu / .h / .hpp, it has the appropriate license header (see CONTRIBUTING.md). No new source file.
  • If this is a breaking change, the commit message uses ! or BREAKING CHANGE: and the migration path is documented below. Not a breaking change.
  • If this PR adds an ADR, the ADR row lives in docs/adr/_index_fragments/<NNNN-slug>.md and nothing else is touched for the index. No new ADR: ADR-2795 (port(upstream): libvmaf/adm: share computation across viewing distances (cffd5b77d + 33e5f0aca) #2646) decides the GPU twins' step.

Bug-status hygiene (ADR-0165)

  • docs/state.md updated in this PR with a row in the appropriate section (Open / Recently closed / Confirmed not-affected / Deferred). Not needed — no state delta: a feature step of the upstream port; it opens and closes no bug.

Netflix golden-data gate (ADR-0024)

  • I did not modify any assertAlmostEqual(...) score in the Netflix golden Python tests, except by porting Netflix's own updated assertion verbatim from upstream (value and places as upstream has them, measured against the fork's CPU build first; ADR-1828).
  • If I believe a golden value must change, I have explained why below AND pinged @lusoris for a CODEOWNERS exception. No golden value changes.

Cross-backend numerical results

adm_sycl against the CPU, both distances: 0 differences in test_sycl_adm_parity (25 values with debug at 8 and 10 bits, 14 values with the model options, 14 values for the merged registrations). adm.sycl stays declared exact (scripts/ci/exact_twins.d/adm.sycl).

Deep-dive deliverables (ADR-0108)

  • Research digest — no digest needed: ADR-2795 (port(upstream): libvmaf/adm: share computation across viewing distances (cffd5b77d + 33e5f0aca) #2646) covers the design; this pull request applies it to the SYCL twin.
  • Decision matrix — no alternatives: ADR-2795's matrix covers the choice; the per-distance graph mirrors the CPU's split.
  • AGENTS.md invariant note — core/src/feature/sycl/AGENTS.d/adm.md and core/src/feature/AGENTS.d/adm-view-distance-merge.md.
  • Reproducer / smoke-test command — pasted below under "Reproducer".
  • CHANGELOG fragment — changelog.d/added/adm-sycl-shared-viewing-distances.md.
  • Rebase note — docs/rebase-notes.d/adm-sycl-shared-viewing-distances.md.

Reproducer

CC=icx CXX=icpx meson setup build core -Db_lto=false -Denable_sycl=true -Dsycl_icpx_aot_targets=dg2-g11
ninja -C build
flock ~/.cache/vmafx-locks/sycl-a380.lock timeout 600 build/test/test_sycl_adm_parity
build/test/test_sycl_kernel_scratch && build/test/test_adm_view_merge

Known follow-ups

* fix(deps): move to golusoris v0.13.1 and core v0.10.1

Moves the Go module from golusoris v0.12.0 and core v0.9.2 to v0.13.1 and
v0.10.1, and applies the breaking changes of golusoris#633 that reach VMAFx
call sites:

- jobs.Register returns an error. backend.RegisterLeaseSweep returns it and
  the controller stops the River start on it, where River used to panic on a
  duplicate registration. sweep_test.go covers fresh, nil and uninitialised
  registries, a nil sweeper and a second registration.
- testutil/pg refuses a test image without a digest. storetest.Image and
  OldestImage are pinned to the index digests of postgres:18.6-alpine and
  postgres:16.15-alpine; without the pin every Postgres-backed controller
  test fails at start.
- httpx/server treats http.limits.body=0 as the 10 MiB default and adds
  http.limits.unlimited. The platform definition documents both keys, the
  env tables are regenerated, and hardening_test.go pins both behaviours on
  the production graph.

No other item of the golusoris v0.13.0 migration guide reaches a package
VMAFx imports. v0.13.1 over v0.13.0 is bug fixes in packages VMAFx does not
import.

Signed-off-by: Lusoris <lusoris@proton.me>
…ce (#2657)

* feat(sycl): evaluate two ADM viewing distances in one adm_sycl instance

adm_sycl takes adm_norm_view_dist_extra and the shared merge callback
and name hook (adm_view_dist.c). Each scale's DWT runs once per frame;
the decouple + CSF pass and the denominator / contrast-masking
reductions run once per distance, with that distance's fixed-point CSF
weights and into its own block of the accumulator buffer, which the one
memset and the one readback of the frame cover. The host concludes each
distance with its own weights and normalisation shifts and files the
second under the <base>:nvde keys, without debug scores. Both distances
return the CPU's bits (ADR-2795; test_adm_two_views_exact at 8 and 10
bits and under the model options, test_adm_merged_registrations_exact).

Signed-off-by: Lusoris <lusoris@proton.me>
@lusoris
lusoris force-pushed the port/upstream-cffd5b77-sycl branch from a70a5b8 to 61da259 Compare October 9, 2026 09:53
@lusoris
lusoris merged commit 61da259 into master Oct 9, 2026
11 of 38 checks passed
@lusoris
lusoris deleted the port/upstream-cffd5b77-sycl branch October 9, 2026 09:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type:feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant