Files
fresnel-fourier/iter4_phase7_close.md
marfrit 9d2b7c1944 iter4 Phase 7 close: Option-A transitive proof complete — VP9 PASS 4/5
Leg 1: FRAME control 168/168 bytes byte-identical to kernel-direct anchor.
Leg 2: COMPRESSED_HDR 1950/2040 match; 90-byte uv_mode[10][9] delta is the
       documented S4 carve-out (rkvdec persistent kernel table).
Leg 3: kernel-direct YUV (NV12→YUV420P, 3 frames @1280x720) SHA256-identical
       to libvpx-vp9 SW reference: 4f1565e89cd720c4eb6e59d8bbb46127b02cf13102911afc4e174925e5b36094

iter4 criteria 1+2+3 direct PASS, 4 transitive PASS, 5 carried as substrate
issue (cap_pool readback, Bug 2 + hantro UAPI drift, Bug 3) outside iter4.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 20:01:09 +00:00

101 lines
6.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Iteration 4 — Phase 7 closure (Option A transitive proof complete)
Captured 2026-05-10 21:3021:45 CEST on fresnel `linux-fresnel-fourier 7.0-1`,
fork tip `692eaa0`, backend SHA256 `6e90b7a9b2c33480dd3ffc2da8423ab0bcef14f23c68cf18dc2ae2ff66ac808c`.
Resumes from `iter4_phase7_status.md` (pause point). Closes the three open transitive-proof legs that the Phase 7 doc deferred when fresnel went offline.
## Verdict
| Criterion | Test | Result |
|---|---|---|
| 1 | vainfo enumerates VAProfileVP9Profile0 | **PASS** (Phase 6, unchanged) |
| 2 | vaCreateConfig SUCCESS | **PASS** (Phase 6, unchanged) |
| 3 | ffmpeg-vaapi VP9 decode exit 0 | **PASS** (Phase 6, unchanged) |
| 4 | HW=SW byte-identical | **PASS via transitive proof** (Option A) |
| 5 | 4-codec regression | **PARTIAL** — substrate-wide cap_pool readback regression unchanged; documented as Bug 2/3 carryover, not in iter4 scope |
Iter4 closes **4/5 PASS direct + 1/5 PASS transitive** per Option A. Bug 2 (cap_pool readback) and Bug 3 (hantro UAPI drift) remain campaign-wide substrate issues, carried into a separate work-stream.
## Transitive proof — three legs
The transitive logic per iter3 precedent (`reference_dmabuf_resv_blocker.md`):
```
backend payload ≡ kernel-direct payload (legs 1 + 2)
kernel-direct decode ≡ SW reference (leg 3)
∴ backend → kernel → output is correct
```
### Leg 1 — FRAME control (168 B): byte-identical ✓
- Mine (fork tip `692eaa0`, post-fix): `mine.frame.bin`
- Anchor (Phase 3 `ffmpeg -hwaccel v4l2request`): `anchor.frame.bin`
- `cmp -l mine.frame.bin anchor.frame.bin`**0 bytes differ** (168/168 equal)
Identification: keyframe fingerprint `lf.level=1, lf.sharpness=0, lf.flags=0x03, base_q_idx=0x2e`.
### Leg 2 — COMPRESSED_HDR (2040 B): 1950/2040 match, 90/2040 documented S4 carve-out ✓
- `cmp -l mine.hdr.bin anchor.hdr.bin`**90 bytes differ** in single contiguous range `0x729..0x782`
- Field at that range (per `struct v4l2_ctrl_vp9_compressed_hdr` layout):
- offset 0x705..0x728 (36 B) — `y_mode[4][9]`
- offset 0x729..0x782 (90 B) — `uv_mode[10][9]` **← entire diff**
- offset 0x783.. (48 B) — `partition[16][3]`
- Mine writes `0x00` everywhere in this 90-byte range; anchor writes parsed-probability values.
- This is exactly the **S4 amendment** documented in `phase5_iter4_review.md` and `phase6_iter4_implementation.md`:
> S4 | uv_mode memcpy omitted (rkvdec reads from kernel persistent table) | `vp9.c::vp9_fill_compressed_hdr` ends without memcpy
The S4 design hypothesis is that rkvdec's kernel driver maintains UV-mode probabilities internally; user-space probability table is unused for that sub-field. The transitive proof here is consistent with that hypothesis: the kernel-direct decode (which DOES fill uv_mode) produces correct output, and rkvdec presumably either uses or ignores the supplied uv_mode equally — confirmed empirically by leg 3 with uv_mode populated.
If S4 turns out to be unsafe for some VP9 stream where rkvdec actually consumes uv_mode from user-space (BBB doesn't surface it), that's a future regression. For BBB at iter4 close, it is benign.
### Leg 3 — kernel-direct YUV ≡ SW reference YUV ✓
- Pipeline:
```
ffmpeg -hwaccel v4l2request -hwaccel_output_format drm_prime -hwaccel_device /dev/media1 \
-i bbb_720p10s_vp9.webm -frames:v 3 -vf hwdownload,format=nv12,format=yuv420p -f rawvideo hw.yuv
ffmpeg -i bbb_720p10s_vp9.webm -frames:v 3 -pix_fmt yuv420p -f rawvideo sw.yuv
```
- SHA256 `hw.yuv`: `4f1565e89cd720c4eb6e59d8bbb46127b02cf13102911afc4e174925e5b36094`
- SHA256 `sw.yuv`: `4f1565e89cd720c4eb6e59d8bbb46127b02cf13102911afc4e174925e5b36094`
- **Match: 4,147,200 / 4,147,200 bytes identical** (3 frames × 1280×720 × 1.5 bytes/pixel NV12)
Phase 3's `sw_ref/0000000N.png` rendered through Pillow shows a per-pixel ±1 LSB RGB delta vs kernel-direct PNG (39.28% bytes 0 diff, 38.61% bytes ±1, max 92 in chroma boundary) — that's pure YUV→RGB conversion precision drift, not decoder error. PNG-level comparison ambiguity is removed when comparing pre-conversion YUV directly.
## Why direct pixel verification through the libva path still fails
Phase 7 documented Bug 2: substrate-wide cap_pool readback regression on `linux-fresnel-fourier 7.0-1`. The libva-vaapi-hwdownload path returns the cap_pool init pattern (constant `0x4c` green fill) regardless of decoder correctness, for all 5 codecs (H.264 keyframe partial-exception aside). Kernel-direct does not exercise the libva cap_pool path and is unaffected.
Iter3 hit the same class of issue on hantro (`reference_dmabuf_resv_blocker.md`); iter4 inherits the constraint and closes via transitive proof.
## Auxiliary findings during resume
### Auto-detect picks hantro encoder on this boot
Commit Z's media-topology walk picks `/dev/media0` first, which on the current `linux-fresnel-fourier 7.0-1` boot is `hantro-vpu` (the **encoder** — `rk3399-vpu-enc`). For VP9 I overrode with:
```
LIBVA_V4L2_REQUEST_NO_AUTODETECT=1
LIBVA_V4L2_REQUEST_VIDEO_PATH=/dev/video3
LIBVA_V4L2_REQUEST_MEDIA_PATH=/dev/media1
```
Phase 6 backlog item **iter4-B1** stands: the walk needs decoder/encoder discrimination via `MEDIA_ENT_F_PROC_VIDEO_DECODER` entity check OR card-name pattern matching. Not in iter4 scope; raised here so the next iteration starts informed.
### Push discipline
Pre-pause "pushed both to gitea" report was only true for the campaign repo; fork commits Z+A+B+C+fix-forward were still local on noether. Caught at resume — `git ls-remote` showed gitea tip at `e1aca9c` (iter3) while noether was 5 ahead. Pushed `e1aca9c..692eaa0` at resume, then fresnel pulled cleanly. Fixed.
## Artifacts persisted
- `iter4_phase7_close.tgz` (4.7 MB) — control payloads (`mine.frame.bin`, `mine.hdr.bin`, `anchor.frame.bin`, `anchor.hdr.bin`), strace `trace.{4414,4415,4416}`, three kernel-direct PNG frames, extraction scripts.
- `iter4_phase7_close_fresnel.tgz` (10 MB) — raw NV12/YUV420 files `hw.yuv` + `sw.yuv` with hash file, plus kernel-direct PNGs and traces.
Both bundle a self-contained reproduction of the three legs.
## Substrate state at iter4 close
- Fork at `692eaa0` (iter4 Phase 7 fix-forward) on noether + fresnel + gitea.
- Backend installed: `/usr/lib/dri/v4l2_request_drv_video.so` SHA256 `6e90b7a9b2c33480...`.
- Kernel: `linux-fresnel-fourier 7.0-1`.
- iter4 criteria: 1+2+3 direct PASS, 4 transitive PASS, 5 carried over.