Skip to content

Commit 835e097

Browse files
lusorisclaude
andcommitted
feat: scaffold Pelorus + smart-deband flagship (vf_pelorus_deband_vulkan)
Pelorus is a zero-copy GPU pre-encode pipeline: Vulkan compute + FFmpeg filters that fix the psychovisual flaws of a hardware encoder in VRAM before it sees the pixels, to close the BD-rate gap to CPU encoders. Codec-agnostic (deband/denoise/motion help HEVC and AV1 alike; FGS covers AV1 AOM + HEVC/VVC H.274). Sibling of VMAFx/vmafx (the quality oracle, which dropped Vulkan). This initial scaffold lands: - libpelorus: the Pelorus<->vmafx side-data interop ABI (interop.h/.c, versioned, UUID-keyed, append-only) + the smart-deband parameter contract, with a shared conformance fixture. Builds + tests green (meson). - vf_pelorus_deband_vulkan: the flagship f3kdb-style smart deband (inline GLSL + side-data emit), shipped as ffmpeg-patches/0001 against n8.1.1 (applies via git am --3way). Standalone reference shader compiles to SPIR-V. - Conventions adapted from golusoris + vmafx: principles.md, 8 ADRs, per-surface docs, changelog fragments, per-package AGENTS.md, ADR allocator. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0 parents  commit 835e097

55 files changed

Lines changed: 4730 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.clang-format‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
# Pelorus C style: K&R, 4-space, 100 columns. Matches the vmafx sibling so the
3+
# shared interop translation unit reads identically in both repos.
4+
BasedOnStyle: LLVM
5+
Language: Cpp
6+
IndentWidth: 4
7+
TabWidth: 4
8+
UseTab: Never
9+
ColumnLimit: 100
10+
AllowShortFunctionsOnASingleLine: None
11+
AllowShortIfStatementsOnASingleLine: false
12+
AllowShortLoopsOnASingleLine: false
13+
AlwaysBreakAfterReturnType: None
14+
BinPackArguments: true
15+
BinPackParameters: true
16+
BreakBeforeBraces: Linux
17+
BreakBeforeBinaryOperators: None
18+
BreakBeforeTernaryOperators: false
19+
BreakStringLiterals: false
20+
ContinuationIndentWidth: 4
21+
DerivePointerAlignment: false
22+
PointerAlignment: Right
23+
ReflowComments: false
24+
SortIncludes: false
25+
SpaceAfterCStyleCast: false
26+
SpaceBeforeAssignmentOperators: true
27+
SpaceBeforeParens: ControlStatements
28+
SpacesInParentheses: false
29+
SpacesInSquareBrackets: false
30+
Standard: c++17
31+
IncludeBlocks: Preserve

‎.clang-tidy‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# See docs/principles.md §2 for the philosophy. Enables the families that map
2+
# directly to NASA Power-of-10, JPL-C-STD, CERT-C, and MISRA-C:2012 informative.
3+
# Mirrors the vmafx sibling's profile so the shared interop TU lints identically.
4+
5+
Checks: >
6+
-*,
7+
bugprone-*,
8+
cert-*,
9+
clang-analyzer-*,
10+
concurrency-*,
11+
misc-*,
12+
performance-*,
13+
portability-*,
14+
readability-braces-around-statements,
15+
readability-function-size,
16+
readability-isolate-declaration,
17+
readability-misleading-indentation,
18+
readability-non-const-parameter,
19+
readability-redundant-control-flow,
20+
-bugprone-easily-swappable-parameters,
21+
-misc-include-cleaner,
22+
-misc-no-recursion,
23+
24+
WarningsAsErrors: >
25+
bugprone-integer-division,
26+
bugprone-infinite-loop,
27+
cert-flp30-c,
28+
cert-msc30-c,
29+
clang-analyzer-core.*,
30+
clang-analyzer-deadcode.DeadStores,
31+
clang-analyzer-security.*,
32+
performance-no-int-to-ptr
33+
34+
# Lint Pelorus code; the FFmpeg patch sources under ffmpeg-patches/files/ build
35+
# against FFmpeg's tree and follow FFmpeg conventions, so they are excluded.
36+
HeaderFilterRegex: '^libpelorus/(include|src|test)/.*\.(h|hpp)$'
37+
38+
FormatStyle: file
39+
40+
CheckOptions:
41+
- key: readability-braces-around-statements.ShortStatementLines
42+
value: '2'
43+
# Power-of-10 rule 4 — a function fits a printed page.
44+
- key: readability-function-size.LineThreshold
45+
value: '75'
46+
- key: readability-function-size.StatementThreshold
47+
value: '120'
48+
- key: readability-function-size.BranchThreshold
49+
value: '20'
50+
- key: readability-function-size.NestingThreshold
51+
value: '5'

‎.editorconfig‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
root = true
2+
3+
[*]
4+
charset = utf-8
5+
end_of_line = lf
6+
insert_final_newline = true
7+
trim_trailing_whitespace = true
8+
indent_style = space
9+
indent_size = 4
10+
max_line_length = 100
11+
12+
[*.{c,h,cpp,hpp,cc,comp,glsl,vert,frag}]
13+
indent_size = 4
14+
15+
[*.{js,ts,json,jsonc,yaml,yml,toml}]
16+
indent_size = 2
17+
18+
[*.{md,rst}]
19+
trim_trailing_whitespace = false
20+
21+
[Makefile]
22+
indent_style = tab
23+
24+
[*.{sh,bash}]
25+
indent_size = 4

‎.gitignore‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Build trees (meson/ninja)
2+
/build/
3+
/build-*/
4+
/subprojects/*/
5+
!/subprojects/*.wrap
6+
7+
# Compiled shaders / objects
8+
*.spv
9+
*.o
10+
*.obj
11+
12+
# Local planning / session state (NOT tracked — see .workingdir/STATE.md
13+
# "How to use this file"; the vmafx convention + the user's global rule both
14+
# keep these local rather than committed).
15+
/.workingdir/
16+
/.scratch/
17+
18+
# pkg-config / install staging
19+
/dist/
20+
21+
# Editor / OS cruft
22+
*.swp
23+
*~
24+
.DS_Store
25+
/.cache/
26+
/.vscode/
27+
/compile_commands.json

‎AGENTS.md‎

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<!-- markdownlint-disable MD013 -->
2+
# Agent guide — Pelorus
3+
4+
> Cross-tool context for Claude Code, Cursor, Aider, Codex, Continue, and other
5+
> coding assistants. **Read this before suggesting changes.** Then read the
6+
> per-subdirectory `AGENTS.md` for the area you're touching. Claude Code users:
7+
> [CLAUDE.md](CLAUDE.md) extends this file.
8+
9+
## What this repo is
10+
11+
`Pelorus` is a GPU **pre-encode** pipeline: Vulkan compute filters + FFmpeg
12+
filters that fix the psychovisual flaws of a hardware video encoder *before* it
13+
sees the pixels, entirely in VRAM (zero-copy). The goal is to close the BD-rate
14+
gap between fixed-function GPU encoders (NVENC/AMF/QSV) and slow CPU encoders
15+
(x265/SVT-AV1) — debanding, temporal denoise, film-grain synthesis, and
16+
optical-flow motion hints. These are **codec-agnostic**: deband/denoise/motion
17+
help HEVC (`hevc_nvenc`/`hevc_qsv`/`hevc_vaapi`/`hevc_amf`, rivaling x265) and
18+
AV1 equally; only film-grain is codec-specific (AV1 = AOM, HEVC/VVC = H.274).
19+
20+
Pelorus is the sibling of **vmafx** (the VMAF fork), hosted under the same
21+
`vmafx` GitHub org. vmafx dropped its own Vulkan backend (its ADR-0726), so
22+
Pelorus is the Vulkan home; vmafx stays the **quality oracle**. The two are
23+
bidirectionally wired: Pelorus filters write a shared side-data blob that vmafx
24+
reads for perceptually-weighted scoring, and Pelorus tunes its filter strength
25+
against VMAF using vmafx's autotune loop. See
26+
[docs/architecture/overview.md](docs/architecture/overview.md) and
27+
[docs/principles.md](docs/principles.md).
28+
29+
## Hard rules
30+
31+
1. **Never break the `libpelorus` public ABI without a `Migration:` footer.**
32+
The `PelorusSideData` interop blob is append-only (interop.h R1/R2): add a
33+
field or section bit and bump `PELORUS_ABI_MINOR` — never reorder, resize, or
34+
remove. A breaking change is forbidden; mint a new section bit instead.
35+
2. **All errors flow through `pel_result`.** No bare `return -1;` across a
36+
`libpelorus` API boundary. Every non-void return is checked or `(void)`-cast.
37+
3. **No global mutable state / no static-init side effects.** Lifecycle is
38+
explicit. Banned C functions: `gets`, `strcpy`, `strcat`, `sprintf`,
39+
`strtok`, `atoi`, `atof`, `rand`, `system` (see docs/principles.md §1.2).
40+
4. **Keep the two deband implementations in lockstep.** The standalone
41+
reference shader (`libpelorus/shaders/pelorus_deband.comp`) and the FFmpeg
42+
filter's inline GLSL (`ffmpeg-patches/files/vf_pelorus_deband_vulkan.c`)
43+
implement the same algorithm; edit both together.
44+
5. **Patch-stack sync.** A change to any `libpelorus` surface the FFmpeg patches
45+
consume updates `ffmpeg-patches/files/` + the regenerated patch in the same
46+
PR. Verify with a full series replay (`ffmpeg-patches/test/build-and-run.sh`),
47+
not per-patch `git apply --check`.
48+
6. **Every PR leaves touched files lint-clean** to `-Wall -Wextra -Werror` +
49+
clang-tidy. A `// NOLINT` carries an inline citation.
50+
7. **Every merged commit: 0 warnings · clang-tidy clean · `meson test
51+
--suite=fast` green · the deband shader compiles (glslang).**
52+
53+
See [docs/principles.md](docs/principles.md) for the full Power-of-10,
54+
SEI-CERT-C, style, and Vulkan-usage contract, and [CONTRIBUTING.md](CONTRIBUTING.md)
55+
for the per-PR deliverables (ADRs, per-surface docs, changelog fragments).
56+
57+
## Repository layout
58+
59+
```text
60+
Pelorus/
61+
├── meson.build meson_options.txt # build root (libpelorus + tests + shaders)
62+
│
63+
├── libpelorus/ # the shared core (vendored by vmafx too)
64+
│ ├── include/pelorus/
65+
│ │ ├── pelorus.h # umbrella: version, pel_result
66+
│ │ ├── interop.h # the Pelorus<->vmafx side-data ABI
67+
│ │ └── deband.h # smart-deband parameter contract
68+
│ ├── src/ # interop.c (pack/parse), deband_params.c
69+
│ ├── shaders/ # standalone reference .comp shaders
70+
│ └── test/ # interop ABI conformance fixture
71+
│
72+
├── ffmpeg-patches/ # vf_pelorus_* filters, stacked vs n8.1.1
73+
│ ├── files/ # canonical filter sources (edit here)
74+
│ ├── 0001-*.patch series.txt # generated artifacts + apply order
75+
│ ├── generate.sh # regenerate patches from files/
76+
│ └── test/build-and-run.sh # apply + build + smoke-test gate
77+
│
78+
├── docs/
79+
│ ├── principles.md # the coding + Vulkan-usage contract
80+
│ ├── adr/ # Architecture Decision Records (Nygard)
81+
│ ├── architecture/ api/ metrics/ # per-surface docs (C4, interop, filters)
82+
│ ├── usage/ backends/ development/ # pipeline, Vulkan path, build/release
83+
│ └── research/ # deep-dive digests
84+
│
85+
├── changelog.d/ # Keep-a-Changelog fragments (rendered)
86+
└── AGENTS.md CLAUDE.md CONTRIBUTING.md README.md
87+
```
88+
89+
Per-subdirectory `AGENTS.md` files give unit-level conventions + the
90+
rebase-sensitive invariants.
91+
92+
## Common tasks
93+
94+
| Task | Command |
95+
|---|---|
96+
| Configure + build | `meson setup build && ninja -C build` |
97+
| Fast test gate | `meson test -C build --suite=fast` |
98+
| Install (for the FFmpeg patches) | `ninja -C build install` |
99+
| Regenerate FFmpeg patches | `cd ffmpeg-patches && ./generate.sh` |
100+
| Apply + build + smoke FFmpeg | `ffmpeg-patches/test/build-and-run.sh` |
101+
| Lint | `clang-format --dry-run -Werror` + `clang-tidy` on touched files |
102+
| Reserve an ADR number | `scripts/adr/next-free.sh --claim <slug>` |
103+
104+
## Pinned upstream references
105+
106+
| Component | Version |
107+
|---|---|
108+
| FFmpeg base for the patch stack | `n8.1.1` |
109+
| FFmpeg Vulkan filter model | `libavfilter/vf_gblur_vulkan.c`, `vf_nlmeans_vulkan.c` |
110+
| AV1 film-grain struct mirrored by interop §(d) | `libavutil/film_grain_params.h` |
111+
| vmafx control plane (autotune) | `libvmaf_tune` filter, `vmafx-server` `/v1/score`, `vmaf-mcp` |
112+
113+
## When in doubt
114+
115+
Read [docs/principles.md](docs/principles.md), then the per-subdirectory
116+
`AGENTS.md` for the area you're touching. Decisions live in
117+
[docs/adr/](docs/adr/); the project plan + current status live in
118+
`.workingdir/PLAN.md` + `.workingdir/STATE.md` (local, gitignored).

‎CHANGELOG.md‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<!-- markdownlint-disable MD013 MD024 -->
2+
# Changelog
3+
4+
All notable changes to Pelorus are documented here. The format is
5+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning is
6+
[SemVer](https://semver.org/). The `[Unreleased]` block is **rendered** from
7+
`changelog.d/` fragments by `scripts/release/concat-changelog-fragments.sh` —
8+
do not edit it by hand (see [changelog.d/README.md](changelog.d/README.md)).
9+
10+
## [Unreleased]
11+
12+
### Added
13+
14+
- **`libpelorus`** (`libpelorus/`): the shared core library — the Pelorus⇄vmafx
15+
side-data interop ABI (`pelorus/interop.h`, `pelorus/interop.c`) and the
16+
smart-deband parameter contract (`pelorus/deband.h`). The `PelorusSideData`
17+
blob is a versioned, UUID-keyed, append-only AVFrame side-data format both
18+
`vf_pelorus_*` and vmafx's `vf_libvmaf*` read/write. Ships a shared
19+
conformance fixture (`libpelorus/test/interop_test.c`). ADR-0103, ADR-0105.
20+
- **`vf_pelorus_deband_vulkan`**: the flagship Vulkan compute smart-deband
21+
filter — f3kdb-style flat-test + TPDF/blue-noise grain + a local-variance
22+
detail-protection mask, zero-copy in VRAM. Shipped as
23+
`ffmpeg-patches/0001-add-vf_pelorus_deband_vulkan.patch` against n8.1.1.
24+
ADR-0102, ADR-0104.
25+
- **Reference shader** (`libpelorus/shaders/pelorus_deband.comp`): standalone
26+
f3kdb deband, CI-compiled to SPIR-V.
27+
- **Project scaffold**: principles, ADRs (0001/0100/0102/0103/0104/0105/0106/0108),
28+
per-surface docs, the FFmpeg patch tooling (`generate.sh`,
29+
`test/build-and-run.sh`), and CI-style local gate.
30+
31+
[Unreleased]: https://github.com/vmafx/pelorus/commits/master

‎CLAUDE.md‎

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<!-- markdownlint-disable MD013 -->
2+
# Claude Code guide — Pelorus
3+
4+
> Claude Code-specific guide. For cross-tool conventions read [AGENTS.md](AGENTS.md)
5+
> first; this file extends it. Repo: `vmafx/pelorus` (hosted under the `vmafx`
6+
> GitHub org, alongside `VMAFx/vmafx`). Run `gh repo set-default vmafx/pelorus`
7+
> at the start of a session before any `gh` command.
8+
9+
## What this is
10+
11+
The GPU pre-encode sibling of vmafx. Vulkan compute + FFmpeg filters that
12+
pre-process frames in VRAM to close the BD-rate gap to CPU encoders. Flagship:
13+
`vf_pelorus_deband_vulkan` (smart deband). The shared `libpelorus` interop ABI
14+
is what makes Pelorus⇄vmafx bidirectional.
15+
16+
## How to build / test
17+
18+
```bash
19+
meson setup build && ninja -C build # libpelorus + interop test + shader
20+
meson test -C build --suite=fast # the pre-push gate
21+
ninja -C build install # install so the FFmpeg patches see it
22+
cd ffmpeg-patches && ./generate.sh # regenerate the patch stack
23+
ffmpeg-patches/test/build-and-run.sh # apply + build + smoke the filter
24+
```
25+
26+
## Where the code is
27+
28+
- `libpelorus/include/pelorus/interop.h` — the side-data ABI (the cross-repo
29+
contract; append-only).
30+
- `libpelorus/src/interop.c` — pack/parse; **vendored verbatim by vmafx too**.
31+
- `libpelorus/shaders/pelorus_deband.comp` — standalone reference shader.
32+
- `ffmpeg-patches/files/vf_pelorus_deband_vulkan.c` — the in-tree filter (inline
33+
GLSL). Keep its algorithm in lockstep with the `.comp`.
34+
35+
## Tone
36+
37+
- Be terse. No preamble.
38+
- When changing the `libpelorus` public ABI: write the `Migration:` footer in
39+
the commit body, with before/after C snippets, and bump `PELORUS_ABI_MINOR`.
40+
- When adding a dependency: state the alternative you considered and why this
41+
one wins, in the ADR.
42+
- Never add static-init side effects. Lifecycle is explicit.
43+
44+
## Don't
45+
46+
- Don't edit the two deband implementations independently — the `.comp` and the
47+
filter's inline GLSL must stay in lockstep (AGENTS.md hard rule 4).
48+
- Don't reorder/resize/remove a field in any `PelorusSideData` struct — it is a
49+
frozen wire ABI (interop.h R1/R2). Append only; new meaning = new section bit.
50+
- Don't hand-edit a generated `*.patch` — edit `ffmpeg-patches/files/` and rerun
51+
`generate.sh`.
52+
- Don't `printf`/`fprintf(stderr,...)` from library code paths meant to be
53+
embeddable; return a `pel_result` and let the host log.
54+
- Don't silence a linter without an inline justification next to the `// NOLINT`.
55+
- Don't create new top-level markdown docs unless the task needs them; extend
56+
the existing `docs/` topic tree.
57+
58+
## Per-PR deliverables (mirrors vmafx)
59+
60+
Every non-trivial PR ships, in the same PR: an **ADR** (claim the number with
61+
`scripts/adr/next-free.sh --claim <slug>`), **per-surface docs** under `docs/`,
62+
a **changelog.d fragment**, and — for anything touching a surface the FFmpeg
63+
patches consume — the **regenerated patch**. See [CONTRIBUTING.md](CONTRIBUTING.md).
64+
65+
## Project state
66+
67+
- v0.1.0 scaffold. Landed: `libpelorus` interop ABI + smart-deband flagship
68+
(`vf_pelorus_deband_vulkan`, working) + the patch stack against n8.1.1.
69+
Stubs/roadmap: temporal denoise, FGS estimation + BSF, optical-flow MV hints,
70+
`vf_pelorus_analyze` (measured banding/variance maps).
71+
- Plan + status: `.workingdir/PLAN.md` and `.workingdir/STATE.md` (local).
72+
73+
## Every commit: keep docs + state in sync
74+
75+
- Update `.workingdir/STATE.md` session log with the commit summary.
76+
- Update [README.md](README.md) "Landed so far" when a build-order step lands.
77+
- Update [AGENTS.md](AGENTS.md) layout tree when adding top-level packages.
78+
- Write a per-subpackage `AGENTS.md` for any new module.
79+
- Land the ADR + per-surface docs + changelog fragment in the same PR.

0 commit comments

Comments
 (0)