Repository navigation
feat(api): split libvmaf into libvmafx.so.1 and a compat libvmaf.so.3 on the VMAFx API (RC4 WP6) - #2303
Merged
Conversation
lusoris
force-pushed
the
rc4/api-wp5-provenance
branch
from
October 8, 2026 08:06
6a3d44d to
b396d21
Compare
12 of 18 tasks
lusoris
force-pushed
the
rc4/api-wp5-provenance
branch
from
October 8, 2026 08:41
b396d21 to
c3fdc20
Compare
lusoris
force-pushed
the
rc4/api-wp6-compat
branch
from
October 8, 2026 09:29
24e4104 to
0c6dcd8
Compare
lusoris
marked this pull request as ready for review
October 8, 2026 09:29
… on the VMAFx API (RC4 WP6) (#2303) * feat(api): split libvmaf into libvmafx.so.1 and a compat libvmaf.so.3 on the VMAFx API (RC4 WP6, ADR-2094) libvmafx.so.1 now holds the engine and the VMAFx API and exports vmafx_ symbols only. libvmaf.so.3 defines the libvmaf functions on exported vmafx_ symbols and links nothing else, so a libvmaf function the new API cannot express fails the link (ADR-1852 decision D3). - core/api/vmafx.toml lists all 107 libvmaf functions as [[compat]] entries: generated shims and glue, hand-written ones in core/src/compat/libvmaf/, and declared engine exceptions for the CUDA and SYCL functions (HIP and Metal while built) until their WP3 lanes land. - The VMAFx API gains what the compat library needs (ABI 0.1.6): the libvmaf bridge for pictures, models and model sets, context-owned preallocated frames, perceptual side data, the context backend, frame converters, the tiny-AI and MCP functions, and vmafx_backend_name(). - Every engine translation unit compiles with the generated engine_names_gen.h, which names the engine's own libvmaf bodies vmaf_engine_<stem>; the WP2 forwarders are gone. - test_compat_conformance runs every scenario through the old bodies and the compat library and requires equal traces (every score as %a) and a call of every compat function; planted divergence and coverage defects fail. check_exported_symbols checks both libraries against generated lists, and test_compat_library_gates shows an engine symbol does not link. The Netflix golden gate passes through the compat library: 280 passed, 3 skipped, assertions untouched. * feat(api): opt-in libvmaf deprecation, upstream consumer conformance and the migration guide (RC4 WP6) Every libvmaf declaration now names its VMAFx successor, upstream FFmpeg and GStreamer are checked against the compat library, and the split is documented for users and packagers. - core/include/libvmaf/*.h: each exported function carries VMAF_DEPRECATED("use <successor>"), a compiler warning only when the consumer defines VMAF_ENABLE_DEPRECATION_WARNINGS (opt-in in 1.0, ADR-1852 decision D7). test_libvmaf_deprecation holds every marker to the target core/api/vmafx.toml names, shows the opt-in warning, and shows that a default build and the library itself compile clean under -Werror. The public-API Doxyfile strips the marker as a function-like macro. - scripts/ci/upstream-ffmpeg-compat.sh and upstream-gstreamer-compat.sh build unpatched FFmpeg (FFMPEG_TAG) and the GStreamer vmaf element (GST_PLUGINS_BAD_VERSION, a new Renovate git-tags manager) against an installed libvmaf and compare their scores with the vmaf command line and a reference library as exact text. The Upstream Consumers workflow runs both (#2237). - The compat tensor records of vmaf_dnn_session_run() cross as member-wise copies instead of a pointer cast between distinct struct types, in the compat library and in libvmafx. vmafx_frame_convert() refuses a NULL destination itself if the engine ever accepts one. The conformance test records every return value it used to discard, reads its plant through the environment snapshot (ADR-0488), and covers a real tiny-AI session. - The MCP CI lane also runs the compat conformance and export checks, the only lane whose build has the vmaf_mcp_* compat functions. - Images, Go builds and the CLI artifact take libvmafx.so* next to libvmaf.so*; CGO_LDFLAGS link -lvmaf -lvmafx. - Docs: "Migrating from libvmaf.h" with the deliberate differences, the generated migration table docs/api/vmafx/compat.md, the upstream consumer guide, a changelog fragment, a rebase note and a docs/state.md row for the backend exceptions and the release staging WP12 owns. * fix(api): assert the compat outputs, give each conformance run its own report, plan the two newer libvmaf functions (RC4 WP6) The pre-push assertion-density gate and a parallel test run found two gaps in the compat layer; this closes them and records how the two libvmaf functions newer than the RC4 base map onto the VMAFx API. - The generated score shims assert what a successful call guarantees (`post` in core/api/vmafx.toml: the record's index is the one asked for), and vmaf_picture_convert(), vmafx_frame_from_picture() and vmafx_context_acquire_frame() assert the frame and picture they hand on. - The three conformance tests (plain and the two planted defects) ran at once and shared one report file, so a run could read another's XML as its JSON. Each run now writes its own file, named after its planted defect. - scripts/ci/upstream_consumer_scores.py gives its dict its type arguments (mypy type-arg). - ADR-2094, docs/state.md and the compat agent page record that master's vmaf_set_sample_range_check_enabled() (#2221) and #2300's vmaf_set_input_colorimetry() (ADR-2093) need compat entries when the RC4 chain moves onto master, colorimetry on the colour VMAFx frames carry with a context default; test_libvmaf_deprecation refuses either one missing. * docs(adr): accept ADR-2094 with the maintainer's answer on engine names The maintainer accepted ADR-2094 by popup on 2026-10-06, as recommended: the engine's libvmaf bodies get their engine names from the generated forced header (core/src/vmafx/engine_names_gen.h), not from renaming them in the source or from partial linking. The answer is cited in the ADR's References; the status, the deciders and the index row move to Accepted. * fix(api): give the compat library the two newer libvmaf functions on frame colour and a context option (RC4 WP6) On the chain restacked onto master the vmaf command line did not link: master's vmaf_set_sample_range_check_enabled() (#2221) and vmaf_set_input_colorimetry() (#2300) had no compat entry, and libvmafx.so.1 hides the engine's bodies. Both are now compat functions on the VMAFx API, as ADR-2094 planned. - vmaf_set_sample_range_check_enabled() sets the new context option check_sample_range of vmafx_context_set_option() (ADR-1918). - vmaf_set_input_colorimetry() calls the new vmafx_context_set_default_color() and keeps no state of its own. - VmafxFrameDesc gains color, a VmafxColor appended at its end (ABI 0.1.6, the patch this pull request already raises). Host, wrapped, pool and preallocated frames carry the colour of their desc; a frame whose colour is all UNKNOWN takes the context default. - vmafx_submit() hands each pair's colour to the engine's conversion state (vmaf_engine_set_pair_colorimetry()). The same colour is accepted at any time; another one after the first converted pair is VMAFX_E_BUSY, as libvmaf's -EBUSY, and the pair is not counted. - Both functions carry their VMAF_DEPRECATED marker and a conformance scenario (sample_range; colorimetry with a conversion_target model). - The Python emitter converts a record with one struct of numbers back to C, so FrameDesc keeps to_c() now that it holds a Color. - The lane's regenerated binding and master's ten newer tests linked through vmaf_test_link are part of this commit. - clang-tidy (cpu lane, container) on the 30 translation units this pull request touches: 0 findings after guarding the port write-back of vmaf_mcp_start_sse() against a NULL config (the analyzer's null dereference; a NULL config never reaches it) and splitting a test. The cpu baseline, the measured-source list and the ADR citation registry are updated with it; the shared vt_psnr_context() test helper replaces a second copy. Tests: test_vmafx_frame_input (new), test_vmafx_context, test_vmafx_python_binding, the conformance scenarios and the generator tests fail without the change; planted defects (desc minimum of the grown size, frames without their colour, the default overriding a frame's colour, and in a zimg build a dropped compat colour, a default setter without the engine's -EBUSY, an equal colour treated as a change) are each refused. Docs: the frame colour section of the VMAFx guide, the migration note, the sample range and pictures pages, the changelog fragment, the rebase note and state row T-VMAFX-COMPAT-NEWER-LIBVMAF-FUNCTIONS-2026-10-07. * ci(api): give each master push of the upstream consumer workflow its own concurrency group Master's concurrency contract (ADR-1673) refused the Upstream Consumers workflow this pull request adds: its group was shared between master pushes with cancel-in-progress on, so a later push could cancel an earlier master run. The group now names the commit on master and the ref elsewhere, as the other workflows do; test_master_concurrency_contract passes again (it failed on the previous group). * test(docker): pin both SONAME chains and pkg-config files the image builders copy after the library split WP6 builds libvmafx.so.1 next to the compat libvmaf.so.3 and the Go and node image builders copy both chains and both .pc files; the runtime contract test still pinned the libvmaf-only find command and failed on the split. * ci(api): run the upstream consumer workflow at the full CI tier Master's CI tiers (ADR-2169) route every pull-request workflow through ci-tier.yml; the routing contract test refused upstream-consumers.yml, which ran its 45-minute FFmpeg and GStreamer build on the release pull request too. The job now needs the tier job and runs at the full tier, like the FFmpeg lanes; the guide and the workflow table say so. * fix(api): clear the caller's pictures once vmaf_read_pictures() owns them in a CUDA build A CUDA build released host translations that are struct copies of the caller's pictures and left the caller's VmafPicture structs pointing at released storage; a CPU build clears them through vmaf_picture_unref(). The compat library (ADR-2094) clears them, so test_compat_conformance failed in every CUDA build. vmaf_engine_read_pictures() now clears both structs once the context owns the pictures, in every build; the body moves to read_pictures_owned(). * test(api): read the read-pictures body where it now lives in the GPU fallback contract vmaf_engine_read_pictures() wraps read_pictures_owned() since it clears the caller's pictures; the contract that fallbacks resolve before the CUDA translation reads the helper's body. * fix(api): keep the Windows-only SYCL importer out of the ELF legacy version script core/src/vmafx_legacy_sycl.map named vmaf_sycl_import_d3d11_surface, which only _WIN32 builds define, so every Linux SYCL link failed (--no-undefined-version: undefined version VMAF_LEGACY_SYCL). [[compat]] gains platform = "windows": the legacy map omits the name, its compat-list row ends in @windows and check_exported_symbols.py expects it only on Windows. The generator refuses another platform; both cases are tests that fail on the previous head. * build(test): link the Rust twin registry test through vmaf_test_link after the library split WP6 (ADR-2094) removed libvmaf.get_static_lib(): libvmaf is the compat library, and white-box tests link the engine through vmaf_test_link. test_rust_twin_registry (framework #2086, on master) still asked for the static libvmaf, so meson setup failed with -Denable_rust_features=true. The citation map loses the compat_libvmaf_gen.c sites the split removed and gains the sites of the restack. * docs(api): move the rebase notes to a fragment and leave the rendered files to the landing render (ADR-2197) * build(test): link the Rust predictor and CAMBI kernel tests through vmaf_test_link test_predict_rust_ops, test_rust_predict and test_rust_cambi_kernels came to master with #2085 and #2090 and link libvmaf.get_static_lib(). After the library split libvmaf.a is the compat library: a white-box test links vmaf_test_link (libvmaf.a with libvmafx.a, or both shared libraries), as every other white-box test of this PR does. * ci(tidy): measure the translation units of WP6 (library split) in the cpu lane The clang-tidy coverage rule on master requires every tracked translation unit to be read by a lane. The new and touched units of this pull request were measured in the dev container (scripts/dev/tidy-lane.sh --write --only ... cpu, clang-tidy 22.1.8): 0 findings, 0 uncited NOLINT; they join the cpu lane's measured sources. * fix(api): state the compat SSE start's invariants as assertions vmaf_mcp_start_sse() reached 20 lines without an assertion, which the pre-push Power-of-10 assertion-density gate (scripts/ci/assertion-density.sh) refuses for fork-added functions. It now asserts the two facts its tail relies on: a successful start had a configuration (a NULL one fails with -EINVAL before), and the bound port fits the libvmaf struct's 16-bit field. The guard on the write stays for release builds. assertion-density: pass; clang-tidy cpu lane on core/src/compat/libvmaf/mcp.c: 0 findings. Signed-off-by: Lusoris <lusoris@proton.me>
lusoris
force-pushed
the
rc4/api-wp6-compat
branch
from
October 8, 2026 09:54
0c6dcd8 to
14dc297
Compare
This was referenced Oct 8, 2026
lusoris
added a commit
that referenced
this pull request
Oct 8, 2026
… ships them (#2604) * fix(licensing): record libvmafx beside libvmaf in every artifact that ships them After the library split (#2303) every image that ships the library failed its licence check (ADR-1503): "no recorded licence: usr/local/lib/libvmafx.so", libvmafx.so.1, libvmafx.so.1.0.0 and usr/local/lib/pkgconfig/libvmafx.pc. tools/rc1-tester/image/licensing.json recorded libvmaf.so* and libvmaf.pc only. The vmafx-binaries components of production-cli-image, production-server-image, production-go-server-image, production-node-image and release-native now record libvmafx.so* (and pkgconfig/libvmafx.pc where libvmaf.pc is recorded), under the same licences; the controller, CUDA, ROCm, oneAPI and published-RC records inherit them. test_every_installed_library_is_recorded_beside_its_sibling reads the installed libraries from the pkg-config files core/src/meson.build generates and fails on a record that names one without the other: 19 findings on the old manifest, none now; a planted record proves the finder. Verification on a buildx builder capped at 4 CPUs and 8 GiB: the licence-check stages of the controller, go-server, node, CLI and server images pass; the controller stage with the old manifest fails with the four lines above. rc1-tester tests: 501 passed. Signed-off-by: Lusoris <lusoris@proton.me>
This was referenced Oct 8, 2026
fix(release): ship libvmafx with libvmaf in the GPU and tester images and the release download
#2606
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
RC4 work package 6: the library is split into
libvmafx.so.1(engine + VMAFx API,vmafx_*exports only) and a thinlibvmaf.so.3that implements the libvmaf API on the exportedvmafx_*functions alone, so a libvmaf function the new API cannot express fails the link (ADR-1852 decision D3). WP5 #2288 is on master; this PR's diff is against master. Implementation decisions are in ADR-2094 (Accepted by maintainer popup on 2026-10-06: "Forced header (Recommended)", cited in its References; it implements ADR-1852 D3 and D7 and design section 2.11).What changes:
[[compat]]entries incore/api/vmafx.toml: 14 generated shims, 5 generated glue, 63 hand-written (core/src/compat/libvmaf/, each declaring thevmafx_*functions it calls), 25 declared engine exceptions (the 5 CUDA and 20 SYCL functions keep the engine's definition until their WP3 lane lands; CUDA device frames are feat(api): import CUDA device frames with event fences and GL textures (RC4 WP3, ADR-2023) #2277). The 5 HIP and 9 Metal functions are compat functions in builds without those backends and engine exceptions in builds with them, until their WP3 lanes. Every exception names what ends it (until) and is exported fromlibvmafx.so.1in a version nodeVMAF_LEGACY_<BACKEND>(core/src/vmafx_legacy_<backend>.map).libvmafxlinks the engine withhide_unlisted = true(local: *;);libvmaf.so.3is a separate target linked with-Wl,--no-undefinedagainstlibvmafx.so.1. Every engine translation unit compiles with the generatedcore/src/vmafx/engine_names_gen.h, which names the engine's own libvmaf bodiesvmaf_engine_<stem>, so upstream syncs port into the engine sources unchanged.libvmafx.pcandlibvmaf.pc(Requires: libvmafx) are generated;pkg-config --libs libvmafgives-lvmaf -lvmafx.VMAFX_0.1): the libvmaf bridge for pictures, models and model sets, context-owned preallocated frames, perceptual side data, the context backend, frame converters, the tiny-AI functions (vmafx/dnn.h), the embedded MCP server (vmafx/mcp.h) andvmafx_backend_name().VMAF_DEPRECATED("use <successor>"), a warning only with-DVMAF_ENABLE_DEPRECATION_WARNINGS(opt-in in 1.0, default in 1.1, removal in 2.0).scripts/ci/upstream-ffmpeg-compat.sh(unpatched FFmpegFFMPEG_TAG,libvmaffilter,--cudaforlibvmaf_cuda) andscripts/ci/upstream-gstreamer-compat.sh(unpatched gst-plugins-badvmafelement, pinnedGST_PLUGINS_BAD_VERSIONwith a Renovate git-tags manager, CI conformance: upstream GStreamer vmaf element on the libvmaf.so.3 compatibility layer #2237) build against an installed library and compare scores with the CLI and a reference library as exact text;.github/workflows/upstream-consumers.ymlruns both.libvmafx.so*, Go builds link-lvmaf -lvmafx, the MCP CI lane runs the compat checks.--abi-check --against-ref origin/rc4/api-generation-prototype:definition is an append-only successor of origin/rc4/api-generation-prototype (347 additions); againstorigin/rc4/api-wp5-provenance: 84 additions. No break of a prototype entry, so no new minor.no ffmpeg-patches update needed: the libvmaf headers gain only
VMAF_DEPRECATEDmarkers, empty unless the consumer definesVMAF_ENABLE_DEPRECATION_WARNINGS; unpatched upstream FFmpeg n9.0.2 builds against the split library and scores identically (below).Landing (Q-083)
Lands bottom-up per Q-083:
v1.0.0-rc.3is tagged, so RC4 lands through the merge train, one API PR at a time. Its base #2288 is on master; this PR was rebased onto masterae8ccbb23(the base's commits dropped), retargeted tomaster, and #2277 (WP3 CUDA) follows once it lands. The API docs keep marking the VMAFx API as a preview (docs/api/vmafx/index.md, ABI 0.x) until the rc.4 cut. ADR-2094 is Accepted.ABI check against master:
python3 scripts/codegen/vmafx-api.py --abi-check --against-ref origin/master->definition is an append-only successor of origin/master (86 additions). Additions only, nodeVMAFX_0.1,abi_version0.1.6 (patch bump over master's).Rebase: generated files take master's side and are regenerated once at the tip (
vmafx-api.py --write, the AGENTS indexes); under render at landing (ADR-2197) this PR carries no rendered file (CHANGELOG.md, the ADR index, tag and title pages,docs/rebase-notes.md): its rebase note is a fragment underdocs/rebase-notes.d/, and the citation map is derived from the tree (ADR-2200);docs/state.mdbyscripts/dev/resolve-state-md-conflict.py.Two contracts master gained since this draft needed changes here, each its own commit: the e2e runtime contract test pinned the libvmaf-only SONAME
findof the image builders, so it now pins both chains and both.pcfiles the split copies (scripts/ci/test_e2e_runtime_contract.py); and master's CI tiers (ADR-2169) route every pull-request workflow throughci-tier.yml, soupstream-consumers.ymlgains thetierjob and runs its 45-minute FFmpeg and GStreamer build at the full tier (master push, dispatch, fork pull request,ci: full), like the FFmpeg lanes;docs/development/upstream-consumers.mdand the workflow table ofdocs/development/ci.mdsay so. The compat library also carries the two libvmaf functions newer than this draft (vmaf_set_sample_range_check_enabled(),vmaf_set_input_colorimetry(), ADR-2094 plan).A CUDA build of the stack failed
test_compat_conformance: aftervmaf_read_pictures()the engine left the caller'sVmafPicturestructs set (its CUDA path releases host translations that are struct copies of them), while the compat library clears them (scoring scenarioconsumed 0 0againstconsumed 1 1).vmaf_engine_read_pictures()now wrapsread_pictures_owned()and clears both caller structs once the context owns the pictures, in every build; the GPU fallback source contract reads the helper's body (state rowT-ENGINE-CUDA-READ-LEAVES-CALLER-PICTURES-2026-10-07, rebase note). On the previous base a CUDA build of the stack gave 0 warnings and--suite=fast482 OK, 0 fail. tidy: cpucore/src/libvmaf.c0 findings.Two more fixes for the stack and master:
core/src/vmafx_legacy_sycl.mapnamedvmaf_sycl_import_d3d11_surface, which only_WIN32builds define, so every Linux SYCL link failed (--no-undefined-version).[[compat]]gainsplatform = "windows": the ELF legacy map leaves the name out, its row incore/src/libvmaf_symbols.txtends in@windows, andcheck_exported_symbols.pyexpects it only on Windows. Two generator tests fail on the previous head. And the Rust tests master gained (test_rust_twin_registryfrom #2086,test_predict_rust_opsandtest_rust_predictfrom #2085,test_rust_cambi_kernelsfrom #2090) asked forlibvmaf.get_static_lib(), which the split removes, someson setup -Denable_rust_features=truefailed: they linkvmaf_test_linknow (grep -c get_static_lib core/test/meson.buildis 0); a Rust-enabled configure succeeds and the four tests pass. The rebase note is the fragmentdocs/rebase-notes.d/vmafx-libvmaf-compat-split.md(ADR-2197). The pre-push assertion-density gate refusedvmaf_mcp_start_sse()incore/src/compat/libvmaf/mcp.c(20 lines, no assertion): it now asserts that a successful start had a configuration and that the bound port fits the 16-bit field (clang-tidy cpu: 0 findings).Local gate on the rebased head (CPU,
-Db_lto=false,-j4, warnings as errors): build 0 warnings;--suite=fast410 OK, 0 fail (test_gpu_picture_pool_uafon its own withMALLOC_PERTURB_=0, as #2547 sets it: OK); codegen tests 132 passed; affected suites: tooling 2479 passed, 6 skipped, 51 shell suites ok;go vet ./...clean andgo test ./...68 packages ok against this build (CGO_LDFLAGS=-L build-cpu/src -lvmaf -lvmafx -lm, asgo-ci.ymlnow links);make test-netflix-golden GOLDEN_NINJA_JOBS=4280 passed, 3 skipped;preflight.sh --stage msvcismpass; Rust-enabled build (-Denable_rust_features=true) 0 warnings,--suite rust5 OK; clang-tidy cpu lane on the 30 translation units this PR touches: 0 findings. CUDA build of the API stack through #2290 on master 4994380 (-Denable_cuda=true, warnings as errors): 0 warnings,--suite=fast489 OK, 0 fail. New compiler-specific constructs are guarded (VMAF_DEPRECATEDhas an MSVC__declspecbranch and an empty fallback; the trace format attribute and the C23va_startworkaround are GCC/clang-only with plain fallbacks). The merge train builds CPU and CUDA and runs its own gates (deliverables, state-md, silent revert,praetorctl audit).Type
feat— new featureChecklist
make format && make lintis green locally — the commit hooks pass.python3 scripts/ci/run_meson_test.py -- -C build --suite=fast --num-processes 4-> 377 OK, 2 expected fail (the planted conformance defects), 0 fail, 1 skipped (test_vmafx_api_abi_append_only: the merge base withorigin/masterhas no definition yet) on a CPU build (-Denable_dnn=disabled -Db_lto=false) atf324808b4, three times for the conformance trio run in parallel; the WP6 tests also pass in a DNN build (ONNX Runtime) and an MCP build (-Denable_mcp=true, every transport);scripts/codegen/tests110 OK;scripts/ci/tests/test_upstream_consumer_scores.py10 passed;make docs-figures,make docs-fragments-check, the public-API Doxygen build (WARN_AS_ERROR, output identical to before the markers) clean./cross-backend-diffand the worst ULP is ≤ 2. — not applicable: no SIMD or kernel code touched; GPU host functions stay the engine's (declared exceptions)..c/.cpp/.cu/.h/.hpp, it has the appropriate license header (EUPL-1.2, fork-authored).!orBREAKING CHANGE:and the migration path is documented below. — not a breaking change: the libvmaf ABI is kept (same SONAME, same symbols, a binary linked against the previouslibvmaf.so.3runs unchanged); the definition is append-only.docs/adr/_index_fragments/<NNNN-slug>.mdand the slug is appended todocs/adr/_index_fragments/_order.txt— ADR-2094, number claimed withscripts/adr/next-free.sh --claim, indexes regenerated.tidy: cpu
core/src/compat/libvmaf/{context,dnn,libvmaf_gen,model,picture,score,status_errno_gen,backend_absent_hip,backend_absent_metal}.c,core/src/vmafx/{bridge,context_frames,convert,tiny_model,mcp_server,frame_pool,model,context}.c,core/src/{libvmaf,model}.c,core/src/{dict,output}.cpp,core/src/feature/psnr.c,core/tools/vmaf.cpp,core/test/{test_compat_conformance,test_compat_conformance_api,test_compat_conformance_scoring,compat_conformance_table_gen,test_vmafx_abi_layout}.c(and through them the forcedengine_names_gen.h, the conformance header and the libvmaf headers with their markers): 0 findings in WP6 files, 0 uncited NOLINT (dev container, clang-tidy 22.1.8,scripts/dev/tidy-lane.sh --only ... cpu). A first run found 21 (two pointer casts throughvoid *between the libvmaf and VMAFx tensor records, now member-wise copies; an analyzer NULL path invmafx_frame_convert(); the C23va_startthe VAList analyzer does not model;getenvin a test; integer-to-pointer state sentinels; out-of-range enum values the tests pass on purpose, now underNOLINTNEXTLINE(...EnumCastOutOfRange) -- ADR-1080ascore/test/dnn/test_tensor_io.cdoes;NULLsentinels in the generated header); all fixed. The run also reports the 7core/include/libvmaf/picture.hfindings WP5 and WP8 recorded (pre-existing on this base, from the C++ TUs;origin/mastercarries their ADR-1470 NOLINT blocks). GPU lanes not measured: no GPU translation unit touched; the forced engine-name header holds#defines only.scripts/dev/preflight.sh --stage msvcism: pass.praetorctl audit --offline: governance gates passed, HISS scan clean for the touched files (the first commit attempt found 7 HISS-07 shell findings in the new consumer scripts: strict mode in the sourced library and six|| true; all fixed).Bug-status hygiene (ADR-0165)
docs/state.mdupdated — new open rowT-VMAFX-COMPAT-BACKEND-EXCEPTIONS-2026-10-06: the CUDA / SYCL (and HIP / Metal when built) libvmaf functions remain engine exceptions until their WP3 lanes; the upstream FFmpeglibvmaf_cudaleg is written but unrun (no device on this host); release artifacts stagelibvmaf.so*withoutlibvmafx.so*until WP12.Netflix golden-data gate (ADR-0024)
assertAlmostEqual(...)score in the Netflix golden Python tests.Golden gate through the compat library (
core/build-goldenbuilt with gcc,GOLDEN_NINJA_JOBS=4,make VENV=<repo>/.venv test-netflix-golden; the CLI the harness runs linkslibvmaf.so.3onlibvmafx.so.1): 280 passed, 3 skipped at8a18b9628and again atf324808b4, the same as WP5.Cross-backend numerical results
Not applicable: no extractor or kernel changed. Scores through the compat library equal the old libvmaf bodies bit for bit (
test_compat_conformance, every score as%a) and the CLI's (FFmpeg and GStreamer below).Performance (if
perforfeat)No scoring path changed. A libvmaf call costs one extra call through the exported
vmafx_*function;vmaf_read_pictures()wraps the pictures as frames by reference (no copy). Generic tiny-AI runs copy up to four tensor records of each kind on the stack (more in the heap), as the engine already does.Deep-dive deliverables (ADR-0108)
docs/adr/2094-libvmaf-compat-library-split.md## Alternatives considered(renaming every body in the source, partial linking of the static engine, GPU compat functions now, frame pools for preallocation, a private copy of engine helpers in the compat library).AGENTS.mdinvariant note — newcore/src/AGENTS.d/vmafx-compat.md, updatedcore/src/AGENTS.d/vmafx-api.md, index entry indocs/development/rebase-sensitive-invariants.md; indexes regenerated.changelog.d/added/api-libvmaf-compat-library.md.docs/rebase-notes.d/vmafx-libvmaf-compat-split.md(fragment, ADR-2197), "libvmaf compat library on libvmafx: engine names, split library targets".User documentation:
docs/api/vmafx/index.md"Migrating from libvmaf.h" (layout, bridge, deprecation warnings, the deliberate differences, backend exceptions); generateddocs/api/vmafx/compat.md(all 107 functions, the VMAFx calls each is written on, kind, library);docs/api/index.md;docs/development/upstream-consumers.md;docs/development/api-generation.md(the[[compat]]table and outputs); generated reference pages for the new headers.Reproducer
python3 scripts/codegen/vmafx-api.py --check python3 scripts/codegen/vmafx-api.py --abi-check --against-ref origin/master meson setup build core -Denable_dnn=disabled -Db_lto=false nice -n 10 ninja -C build -j4 python3 scripts/ci/run_meson_test.py -- -C build test_compat_conformance \ test_compat_conformance_planted_score test_compat_conformance_planted_uncovered \ check_exported_symbols check_exported_symbols_libvmaf test_compat_library_gates \ test_libvmaf_deprecation test_vmafx_api_generator nm -D --defined-only build/src/libvmafx.so.1 | grep -c ' vmaf_' # 0 nm -D --defined-only build/src/libvmaf.so.3 | grep -c ' T vmaf_' # 74 (82 with -Denable_mcp=true) meson setup _inst core --prefix "$PWD/_prefix" -Denable_dnn=disabled -Db_lto=false ninja -C _inst -j4 install scripts/ci/upstream-ffmpeg-compat.sh --prefix "$PWD/_prefix" --against-cli scripts/ci/upstream-gstreamer-compat.sh --prefix "$PWD/_prefix" --against-cliTests
test_compat_conformance(fast)%aand every report format, preallocated pictures, pictures, conversion, perceptual weighting, tiny-AI with a real session onmodel/tiny/smoke_v0.onnxin DNN builds, HIP / Metal absent, MCP) run through the old libvmaf bodies and throughlibvmaf.so.3; traces must be equal and every compat function of the build must have been called (74 in a CPU build, 82 with MCP); the 25 engine exceptions are listed as suchtest_compat_conformance_planted_score,_planted_uncovered(fast,should_fail)check_exported_symbols,check_exported_symbols_libvmaf(fast)libvmafx.so.1exports exactly thevmafx_*list in its version nodes plus the built backends' legacy nodes, novmaf_otherwise;libvmaf.so.3exports exactly the libvmaf functions the generated list gives this build, unversionedtest_compat_library_gates(fast)vmafx_*function links againstlibvmafx.so.1under--no-undefined; one reachingvmaf_engine_initorvmaf_initdoes not; each export check fails on its list with a row removedtest_libvmaf_deprecation(fast)vmafx_version_string; a default build andVMAF_BUILDING_LIBVMAFcompile clean under-Werror; planted missing / wrong / unlisted markers foundtest_vmafx_api_compat.py(15)[[compat]]rules (unknown kind, engine without backend, exception without end, unknown backend, manual source outside the compat directory, undeclared call), engine-name header, legacy maps, symbol list rows, conformance tables, manual sources call exactly their declaredvmafx_*functions (planted mismatch found), checker on planted missing, extra and versioned exportstest_upstream_consumer_scores.py(10)test_gpu_public_header_docs(fast)Gates shown failing on planted defects (measured on this branch)
vmaf_score_at_indexone ulp offtest_compat_conformance(VMAF_COMPAT_PLANT=score)scenario scoring: traces differ at line 38, exit 1picturesskippedtest_compat_conformance(VMAF_COMPAT_PLANT=uncovered)no conformance case calls vmaf_picture2_alloc(and 4 more), exit 1vmaf_initrow removed from the compat listcheck_exported_symbols --library vmafvmafx_submitrow removed from the VMAFx listcheck_exported_symbols --library vmafx--no-undefinedundefined reference to vmaf_engine_inittest_libvmaf_deprecationvmafx_*functiontest_vmafx_api_compat.pya.c: calls vmafx_y, no entry declares ittest_vmafx_api_compat.py-1vs-22, overriding an extractor the model lacks)test_compat_conformance, golden gatecore/src/compat/libvmaf/model.cvmaf_set_sample_range_check_enabledandvmaf_set_input_colorimetrydeclared in the headers without entriestest_libvmaf_deprecationexported but not in core/api/vmafx.toml [[compat]]for bothtest_compat_conformancetraces differ at line 162; fixed inf324808b4(one file per run)Upstream consumers (this host, CPU)
libvmaffilter, 3 frames: built against the compat prefix; IDENTICAL to the same binary on the base library and to the CLI (45 values,PASS).vmafelement, unpatched: IDENTICAL to the base library and the CLI (45 values,PASS).libvmaf_cuda: not run (no CUDA device on this host; the leg printsSKIPand never passes).Builds measured
CPU (
-Denable_dnn=disabled -Db_lto=false); DNN (-Denable_dnn=enabled, ONNX Runtime): conformance equal including real session runs; MCP (-Denable_mcp=true, every transport): 82 exports, conformance equal. GPU builds not measured here (device lanes own them); Windows.deflinking and the darwin export list are WP12.Known follow-ups
scripts/release/build-native-release-artifacts.sh,supply-chain.yml) must stagelibvmafx.so*; install tests; Windows.def; macOS export list.vmaf_set_sample_range_check_enabled()(feat(api): add an opt-in check that refuses samples above 2^bpc - 1 #2221, already on master) andvmaf_set_input_colorimetry()(port(upstream): HDR-VMAF groundwork: input colorimetry, model conversion_target, conversion in read_pictures (#1671-#1675, #1677, #1678) #2300, ADR-2093, in the train). With them the libvmaf surface is 109 functions. Both are compat functions on this branch (done on the restack onto master); the colorimetry maps onto colour carried by VMAFx frames (VmafxFrameDescgrows aVmafxColorat its end, a context function sets the colour of frames that carry none, the submit hands each pair's colour to the engine's conversion state,VMAFX_E_BUSYas libvmaf's-EBUSY), with a conformance scenario on aconversion_targetmodel (ADR-2094 follow-ups).test_libvmaf_deprecationrefuses a header function without an entry: with both declarations planted into this branch's headers it fails naming both.