Skip to content

fix(feature): score the chroma planes in the CUDA and HIP float_ms_ssim twins - #1917

Merged
lusoris merged 1 commit into
masterfrom
fix/ms-ssim-chroma-cuda-hip
Oct 3, 2026
Merged

lusoris merged 1 commit into
masterfrom
fix/ms-ssim-chroma-cuda-hip

Conversation

@lusoris

@lusoris lusoris commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

Summary

float_ms_ssim with enable_chroma=true now runs on the CUDA and HIP twins and returns the CPU extractor's float_ms_ssim, float_ms_ssim_cb and float_ms_ssim_cr bit for bit. Before, the HIP twin accepted the option, scored luma only and wrote neither chroma score without a warning (wrong output), and the CUDA twin had no such option, so the request ran on the CPU. Closes T-MS-SSIM-GPU-CHROMA-OPTION-DRIFT-2026-09-06.

The defect, measured on master 9aa9904

--feature float_ms_ssim=enable_chroma=true --precision max, 1920x1080 10 px checkerboard pair:

Backend Extractor that ran Outputs per frame
--backend cuda float_ms_ssim (CPU; float_ms_ssim_cuda cannot honour option 'enable_chroma') float_ms_ssim, _cb, _cr
--backend hip integer_ms_ssim_hip float_ms_ssim only, on all 3 frames

The fix

  • core/src/feature/cuda/integer_ms_ssim_cuda.c, core/src/feature/hip/integer_ms_ssim_hip.c: geometry, pyramid, pinned level 0 and per-window term buffers live per plane (MsSsimPlaneCuda, MsSsimPlaneHip); submit runs the existing luma pipeline (the ADR-1403 / ADR-1465 kernels) once per scored plane on the one stream, and collect adds each plane's terms with the existing raster-order host sums, validates every plane, prepares every score and then emits luma (with the enable_lcs means) and the chroma scores, as float_ms_ssim.c does. No kernel changes.
  • Both twins declare the CPU's four options (enable_lcs, enable_db, clip_db, enable_chroma) and provide the three plane features; the HIP option is kept (HISS-14). The plane count (YUV400P luma only) and the ceil-subsampled plane size come from the existing device-free core/src/feature/metal/float_ms_ssim_option_semantics.h rather than a third copy; a chroma plane below 176 pixels is refused at init with the CPU's message.
  • HIP: level 0 is uploaded straight into the pyramid (the d_ref0 / d_cmp0 staging copy is gone), and the allocation failure ladder is replaced by one NULL-safe release.
  • Gate: new cell float_ms_ssim_chroma in both gate scripts, exact on CUDA, SYCL and HIP (scripts/ci/exact_twins.d/float_ms_ssim_chroma.{cuda,sycl,hip}). On a fixture whose chroma is below 176 pixels (the default 576x324 4:2:0 pair) the CPU refuses the option, so the cell is reported SKIP with that reason (FEATURE_MIN_CHROMA_DIM) instead of erroring.
  • Found on the way: the CUDA and HIP MS-SSIM parity tests freed the options dictionary after a failed vmaf_use_feature(), which consumes it on every path (a double free, hit by the new tests against master's CUDA twin). Fixed in both files.

Tests

  • test_cuda_float_ms_ssim_parity, test_hip_ms_ssim_parity: chroma == on every output of 3 frames on 4:2:0 353x355 8 bit (177x178 chroma), 4:2:2 352x192 10 bit and 4:4:4 256x192 8 bit, with enable_lcs and with enable_db, plus an identical 4:2:0 pair with enable_db + clip_db; and the geometry verdicts (4:2:0 256x192 refused as on the CPU, YUV400P luma-only with the CPU's bits). Built against master's twins both fail: CUDA vmaf_use_feature rejects enable_chroma (-22), HIP no float_ms_ssim_cb at frame 0, and the HIP refusal case scores instead of refusing.
  • test_cuda_exact_twins, test_hip_exact_twins: enable_chroma row (640x480 4:2:0, 8 and 10 bit, ==).
  • Device-free: test_cuda_twin_option_parity / test_hip_twin_option_parity option rows; test_cuda_float_ms_ssim_exact_contract.py and test_hip_kernel_source_contract.py check the per-plane pieces and catch a planted n_planes = 1u and a luma-only provided_features; test_cross_backend_parity_gate.py covers the cell's names, the chroma size rule and the SKIP.

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).
  • make format && make lint is green locally. (Commit hooks, clang-format, black and ruff on the touched files; praetorctl audit passes; clang-tidy on the cuda and hip lanes in the dev container: see the numbers below.)
  • Unit tests pass: python3 scripts/ci/run_meson_test.py -- -C build. (--suite=fast on the CUDA build with an RTX 4090: 329 of 329; on the gfx1036 the HIP MS-SSIM, exact-twin, option, arith, first-frame and smoke tests pass.)
  • If I touched any SIMD/GPU code path, I ran /cross-backend-diff and the worst ULP is ≤ 2. (0 on every value, below.)
  • If I touched a feature extractor with SIMD/GPU twins, I either updated every twin or listed the gap under "Known follow-ups" below. (SYCL and Metal already compute chroma; SYCL measured below.)
  • If I added a new .c / .cpp / .cu / .h / .hpp, it has the appropriate license header (see CONTRIBUTING.md). (No new C file.)
  • If this is a breaking change, the commit message uses ! or BREAKING CHANGE: and the migration path is documented below. (Not breaking: the public option set grows on CUDA and stays on HIP.)
  • If this PR adds an ADR, the ADR row lives in docs/adr/_index_fragments/<NNNN-slug>.md. (No ADR: the CPU extractor's behaviour is the specification.)

Bug-status hygiene (ADR-0165)

  • docs/state.md updated in this PR: T-MS-SSIM-GPU-CHROMA-OPTION-DRIFT-2026-09-06 moved to Recently closed with the evidence; the RC3 twin-exactness group row, the T-BUG048-GPU-OPTION-PARITY-REMAINDER-2026-09-26 row and the Netflix#1414 not-affected row updated.

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, I have explained why below AND pinged @lusoris for a CODEOWNERS exception. (None changes: no CPU code is touched.)

Cross-backend numerical results

--feature float_ms_ssim=enable_chroma=true, alone and with enable_lcs, enable_db and enable_db:clip_db, --precision max, against --backend cpu of a GCC build. Fixtures: 1080p checkerboards (1 px, 10 px; 3 frames each), Netflix 576x324 4:2:2 10 bit (48), Netflix 4:4:4 8 and 10 bit and against itself (48 each; made with ffmpeg 9.0.2 from the Netflix pair), BBB 1920x1080 4:4:4 (24, scaled from the 4K source), BBB 3840x2160 4:2:0 (30).

Backend Values identical (with enable_chroma) Luma-only values Netflix 576x324 4:2:0 (288x162 chroma)
CUDA, RTX 4090 6804 of 6804 300 of 300 refused (exit 234), as the CPU
HIP, gfx1036 6804 of 6804 300 of 300 refused (exit 234), as the CPU
SYCL, Arc A380 (master build, unchanged twin) 5508 of 5508 against its own binary's CPU 300 of 300 refused (exit 234), as the CPU

feature_backends names float_ms_ssim_cuda, integer_ms_ssim_hip and float_ms_ssim_sycl in every run. SYCL against the GCC build: 52 of 6804 values differ by at most 2.1e-14, all pow() / log10() of the Intel math library in the host combine (T-ICX-LIBIMF-HOST-MATH-2026-10-01).

Parity gate, tolerance 0: float_ms_ssim_chroma max abs diff 0 on CUDA, HIP and SYCL (10 px checkerboard; on CUDA and HIP also the 1 px checkerboard, Netflix 4:4:4 and 4:2:2 10 bit and BBB 1080p 4:4:4; on SYCL also BBB 1080p 4:4:4), SKIP on the Netflix 4:2:0 pair; float_ms_ssim and float_ms_ssim_lcs unchanged at 0.

Performance (if perf or feat)

Luma-only runs are unchanged in work. With chroma, ms per frame, (t(N) - t(2)) / (N - 2), median of 3:

Input CUDA luma CUDA with chroma HIP luma HIP with chroma
BBB 3840x2160 4:2:0, N = 42 33.0 46.5 186.7 257.8
BBB 1920x1080 4:4:4, N = 24 9.7 22.6 39.1 119.2

Deep-dive deliverables (ADR-0108)

  • Research digest — no digest needed: the measurements are in the state row, the exact-twin fragments and docs/metrics/ms-ssim.md.
  • Decision matrix — no alternatives: only-one-way fix. The CPU extractor's per-plane behaviour is the specification, and removing the HIP option was ruled out (HISS-14).
  • AGENTS.md invariant note — core/src/feature/cuda/AGENTS.d/ms-ssim.md, core/src/feature/hip/AGENTS.d/ms-ssim.md (indexes regenerated), core/src/feature/metal/AGENTS.md (the shared semantics header) and docs/development/rebase-sensitive-invariants.md.
  • Reproducer / smoke-test command — pasted below under "Reproducer".
  • CHANGELOG fragment — changelog.d/fixed/ms-ssim-chroma-cuda-hip.md.
  • Rebase note — docs/rebase-notes.md, "float_ms_ssim_cuda and integer_ms_ssim_hip score the chroma planes".

Reproducer

meson setup build-cuda core -Denable_cuda=true -Denable_sycl=false -Db_lto=false
ninja -C build-cuda
build-cuda/test/test_cuda_float_ms_ssim_parity
python3 core/test/test_cuda_float_ms_ssim_exact_contract.py
python3 scripts/ci/cross_backend_parity_gate.py --vmaf-binary build-cuda/tools/vmaf \
  --reference python/test/resource/yuv/checkerboard_1920_1080_10_3_0_0.yuv \
  --distorted python/test/resource/yuv/checkerboard_1920_1080_10_3_10_0.yuv \
  --width 1920 --height 1080 --backends cpu cuda \
  --features float_ms_ssim float_ms_ssim_lcs float_ms_ssim_chroma

meson setup build-hip core -Denable_hip=true -Denable_hipcc=true -Dhip_gfx_targets=gfx1036 \
  -Denable_cuda=false -Denable_sycl=false -Db_lto=false
ninja -C build-hip
build-hip/test/test_hip_ms_ssim_parity
python3 core/test/test_hip_kernel_source_contract.py

Known follow-ups

  • Metal computes chroma since ADR-1334 but is not measured on an Apple device here; its rows stay with the Metal lane.
  • T-ICX-LIBIMF-HOST-MATH-2026-10-01: the SYCL build's host pow() / log10() differences against a GCC build, not chroma-specific.

@github-actions github-actions Bot added the type:bug Something isn't working label Oct 3, 2026
@lusoris
lusoris force-pushed the fix/ms-ssim-chroma-cuda-hip branch from a70db09 to 6840e28 Compare October 3, 2026 12:17
…im twins (#1917)

* fix(feature): score the chroma planes in the CUDA and HIP float_ms_ssim twins

integer_ms_ssim_hip accepted enable_chroma, scored luma only and wrote
neither float_ms_ssim_cb nor float_ms_ssim_cr, without a warning.
float_ms_ssim_cuda had no enable_chroma, so such a request ran the CPU
extractor.

Both twins now keep geometry, pyramid and term buffers per plane and run
the luma pipeline (the ADR-1403 / ADR-1465 kernels and raster-order host
sums) once per scored plane, as float_ms_ssim.c does. They declare the
CPU's four options, provide the three plane features, take the plane
count and the ceil-subsampled plane size from the shared
float_ms_ssim_option_semantics.h, and refuse a chroma plane below 176
pixels with the CPU's message. The HIP option is kept (HISS-14).

On an RTX 4090 and a gfx1036 every value matches --backend cpu at
--precision max: 6804 of 6804 on each with enable_chroma, alone and with
enable_lcs, enable_db and clip_db (1080p checkerboards, Netflix 576x324
4:2:2 10 bit and 4:4:4 8 and 10 bit, BBB 1080p 4:4:4 and 4K 4:2:0).

The parity gate gets a float_ms_ssim_chroma cell, exact on CUDA, SYCL
and HIP, reported SKIP on a fixture whose chroma is below 176 pixels.
New chroma cases in test_cuda_float_ms_ssim_parity, test_hip_ms_ssim_parity
and both exact-twin tests fail on the old twins. The tests no longer free
the options dictionary after a failed vmaf_use_feature(), which consumes it.

Closes T-MS-SSIM-GPU-CHROMA-OPTION-DRIFT-2026-09-06.

* refactor(feature): brace the multi-line branches clang-tidy flags in the MS-SSIM chroma code
@lusoris
lusoris force-pushed the fix/ms-ssim-chroma-cuda-hip branch from 6840e28 to 7ac0744 Compare October 3, 2026 12:24
@lusoris
lusoris merged commit 7ac0744 into master Oct 3, 2026
53 of 54 checks passed
@lusoris
lusoris deleted the fix/ms-ssim-chroma-cuda-hip branch October 3, 2026 12:24
lusoris added a commit that referenced this pull request Oct 3, 2026
…t the gate's own skips as neutral

#1917 added the parity gate's float_ms_ssim_chroma feature, which the gate
skips (status SKIP) where a chroma plane is below 176 pixels, as on the
576x324 fixtures. The macOS bundle runs it on Metal (metal-rows.json, the
float_ms_ssim row), test_metal_report_rows_contract holds the gate list to
the features with a Metal twin, and a SKIP cell neither fails the report's
metal_gate nor counts as evidence for a row.
lusoris added a commit that referenced this pull request Oct 3, 2026
…t the gate's own skips as neutral

#1917 added the parity gate's float_ms_ssim_chroma feature, which the gate
skips (status SKIP) where a chroma plane is below 176 pixels, as on the
576x324 fixtures. The macOS bundle runs it on Metal (metal-rows.json, the
float_ms_ssim row), test_metal_report_rows_contract holds the gate list to
the features with a Metal twin, and a SKIP cell neither fails the report's
metal_gate nor counts as evidence for a row.
lusoris added a commit that referenced this pull request Oct 3, 2026
…w (ADR-1496) (#1918)

* test(metal): make the macOS tester report measure every open Metal row (ADR-1496)

A tester run of the macOS bundle is the only place a Metal twin meets an
Apple device, and the next run has to measure every open Metal row of
docs/state.md at once.

- Parity gate: a `metal` backend in both gate scripts and `--hold-exact`,
  which compares a backend's cells exactly at `--precision max` (ciede at
  the LIBM_TWINS bound) before a fragment lists it. Metal leaves
  UNGATED_BACKENDS (T-GATE-NO-METAL-BACKEND-2026-10-02).
- Metal parity tests: every test_metal_*_parity compares with `==` on the
  CUDA, HIP and SYCL twins' cases (shared *_twin_parity.h headers) plus the
  cases the open rows need, runs every case after a failure and prints one
  `@case` verdict line per case (core/test/metal_twin.h). Without a device
  every case skips. test_metal_twin_option_parity compares every Metal
  twin's option table, provided features and TEMPORAL flag with the CPU's.
  The same sources build on every host as self-tests with the CPU in the
  twin's place (suite metal-selftest, also fast).
- Kernels: every .metal compiles with -std=metal3.1
  -mmacosx-version-min=14.0, so the metallib loads on the bundle's macOS 14
  floor instead of carrying the runner SDK's deployment target.
- Tester report (schema 2): runs the staged gate on every fixture
  (`metal_gate`, a check of the verdict), keeps per-case verdicts
  (`unit_tests.cases`) and evaluates tools/rc1-tester/image/metal-rows.json
  (`metal_rows`: per row pass, fail or not measured). The bundle lists every
  Metal parity test and carries the gate, its fragments and the ADRs they
  cite.
- Contract tests: the row map against docs/state.md, the tests, the unit
  list and the gate; the kernels' target arguments against the bundle.

The 18 Metal rows say which cases measure them; none is closed: they close
with the tester's report.

* test(metal): write the psnr aggregate files with mkstemp, not under a getenv() path

test_metal_integer_psnr_parity reads the apsnr_* aggregates back from the
JSON output. It built the file name from getenv("TMPDIR"), which the cpu
tidy lane reports (concurrency-mt-unsafe, 3 findings in a new file). It now
takes mkstemp() in /tmp and GetTempPathA() on Windows, the pattern of
core/test/AGENTS.d/temp-files-and-output.md. Tidy lane cpu on the file: 0.

* fix(ci): keep the interpreter archive out of the macOS tester bundle's release assets

build-macos-tester-bundle.sh downloaded the python-build-standalone archive
into its output directory, and macos-tester-bundle.yml uploads, attests,
signs and releases every *.tar.gz there: tester-20261003-c12763f3 carries
pbs.tar.gz and its cosign bundle, a third-party file signed with the
project's identity. The script removes the archive and its extraction
directory once the interpreter is staged. test_bash32_compat fails without
the removal (T-TESTER-BUNDLE-PUBLISHES-INTERPRETER-ARCHIVE-2026-10-03).

* test(metal): update contract tests for unified metal parity harness

* test(metal): run the float_ms_ssim_chroma gate cell on Metal and count the gate's own skips as neutral

#1917 added the parity gate's float_ms_ssim_chroma feature, which the gate
skips (status SKIP) where a chroma plane is below 176 pixels, as on the
576x324 fixtures. The macOS bundle runs it on Metal (metal-rows.json, the
float_ms_ssim row), test_metal_report_rows_contract holds the gate list to
the features with a Metal twin, and a SKIP cell neither fails the report's
metal_gate nor counts as evidence for a row.

* test(metal): adapt float_moment parity test to master exact sum API

This branch was successfully deployed

1 active deployment
github-pages — 7ac07442 Deployed Oct 3, 2026 by lusoris via deploy #4214
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