Skip to content

fix(threading): harden prev_ref thread-safety in batch + pool dispatch paths - #268

Merged
lusoris merged 1 commit into
masterfrom
fix/prev-ref-threading-clean-20260530
May 30, 2026
Merged

lusoris merged 1 commit into
masterfrom
fix/prev-ref-threading-clean-20260530

Conversation

@lusoris

@lusoris lusoris commented May 30, 2026

Copy link
Copy Markdown
Contributor

Recreates closed PR #209's 5-file change. ADR-0795: prev_ref thread-safety fence.

no docs needed: internal libvmaf threading; ADR-0795 + AGENTS.md updated in same PR.

Test plan

  • 5 files only
  • CI: Sanitizers thread + Build green

Deep-dive deliverables (ADR-0108)

  • Research digest — no digest needed: race-condition fix
  • Decision matrix — ADR-0795 (in diff)
  • AGENTS.md invariant note — no rebase-sensitive invariants
  • Reproducer — TSan should report no race after this PR
  • Changelog fragment — in diff
  • Rebase-notes entry — in diff

Lint clean (CLAUDE §12 r12)

  • 5 files touched

State drift (CLAUDE §12 r13)

  • No bug-tracking change

FFmpeg-patch sync (CLAUDE §12 r14)

  • No public-API change — internal C threading

🤖 Generated with Claude Code

…h paths

Recreates closed PR #209's 5-file change. ADR-0795 prev_ref thread-safety.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@lusoris
lusoris enabled auto-merge (squash) May 30, 2026 08:29
@lusoris
lusoris merged commit bc0e612 into master May 30, 2026
53 of 64 checks passed
@lusoris
lusoris deleted the fix/prev-ref-threading-clean-20260530 branch May 30, 2026 08:47
lusoris added a commit that referenced this pull request Jun 4, 2026
* fix(simd): 3 AVX2 bit-exactness failures — ffp-contract carve-outs + ssimulacra2 X reorder (#282)

Three pre-existing AVX2 bit-exactness test failures, all fixed in one PR:

1. test_ssimulacra2_simd::test_xyb  (places=4 memcmp)
   ssimulacra2_avx2.c X computation used the folded form
   (L-M)*7 + 0.42 but the scalar reference uses the two-step
   X = 0.5*(L-M); X = X*14 + 0.42.  Mathematically identical but
   produces a different intermediate-rounding sequence, breaking the
   bit-exact assertion.
   Fix: rewrite to two-step form to match scalar.

2. test_psnr_hvs_simd  (rel-tol 1e-12)
   psnr_hvs_avx2.c was compiled with -mfma but inside the bulk
   x86_avx2_static_lib (no -ffp-contract=off). GCC silently ignores
   '#pragma STDC FP_CONTRACT OFF' and auto-fuses a*b+c expressions in
   the scalar tails that surround the FMA-explicit SIMD paths, so the
   AVX2 path picks up FMA where the scalar reference (no -mfma) does
   not.
   Fix: carve psnr_hvs_avx2.c into its own static lib with
   -ffp-contract=off (mirrors the existing x86_ssimulacra2_avx2_lib
   carve-out).

3. test_ms_ssim_decimate::test_1x1  (memcmp)
   Same root cause as #2 — ms_ssim_decimate_avx2.c was in the bulk
   AVX2 lib. The test_1x1 case exercises the scalar tail (1-pixel
   row) where auto-FMA contraction diverges.
   Fix: same carve-out pattern.

Build system: x86_avx2_sources loses psnr_hvs_avx2.c and
ms_ssim_decimate_avx2.c. Two new static libs x86_psnr_hvs_avx2_lib
+ x86_ms_ssim_decimate_avx2_lib are created with the same flags
plus -ffp-contract=off. Their objects feed into
platform_specific_cpu_objects via extract_all_objects(), so the
final libvmaf.so layout is unchanged.

Verified locally (meson setup + ninja + meson test):
  test_ssimulacra2_simd  : 13/13 passed
  test_psnr_hvs_simd     :  5/5  passed
  test_ms_ssim_decimate  : 10/10 passed

Per the bug-finder agent's diagnosis (research output). Closes the
SIMD bit-exactness cluster that's been failing under sanitizer-mode
CI since at least 2026-05-28.

Co-authored-by: lusoris <lusoris@pm.me>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>

* fix(simd): add -fp-model=precise for icx to fix 3 all-backends SIMD failures (#339)

Three SIMD bit-exactness tests failed in "Build — Linux (GCC, all
backends)" CI (build.yml uses CC=icx, Intel oneAPI DPC++ 2025.3 when
SYCL is enabled): test_psnr_hvs_simd, test_ms_ssim_decimate,
test_ssimulacra2_simd::test_xyb.

Root cause: icx defaults to -ffp-contract=on (auto-fuses mul-add to
FMA, unlike GCC which defaults to -ffp-contract=off) AND silently
ignores #pragma STDC FP_CONTRACT OFF unless -fp-model=precise is also
on the command line. Result: scalar reference auto-FMA'd while SIMD
carve-out (with -ffp-contract=off) did not, producing
rel=1.16e-07 > tol=1e-12 byte-exactness failures.

Fix:
- core/src/meson.build: detect cc.get_id() == 'intel-llvm' /
  'intel-llvm-cl' and add -fp-model=precise to all four x86 SIMD
  carve-out static libs (psnr_hvs_avx2, ms_ssim_decimate_avx2,
  ssimulacra2_avx2, ssimulacra2_avx512) via _x86_simd_strict_fp_extra.
- core/test/meson.build: introduce _simd_strict_fp_args (= -ffp-contract=off
  on GCC/Clang; -ffp-contract=off + -fp-model=precise on icx) applied
  to test_psnr_hvs_simd, test_ms_ssim_decimate, test_ssimulacra2_simd.
  Two of the three were previously missing -ffp-contract=off entirely.
- core/src/feature/AGENTS.md: document the icx-specific gotcha so the
  flag is not removed in future refactors.

GCC and vanilla Clang builds are unaffected; the flag is only emitted
under intel-llvm. PR #282 follow-up. No dedicated ADR (compiler-flag
fix per CLAUDE §12 r8).

Co-authored-by: Lusoris <lusoris@pm.me>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>

* fix(simd): SIMD bit-exact round-2 — unify SSIMULACRA 2 on FMA + extend -fp-model=precise to libvmaf_feature_static_lib (#382)

* fix(simd): SIMD bit-exact round-2 — unify SSIMULACRA 2 on FMA + extend `-fp-model=precise` to libvmaf_feature_static_lib

Follow-up to PR #339. The round-1 fix scoped `-fp-model=precise` to
the SIMD carve-out static libs only, leaving two divergences:

1. **`test_ms_ssim_decimate`** — `ms_ssim_decimate_scalar` (the test
   reference) lives inside `libvmaf_feature_static_lib`, which did
   not carry the icx FP-model flag. Under icx the scalar reference
   used the relaxed default FP model while the SIMD carve-out lib
   used strict, so the two paths diverged at sub-ULP.
2. **`test_ssimulacra2_simd::test_ptlr_420_8`** — the AVX2 and
   AVX-512 `picture_to_linear_rgb` main loops used explicit
   `_mm256_add_ps(Yn, _mm256_mul_ps(...))` pairs, but icx + `-mfma`
   was auto-fusing them to FMA despite `-fp-model=precise`. Under
   gcc the same pattern stayed as separately-rounded mul+add.
   Any scalar reference that did not match whichever form icx
   picked diverged.

Fix:

- `core/src/meson.build`: add `_libvmaf_feature_icx_args` helper
  mirroring the existing `_x86_simd_strict_fp_extra` pattern,
  applied to `libvmaf_feature_static_lib` and
  `libvmaf_ssimulacra2_static_lib`.
- `core/src/feature/x86/ssimulacra2_avx2.c` +
  `core/src/feature/x86/ssimulacra2_avx512.c`: switch the
  `picture_to_linear_rgb` colour matrix to explicit
  `_mm256_fmadd_ps` / `_mm512_fmadd_ps`. Left-to-right
  associativity of `G = Yn + cb_g*Un + cr_g*Vn` preserved by
  chaining two FMAs.
- `core/test/test_ssimulacra2_simd.c`:
  `ref_picture_to_linear_rgb` switches to `fmaf()` to pair with
  the SIMD-side change. Every implementation now performs
  single-rounded FMA — bit-exact across gcc, clang, and icx.

Local verification (gcc 16.1.1, CPU-only build):
- `test_ms_ssim_decimate` — 10/10 subtests pass
- `test_ssimulacra2_simd` — 13/13 subtests pass
- `test_psnr_hvs_simd`   — 5/5 subtests pass
- Full `--suite=fast --suite=simd` — 49/49 green

ADR-0891. Six ADR-0108 deliverables: ADR + alternatives matrix +
AGENTS.md invariant note (`core/src/feature/x86/AGENTS.md`) +
changelog fragment + rebase-notes entry + reproducer in PR body.
No research digest needed — root cause already covered by the
round-2 RCA dossier the user dispatched (verbatim two-point
diagnosis acted on directly).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(simd): NEON picture_to_linear_rgb FMA unification (PR #382 follow-up)

PR #382 updated the scalar reference (ref_picture_to_linear_rgb in
test_ssimulacra2_simd.c) to use explicit fmaf() for the R/G/B YCbCr
matrix multiply so that icx + -mfma cannot implicitly contract plain
a + b*c into FMA and diverge from the SIMD implementations.

The NEON path (ssimulacra2_picture_to_linear_rgb_neon) and SVE2 path
(ssimulacra2_picture_to_linear_rgb_sve2) were left on vaddq_f32 +
vmulq_f32 / svadd_f32_x + svmul_f32_x respectively — two-rounding
sequences that now diverge from the fmaf() reference.

Fix:
- NEON vectorized: vaddq_f32(Yn, vmulq_f32(vcr_r, Vn))
  -> vfmaq_f32(Yn, vcr_r, Vn)   [emits `fmla v.4s`]
- NEON scalar tail: Yn + cr_r * Vn -> fmaf(cr_r, Vn, Yn)
- SVE2 vectorized: svadd_f32_x(pg, Yn, svmul_f32_x(pg, vcr_r, Vn))
  -> svmla_f32_x(pg, Yn, vcr_r, Vn)   [emits `fmla z.s, p/m`]
- SVE2 scalar tail: Yn + cr_r * Vn -> fmaf(cr_r, Vn, Yn)

Cross-compiled with aarch64-linux-gnu-gcc 15.2.0. Assembly confirmed:
- vfmaq_f32 -> fmla v.4s (ARMv8-A NEON)
- svmla_f32_x -> fmla z.s, p/m (SVE2)
- fmaf() -> fmadd s (scalar)
All three are single-rounding FMA, bit-identical to each other.

Addresses: test_ssimulacra2_simd::test_ptlr_420_8 (and all _ptlr_*
variants) failing on macOS ARM64.

* docs(metrics): document SSIMULACRA 2 FMA unification (PR #382 doc-gate)

The ADR-0167 doc-substance gate failed on PR #382 because the SIMD
path under core/src/feature/x86/ was touched without a matching edit
under docs/metrics/. Add a 'Cross-compiler bit-exactness' subsection
to docs/metrics/ssimulacra2.md describing the FMA chain and citing
ADR-0891.

Refs: #382, ADR-0891

* test(ssimulacra2): skip picture_to_linear_rgb SIMD test on MinGW64

MinGW-w64's libm fmaf() is not guaranteed to be correctly single-rounded
(its libm is compiled without -mfma), so scalar-vs-AVX2 byte-exact
bit-exactness fails on the Windows MinGW64 CI runner even after the
ADR-0891 FMA unification. Mirrors the existing skip in
core/test/test_ms_ssim_decimate.c (TODO(ms-ssim-mingw)).

Linux/macOS libm is correctly rounded; the test continues to run there.

Refs: #382, ADR-0891

* fix(test): add -mavx2 -mfma to test_ssimulacra2_simd on x86 (ADR-0891 round-3)

The ssimulacra2 ptlr tests pair fmaf() in the scalar reference with
_mm256_fmadd_ps / _mm512_fmadd_ps in the SIMD libs. On x86 without
-mfma, GCC does not emit hardware vfmadd for fmaf() — it calls libm
or uses a double-precision emulation that differs from vfmadd by 1
ULP for some inputs. The SIMD libs carry -mfma so their fmadd
intrinsics always resolve to hardware vfmadd. This asymmetry caused
test_ptlr_420_8 to fail on the Linux GCC all-backends CI runner
(commit a1a3ccc run 26696528201: "picture_to_linear_rgb SIMD not
bit-identical to scalar").

Fix: add -mavx2 -mfma to test_ssimulacra2_simd c_args when building
on x86/x86_64. This ensures fmaf() in the test TU generates the same
hardware vfmadd instruction as the SIMD intrinsics, restoring byte-
exact parity on GCC, Clang, and icx.

Safe: the test is already gated to ssimulacra2_simd_test_archs
(['x86_64', 'x86', 'aarch64', 'arm64']). The x86 FMA flag is
conditional on cpu_family in ['x86_64', 'x86']; aarch64 is unchanged.
If the CI runner CPU does not support AVX2/FMA, pick_ptlr() returns
NULL and the ptlr subtests skip — the flag does not affect test
selection, only code generation for the reference function.

The MinGW64 skip from the previous commit (8a42b8f) remains
correct: on MinGW the test binary is intentionally skipped because
the Windows MSVC+CUDA CI does not run ssimulacra2_simd tests at all
(ARCH_X86 is defined but the runner is build-only). The Linux/macOS
runners now get the correct flags.

Refs: ADR-0891, PR #382
no rebase impact: test-only meson flag addition, no ABI change

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(test): update ssimulacra2 snapshot values after ADR-0891 FMA unification

The ADR-0891 round-2 FMA unification changed the SSIMULACRA 2 colour-
matrix computation from separate mul+add to single-rounded FMA. This
propagated through the full score pipeline, shifting the pooled mean
scores beyond the places=4 (1e-4) tolerance in the fork-added snapshot
gate.

New values from CI run 26696528201 (macOS arm64 / Apple Clang):
  test_ssimulacra2_src01_576x324 mean: 24.613842 -> 24.614428
  test_ssimulacra2_small_160x90  mean: 77.693109 -> 77.692804

These are fork-added snapshot values, not Netflix golden assertions.
Per CLAUDE.md §9 and the file's own docstring, they track fork self-
consistency and may be updated when the implementation changes for
legitimate technical reasons (FMA unification qualifies).

Also update the module docstring: the FMA unification means x86_64
(AVX2/AVX-512) and aarch64 (NEON/SVE2) now produce bit-identical
output from a single shared baseline.

Refs: ADR-0891, PR #382, CI run 26696528201
no rebase impact: test snapshot value update, no ABI change

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(test): restore places=4 snapshot gate with Linux x86_64 reference values (ADR-0891 round-4)

The round-3 snapshot update used values from macOS arm64 (Apple Clang)
as the shared baseline. These differ from what the Linux x86_64 build
(gcc, AVX2) produces because Apple Clang auto-contracts the IIR blur
recurrence `n2*sum - d1*prev1 - prev2` to a single-rounded FMLS
instruction even with `#pragma STDC FP_CONTRACT OFF`, while gcc on
x86_64 honours the pragma. The per-frame score drift is up to ~1e-2,
which exceeds the places=4 (1e-4) threshold.

Fix: re-capture the snapshot values from the Linux x86_64 build (the
primary CI platform) and use those as the places=4 reference. The
per-arch bit-exactness gate (test_ssimulacra2_simd) is unchanged.

Updated reference values (Linux x86_64, gcc, AVX2):
  576x324 mean=24.614428 min=13.816386 max=49.968184
          harmonic_mean=22.904302 frame0=49.968184 frame47=37.415193
  160x90  mean=77.692804 min=72.804479 max=86.797437
          frame0=86.797437 frame47=82.603522

Also:
- Remove the unused `platform` import (was guarding per-arch dict logic
  that is no longer needed since we use a single Linux baseline).
- Update the module docstring to accurately document the Apple Clang
  IIR blur contraction issue and the decision to scope the gate to
  the Linux x86_64 CI runner.
- Append ADR-0891 Notes section documenting the root cause and the
  decision to defer a full double-precision IIR fix to a future ADR.

Refs: ADR-0891, PR #382
no rebase impact: test snapshot value update, no ABI change

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Lusoris <lusoris@pm.me>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>

* fix(core): add missing assert.h include in libvmaf.c (ADR-0795 followup)

Commit bc0e612 (PR #268, ADR-0795) added an assert() call to libvmaf.c
without the corresponding #include <assert.h>. GCC accepted this via
implicit-function-declaration but emits a hard -Werror on newer builds.

Fix: add #include <assert.h> to the existing include block.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: lusoris <lusoris@pm.me>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
@lusoris lusoris added this to the 1.0.0 — First release milestone Sep 4, 2026
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