Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .claude/hooks/auto-format-on-edit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,17 @@ fi

[[ -z "$file" || ! -f "$file" ]] && exit 0

# Never reformat files that are explicitly upstream-touched or generated
# Never reformat files that are explicitly upstream-touched or generated.
# Vendored verbatim mirrors (pelorus interop ABI, ADR-1113) must stay
# byte-identical to their single source of truth — clang-format would
# rewrap them and break the sync guard (scripts/sync-pelorus-interop.sh).
case "$file" in
*/subprojects/* | */build/* | */testdata/*.yuv | *.json | *.onnx | *.pkl)
exit 0
;;
*/core/src/interop/pelorus_* | */core/include/libvmaf/pelorus/*)
exit 0
;;
esac

case "$file" in
Expand Down
11 changes: 11 additions & 0 deletions .cppcheck-suppressions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@
# Upstream Netflix svm.cpp — entirely vendored, not modified by fork.
*:core/src/svm.cpp

# Vendored Pelorus interop ABI — verbatim mirror of VMAFx/pelorus@835e097,
# byte-identical to its single source of truth (ADR-1113). Not editable by the
# fork; lint findings must be fixed upstream in pelorus and re-synced via
# scripts/sync-pelorus-interop.sh.
*:core/src/interop/pelorus_interop.c
*:core/src/interop/pelorus_deband_params.c
*:core/src/interop/pelorus_version.c
*:core/include/libvmaf/pelorus/interop.h
*:core/include/libvmaf/pelorus/pelorus.h
*:core/include/libvmaf/pelorus/deband.h

# False positives we've reviewed; revisit yearly.
unusedFunction:core/src/feature/*_avx512.c
unusedFunction:core/src/feature/*_avx2.c
Expand Down
15 changes: 15 additions & 0 deletions .github/workflows/lint-and-format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,13 @@ jobs:
# lint a file that no longer exists on disk; without this filter
# the script passes a missing path to clang-tidy and fails with
# `clang-diagnostic-error: no such file or directory`.
# - core/src/interop/pelorus_*.c +
# core/include/libvmaf/pelorus/ — vendored verbatim mirror of
# the Pelorus interop ABI (ADR-1113), byte-identical to its
# single source of truth; cannot absorb fork-local lint fixes
# without breaking scripts/sync-pelorus-interop.sh. Same posture
# as core/src/svm.cpp (vendored, excluded). Fix upstream in
# pelorus and re-sync.
if [ "$GH_EVENT_NAME" = "pull_request" ]; then
files=$(git diff --name-only --diff-filter=d \
"origin/${PR_BASE_REF}...HEAD" \
Expand All @@ -186,6 +193,8 @@ jobs:
| grep -v '^core/test/test_hip' \
| grep -v '^core/test/fuzz/' \
| grep -v '^core/src/compat/win32/' \
| grep -v '^core/src/interop/pelorus_' \
| grep -v '^core/include/libvmaf/pelorus/' \
| tr '\n' ' ')
elif [ "$GH_EVENT_NAME" = "push" ]; then
before="$PUSH_BEFORE_SHA"
Expand Down Expand Up @@ -213,6 +222,8 @@ jobs:
| grep -v '^core/test/test_hip' \
| grep -v '^core/test/fuzz/' \
| grep -v '^core/src/compat/win32/' \
| grep -v '^core/src/interop/pelorus_' \
| grep -v '^core/include/libvmaf/pelorus/' \
| tr '\n' ' ')
else
files=""
Expand All @@ -237,6 +248,8 @@ jobs:
| grep -v '^core/test/test_hip' \
| grep -v '^core/test/fuzz/' \
| grep -v '^core/src/compat/win32/' \
| grep -v '^core/src/interop/pelorus_' \
| grep -v '^core/include/libvmaf/pelorus/' \
| tr '\n' ' ')
fi
else
Expand All @@ -261,6 +274,8 @@ jobs:
| grep -v '^core/test/test_hip' \
| grep -v '^core/test/fuzz/' \
| grep -v '^core/src/compat/win32/' \
| grep -v '^core/src/interop/pelorus_' \
| grep -v '^core/include/libvmaf/pelorus/' \
| tr '\n' ' ')
fi
[ -z "$files" ] && { echo "no C/C++ changes"; exit 0; }
Expand Down
4 changes: 3 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ repos:
hooks:
- id: clang-format
types_or: [c, c++, cuda]
exclude: '^subprojects/|^core/test/data/'
# core/src/interop + core/include/libvmaf/pelorus = vendored read-only
# mirror of VMAFx/pelorus (ADR-1113); must stay byte-identical, never reformatted.
exclude: '^subprojects/|^core/test/data/|^core/src/interop/|^core/include/libvmaf/pelorus/'

- repo: https://github.com/psf/black
rev: 26.5.1
Expand Down
11 changes: 8 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ lint-c: $(BUILD_DIR)
@command -v cppcheck >/dev/null || { echo "cppcheck not found; skipping"; exit 0; }
@echo "--- clang-tidy ---"
@FILES=$$(git ls-files 'core/src/**/*.c' 'core/src/**/*.cpp' 'core/tools/*.c' \
| grep -v '^subprojects/'); \
| grep -v '^subprojects/' \
| grep -v '^core/src/interop/pelorus_'); \
clang-tidy -p $(BUILD_DIR) --quiet $$FILES
@echo "--- cppcheck ---"
cppcheck --enable=all --inline-suppr \
Expand Down Expand Up @@ -173,7 +174,9 @@ lint-md:
format:
@command -v clang-format >/dev/null && \
clang-format -i $$(git ls-files '*.c' '*.h' '*.cpp' '*.hpp' '*.cu' '*.cuh' \
| grep -v '^subprojects/' | grep -v '^core/test/data/') || true
| grep -v '^subprojects/' | grep -v '^core/test/data/' \
| grep -v '^core/src/interop/pelorus_' \
| grep -v '^core/include/libvmaf/pelorus/') || true
@command -v black >/dev/null && black python/ ai/ scripts/ 2>/dev/null || true
@command -v isort >/dev/null && isort python/ ai/ scripts/ 2>/dev/null || true
@command -v shfmt >/dev/null && shfmt -w -i 2 -ci $$(git ls-files '*.sh') || true
Expand All @@ -183,7 +186,9 @@ format-check:
@command -v clang-format >/dev/null && \
clang-format --dry-run --Werror \
$$(git ls-files '*.c' '*.h' '*.cpp' '*.hpp' '*.cu' '*.cuh' \
| grep -v '^subprojects/' | grep -v '^core/test/data/') || true
| grep -v '^subprojects/' | grep -v '^core/test/data/' \
| grep -v '^core/src/interop/pelorus_' \
| grep -v '^core/include/libvmaf/pelorus/') || true
@command -v black >/dev/null && black --check python/ ai/ scripts/ 2>/dev/null || true
@command -v isort >/dev/null && isort --check-only python/ ai/ scripts/ 2>/dev/null || true
@command -v shfmt >/dev/null && shfmt -d -i 2 -ci $$(git ls-files '*.sh') || true
Expand Down
27 changes: 27 additions & 0 deletions changelog.d/added/1113-vendor-pelorus-interop-abi.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Vendored Pelorus interop ABI + sync guard + conformance gate (ADR-1113)

Vendored the Pelorus <-> vmafx data-plane interop ABI into vmafx as a pinned,
read-only, append-only mirror of `VMAFx/pelorus@835e097`. The ABI is a small,
CPU-only, dependency-free C surface (a self-describing per-frame side-data blob
plus its pack/parse pair); it stays single-sourced in Pelorus (Pelorus
ADR-0103) and vmafx mirrors it byte-for-byte so the two repos build and test
independently with no submodule and no Vulkan coupling.

- **Vendored files** (byte-identical to Pelorus except a `VENDORED FROM … DO NOT
EDIT` banner + a `pelorus/<x>.h` → `libvmaf/pelorus/<x>.h` include rewrite):
- `core/include/libvmaf/pelorus/{pelorus,interop,deband}.h`
- `core/src/interop/pelorus_{interop,deband_params,version}.c` (compiled into
`libvmaf`)
- **Sync guard** `scripts/sync-pelorus-interop.sh` — pins
`PELORUS_VENDOR_SHA=835e097`, reads the pinned commit's git tree object,
diffs the mirror, and exits non-zero on any drift (`--update` re-vendors after
a deliberate ABI-minor bump). Records the synced `PELORUS_ABI_MINOR` (0).
- **Shared conformance fixture** `core/test/test_pelorus_interop.c` — the same
seven vectors Pelorus runs (roundtrip, forward-compat, abi-major-mismatch,
foreign-buffer, header-only, truncation, deband-params), wired into the `fast`
suite. A green run proves vmafx's vendored parser is byte-compatible with
Pelorus's writer. The `_Static_assert` size locks in `interop.h` are enforced
at compile time in every including translation unit.

Docs: `docs/api/pelorus-interop.md`. The reader side (perceptual weighting) and
the autotune control plane are separate, later workstreams.
111 changes: 111 additions & 0 deletions core/include/libvmaf/pelorus/deband.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
*
* Copyright 2026 Lusoris
*
* Licensed under the BSD+Patent License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSDplusPatent
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

/*
* VENDORED FROM VMAFx/pelorus@835e097 — DO NOT EDIT. Append-only ABI; single
* source of truth is pelorus. Re-sync via scripts/sync-pelorus-interop.sh.
* See docs/adr/1113-vendor-pelorus-interop-abi.md.
*
* Local edit vs the pelorus original: the intra-pelorus #include below is
* rewritten from "pelorus/pelorus.h" to "libvmaf/pelorus/pelorus.h" so it resolves
* under core/include/. Nothing else is changed.
*/

/*
* deband.h — parameter contract for the Pelorus smart-deband filter.
*
* Shared by the FFmpeg vf_pelorus_deband_vulkan filter (which mirrors these
* fields into its compute-shader push constants) and the vmafx autotune loop
* (which sweeps deband strength against VMAF as the oracle). The wire form of
* the push constants lives in shaders/pelorus_deband.comp; this struct is the
* host-side, AVOption-addressable view of the same parameters.
*
* Algorithm: a psychovisual debander modeled on flash3kyuu_deband (f3kdb) with
* the core flat-test taken from FFmpeg's vf_deband.c (4-tap rotated reference
* sampling, per-plane threshold, average-or-keep), extended with TPDF/blue-noise
* grain injection, a local-variance detail-protection mask, and 16-bit-internal
* dither-down. See docs/metrics/deband.md and docs/research/0101-smart-deband.md.
*/
#ifndef PELORUS_DEBAND_H
#define PELORUS_DEBAND_H

#include <stdint.h>

#include "libvmaf/pelorus/pelorus.h"

#ifdef __cplusplus
extern "C" {
#endif

enum pel_deband_sample_mode {
PEL_DEBAND_SAMPLE_COLUMN = 1, /* 2 vertical taps */
PEL_DEBAND_SAMPLE_SQUARE = 2, /* 4 rotated taps (== vf_deband; DEFAULT) */
PEL_DEBAND_SAMPLE_ROW = 3, /* 2 horizontal taps */
PEL_DEBAND_SAMPLE_SQUARE_ROT = 4 /* 4 taps + per-frame ring rotation */
};

enum pel_deband_blur_mode {
PEL_DEBAND_BLUR_AVERAGE = 0, /* flat iff |center-avg| < thr (vf_deband=1) */
PEL_DEBAND_BLUR_ALLREFS = 1 /* flat iff every tap within thr (vf_deband=0)*/
};

enum pel_deband_dither_mode {
PEL_DEBAND_DITHER_NONE = 0,
PEL_DEBAND_DITHER_BAYER8 = 1, /* ordered 8x8 Bayer */
PEL_DEBAND_DITHER_BLUENOISE = 2 /* hashed TPDF (DEFAULT) */
};

/* Flag bits, mirrored 1:1 into the shader push-constant `flags` word. */
enum pel_deband_flags {
PEL_DEBAND_FLAG_DYNAMIC_GRAIN = 1u << 0, /* re-seed grain each frame */
PEL_DEBAND_FLAG_PROTECT_DETAIL = 1u << 1,/* gate off textured regions */
PEL_DEBAND_FLAG_COUPLING = 1u << 2 /* all planes must agree (4:4:4) */
};

/*
* Deband parameters. Thresholds and grain amplitudes are NORMALIZED in [0,1]
* relative to full range, independent of bit depth (the shader works in a
* 16-bit internal domain). Per-plane order is {Y, Cb, Cr, A}.
*/
typedef struct PelorusDebandParams {
int32_t range; /* reference-sampling radius in pixels (1..31) */
float thr[4]; /* per-plane normalized threshold */
float grain[4]; /* per-plane normalized grain amplitude */
float softness; /* blend transition width (0 = hard vf_deband) */
float detail_thr; /* detail-mask activity threshold (normalized) */
int32_t sample_mode; /* enum pel_deband_sample_mode */
int32_t blur_mode; /* enum pel_deband_blur_mode */
int32_t dither_mode; /* enum pel_deband_dither_mode */
uint32_t planes; /* bitmask of planes to process (default 0xF) */
uint32_t flags; /* enum pel_deband_flags */
int32_t out_depth; /* output bit depth (8/10/12/16; 0 = same as input) */
} PelorusDebandParams;

/* Fill p with the dark-scene pre-encode defaults (range=15, square sampling,
* average+soft blend, blue-noise dynamic grain, detail protection on). */
void pel_deband_params_default(PelorusDebandParams *p);

/* Validate p against documented ranges. Returns PEL_OK or PEL_ERR_RANGE; on
* error and if `what` is non-NULL, *what points at a static field name. */
pel_result pel_deband_params_validate(const PelorusDebandParams *p,
const char **what);

#ifdef __cplusplus
}
#endif
#endif /* PELORUS_DEBAND_H */
Loading
Loading