Files
fresnel-fourier/iter4_phase7_close.md
T
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

6.3 KiB
Raw Blame History

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.bin0 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.bin90 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 encoderrk3399-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.