Skip to content

feat(rust): integer motion extractor in Rust (motion_rust), RC4 lane M - #2096

Merged
lusoris merged 3 commits into
masterfrom
rc4/motion-twin
Oct 8, 2026
Merged

lusoris merged 3 commits into
masterfrom
rc4/motion-twin

Conversation

@lusoris

@lusoris lusoris commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

RC4 lane M of #1723: the integer motion extractor (motion_sad_score, motion2, motion3, motion_five_frame_window, motion_moving_average) in Rust as motion_rust, bit-identical to integer_motion.c. Lands bottom-up per Q-083 after the framework #2086 (ADR-1713, accepted as written, ledger Q-087); the lane's commits are squashed onto the rebased framework (no conflict).

Landed base: #2086 (framework, ADR-1713) is on master; this branch was rebased onto master 61c90165b with the framework's commits dropped, retargeted to master, and queued with the other twins (Q-083). Re-gate on that head: Rust build 0 warnings, --suite=fast --suite=rust 386 OK, 0 fail; harness --feature motion on netflix, checker1, checker10 at --threads 0,1,4: 27 of 27 EQUAL (five-fixture run below from the framework head, same twin and C code).

Re-run on the rebased head (-Denable_rust_features=true -Db_lto=false, -j4): rust_twin_diff.py --feature motion --fixtures netflix,checker1,checker10,sparks10,bbb4k --threads 0,1,4 → 45 of 45 cells EQUAL (bbb4k all 200 frames), compared with master's current integer_motion.c; fast + rust suites 380 OK, 0 fail; cargo test -p vmafx-fex -p vmafx-fex-motion 14 passed; cargo fmt --check, cargo clippy --workspace --all-targets -D warnings clean; pre-commit run --files over every changed file: pass.

Incremental motion (#2290, ADR-2090) is not on master yet; when it lands it gives the C extractor an advance hook and carries the shim trampoline and motion_rust::advance with it (maintainer answer Q-093, request MI-1), so this twin keeps deriving motion2 / motion3 at its flush until then.

Crate core/src/rust/feature/motion (vmafx-fex-motion, forbid(unsafe_code), no dependency beyond vmafx-fex): sad.rs (motion_score_pipeline_8 / _16, reflect-101 mirror, i32 8-bit path and i64 9-16-bit path, per-row u32 wrap), window.rs (motion_flush_one, vmaf_motion_window_flush, motion_blend, C MIN ternary), extractor.rs (init / extract / flush), options.rs. All eight options of the C table are implemented; none refused.

Evidence (zeus, build-rs, CPU only, -Db_lto=false)

rust_twin_diff.py --feature motion --fixtures netflix,checker1,checker10,sparks10,bbb4k (all 200 4K frames), exit 0, every cell EQUAL, 0 max abs diff:

cell (derived) netflix checker1 checker10 sparks10 bbb4k
#0 default 48/48 3/3 3/3 5/5 200/200
#1 motion_max_val=18 (SDR models) 48/48 3/3 3/3 5/5 200/200
#2 motion_five_frame_window=1:motion_moving_average=1:motion_max_val=18 (HFR models) 48/48 3/3 3/3 5/5 200/200

Both feature_backends checked by the harness (motion / motion_rust). Extra option sets (36 runs, netflix, checker1, sparks10; all EQUAL): motion_force_zero, debug (4 metrics), debug + five-frame, mbf=0.5:mbo=10, mfw=2, mmxv=5, ffw, mma, a combined set, force_0 + debug, mbf=0:mbo=0, mfw=0.

Proven check: changing the horizontal rounding constant (1 << 15 to 1 << 14) turns the netflix cells into 229e9-ulp diffs; reverted.

Unit tests (cargo test -p vmafx-fex-motion): 72 SAD cases (sizes 3x3 to 576x324, 8/10/12/16 bit, random / one-bit / sparse) with values taken from the compiled C pipelines; the flush derivation against a line-by-line port of motion_flush_one (three-frame, five-frame, moving average, blend); C MIN NaN behaviour. cargo clippy -p vmafx-fex-motion --all-targets -- -D warnings and cargo fmt --all --check clean; Meson suite rust 3/3.

Throughput (single thread, --threads 1, ms/frame, median of 3, wall time of the whole vmaf --feature motion run, host load 40-57 during the runs so noisy)

size C (AVX2) Rust (scalar)
576x324 0.32 1.00
3840x2160 4.39 19.67

Speed is not a gate in RC4. The --threads 1 measurement needed the local patch of request M-1 (below).

Request to F

rc4-requests/M-1.md: with --threads N (N >= 1) a Rust twin that has a flush fails (problem flushing context): flush_non_temporal_cpu_extractors() flushes the shared, never-initialised fex, so twin_flush() finds no instance. A 5-line fix in core/src/libvmaf.c (initialise the shared context of a Rust twin before its flush) is given in the request and verified (threads 1 and 4 equal to C); not committed here, F owns the file. The harness cells run with the default thread setting, so they do not see it.

Deliverables (ADR-0108)

  • Research digest — no digest needed: statement-by-statement port, the framework digest (Research-2151, feat(rust): add the RC4 Rust extractor framework (ADR-1713) #2086, on master) covers the design.
  • Decision matrix — no alternatives: only-one-way port (framework ADR-1713 makes the decisions).
  • AGENTS.md invariant note — no rebase-sensitive invariants beyond the rebase note; the twin mirrors integer_motion.c.
  • Reproducer / smoke-test command — below.
  • CHANGELOG fragment — changelog.d/added/rust-motion-twin.md.
  • Rebase note — docs/rebase-notes.d/rust-motion-twin.md, "Rust motion twin mirrors integer_motion.c".

Docs: docs/metrics/motion.md "Rust implementation". State: no state delta, no C defect found. Provenance: rust-integer-motion family in scripts/dev/relicense_provenance.toml.

Reproducer

meson setup build-rs core -Denable_rust_features=true -Denable_cuda=false \
  -Denable_sycl=false -Denable_hip=false -Db_lto=false
nice -n 15 ninja -C build-rs -j2
python3 scripts/ci/rust_twin_diff.py --vmaf build-rs/tools/vmaf --feature motion \
  --fixtures netflix,checker1,checker10,sparks10,bbb4k
cargo test -p vmafx-fex-motion

@lusoris lusoris added the rc4 RC4: the vmaf_v1.0.16_3d0h path in Rust; lands after the v1.0.0-rc.3 tag label Oct 5, 2026
@github-actions github-actions Bot added the type:feature New feature or request label Oct 5, 2026
@lusoris
lusoris force-pushed the rc4/rust-extractor-framework branch from e99e0f4 to 5962a1a Compare October 7, 2026 14:26
@lusoris
lusoris force-pushed the rc4/rust-extractor-framework branch from 5962a1a to e8886a9 Compare October 7, 2026 15:19
@lusoris
lusoris force-pushed the rc4/rust-extractor-framework branch from e8886a9 to ccf7ccf Compare October 7, 2026 16:43
Base automatically changed from rc4/rust-extractor-framework to master October 7, 2026 16:44
@lusoris
lusoris marked this pull request as ready for review October 7, 2026 17:03
@lusoris
lusoris force-pushed the rc4/motion-twin branch 2 times, most recently from 07e531e to 9513474 Compare October 7, 2026 19:57
…2548 landed twice (#2560)

* fix(test): drop the second MALLOC_PERTURB_ entry and state row that #2548 landed twice

#2548 landed the merge train's squash of its first revision, which still carried its own MALLOC_PERTURB_=0 entry for test_gpu_picture_pool_uaf and a state row for the same bug; #2547 had already landed both. core/test/meson.build now lists the entry once (#2547's), docs/state.md keeps T-GPU-POOL-UAF-TEST-FILLS-HOST-MEMORY-2026-10-08 only, and the #2548 changelog and rebase-note fragments no longer claim that fix.

Signed-off-by: Lusoris <lusoris@proton.me>
… (ADR-2199) (#2423)

* ci(hooks): run actionlint under a deadline so a deadlock fails loudly (ADR-2199)

actionlint v1.7.12 writes the script of a `run:` block to shellcheck's stdin
pipe before it starts shellcheck, so it deadlocks when the script is larger
than the pipe. A user over fs.pipe-user-pages-soft gets pipes of 8 KiB, which
made the pre-push hook hang on about every other push. scripts/ci/run_actionlint.py
gives the run 90 seconds, terminates the process group, names the cause and
exits 124; it never reports a pass for a run that did not finish. The hook and
`make lint-actions` both go through it. The defect is upstream
(rhysd/actionlint#702). Decision Q-078.

Restore SIGQUIT for actionlint and signal only it, so the hang dump survives
a background start where non-interactive shells ignore SIGQUIT by default.

Signed-off-by: Lusoris <lusoris@proton.me>
#2096)

* feat(rust): port the integer motion extractor to Rust (motion_rust)
@lusoris
lusoris merged commit 615f8b0 into master Oct 8, 2026
8 of 34 checks passed
@lusoris
lusoris deleted the rc4/motion-twin branch October 8, 2026 02:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rc4 RC4: the vmaf_v1.0.16_3d0h path in Rust; lands after the v1.0.0-rc.3 tag type:feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant