# iter8 fork build + vainfo smoke — fresnel 2026-05-07

Companion evidence for Phase 0 deliverable #3.

## TL;DR

The libva-v4l2-request-fourier fork **loads and initializes cleanly on RK3399** (Pinebook Pro / fresnel) at master tip `65969da` (iter8 Phase 4). Both V4L2 decode bindings advertise profiles via `vainfo`:

- **rkvdec** (`/dev/video3` + `/dev/media1`): H.264 (5 profiles incl. MVC/Stereo), HEVCMain.
- **hantro-vpu-dec** (`/dev/video5` + `/dev/media2`): MPEG-2 Simple, MPEG-2 Main.

This is the first proof that ohm-hardened iter1..iter5 + iter6..iter8 incremental work doesn't trip a cross-SoC initialization bug on RK3399. Substrate confirmed.

## Substrate decision: master, not pinned-iter5

[`README.md`](../../README.md) and [`phase0_findings.md`](../../phase0_findings.md) describe the substrate as "iter5-end fork." That framing was correct on 2026-05-05 when libva-multiplanar reached the iter5 close. Between then and the fresnel-fourier scaffold (2026-05-07) the libva-multiplanar campaign continued through iter6 / iter7 / iter8 (see `~/src/libva-multiplanar/phase0_findings_iter{6,7,8}.md`). Building from master picks up the iter6–iter8 hardening:

| Tip commit | Subject | Author |
|---|---|---|
| 65969da | iter8 Phase 4: tests/run_perf_binding_cell.sh — perf binding cell harness | claude-noether |
| dcaa1f1 | docs: clarify Rockchip silicon — PineTab2 is RK3566, not RK3568 | claude-noether |
| 7bd0818 | iter7 Phase 7 finalization: OUTPUT-pool teardown + test refinements | claude-noether |
| 988b848 | iter7: A+B+C — slot-leak fix, cap_pool harness, msync verify harness | claude-noether |
| a09c03c | iter6 fix: per-OUTPUT-slot request_fd binding via REINIT | claude-noether |
| c8b6ede | iter5 sweep follow-up: remove additional DEBUG sites flagged by Phase 5 review | Markus Fritsche |
| b993355 | iter5 Track E: move LAST_OUTPUT_WIDTH/HEIGHT from process-global to per-driver-data | Markus Fritsche |
| 843febc | iter5 sweep: remove iter1 slice_header parse + VAPicture dump + Sync RETURN trace | Markus Fritsche |

A correction commit on this campaign should land updating `README.md` and `phase0_findings.md` to read "iter8-end fork" or simply "current master."

## Build environment (verified)

- Host: fresnel, kernel `6.19.9-99-eos-arm`.
- Toolchain: gcc 15.2.1, ld.bfd 2.46, ninja 1.13.2, meson 1.11.1, pkgconf 2.5.1.
- Dependencies: libva 1.23.0, libdrm 2.4.131, ccache available (used).
- All 8 kernel UAPI symbols required by the fork are present in `/usr/include/linux/`: `MEDIA_IOC_REQUEST_ALLOC`, `V4L2_CTRL_WHICH_REQUEST_VAL`, `V4L2_PIX_FMT_MPEG2_SLICE`, `V4L2_PIX_FMT_SUNXI_TILED_NV12`, `V4L2_PIX_FMT_HEVC_SLICE`, `V4L2_PIX_FMT_VP9_FRAME`, `V4L2_PIX_FMT_VP8_FRAME`, `V4L2_PIX_FMT_H264_SLICE`.

## Compiled-in codecs

From `src/meson.build`:

```
sources = [
    ...
    'mpeg2.c',
    'h264.c',
    'h264_slice_header.c',
    'request_pool.c',
    'cap_pool.c',
#    'h265.c'
]
```

In the build:

- ✅ MPEG-2 (mpeg2.c)
- ✅ H.264 (h264.c + h264_slice_header.c)
- ❌ HEVC (h265.c **commented out** in src/meson.build)
- ❌ VP8 (no source file)
- ❌ VP9 (no source file)

## Build + install

Configuration: `meson setup --prefix=/usr build`. Default release mode; no debug toggles set.

Build: `ninja -C build`. 20 compile/link targets, two harmless warnings from `request_pool.c` about `struct v4l2_ext_control` forward-declaration scope in `v4l2.h` (incomplete-type-in-prototype). Final artefact:

- `build/src/v4l2_request_drv_video.so` — 302152 bytes, ELF aarch64, BuildID `89addcc37a8e6ed2240b0e7ef78789a2e09a2245`, debug_info present (not stripped).
- Single exported symbol matching libva's expected entry: `__vaDriverInit_1_23`.

Install: `sudo ninja -C build install`. Lands at `/usr/lib/dri/v4l2_request_drv_video.so`, mode 0755 root:root.

Size delta vs. ohm baseline: 302 KB on fresnel vs. ~265 KB documented for ohm at iter5-end (per `~/src/libva-multiplanar/phase0_findings.md`). The +37 KB is consistent with iter6–iter8 additions (per-OUTPUT-slot REINIT, slot-leak fix, cap_pool harness, OUTPUT-pool teardown, msync verify) plus possibly different gcc minor; not investigated further.

## vainfo runs

### Pass 1 — rkvdec binding

```
LIBVA_DRIVER_NAME=v4l2_request \
LIBVA_V4L2_REQUEST_VIDEO_PATH=/dev/video3 \
LIBVA_V4L2_REQUEST_MEDIA_PATH=/dev/media1 \
LIBVA_TRACE=/tmp/vainfo_rkvdec.trace \
vainfo --display drm --device /dev/dri/renderD128
```

Output:

```
vainfo: VA-API version: 1.23 (libva 2.22.0)
vainfo: Driver version: v4l2-request
vainfo: Supported profile and entrypoints
      VAProfileH264Main               :	VAEntrypointVLD
      VAProfileH264High               :	VAEntrypointVLD
      VAProfileH264ConstrainedBaseline:	VAEntrypointVLD
      VAProfileH264MultiviewHigh      :	VAEntrypointVLD
      VAProfileH264StereoHigh         :	VAEntrypointVLD
      VAProfileHEVCMain               :	VAEntrypointVLD
```

LIBVA_TRACE log shows `vaInitialize SUCCESS`, `vaQueryConfigProfiles SUCCESS`, six `vaQueryConfigEntrypoints SUCCESS` — all clean.

### Pass 2 — hantro-vpu-dec binding

```
LIBVA_DRIVER_NAME=v4l2_request \
LIBVA_V4L2_REQUEST_VIDEO_PATH=/dev/video5 \
LIBVA_V4L2_REQUEST_MEDIA_PATH=/dev/media2 \
vainfo --display drm --device /dev/dri/renderD128
```

Output:

```
vainfo: Supported profile and entrypoints
      VAProfileMPEG2Simple            :	VAEntrypointVLD
      VAProfileMPEG2Main              :	VAEntrypointVLD
```

### Pass 3 — no env vars (sanity)

```
LIBVA_DRIVER_NAME=v4l2_request vainfo
```

Driver loads, but enumerates **zero** profiles. As expected — `request.c:149` reads `LIBVA_V4L2_REQUEST_VIDEO_PATH` to choose the V4L2 device; without it, no probe target exists and nothing gets advertised. Graceful no-op rather than crash.

## Findings worth flagging for Phase 1+

### F1. HEVC profile enumerated despite `h265.c` not compiled

`src/config.c:146` probes the V4L2 device for `V4L2_PIX_FMT_HEVC_SLICE` capability:

```c
profiles[index++] = VAProfileHEVCMain;  // line 151, gated by V4L2 probe
```

On rkvdec the probe succeeds (because `/dev/video3` `--list-formats-out` advertises `S265`), so `vaQueryConfigProfiles` returns HEVCMain. But `src/h265.c` is excluded from the meson build. The control-submission path (probably a `picture.c` switch over `surface->profile`) must therefore either have HEVC paths also commented out, or carry an unreachable guard, since the link succeeded with no h265 symbols defined. **A consumer that calls `vaCreateConfig(VAProfileHEVCMain)` will get a config back** (config.c:171 includes HEVCMain in the validation list) **and can attach buffers, but the actual control submission will likely fail or fault** at the dispatch step. This is a wedge between the enumerator's optimism and the build's honesty. Phase 2 source-read should map exactly which call returns what for HEVC today; Phase 4 should decide whether to:

- (a) re-enable `h265.c` in the meson build (substrate revival — the iter5 sweep stripped it for build-cleanly reasons; on rkvdec the kernel surface is mainline and may "just work"),
- (b) gate enumeration on a compile-time `#ifdef` so `h265.c` exclusion implies HEVCMain isn't advertised,
- (c) leave both stripped permanently if HEVC can't be made to work on RK3399 cheaply.

### F2. MultiviewHigh + StereoHigh enumerated unconditionally on H.264-capable nodes

`config.c:140-141` adds these two profiles without any V4L2-side capability check:

```c
profiles[index++] = VAProfileH264MultiviewHigh;
profiles[index++] = VAProfileH264StereoHigh;
```

RK3399 rkvdec hardware does not support MVC. A consumer asking for these profiles will succeed at config creation but fail at decode. For binding-cell scoping, fresnel-fourier's H.264 cell should explicitly use `VAProfileH264Main` or `VAProfileH264High` and never the MVC/Stereo variants. (mpv and Firefox don't ask for them anyway; chromium-fourier shouldn't either. Low risk.)

### F3. Per-codec routing is one-env-var-per-process

`request.c:149`/`171` reads `LIBVA_V4L2_REQUEST_VIDEO_PATH` and `_MEDIA_PATH` once at init and binds to that single V4L2 device. That means **a single libva backend instance can talk to either rkvdec or hantro-vpu-dec, but not both.** For an mpv invocation that wants H.264 (rkvdec) and a separate one that wants MPEG-2 (hantro), the env vars differ.

This was one of the open Phase 0 questions ("rkvdec node device path env var — backend likely uses LIBVA_V4L2_REQUEST_VIDEO_PATH for one node. Two nodes may need a richer config: explicit per-codec routing or a probe loop. Phase 4 design decision."). The smoke-test confirms the answer: **one env var, one device, no in-backend probe.** Phase 4 work for fresnel-fourier — if a single Plasma session needs to decode both H.264 and MPEG-2 — will need either:

- (a) per-process env override (e.g., a wrapper script for mpv that picks the device based on file extension or codec hint), or
- (b) a backend code change to discover both decode nodes and route by VAProfile at config time.

Option (b) is the more general fix and matches the codec-coverage scope this campaign locked. Option (a) is the pragmatic hack until then.

### F4. The `Trying display: drm` path works without a Plasma session being involved

Pass 1/2 used `--display drm --device /dev/dri/renderD128`. That's the headless render-node path. It succeeded without Wayland or X11 in the consumer process, which means **vainfo as a smoke test does not require the SDDM watchpoint to be green**. Useful for future scripted smoke runs (CI-style) — they can run from an SSH session with no DISPLAY/WAYLAND_DISPLAY.

The `pacman -Qq linux-api-headers` query returned the package name without a version (likely a pacman query format quirk), but the symbol-presence sweep confirms the headers are functionally complete.

## Where this leaves Phase 0

| Deliverable | Status |
|---|---|
| #1 SDDM recovery | done as watchpoint (`phase0_recovery_2026-05-07.md`) |
| #2 V4L2 inventory | done (`v4l2_inventory.txt` + `v4l2_inventory_findings.md`) |
| **#3 fork build + vainfo smoke** | **done (this file) — substrate confirmed** |
| #4 H.264 baseline trace | next; needs an H.264 fixture (#5 first) |
| #5 per-codec test fixtures | next |
| #6 chromium-fourier cross-validator trace | needs #5 |
| Phase 0 close — commit + push as claude-noether | last; depends on #4 + #6 |

## Re-run incantation (for future sessions)

```bash
ssh fresnel '
cd ~/src/libva-v4l2-request-fourier
git pull --ff-only
ninja -C build
sudo ninja -C build install

LIBVA_DRIVER_NAME=v4l2_request \
LIBVA_V4L2_REQUEST_VIDEO_PATH=/dev/video3 \
LIBVA_V4L2_REQUEST_MEDIA_PATH=/dev/media1 \
vainfo --display drm --device /dev/dri/renderD128
'
```
