Skip to content

refactor(interop): re-vendor Pelorus at the commit that opens the qp-report CSV with _wfsopen (ADR-1113) - #2424

Merged
lusoris merged 3 commits into
masterfrom
refactor/pelorus-revendor-wfsopen
Oct 7, 2026
Merged

lusoris merged 3 commits into
masterfrom
refactor/pelorus-revendor-wfsopen

Conversation

@lusoris

@lusoris lusoris commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

BLUF: the vendored Pelorus mirror is byte-identical to pelorus again. The MSVC zero-warnings series (#2393) had to keep a local _wfsopen edit in core/src/interop/pelorus_qp_report_csv.c to clear C4996; that fix is now pelorus's own code (VMAFx/pelorus #89, fixing VMAFx/pelorus#88), so PELORUS_VENDOR_SHA moves to 4aae30711c65 and scripts/sync-pelorus-interop.sh --update re-renders the ten vendored files.

  • The only content change in the mirror is the comment text pelorus wrote around the same _wfsopen(..., _SH_DENYNO) call; the code is unchanged against master.
  • The drift check fails on the master tree (FAIL: vendored Pelorus interop ABI has drifted from pelorus@5f5614b0229d..., exit 1: the hosted Lint / Pre-Commit failure of run 37618697250) and passes on this head: OK: vendored Pelorus interop ABI matches pelorus@4aae30711c655510305e9c14b403f991d342f760 (ABI 1.3, minor=3).
  • test_pelorus_interop passes on a Linux CPU build.
  • tidy: the ten vendored files are outside the cpu lane's compile database (the mirror is linted in pelorus with the VMAFx profile; pelorus docs(research): hardware measurement of CUDA ms_ssim_decimate + adm_cm (Research-0749 / ADR-0750) #89's libpelorus (build · test · format · tidy) check passed).

Type

  • refactor — no behaviour change

Checklist

  • Commits follow Conventional Commits.
  • Vendored files are rendered by the script, never edited by hand (ADR-1113).

Bug-status hygiene

  • no state delta: re-vendor; the local edit becomes pelorus's own code, no open row covers it.

Netflix golden-data gate

  • I did not modify any assertAlmostEqual(...) score in the Netflix golden Python tests.

Deep-dive deliverables

  • Research digest — no digest needed: trivial re-pin.
  • Decision matrix — no alternatives: only-one-way fix (ADR-1113 forbids editing the mirror).
  • AGENTS.md invariant note — no rebase-sensitive invariants beyond ADR-1113's existing mirror rule.
  • Reproducer / smoke-test command — below.
  • CHANGELOG fragment — changelog.d/changed/pelorus-revendor-wfsopen.md.
  • Rebase note — docs/rebase-notes.md.

Reproducer

scripts/sync-pelorus-interop.sh /path/to/pelorus   # a clone of VMAFx/pelorus that has 4aae307
python3 scripts/ci/run_meson_test.py -- -C <cpu build> test_pelorus_interop

@github-actions github-actions Bot added the type:refactor Internal refactor label Oct 7, 2026
lusoris and others added 3 commits October 7, 2026 17:34
…y tester leg before a cut (ADR-2198) (#2409)

* ci: build the Windows SYCL zip where its inputs change and check every tester leg before a cut (ADR-2198)

The x64 SYCL tester zip was red on master for two days with nothing required noticing. A pull request now builds it when an input it reads changes, in the light tier through own_input_lanes, and the cut pull request runs scripts/release/check-candidate-legs.py.

* docs: regenerate the indexes and the citation map after rebasing
* feat(rust): add the RC4 Rust extractor framework crates and the psnr reference twin

The workspace gains vmafx-fex (the ABI of the Rust twins, the Extractor
trait, option, picture and host views, libm wrappers), the reference twin
vmafx-fex-psnr, empty crates for the speed_chroma, motion, adm, cambi and
prediction lanes, and vmafx-core-rs, the one Rust archive libvmaf links.
The TAD pilot becomes an rlib of that archive and drops its unused
build-time cbindgen dependency, so the Rust build needs no network. The C
header of the ABI is generated by scripts/dev/rust-abi-header.sh and
committed. ADR-1713.

* feat(rust): register the Rust twins through a C shim and select them with VMAF_FEATURE_IMPL

core/src/rust/shim/rust_twins.cpp turns each Rust twin into a
VmafFeatureExtractor named <c name>_rust that copies the C extractor's
descriptor (options, priv size, provided features, flags) and drives the
Rust entry points with the parsed options, plane views, the previous
references and the C feature collector. feature_extractor.cpp walks the
static list and then the Rust extractors the shim installs at vmaf_init(),
skips Rust twins in plain feature-name lookups, audits them like device
twins and offers vmaf_feature_extractor_impl_select(), which libvmaf
applies on every registration path when VMAF_FEATURE_IMPL=rust.

Meson builds one archive (vmafx-core-rs) with an offline cargo build and
a depfile, links it into libvmaf only, and keeps its symbols out of the
dynamic symbol table with --exclude-libs. The TAD pilot is now registered
in Rust builds; before, feature_extractor.cpp never saw HAVE_RUST_TAD.

scripts/ci/rust_twin_diff.py runs both implementations of one binary and
requires equal doubles on every metric and a receipt naming the twin; the
psnr reference twin is equal on the Netflix pair, both checkerboard pairs,
the 10-bit sparks pair and 200 frames of 4K. ADR-1713.

* feat(rust): give the Rust twins a host log and a log-only close

Lane request S-1: VmafxRsHost gains a `log` callback (appended, so no
offset moves) and the twin's close entry point receives a host whose
collector callbacks fail but whose log works, so a twin can report what
its C extractor logs at close. Extractor::close() runs before the state
drops; Host::log_fmt formats into a stack buffer without allocating.

Lane request P-1: the header script also generates
core/src/rust/include/vmafx_rs_predict.h once the predictor's cbindgen
configuration exists, and vmaf_feature_impl_rust_requested() is the one
reader of VMAF_FEATURE_IMPL for every Rust path.

The build wrapper keeps a plain subprocess call: the safe_subprocess
helper resolves symlinks and a rustup cargo is a symlink to rustup. The
shim asserts its preconditions (Power-of-10 rule 5) and propagates the
dictionary free result instead of discarding it.

* docs(rust): document the Rust extractor framework and gate it in the Rust workflow

docs/development/rust-extractor-framework.md explains how to build and
select the Rust path, how a twin is registered and written, the arithmetic
rules for bit identity, the differential harness and its fixtures, and the
tests. VMAF_FEATURE_IMPL joins the environment variable reference; the
build-flag, Rust guide and TAD pages follow the new build. A core/src
AGENTS.d page records the invariants of the framework, the TAD crate's
AGENTS.md its new shape.

The Rust workflow runs fmt and clippy on every workspace crate, checks the
cbindgen header, builds libvmaf with the Rust extractors, runs the rust
Meson suite and the harness on the Netflix and checkerboard pairs; the
impact selector covers the new paths and the Meson runner inventory lists
the workflow. docs/state.md records the two defects fixed on the way and
the missing container toolchain.

* fix(rust): format the host text buffer and record the psnr twin's provenance

Lane request C-1: cargo fmt --all --check failed on the StackText literal
in vmafx-fex's host.rs. Lane request A-1: the reference twin ports
integer_psnr.c statement by statement, so relicense_provenance.toml gives
it the netflix-integer-psnr family (documented-port instead of
provenance-unreviewed); each RC4 lane adds the block of its own crate.

* build(rust): give the libvmaf-linked crates a workspace without external crates

An offline cargo build from an empty CARGO_HOME failed in the root
workspace ("failed to select a version" for bindgen, required by
vmafx-sys): cargo resolves the whole workspace even for one package. The
framework crates, the twins, the predictor, vmafx-core-rs and the TAD
crate now form core/src/rust/Cargo.toml, whose lockfile holds only those
nine crates; the root workspace keeps the bindings and excludes both
directories. Meson builds against the new manifest, and the Rust workflow
runs fmt, clippy and tests on both workspaces and builds vmafx-core-rs
offline from an empty CARGO_HOME, which fails as soon as a crate there
gains an external dependency. The TAD rebuild step went with TAD's
build.rs.

* fix(rust): initialise a Rust twin's shared context before a threaded flush

Lane request M-1: with --threads N, flush_non_temporal_cpu_extractors()
calls flush() on the shared context of a non-temporal extractor, which is
never initialised (only the per-thread copies are). The C extractors'
flush needs no init state; a Rust twin's flush found no instance and the
run failed with "problem flushing context". init_shared_rust_twin()
initialises that context with the run's picture parameters first. Lane M
measured motion_rust with the five-frame window equal to C at --threads 1
and 4 with the change, failing without it.

Lane request A-2: the harness runs every cell once per --threads value
(default 0,1), and a cell where both sides refuse the input with the same
exit status is REFUSED and passes (speed_chroma at prescale 0.5 refuses
480x270 in C), while one side refusing alone stays an error.

* test(rust): run every twin cell serially and with thread pools of 1 and 4

The flush of a non-temporal extractor runs on the shared context only with
a thread pool, so the harness default becomes --threads 0,1,4 and the rust
Meson suite's harness test runs all three. motion and motion_v2 are the only
non-temporal C extractors with a flush; motion_rust (lane M) is the twin
that exercises init_shared_rust_twin() and failed at --threads 1 and 4
without it.

* docs(rust): give each RC4 twin its own row in the framework guide

Lane request A-3: one row per twin, so the PR that lands a twin edits only
its own row.

* fix(rust): hold the framework to the licence and tidy-coverage gates master gained

Two gates landed on master after the framework branched:

- The package licence check (ADR-1699) wants each crate's `license` to be
  the AND of the licences of the files it ships. Every crate of the
  libvmaf-linked workspace ships the root README.md (BSD-2-Clause-Patent in
  REUSE.toml) next to its EUPL-1.2 sources, and the psnr twin carries
  statements ported from integer_psnr.c, so the workspace declares
  "EUPL-1.2 AND BSD-2-Clause-Patent", as vmafx-tad already does.
- The clang-tidy coverage rule wants every tracked translation unit read by
  a lane or named in an exception. The shim and its two tests build only
  with -Denable_rust_features=true, and the dev image every lane runs in
  has no Rust toolchain yet, so they get the entry tad_rust.c and
  test_tad_rust.c already have, with the same expiry, until a pinned
  toolchain is in dev/Containerfile and a lane reads them.

* docs(adr): accept ADR-1713 for the RC4 Rust extractor framework (Q-087)

The maintainer accepted the framework decision as written (ledger Q-087,
2026-10-07): Rust twins registered next to the C extractors as
`<name>_rust`, selected with VMAF_FEATURE_IMPL, one Rust archive, the C ABI
unchanged. The status line, the deciders and the index row say so, and the
References cite the ledger entry.

* fix(test): return the TAD test's failure message without discarding const

expect_score() took its message as `const char *` and returned it as the
`char *` the test runner expects, which GCC reports as
-Wdiscarded-qualifiers. The file builds only with
-Denable_rust_features=true, so no warning-checked leg compiled it before
the Rust framework gate. The message is a string literal like every other
message the runner returns; it is now passed as `char *`.

* test(build): follow the TAD pilot into the Rust framework in the stale-text contract

The contract that landed on master (#1976, defect 18) checks that a build
without enable_rust_features neither compiles tad_rust.c nor registers
TAD, and it spelled that through the pilot's own Meson list
(rust_tad_direct_sources) and its #if HAVE_RUST_TAD registry entry. The
framework (ADR-1713) replaced both: tad_rust.c is a direct libvmaf source
inside the Rust block next to the shim, and the shim registers the pilot.
The test now reads that shape: the source is listed once, inside the Rust
block, the shim assigns vmaf_fex_tad, and the static registry does not name
it. Moving the source out of the Rust block fails the test.

* fix(rust): give the VMAF_FEATURE_IMPL selector enum a one-byte underlying type

clang-tidy 22.1.8 (cpu lane, dev container) reported performance-enum-size on enum class FeatureImpl in core/src/feature/feature_extractor.cpp: three enumerators fit std::uint8_t. The lane now reads 0 findings in feature_extractor.cpp and libvmaf.c.

* build(docker): copy the Rust twin harness into the tester image's build stages

Master's tester image contract (tools/rc1-tester/tests/
test_dockerfile_script_imports.py) requires every Meson build stage to copy
each file outside core/ that the Meson tree names; the framework's rust
suite names scripts/ci/rust_twin_diff.py, so the vmaf, SYCL, CUDA and HIP
build stages copy it. The test failed without the four lines.

* docs(adr): regenerate the ADR index pages after the rebase onto master

* docs(adr): regenerate the generated files after the rebase onto master

* docs: regenerate the indexes and the citation map after rebasing
…report CSV with _wfsopen (ADR-1113) (#2424)

* refactor(interop): re-vendor Pelorus at the commit that opens the qp-report CSV with _wfsopen (ADR-1113)

VMAFx/pelorus #89 (fixing #88) moves open_utf8() from the deprecated
_wfopen() to _wfsopen(..., _SH_DENYNO), the local edit the MSVC
zero-warnings series carried in core/src/interop/pelorus_qp_report_csv.c.
PELORUS_VENDOR_SHA moves to 4aae30711c65 and --update re-renders the ten
vendored files; the mirror is byte-identical to pelorus again apart from
the banner and the include rewrite, and the drift check passes.
@lusoris
lusoris force-pushed the refactor/pelorus-revendor-wfsopen branch from b588ab7 to 61c9016 Compare October 7, 2026 16:43
@lusoris
lusoris merged commit 61c9016 into master Oct 7, 2026
98 of 133 checks passed
@lusoris
lusoris deleted the refactor/pelorus-revendor-wfsopen branch October 7, 2026 16:44

This branch was successfully deployed

1 active deployment
github-pages — 61c90165 Deployed Oct 7, 2026 by lusoris via deploy #5418
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type:refactor Internal refactor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant