Skip to content
VMAFxPublic

About

Pelorus — zero-copy GPU pre-encode pipeline (Vulkan compute + FFmpeg filters) that closes the HW-vs-CPU encoder BD-rate gap. Codec-agnostic (HEVC + AV1); sibling of VMAFx/vmafx. EUPL-1.2.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

154 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Pelorus

CI License: EUPL-1.2 OpenSSF Scorecard Sponsor ko-fi

Turn the GPU into an intelligent pre-processor so a hardware encoder rivals a CPU encoder. Pelorus runs Vulkan compute filters on frames while they stay in VRAM, fixing the psychovisual flaws a fixed-function encoder (NVENC/AMF/QSV) can't — dark-scene banding, noise-tax, missing film grain — before the encoder ever sees the pixels. The result is most of the BD-rate win of a slow x265 / SVT-AV1 encode at hardware speed and power.

Codec-agnostic. The deband, denoise, and motion-hint filters are pure pre-processing — they improve HEVC (hevc_nvenc / hevc_qsv / hevc_vaapi / hevc_amf, rivaling x265) exactly as much as AV1 (av1_nvenc, …). Only film-grain synthesis is codec-specific, and Pelorus covers both: AV1 (AOM) and HEVC/H.265 + VVC/H.266 (H.274 / SEI FGC). See docs/principles.md for the engineering contract.

Pelorus is the GPU sibling of vmafx (the VMAF fork), under the same vmafx org. vmafx is the quality oracle; Pelorus is the Vulkan home vmafx is not. They're bidirectionally wired through a shared side-data ABI and vmafx's VMAF-in-the-loop autotune — see docs/architecture/overview.md.

Quick start

# build + install the shared core (the FFmpeg filters link it)
meson setup build && ninja -C build && ninja -C build install

# regenerate and replay the stack at the pinned n9.0.2 commit; both scripts
# first fetch and verify the shared FFmpeg fix series (needs cosign and gh auth)
cd ffmpeg-patches
FFMPEG_REPO=/absolute/path/to/ffmpeg ./generate.sh
FFMPEG_REPO=/absolute/path/to/ffmpeg ./test/build-and-run.sh

# zero-copy: Vulkan decode -> smart deband -> Vulkan Video encode
ffmpeg -init_hw_device vulkan=vk:0 -filter_hw_device vk \
       -hwaccel vulkan -hwaccel_device vk -hwaccel_output_format vulkan \
       -i input.mkv \
       -vf "pelorus_deband_vulkan=range=15:dither=bluenoise:dynamic=1" \
       -c:v hevc_vulkan -pix_fmt vulkan -qp 28 out.mkv  # or: av1_vulkan

NVENC, QSV, VAAPI, and AMF use different FFmpeg hardware-frame domains, so each needs its own boundary instead of AV_PIX_FMT_VULKAN frames. None of them needs hwdownload. NVENC takes a VRAM-to-VRAM hwupload to CUDA on a Vulkan device created with disable_multiplane=1. Behind NVDEC, only graphs that end in one writing filter pass this hop today (#296); with a software decoder the measured graphs pass (8-bit). See NVENC. On Linux, VAAPI and QSV take Pelorus output through hwmap when the last filter runs with tiling=drm (Vulkan output pools). AMF has no Vulkan input in FFmpeg n9.0.2. Only software encoders (libaom, SVT-AV1, x265) need hwdownload. Recipes and the hardware each ran on: the zero-copy pipeline.

Principles

# Rule
1 NASA/JPL Power of 10 (C, original) — bounded loops, checked returns, page-size functions
2 SEI CERT C mandatory; MISRA C:2012 informative — cite rule IDs in review
3 K&R, 4-space, 100 columns (.clang-format, identical to vmafx)
4 C4 + ADRs for every non-trivial decision (docs/adr/)
5 Zero-copy Vulkan: frames never leave VRAM mid-pipeline; validation-clean
6 Append-only interop ABI; never reorder/remove a wire field
7 Per-surface docs + changelog fragment in the same PR as the code

Every merged commit: 0 warnings · clang-tidy clean · meson test --suite=fast green · the deband shader compiles.

Modules

Core — libpelorus/

Module Purpose Key dep
include/pelorus/interop.h + src/interop.c The Pelorus⇄vmafx side-data ABI (pack/parse, append-only, vendored by vmafx) —
include/pelorus/deband.h + src/deband_params.c Smart-deband parameter contract (shared by filter + autotune) —
shaders/pelorus_deband.comp Standalone reference deband shader (f3kdb) glslang
test/interop_test.c Shared ABI conformance fixture —

FFmpeg filters — ffmpeg-patches/

The stack is 22 patches against FFmpeg n9.0.2. It applies on top of the shared FFmpeg fix series (VMAFx/ffmpeg-patches, release pinned in build-config.env), which carries the generic FFmpeg fixes every VMAFx project needs; scripts/fetch-ffmpeg-series.sh fetches and verifies it (ADR-0185, build guide).

Filter Purpose Status
pelorus_deband_vulkan Smart deband (f3kdb): flatten banding + TPDF/blue-noise dither, detail-protected, zero-copy Working
pelorus_dehalo_vulkan Anime/2D dehalo + dering: single-pass GPU port of DeHalo_alpha + FineDehalo, removes the ring next to line-art (luma by default; optional selected chroma); foundation of tune=anime Built (tuning pending)
pelorus_denoise_vulkan Edge-preserving spatio-temporal denoise (the biggest BD-rate lever): NLM-lite joint bilateral + gated temporal averaging over a causal window, with optional motion-compensated warp Working
pelorus_analyze_vulkan Measured banding/variance/edge stats and per-cell maps → interop side data (GPU reduction + readback) Working
pelorus_grain_estimate_vulkan Film-grain param estimation (GPU per-band HF-residual) → PEL_SEC_FILMGRAIN + native AV1 side data Estimator built
pelorus_mc_vulkan Block-matching motion estimator → per-block motion + confidence side data for denoise warping and encoder hints Working
pelorus_fgs (BSF) Inserts a static, AVOption-supplied H.274 FGC SEI into HEVC so a decoder re-synthesizes grain; it does not read estimator frame side data inline Working (static model)
pelorus_scenecut Scene-cut → forced IDR: metadata-only consumer (NOT a Vulkan filter) that reads mc's PEL_SEC_MOTION.has_scene_cut and sets pict_type=I so the encoder opens a fresh GOP at the cut — vendor-neutral, no per-encoder patch; the ffmpeg CLI needs -force_key_frames source Built (BD-rate A/B pending)
pelorus_aa_vulkan Anime warp anti-aliasing (awarpsharp2) + optional line-darkening (FastLineDarken): de-jaggies line-art via blurred-edge-map gradient warp, luma by default with optional selected chroma, no side data; 2nd stage of the anime tune chain Built (defaults unproven)
pelorus_deblock_vulkan Re-encode deblock/dering: gated [1 2 1] low-pass across the prior codec's DCT block grid, smooths blocking so the new encoder skips it as false residual (luma by default; optional selected chroma); runs early, before deband Built (tuning pending)
pelorus_borderfix_vulkan Dirty-line / border repair: clamp the dirty edge band onto the clean interior rect (the zero-copy GPU equivalent of fillborders=smear); all planes, per-plane-pixel widths; runs first, before any other stage Built (deterministic)

Anime tune. For animation, compose the filters into one recommended GPU pre-encode chain — analyze roi=1 (steer bits to the flats) + dehalo (rings) + aa (jaggies) + deband (banding) at 10-bit, then -pelorus_roi 1 on the encoder. A documented, retunable composition, not a new meta-filter. See docs/usage/anime.md (ADR-0125).

Landed so far

  • Step 1 — Core: libpelorus interop ABI + deband param contract + tests.
  • Step 2 — Flagship: vf_pelorus_deband_vulkan smart deband (Vulkan), build-time SPIR-V from its canonical .comp.glsl, side-data emission, patch stack against n9.0.2.
  • Step 3 — vf_pelorus_analyze_vulkan: measured banding/variance/edge stats (GPU reduction + readback) → interop side data.
  • Step 4 — Temporal denoise (vf_pelorus_denoise_vulkan).
  • Step 5 — FGS param estimation (vf_pelorus_grain_estimate_vulkan): GPU per-band HF-residual estimate → PEL_SEC_FILMGRAIN + native AV1 side data.
  • Step 6 — Motion-vector hints: vf_pelorus_mc_vulkan producer (0007 → PEL_SEC_MOTION + PEL_SEC_MOTION_CONF) + the confidence-gated denoise mc=1 warp consumer and the NVENC ME-hint consumer (-pelorus_me_hints, patch 0008). The latter seeds NVENC's external motion search (encode speed only; measured 4090 case showed no gain). ADR-0116 / ADR-0131 / ADR-0114 Tier 3.
  • Step 7 — FGS round-trip: pelorus_fgs BSF (patch 0010) inserts the H.274 FGC SEI into HEVC from static AVOptions (manual estimator-to-BSF mapping; no inline frame-side-data bridge); av1_nvenc carries the estimate into NVENC's hardware AV1 film-grain (NV_ENC_FILM_GRAIN_PARAMS_AV1, -pelorus_film_grain, patch 0011); AV1 software encoders round-trip via native side data. Per-frame HEVC + H.264/VVC legs are follow-ups (ADR-0117 / ADR-0118).
  • Step 8 — Anime dehalo: vf_pelorus_dehalo_vulkan (patch 0014), a single-pass zero-copy GPU port of HAvsFunc DeHalo_alpha + FineDehalo. Removes the bright/dark ring next to line-art (luma by default, with optional selected chroma; pure transform, no interop). Foundation of the planned tune=anime pipeline. Compile-verified and glslang-clean; on-content tuning + the SSIMULACRA2 / edge-region VMAF-NEG / CAMBI proof are follow-ups (ADR-0123 / ADR-0111).
  • Step 9 — Scene-cut → forced IDR: vf_pelorus_scenecut (patch 0016), a metadata-only encoder-steering consumer (NOT a Vulkan filter — no shader, no GPU work). It reads the has_scene_cut flag vf_pelorus_mc_vulkan already emits in PEL_SEC_MOTION and sets pict_type=I on cut frames, so the encoder opens a fresh GOP exactly at the cut. Vendor-neutral (pict_type==I honoured by x264/x265/NVENC/QSV/SVT-AV1 with no per-encoder patch); links libpelorus, not gated on Vulkan; runs on Vulkan frames, no hwdownload needed (ADR-0186). Producer + consumer + mechanism are build-verified; the end-to-end BD-rate A/B (cut-aligned GOPs vs periodic IDR on multi-shot content) is a documented follow-up — no number claimed (ADR-0126 / ADR-0111).
  • Encoder steering (ADR-0114, opt-in -pelorus_roi 1): the analyze roi=1 banding map drives dense per-block delta-QP on NVENC (qpDeltaMap, proven −41% banding), QSV (frame-owned mfxExtMBQP for progressive HEVC+CQP on runtime API 1.28 or newer; sanitizer/compile-complete, on-Intel-HW proof pending; ADR-0146), and the native Vulkan-Video encoders via VK_KHR_video_encode_quantization_map (Tier 2; proven on an RTX 4090 for positive offsets, since that driver accepts no negative delta, and on RADV with Mesa 26.2 for both signs through a host-mapped R32_SINT map; ADR-0166, ADR-0182). The same map also steers SVT-AV1 (libsvtav1) via its per-superblock ROI segment map (SvtAv1RoiMapEvt, ADR-0121, proven on hardware: CAMBI −1.5% at CRF 35, an honest modest gain on a mild synthetic source). Patches 0004 / 0005 / 0009 / 0012 (libaom) / 0013 (SVT-AV1).
  • Closed-loop QP feedback (ADR-0114 step 6 / ADR-0119): new append-only interop section PEL_SEC_QPREPORT (ABI 1.1) carries the encoder's honored per-block QP/bit decisions back into the side-data blob, plus a vendor-neutral reader stub (pel_qp_report_from_blocks). The ABI + pack/parse + conformance test + stub are working; the libavcodec QSV per-block stat-extraction is the HW follow-up.
  • Runnable QP-feedback reader (ADR-0122): a SDK-free x265 --csv stat reader (pel_x265_csv_parse + pel_qp_report_from_x265_frames) populates PEL_SEC_QPREPORT with the encoder's actual per-frame QP/bits/PSNR and a requested-vs-honored honored_fraction — the loop is now demonstrable end-to-end on the HEVC software encoder (the tools/pelorus_qp_report demonstrator; measured, not synthetic). The QSV per-block path stays HW-blocked.
  • Interop ABI 1.4 (ADR-0174 / ADR-0175): PEL_SEC_ENC_TELEMETRY carries one normalised encoder-telemetry record per coded frame (native and H.264-equivalent QP, bits, picture type, PSNR/SSIM, optional maps, a presence bit per field) filled through the FFmpeg-free pelorus/telemetry.h; PEL_SEC_ENCODE_RECORD carries the sha256: digest of the canonical encode record that VMAFx binds to a score (pelorus/encode_record.h); and the motion section names its block edge (block_size_log2). Layout, validation, canonical JSON and digests are tested; the per-encoder adapters and the option-string parsers are follow-ups (#86, #81).
  • Interop ABI 1.5 (ADR-0183): a zero-free carrier form of the blob (COBS under its own UUID) that h264_nvenc and hevc_nvenc write, because NVENC truncates SEI payloads with many zero bytes (#284; rule pinned on an RTX 4090, driver 615.78.08); readers of decoded frames call pel_blob_unwrap(). Across 48 analyze cases on an RTX 4090 no picture loses its side data on either NVENC encoder (hevc_nvenc still strips maps over its budget, ADR-0181).
  • Step 10 — Anime tune chain: vf_pelorus_aa_vulkan (patch 0015) — warp anti-aliasing (awarpsharp2) plus optional line-darkening (FastLineDarken). De-jaggies line-art by warping along the gradient of a blurred edge map, luma by default with optional selected chroma, and side-data-free; the 2nd stage of the anime chain after dehalo.
  • Step 11 — Re-encode deblock: vf_pelorus_deblock_vulkan (patch 0017), a single-pass zero-copy GPU deblock/dering. At the prior codec's DCT block grid it applies a gated [1 2 1] low-pass, smoothing blocking so the new encoder does not spend bits coding it as false residual (luma by default; optional selected chroma). Runs early in the chain, before deband.
  • Step 12 — Border repair: vf_pelorus_borderfix_vulkan (patch 0018), a dirty-line / border repair pass that clamps the dirty edge band onto the clean interior rect — the zero-copy GPU equivalent of fillborders=smear. All planes, per-plane-pixel widths; runs first, before any other stage.

Tooling

meson setup build && ninja -C build      # build (libpelorus + tests + shaders)
meson test -C build --suite=fast         # pre-push gate
ninja -C build install                   # install libpelorus (for the patches)
FFMPEG_REPO=/absolute/path/to/ffmpeg ffmpeg-patches/generate.sh
clang-format --dry-run -Werror libpelorus/**/*.{c,h}   # format check

Status

Pre-1.0 (v0.4.0-rc.2). Public API and the interop ABI may evolve before v1.0.0; the ABI is append-only from here.

License

Pelorus is licensed under the EUPL-1.2, a reciprocal licence, like its sibling vmafx. The files the patch stack adds to FFmpeg stay under FFmpeg's LGPL-2.1-or-later, and vendored files keep their own terms (ADR-0171):

Code Licence Text
libpelorus, tools, scripts, docs, build and CI EUPL-1.2 LICENSE
Filters, bitstream filter, headers and shaders added to FFmpeg (ffmpeg-patches/files/), and the patches LGPL-2.1-or-later LICENSES/LGPL-2.1-or-later.txt
Vendored superpowers skills and Praetor's interfig figure engine MIT LICENSES/MIT.txt

REUSE.toml records the licence of every file, and reuse lint checks it. Releases up to v0.2.2 stay available under BSD-2-Clause-Patent. What the EUPL-1.2 asks of a product that ships libpelorus, and how the licences meet in an FFmpeg build, is in docs/licensing.md.

Support

If Pelorus is useful to you: GitHub Sponsors · Ko-fi.

Standards & Governance

Pelorus adopts the Praetor/HISS policy (ADR-0145) at engine commit 9615f1b9 with a 51-finding legacy baseline (28 HISS-01, 21 HISS-04, two HISS-07). Canonical agent guidance lives in AGENTS.md; generated vendor contexts are checked for drift, and a locked Markdown gate lints the public documentation. The hosted Standards and Documentation Governance workflows run both on every pull request; Standards also fails a baseline that grows against the target branch unless the increase carries a recorded reason. The audit also requires every hosted Meson lane to build with --werror and every tracked C unit to sit in a clang-tidy lane or a dated exception (ADR-0168). The manifest declines no adoption step: it commits Lefthook hooks (opt-in with make hooks-install), a dev container, and a master ruleset that the maintainer applies with praetorctl sync --remote (ADR-0153). The pinned install provides a binary named standardsctl; Praetor's generated text, including the block below, calls the same program praetorctl. Claude Code, Codex, and Gemini CLI sessions run Praetor's pre-tool command hook, so agent users also need a copy named praetorctl on PATH (CONTRIBUTING.md).

Gate Command Description
Native verification make verify-native Builds, tests, formats, lints, and checks changelog rendering
Full verification make verify-all Adds context, HISS, and documentation checks
HISS audit make audit Applies the pinned 51-finding baseline ratchet
Documentation make docs-lint docs-figures Runs the locked Markdown and figure checks (Node.js 22.12 or newer)
Context sync make compile-context Compiles canonical agent guidance to vendor targets
Licensing reuse lint Checks every file's copyright and licence against REUSE.toml (reuse 6.x)

HISS Adopted Documentation Governance

Praetor manages this repository's declared governance policy. This managed block records adoption state; it is not a verification certificate.

Verification: make verify-all runs the repository's configured verification cascade.

HISS Audit: praetorctl audit enforces policy, generated-surface integrity, and the debt ratchet.

Context Sync: praetorctl compile-context --verify verifies every generated agent context against AGENTS.md.

Documentation: make docs-lint enforces locked Markdown style and the private scratch-link policy.

Debt Baseline: .standards-baseline.json anchors the debt ratchet at 51 recorded infractions; audit forbids growth.

About

Pelorus — zero-copy GPU pre-encode pipeline (Vulkan compute + FFmpeg filters) that closes the HW-vs-CPU encoder BD-rate gap. Codec-agnostic (HEVC + AV1); sibling of VMAFx/vmafx. EUPL-1.2.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages