Commit Graph

76 Commits

Author SHA1 Message Date
marfrit 5e2a228cfd iter9 Phase 8 close: α-7 inert as predicted; wire-byte search exhausted
α-7 (monotonic timestamp counter) changed wire bytes but H.264 output
unchanged (71ac099b...). Confirms Phase 5 CRIT-1 prediction: VP9/MPEG-2
PASS via libva with the same v4l2_timeval_to_ns(&ref->timestamp)
pattern; therefore timestamp magnitude was never load-bearing.

5-codec regression sweep: all 4 non-H.264 anchors hold. Zero regression.

Cumulative state after iter8+iter9:
- 6 hypotheses eliminated (libva-readback, slot-binding, stale-residue,
  constraint_set_flags, POC sentinel, reference_ts magnitude)
- libva-vs-kdirect H.264 wire-byte diff is now empirically zero
- α-2 + α-7 shipped as wire-payload hygiene cleanups (zero behavior
  change but cleaner semantics)

iter10 candidate ranking:
1. α-8 OUTPUT bitstream byte dump (compare in-memory slice bytes)
2. α-9 untraced control diff (device-wide controls beyond DECODE_MODE
   + START_CODE)
3. Kernel-side investigation (rkvdec source dive for 16x32 partial-
   decode signature)
4. Pivot to Bug 5 (HEVC) or Bug 6 (VP8)

Two more iterations of diminishing returns suggest either deeper
empirical work (OUTPUT-byte dump or kernel investigation) or pivot
to a different bug.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 13:57:26 +00:00
marfrit 3b0880a97f iter9 Phase 5: CRIT-1 — α-7 contradicted by VP9/MPEG-2 PASS evidence
VP9 (vp9.c:624) and MPEG-2 (mpeg2.c:150,156) use v4l2_timeval_to_ns
identically to H.264. Both PASS via libva with the same gettimeofday-
based giant ns values. If timestamp magnitude were the bug, VP9/MPEG-2
should also fail. They don't.

Reviewer flagged α-7 as low-probability fix and pointed to iter10
kernel-side investigation (M-A vb2_find_buffer_by_timestamp overflow)
if α-7 confirmed inert.

IMP-1: timestamp_counter should live in object_context not driver_data
to avoid multi-context collisions.

Decision: implement α-7 anyway as empirical confirmation (5 min) since
test cost is trivial. If α-7 fails as predicted, iter9 closes PARTIAL
with wire-byte search exhausted; iter10 candidates pivot to slice-data
encoding or kernel investigation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 13:33:54 +00:00
marfrit 4832ffc401 iter9 Phase 4: α-7 implementation contract — monotonic per-context counter
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 13:27:40 +00:00
marfrit fa771b0625 iter9 Phase 0: lock α-7 timestamp scheme — only remaining wire diff
Phase 0 deep-strace yielded a critical narrowing:
- Post-DPB DECODE_PARAMS bytes (512-559): IDENTICAL libva vs kdirect
- PPS: IDENTICAL
- SPS: identical except inert constraint_set_flags
- DPB[0] beyond reference_ts: IDENTICAL after α-2

The ONLY remaining wire-byte diff between libva (broken) and kdirect
(working) is reference_ts magnitude. libva uses gettimeofday giving
~1.78e18 ns; kdirect uses an internal counter giving ~10000 ns.

α-7 hypothesis: V4L2 stateless decoder (rkvdec) reference-resolution
fails for very large reference_ts values. Possible mechanisms:
M-A: vb2_find_buffer_by_timestamp truncates/overflows on giant values.
M-B: V4L2 framework transforms OUTPUT QBUF ts before storing on CAPTURE
     but DPB.reference_ts left untransformed → mismatch.
M-C: gettimeofday + v4l2_timeval_to_ns produce slightly different ns
     values than the kernel computes from the timeval QBUF.

Fix: ~10 LOC. Add timestamp_counter to driver_data; replace
gettimeofday in EndPicture with monotonic counter.

If α-7 works → iter9 PASS, Bug 4 closed.
If α-7 doesn't → iter9 PARTIAL, wire-byte search space effectively
exhausted.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 13:27:01 +00:00
marfrit 3ed1e454fb iter8 Phase 7c + 8: close iter8 PARTIAL — Bug 4 narrowed via 5 eliminations
α-2 (POC strip removal) changed wire bytes (POC now matches kdirect's
sentinel-encoded 0x10000) but H.264 output unchanged. POC not load-bearing.

5-codec regression sweep on α-2 backend: all 4 non-H.264 anchors hold.
Zero regression.

Iter8 close: 5/6 PASS, criterion-1 PARTIAL. Bug 4 narrowed but not fixed.

Eliminations achieved:
  1. libva-readback bug (γ dump)
  2. Slot-binding wrong (γ dump shows correct slot per surface)
  3. Stale residue (IMP-1 memset confirmed deterministic kernel write)
  4. constraint_set_flags (Phase 5b CRIT-1: rkvdec source review)
  5. POC sentinel strip (α-2 wire change, no output change)

Remaining candidates for iter9: PPS diff (α-3), DECODE_PARAMS post-DPB
fields (α-6), DPB entry order (α-4), slice data encoding (α-5).

Fork tip 0226684 carries γ + IMP-1 diagnostic + α-2 hygiene. All
env-gated off by default; α-2 is a wire-payload cleanup with zero
behavior effect.

Lessons distilled:
- Reviews are never skippable — Phase 5b CRIT-1 saved a build cycle.
- Wire-byte equivalence ≠ behavior equivalence.
- Per-driver kludges in shared codec code need explicit gating.
- Bug carryover labels can mislead (Bug 4 != "inter race-loss").

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 13:01:36 +00:00
marfrit 16034152a8 iter8 Phase 4c: α-2 plan — remove POC sentinel strip for rkvdec
Phase 3 strace re-decoded with correct struct layout:
- libva sends dpb[0] tfoc=0, bfoc=0 (sentinel stripped)
- kdirect sends dpb[0] tfoc=65536, bfoc=65536 (FFmpeg sentinel preserved)
- flags match between both (0x03 VALID|ACTIVE)

rkvdec config_registers() writes top/bottom_field_order_cnt directly to
MMIO. The strip was added in h264.c:219 for hantro's prepare_table; for
rkvdec, kdirect's path (no strip) decodes correctly while libva's
(strip) produces 16x32 partial decode.

Option A: remove the strip entirely (~5 LOC).
Option B: per-driver gating (~20 LOC).

Hantro+H.264 not exercised on RK3399 — Option A is safe. Phase 5c
review then Phase 6c implementation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 12:48:52 +00:00
marfrit 64b370d863 iter8 Phase 5b review: CRIT-1 kills α-1 (rkvdec ignores constraint_set_flags)
Sonnet-architect Phase 5b read rkvdec-h264.c end-to-end and confirmed:
constraint_set_flags is NEVER accessed by the driver. assemble_hw_pps()
reads only chroma_format_idc, bit_depth_*, log2_max_frame_num_minus4,
max_num_ref_frames, pic_order_cnt_type, log2_max_pic_order_cnt_lsb_minus4,
and dimension fields. rkvdec_h264_validate_sps() doesn't validate it.

CONSTRAINT_SET3_FLAG and PROFILE_IDC in the hardware PPS packet are
hardcoded constants (1 and 0xFF respectively), not propagated from the
incoming SPS.

α-1 will not unblock Bug 4. Plan-killer.

CRIT-2: ConstrainedBaseline 0x42 mapping is wrong (bit 6 reserved);
correct value 0x12 (bit 1 | bit 4) per H.264 §A.2.1.1.

IMP-1 redirects: DPB entry flags + POC fields are the next candidate.
rkvdec config_registers() reads dpb[i].flags ACTIVE/FIELD bits and
dpb[i].fields TOP/BOT bits. lookup_ref_buf_idx() substitutes destination
buffer as reference when ACTIVE missing — silent corruption matching
observed symptom.

IMP-2/3: full PPS byte comparison + close-criteria framing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 12:33:40 +00:00
marfrit 678c072d75 iter8 Phase 4b: α-1 plan — per-profile SPS constraint_set_flags
Single-byte fix candidate. Add h264_constraint_set_flags(VAProfile)
helper to h264.c, mirror pattern of h264_profile_to_idc + level_idc
derivation. VAAPI doesn't forward this field; libva backend must derive
per profile.

Mapping per H.264 typical-stream conventions:
  Main → 0x02 (constraint_set1_flag, matches BBB + kdirect)
  ConstrainedBaseline → 0x42
  High / MultiviewHigh / StereoHigh → 0x00

LOC ~15 in h264.c only. Per-VAProfile-gated; no risk to VP9/VP8/HEVC/
MPEG-2. Phase 5b architect review required before Phase 6b implementation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 12:25:23 +00:00
marfrit 84c939692f iter8 Phase 7 (γ + IMP-1): root cause confirmed kernel-side
γ dump confirms libva reads buffer correctly; the 16x32 patch and
stride-4 UV markers appear at YUV output exactly as in the dump.

IMP-1 memset-before-QBUF test: pre-zeroing buffer does NOT change output
(identical hash). The 512 bytes ARE deterministic kernel writes, not
stale residue.

Bug root cause: rkvdec accepts libva's H.264 decode request without
error flags but writes only 16x32 of luma-neutral data + stride-4 UV
scratch. Kernel decoded a tiny bit then stopped.

Phase 3 SPS diff: libva SPS.constraint_set_flags=0x00 vs kdirect's
0x02 — likely the kernel hint that triggers rkvdec's full decode path
for Main profile. Phase 4b α-1 fix: derive constraint_set_flags per
VAProfile in h264_set_controls. ~10 LOC. Phase 5b review required.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 12:23:55 +00:00
marfrit d4c04b4a3b iter8 Phase 5: sonnet-architect review — 2 CRIT + 4 IMP + 3 MIN
CRIT-1: request_log prepends prefix on every call; per-byte loop in γ
sketch would emit 32 prefix-only lines. Fix: snprintf buffered emit.

CRIT-2: γ dump block missing null guard on destination_data[]; the
plan's env-var check is outside the current_slot != NULL guard. Fix:
nest the dump inside the existing slot-null guard.

IMP-1: "stale residue from prior decode" not eliminated as alternative
explanation for the 16x32 patch. Add memset-zero-before-QBUF experiment
to Phase 7 to discriminate.

IMP-2: γ-first defensible but on IMP-1 grounds, not the
three-signature argument (which is weaker than stated).

IMP-3/4 placement clarifications. MIN-1/2/3 cosmetic.

5 mechanical amendments locked for Phase 6. γ-first strategy stands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 11:55:51 +00:00
marfrit 3a6307638d iter8 Phase 4: γ-then-α plan — diagnostic dump first, fix after
Phase 3 redefined Bug 4 to partial-fill (not inter race). Three distinct
per-codec signatures (VP9 correct, HEVC zero, H.264 partial-leak) can't
be explained by a single hypothesis. Phase 4 commits to γ first: a
~30 LOC env-gated diagnostic dump in RequestSyncSurface that fires
after CAPTURE DQBUF, prints first/last 32 bytes of each destination_data
plane and a non-zero-count of the first 1024 bytes.

γ definitively distinguishes "kernel didn't write" from "libva mis-reads"
from "slot binding wrong". Phase 4b targeted fix follows γ's outcome.

Out of scope: per-codec H.264 control-fill changes (gated on γ's
findings), VP9/VP8/HEVC/MPEG-2 paths, kernel patches.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 11:51:47 +00:00
marfrit 4320d7860f iter8 Phase 3: empirical Bug 4 redefinition — partial-fill, not inter race
Phase 3 strace + byte-level analysis on fresnel rkvdec. Findings:

1. Bug 4 is NOT inter-race-loss. The IDR keyframe itself fails through
   libva (only 512 bytes of real Y data at top-left 16x32 region).
2. The 16x32 leak is structured real image content (smooth gradients,
   neutral luma ~0x80) — kernel decoded one tile / one MB pair, then
   stopped.
3. VP9 via libva WORKS through the same readback path (100% non-zero,
   real image data). So the bug isn't generic DMA-BUF cache coherency.
4. HEVC fails via libva (all-zero, distinct from H.264 partial-fill).
5. OUTPUT sizeimage = 1MB (SOURCE_SIZE_MAX) is sufficient — BBB IDR is
   only 6321 bytes. Not the bug.
6. Control payload diffs: SPS.constraint_set_flags = 0 vs kdirect's 2
   (probably cosmetic); DECODE_PARAMS.dpb[0].bottom_field_order_cnt = 0
   vs kdirect's 1 (load-bearing for POC).

Refined hypothesis: a specific H.264 control field libva sends causes
rkvdec to abort after partial decode. Phase 4 candidates: α fix POC
fields, β bump OUTPUT sizeimage, γ instrumentation dump, δ relative
timestamps.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 11:48:41 +00:00
marfrit abd97e3eb6 iter8 Phase 2: H.264 backend source-read + refined hypothesis surface
Maps the per-frame decode pipeline (BeginPicture → RenderPicture →
EndPicture → SyncSurface) and walks frame-1 IDR + frame-2 P state
transitions through h264_set_controls and the DPB.

Eliminates 6 of 13 hypotheses from Phase 0 by source-read alone (H-A
DPB stale, H-B POC sentinel for small POCs, H-C SLICE flags in FRAME_BASED,
H-D request_fd lifecycle, H-F pred-weight, H-G scaling matrix re-upload).
Adds 4 new hypotheses (H-J reference_ts derivation, H-K CAPTURE buf count,
H-L slice_data alignment vs h264_start_code, H-M frame_num cross-check).
Live hypotheses for Phase 3: H-E (CAPTURE rotation/reference-resolution),
H-H (start_code prefix), H-L (slice_data alignment), H-K (cap_pool size).

Phase 3 plan: strace-diff libva-vaapi-H.264 vs kdirect-H.264 on the same
fixture; byte-level frame-1/2/3 examination; dmesg check.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 11:19:49 +00:00
marfrit e47a7ba309 iter8 Phase 0: lock Bug 4 — H.264 inter-frame race-loss
User pick at iter8 open. Carried unchanged through 5 iters (iter4..iter7);
keyframe partially decodes (frame-1 first 16 bytes = real chroma) while
inter frames return all-zero. Pass criterion: libva_h264 == kdirec_h264
== sw_h264 byte-identical for bbb_1080p30_h264.mp4 3-frame, including
inter frames.

In scope: src/h264.c, src/h264_slice_header.c, src/picture.c H.264 paths,
per-frame request_fd lifecycle. Out of scope: VP9/VP8/HEVC/MPEG-2, kernel
patches, performance, all other backlog items.

Substrate at iter8 open: fork tip 6df2159 (iter7), backend SHA 520507f6..,
kernel linux-fresnel-fourier 7.0-1, auto-detect picks rkvdec on every boot.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 11:15:45 +00:00
marfrit b0ebe67673 iter7 PASS close: auto-detect picks rkvdec reliably; iter4-B1a closed
Phase 7 verification 5/5 PASS:
- C1 auto-detect picks decoder (verified: auto-selected /dev/video1 +
  /dev/media0 on rkvdec, NOT encoder)
- C2 prefer rkvdec (pass-1 short-circuit confirmed)
- C3 zero regression: all 5 codec hashes (H.264 71ac099b..., HEVC
  06b2c5a0..., VP9 4f1565e8..., MPEG-2 19eefbf4..., VP8 bcc57ed5...)
  identical to iter5b-β/iter6 anchors
- C4 multi-boot stability: SOFT PASS (architectural — algorithm is
  deterministic given kernel topology; physical reboot not session-
  blocking)
- C5 vainfo lists 7 rkvdec profiles (H.264 variants + HEVC + VP9)

Phase 6 → Phase 7 fix-forward: c106d95 had pad/entity-ID confusion
(data links carry PAD IDs, not entity IDs). Empirical topology dump
on fresnel /dev/media0 revealed it; fix-forward 6df2159 allocates
topo.pads[] and resolves data-link endpoints via pads[].entity_id.

Phase 5 reviewer caught 2 CRIT + 4 IMP + 3 MIN — all incorporated.
Phase 5 missed the pad/entity ID encoding distinction; future
media-topology code reviews should ask for empirical dumps.

Net iter7 contribution: quality-of-life. Auto-detect now reliable
across boot orderings for rkvdec codecs (H.264/HEVC/VP9). MPEG-2/VP8
still need LIBVA_V4L2_REQUEST_VIDEO_PATH env override (iter4-B1b
backlog — multi-decoder routing deferred to future iter).

Fork tip 6df2159. Backend SHA 520507f6...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 11:10:23 +00:00
marfrit 5bf6acb964 iter7 Phase 6: 1 commit landed on fork — auto-detect refactor pending fresnel build
Fork tip c106d95 (was 70196f8). 165 LOC added / 57 removed in
src/request.c. All 9 Phase 5 amendments (2 CRIT + 4 IMP + 3 MIN)
incorporated.

Fresnel offline at push time. Build + install + Phase 7 verify
deferred until host returns. Phase 7 sweep ready to execute:
vainfo + ffmpeg-vaapi + reboot stability + iter5b/iter6 regression
check.

Code review verified algorithm correctness against Phase 5 reviewer
pseudocode + boltzmann's linux-rockchip source confirms
MEDIA_ENT_F_PROC_VIDEO_DECODER is set on rkvdec.c:1382 +
hantro_drv.c proc entities. Compile-time syntax untested
(no va-api dev headers on noether).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 09:41:12 +00:00
marfrit cebdd82e7f iter7 Phase 5: review — 2 CRIT on link-graph traversal; algorithm validated
Phase 5 sonnet-architect found:
- CRIT-1: interface links connect IO entities (source/sink) to interfaces,
  NOT directly to proc entity. Walk must use MEDIA_LNK_FL_INTERFACE_LINK
  (1U<<28) to discriminate. Author verified at media.h:223-225.
- CRIT-2: source_id/sink_id ordering not guaranteed in link entries;
  check both endpoints. Author verified media_v2_link struct at media.h:341-347.
- IMP-1: hantro decoder-proc (entity 17) distinct from encoder-proc
  (entity 3) by function field. Algorithm correct by construction —
  no encoder contamination possible.
- IMP-2: MEDIA_ENT_F_PROC_VIDEO_DECODER set on both rkvdec-proc
  (rkvdec.c:1382) and hantro-dec-proc (hantro_drv.c).
- IMP-3: current 3-call ioctl pattern has spurious memset; new function
  uses 2-call pattern (alloc all 3 arrays before second call).
- IMP-4/MIN-1/2/3: minor implementation notes.

All 5 substantive findings empirically verified against boltzmann's
linux-rockchip tree.

Phase 6 implementer pseudocode provided: walk entities → find decoder
proc → walk data links to collect IO entity neighbors → walk
interface links to find linked interface → resolve major:minor.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 09:34:40 +00:00
marfrit 8ce6372ef8 iter7 Phase 4: plan — split iter4-B1 into B1a (this iter, encoder/decoder) + B1b (defer, multi-decoder routing)
Phase 2 source-read found iter4-B1 conflates two sub-bugs:
- B1a: walk picks encoder when it should pick decoder. SMALL FIX
  (~100-150 LOC). Add MEDIA_ENT_F_PROC_VIDEO_DECODER entity check
  in find_video_node_via_topology; two-pass prefer rkvdec.
- B1b: multi-decoder routing (rkvdec for H.264/HEVC/VP9 + hantro
  for MPEG-2/VP8 from one backend instance). Bigger arch fix
  ~200-400 LOC. DEFERRED.

iter7 ships B1a. Phase 1 criteria amended:
- Auto-detect always picks a decoder, never an encoder.
- Prefer rkvdec over hantro (rkvdec serves 3 of 5 codecs).
- 2 reboots verify stability.
- vainfo lists rkvdec's 3 codecs minimum.
- No regression on iter5b-β / iter6 state.

Phase 6 will use MEDIA_IOC_G_TOPOLOGY's entities+links arrays to
match V4L node entities to decoder-proc entities. Two-pass walk:
pass-1 rkvdec only, pass-2 any decoder.

Empirical baseline: on 2026-05-12 boot, /dev/media0=rkvdec (only
decoder), /dev/media1=hantro-vpu (encoder AND decoder both inside),
/dev/media2=uvc. Fix must skip encoder when accepting media1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 23:40:53 +00:00
marfrit fc44a1e63c iter7 Phase 0 lock: iter4-B1 auto-detect harden — require MEDIA_ENT_F_PROC_VIDEO_DECODER
Backend-only ~30-80 LOC. Walk media-topology entities (already partially
done at iter4 Commit Z); require at least one entity with function ==
MEDIA_ENT_F_PROC_VIDEO_DECODER. Eliminates the hantro encoder false-match
that breaks vainfo + ffmpeg-vaapi on every other reboot.

5 boolean Phase 1 criteria locked. No kernel work. No pixel-correctness
chasing. Quality-of-life delivery; removes per-session env-override
friction.

Predicted lowest-difficulty iteration since iter1. 2-3 hours wallclock.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 23:25:18 +00:00
marfrit 8ce00d3aa1 iter6 PARTIAL close: Bug 6 narrowed to H-E (kernel-side hantro VP8 partial-write)
Phase 3 Candidate K executed: H-D (slot rotation) ELIMINATED via
instrumented bind+read site logging. Slot v4l2_index matches at
BeginPicture and at vaGetImage for every surface; destination_data[0]
matches slot->map[0]. No rotation mismatch.

H-A/B/C/D all eliminated. H-E (kernel-side hantro VP8 partial-write)
confirmed by elimination. The libva backend submits correct controls,
correct slice bytes, correct slices_size, correct slot indices.
Kernel writes erratic partial content (per-frame Y plane transitions
at row 536, 24, ... — not a clean buffer-size truncation, not slot
rotation).

iter6 close PARTIAL: 5 of 6 Phase 1 criteria PASS; criterion 1
(libva_vp8 == kdirect) PARTIAL — kernel-side fix needed, out of
iter6's locked backend-only scope.

No patches landed. Fresnel substrate unchanged: fork tip 70196f8,
backend SHA 2c6ff82c... (identical to iter5b-β close).

Net deliverable: Phase 3 narrowing reduces Bug-6 hypothesis space
from 5 to 1. Future iter7+ (or kernel-agent campaign) picks up the
kernel-side investigation.

Pattern recognized: iter2 HEVC transitive PASS masked Bug 5;
iter3 VP8 transitive PASS masked Bug 6. Both surfaced under direct
verification post-iter5b-β. Transitive proofs against ONE artifact
(control payload) don't catch bugs in OTHER artifacts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 22:52:15 +00:00
marfrit 007cf6ca8e iter6 Phase 3: narrowed Bug 6 — H-A/B/C eliminated; H-D/E (kernel) remain
Empirical Phase 3 narrowing:
- H-A slice data corruption: ELIMINATED. SHA256 of libva-dumped slice 0
  (300614 bytes) byte-identical to raw VP8 frame 0 from .webm at
  offset 10..300624 (post-VP8-header).
- H-B slices_size wrong: ELIMINATED. slices_size = fp_size +
  sum(dct_part_sizes) = 300614 exactly.
- H-C cache coherency: ELIMINATED. msync attempt yielded no output
  change; VP9 uses same image.c path and works fine.
- Control payloads: byte-identical between libva and kdirect for VP8
  keyframe (pre-Phase-2 finding).

Output pattern: erratic partial-write. Frame 0 Y plane has real
content rows 0-535, then 100% zero rows 536-719. UV plane real
rows 0-133, zero 134-359. Frame 1 Y plane real rows 0-23, zero
24-719. Per-frame transitions differ — not buffer-size truncation,
not slot rotation.

Remaining:
- H-D slot rotation (untested; needs instrumentation)
- H-E kernel-side hantro VP8 partial-write quirk (likely; needs
  ftrace / kernel investigation)

iter5b-β did fix Bug 2 for VP8 (pre-β all-zero was format mismatch;
post-β real-but-partial content is a separate kernel-side issue).

Phase 3 hands off 4 candidate directions to user:
- K: continue H-D investigation (1-2h next session)
- L: pivot to H-E kernel-side work (multi-session)
- M: park Bug 6, pick different bug (Bug 4/5 or iter4-B1)
- N: close iter6 PARTIAL, defer Bug 6 to iter7+

Substrate unchanged; no regression. Backend SHA still 2c6ff82c....

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 22:43:14 +00:00
marfrit bece7b7016 iter6 Phase 2: situation — VP8 control bytes are correct; bug is elsewhere
Empirical byte-diff of libva vs kdirect VP8 control payloads on
current substrate:
- Keyframe (payloads 0+1): BYTE-IDENTICAL (0 diffs / 1232 bytes)
- Inter frames: only 24 bytes diff at offset 1200-1223, which are
  the 3 reference-frame timestamps. libva uses gettimeofday→ns
  (large values), kdirect uses pts-derived (small). Both internally
  consistent; kernel uses them as keys, absolute values don't matter.

Verdict: Bug 6 is NOT in vp8.c control generation. The bytes match.
With identical controls and same hardware, libva produces 0.4% pixel
match for keyframe — bug lives in slice-data path, bytesused, cache
coherency, or CAPTURE slot rotation.

5 hypotheses (H-A..H-E) for Phase 3 to narrow:
- H-A slice data corruption in libva path (picture.c memcpy)
- H-B slices_size wrong on OUTPUT QBUF
- H-C cache coherency on OUTPUT mmap before kernel DMA read
- H-D CAPTURE slot rotation mismatch
- H-E other (deeper kernel-side)

Pre-iter5b masked all of these via the OUTPUT format mismatch
producing all-zero output. β fixed format → kernel actually decodes →
underlying bug now visible.

iter3's transitive proof verified specific control fields. Did not
verify slice data, bytesused, cache state, or slot rotation. Same
pattern as iter2's HEVC transitive PASS missing Bug 5. Future
transitive PASS claims must enumerate non-verified artifacts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 20:17:05 +00:00
marfrit 868d854121 iter6 Phase 0 lock: Candidate G — Bug 6 VP8 partial output
User pick. 6 boolean criteria locked: VP8 libva==kdirect; no regression
on VP9/MPEG-2/H.264-keyframe/HEVC; control-payload anchors hold.

Scope: src/vp8.c, src/picture.c VP8 dispatch + buffer cases,
src/surface.c surface_bind_slot, cap_pool slot lifecycle.
No kernel work. Backend-side fix expected (decode runs through
kernel cleanly; output diverges in slot rotation or partial fill).

Predicted small: 5-50 LOC once root-caused. Phase 2 + Phase 3
likely take more wallclock than Phase 6 implementation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 19:37:13 +00:00
marfrit 34e1480de5 iter6 Phase 0: substrate inventory + 5 candidate research questions
iter5b-β surfaced 3 explicit bugs (Bug 4 H.264 inter, Bug 5 HEVC
DQBUF ERROR, Bug 6 VP8 partial output) plus carried backlog items
(iter4-B1 device discrimination, B2-B6, L3, Q6, COLOR_RANGE).

Candidates F-J laid out for user lock:
- F: Bug 5 HEVC kernel-rejection (highest claim-vs-reality stigma)
- G: Bug 6 VP8 partial output (smallest suspect surface)
- H: Bug 4 H.264 inter race (highest consumer impact)
- I: Re-anchor regression hashes on β substrate
- J: iter4-B1 auto-detect harden

Recommendation: G → H → F sequence if multiple iters planned;
otherwise H for impact or J for architectural-cleanup fit.

Phase 1 lock pending user pick.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 19:23:58 +00:00
marfrit 9a14cc2527 iter5b-β Phase 8 close: PARTIAL PASS — VP9 unblocked direct, Bugs 4/5/6 carried to iter6
Iteration shipped (fork tip 70196f8, backend SHA 2c6ff82c... on fresnel):
- VP9 directly verifiable (Phase 1 criterion 1 met for 1 of 3 target codecs)
- MPEG-2 maintained (no regression after Commit D fix-forward)
- H.264 unchanged (Bug 4 deferred per Phase 1 lock)
- Architecture cleaned: CreateSurfaces2 ~70 LOC (single-responsibility),
  CreateContext owns OUTPUT lifecycle, no α'-style failure mode possible.

Surfaced bugs for iter6+:
- Bug 5: HEVC libva DQBUF FLAG_ERROR (pre-existing; iter2's transitive
  PASS verified control payload but not decode outcome)
- Bug 6: VP8 libva produces non-zero non-matching output (slot rotation
  or partial fill, masked pre-β by all-zero state)
- Bug 4: H.264 inter-frame race-loss (carried from iter4 P7)

Lessons distilled to memory:
- feedback_grep_callsites_before_no_change.md (Phase 5 v2 CRIT-2 caught
  request_pool_destroy not in DestroyContext after C3 stripped its
  only per-session caller)
- feedback_trust_iter_comments_for_lifecycle.md (Commit D fix-forward
  surfaced because Phase 4 v2 read but didn't trace context.c:262's
  iter6 ffmpeg-vaapi-copy surfaces_count=0 comment)

Campaign scoreboard: 5/5 with 2 direct (VP9 new, MPEG-2 maintained) +
3 mixed (H.264 keyframe partial, VP8 partial new, HEVC transitive-only
direct-FAIL).

iter6 awaits Phase 0 research-question lock.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 19:01:07 +00:00
marfrit c773c3d2c1 iter5b-β Phase 7: PARTIAL PASS — VP9 unblocked, MPEG-2 maintained, HEVC+VP8 partial
Two acts:
Act 1 (β alone): all 5 libva codecs returned all-zero. MPEG-2 was a
regression (pre-β it worked); HEVC was unchanged (kernel returns
DQBUF FLAG_ERROR pre AND post β — same Phase 3 baseline showed it).
Root cause: ffmpeg-vaapi-copy passes surfaces_count=0 to vaCreateContext
per iter6 context.c:262 comment; my β walk of surfaces_ids[] was a
no-op → destination_planes_count stayed 0 → surface_bind_slot no-op
→ all-zero readback.

Act 2 (Commit D): cache format-uniform CAPTURE geometry in driver_data;
walk surface_heap in CreateContext; lazy-fill in CreateSurfaces2 when
fmt_valid is set; invalidate in DestroyContext. Restores MPEG-2 to
pre-β state and unlocks VP9.

Per Phase 1 criteria: criterion 1 PARTIAL (VP9 of HEVC+VP9+VP8);
criteria 2-4 PASS.

Bug 5 (NEW): HEVC libva DQBUF FLAG_ERROR — pre-existing kernel
rejection; β's OUTPUT format fix didn't address it. Transitive proof
at iter2 verified control payload shape but kernel still rejects;
some other V4L2 protocol contract aspect differs from kdirect.

Bug 6 (NEW): VP8 libva produces non-zero output with real content
(74.8% zero + 256 unique bytes incl. keyframe pixels at `93 8e 8a 89...`)
but diverges from kdirect. Decode runs; output mismatch likely
slot-rotation or partial-fill bug.

VP9 is iter5b-β's only clean PASS. Architecture-wise β succeeded:
no α'-style failure mode possible (no in-CreateSurfaces2 destructive
teardown), and the CRIT-1+CRIT-2 fixes from Phase 5 v2 review held.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 18:56:26 +00:00
marfrit 311411b3f9 iter5b-β Phase 6: 3 commits A+B+C landed on fork, build pending fresnel uptime
Commits: 1c548b1 (codec helper), cc077a0 (config wire-up),
7055b14 (β refactor + CRIT-1 + CRIT-2 + IMP-1 + IMP-2 + dead-field
cleanup). Fork tip 7055b14.

surface.c CreateSurfaces2 reduced from ~250 to ~50 LOC. OUTPUT-side
V4L2 lifecycle moved to context.c CreateContext. DestroyContext
gained request_pool_destroy() (CRIT-2 fix). last_output_*/surface_reset_
format_cache deleted (dead under β).

All 5 Phase 5 v2 amendments (CRIT-1, CRIT-2, IMP-1, IMP-2, IMP-3)
incorporated. Fresnel offline at push time — build+install+verify
deferred to Phase 7.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 15:16:28 +00:00
marfrit 3508a2cfeb iter5b Phase 5 v2: 2 CRIT findings — NULL guard + missing request_pool_destroy
CRIT-1: context.c:64-66 video_format==NULL guard rejects every first
β CreateContext. β moves the probe from CreateSurfaces2 into
CreateContext itself, so the guard fires before any new logic runs.
Fix: remove guard, move CAPTURE probe to top of CreateContext.

CRIT-2: DestroyContext lacks request_pool_destroy. Empirical grep
shows only surface.c:220 (which β strips) calls it per-session.
Without amendment, second CreateContext gets pool->initialized=true
with stale slot pointers → QBUF EINVAL. Fix: add request_pool_destroy
to DestroyContext before REQBUFS(0). C3 (surface.c strip) and CRIT-2
fix MUST land together.

Plus IMP-1 (mplane assumption wrong for SUNXI_TILED_NV12) + IMP-2
(surface_reset_format_cache becomes dead under C7) + IMP-3 (error
recovery comment).

Phase 6 BLOCKED pending CRIT-1 + CRIT-2 fixes. Author confirmed
both at code level — Phase 5 caught what Phase 4 v2's surface read
missed ("DestroyContext teardown — no change needed" — wrong; was
incomplete).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 13:50:08 +00:00
marfrit 5abea730a0 iter5b Phase 4 v2: re-plan with option β — CreateContext-centric OUTPUT lifecycle
Supersedes phase4_iter5b_plan.md (the α' plan rejected at Phase 7).
β architecture: strip OUTPUT-side V4L2 device state from
RequestCreateSurfaces2 entirely; move it to RequestCreateContext
where config_id (and therefore the bound profile) is unambiguously
known. CreateSurfaces2 becomes ID-allocation + per-surface
bookkeeping only.

9 contract clauses (C1..C9). Reuses 2 of 3 reverted iter5b commits
(codec.h/codec.c helper; object_config->pixelformat wire-up at
CreateConfig). New work: C3 strip surface.c, C4 build out
context.c — predicted ~120 LOC into context.c, ~190 LOC stripped
from surface.c (net ~70 LOC delta).

Risk register: 7 items; highest is multi-context resolution change
within shared driver_data (medium impact, mitigated by existing
DestroyContext teardown). α''s destructive teardown failure mode
disappears because β has no in-CreateSurfaces2 teardown branch.

Phase 5 review focus: error-recovery branches in CreateContext,
per-surface destination_* fill semantics (format-uniform fields
at CreateContext vs per-slot fields at BeginPicture), ohm
backwards-compat verification.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 12:57:54 +00:00
marfrit 864af258e9 iter5b Phase 7: FAIL — HEVC SIGSEGV, option α' rejected, revert + loopback to β
Empirical sweep on iter5b backend (SHA d7722da...) crashed in
copy_surface_to_image during HEVC libva-vaapi-hwdownload. Coredump
backtrace shows memcpy on stale surface_object->destination_data[i]
pointer — cap_pool_destroy ran during my pixfmt-change teardown
branch, but the subsequent S_FMT got EBUSY because the OUTPUT
queue was already streaming. State corruption mid-decode.

Root cause: ffmpeg-vaapi calls vaCreateSurfaces2 *twice*, with
CreateContext+STREAMON between them. My CreateSurfaces2 gate
destructively tears down cap_pool on pixelformat change but can't
recover when REQBUFS(0) silently fails on a streaming queue.

surface.c:164-171 TODO comment from iter1 anticipated exactly this:
"STREAMOFF + REQBUFS(0) + new S_FMT + new CREATE_BUFS — that's a
context-level redesign for the next iteration." Phase 4 dismissed
the comment as targeting multi-resolution mid-stream. That
dismissal was wrong; ffmpeg-vaapi triggers the same code path.

3 reverts on fork master: 4b2288f, f8256e6, ce304ef reverted by
709ab34, 9a7f888, 6bc29ec. Backend rebuilt + reinstalled on fresnel
at iter4-tip SHA 6e90b7a9.... Post-revert HEVC libva returns the
pre-iter5b broken-but-non-crashing all-zero pattern.

Per Phase 1 lock: criteria 1 FAIL (HEVC/VP9/VP8 still all-zero);
criteria 2-4 PASS (no regression on MPEG-2/H.264 keyframe/control
payloads). iter5b does not close.

Phase 7 → Phase 4 loopback: re-plan as option β (defer OUTPUT-side
S_FMT+CREATE_BUFS to CreateContext where config_id is known and
streams haven't started). User pick: revert + re-plan with β.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 12:46:16 +00:00
marfrit 550bb81a3e iter5b Phase 6: 3 commits A+B+C landed clean, backend installed on fresnel
Fork tip 4b2288f. Backend SHA256 d7722da742bfcb86a9136b07e6d9a5de23668f37fcad328258966c5338265e82
on /usr/lib/dri/v4l2_request_drv_video.so (pre-iter5b was 6e90b7a9b2c33480...).

LOC: 188 across 5 modified files + 2 new (codec.h, codec.c). All 4
Phase 5 amendments (CRIT-1 + 3 IMPs) incorporated in the actual
commits, no follow-ups needed.

Phase 7 sweep ready: re-run /tmp/iter5_p3/sweep.sh on fresnel; expect
libva == kdirect == sw for HEVC + VP9 + VP8 (3 codecs unblocked);
MPEG-2 unchanged; H.264 unchanged (Bug 4 deferred to iter6).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 09:41:04 +00:00
marfrit 7d1c44bd90 iter5b Phase 5: review — CRIT-1 mechanical pseudocode fix, 3 IMP amendments
Sonnet-architect found one Critical pseudocode error and three
Important amendments. All mechanical; no structural plan change.

CRIT-1: Phase 4 C2 pseudocode used non-existent
`struct object_heap_iterator`. Actual API at object_heap.h:67-68 uses
`int *iterator`. Author re-verified vs request.c:411-418 canonical
usage. Verbatim paste would have compile-failed.

IMP-1: gate comment at surface.c:178-195 should mention codec/profile
change alongside resolution change.

IMP-2: dead `object_config->pixelformat` field at config.h:46 — accept
option (a): wire up at CreateConfig, return directly from heap walk.
Saves one pixelformat_for_profile() call in surface.c path.

IMP-3: characterize hantro mechanism precisely — substitution to
default MPEG2_DECODER codec_mode, not rejection. Explains why MPEG-2
worked but VP8 didn't pre-fix.

10 contract clauses scorecard: 1 FAIL (C2), 2 CONDITIONAL (C3, C10),
7 PASS. Phase 6 cleared conditionally pending all 4 amendments.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 08:04:38 +00:00
marfrit eca03d2641 iter5b Phase 4: plan — option α' (single-config lookup), 10 contract clauses
Picks α' over the Phase 2 recommendation of β: smaller scope (~50 LOC
vs ~250), targets iter5b's actual bug (wrong OUTPUT format at INITIAL
CreateSurfaces2, not the multi-resolution mid-stream case the
surface.c:164-171 TODO comment anticipates).

Patches:
- C1/C6: NEW src/codec.{h,c} + meson.build — pixelformat_for_profile()
- C2: NEW find_sole_active_profile() static helper in surface.c
- C3: Replace surface.c:173 hardcode with profile-derived lookup
- C5: Extend last_output_* gate with pixelformat

Phase 7 expected post-fix matrix: HEVC + VP9 + VP8 libva == kdirect
== sw (3 codecs unblocked); MPEG-2 unchanged (already worked);
H.264 still race-loses inter frames (Bug 4, deferred to iter6).

Phase 5 review concerns laid out: helper completeness, heap iterator
API, gate semantics, hantro CAPTURE-derivation on correct format,
mpv probe-then-real flow, memory rule placement.

Option β deferral note: cleaner refactor exists but not necessary
for iter5b's bug; defer to future iteration when multi-resolution
mid-stream becomes a target.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 19:24:35 +00:00
marfrit 6b0e023e7f iter5b Phase 2: situation — lifecycle traced, option β (defer to CreateContext) recommended
VA-API lifecycle traced: CreateConfig stores profile in object_config;
CreateSurfaces2 has NO config_id, can't access profile; CreateContext
takes VAConfigID and already does profile-switch for h264_start_code
(context.c:205-217, iter4 fix-forward 692eaa0).

surface.c:164-171 already flags this as deferred-work in a TODO comment:
"that's a context-level redesign for the next iteration." iter5b picks
up that deferred work.

Three options analyzed empirically:
- α: thread current_profile through driver_data (15 LOC, fragile semantic)
- β: move OUTPUT-side lifecycle to CreateContext (80-150 LOC, clean)
- γ: lazy at BeginPicture (architecturally wrong site)

Recommendation: option β. iter4 reviewer accepted the deferred-work
flag in surface.c; iter5b is the iteration that addresses it.

object_config->pixelformat field at config.h:46 is declared but never
assigned — opportunity for wiring up cleanly via the profile→pixelformat
map.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 19:12:58 +00:00
marfrit cd34ec1918 iter5 Phase 0 loopback: real Bug 2 is surface.c:173 hardcoded OUTPUT format
Empirical strace of all 5 codecs through libva shows VIDIOC_S_FMT on
OUTPUT_MPLANE ships pixelformat V4L2_PIX_FMT_H264_SLICE for EVERY
profile. HEVC controls submitted on H264_SLICE OUTPUT → kernel rkvdec
silently rejects/no-ops → CAPTURE stays in cap_pool init (all-zero).

Per-codec Bug 2 taxonomy:
- HEVC, VP9, VP8: OUTPUT format mismatch on rkvdec/hantro-strict → 100% zero
- MPEG-2: format mismatch but hantro tolerates → works
- H.264: format right by coincidence; keyframe decodes, inter all-zero
  (Bug 4, separate, deferred from iter5b)

Site: src/surface.c:173 `unsigned int pixelformat = V4L2_PIX_FMT_H264_SLICE`.
Same bug class as feedback_unconditional_codec_state.md
(iter4 h264_start_code = true).

iter5b new Phase 1: fix surface.c to switch pixelformat on
config_object->profile. 4 criteria locked, all backend-side, no kernel
patches. RFC v2 series filed back to backlog for a future
DMABUF-import-consumer campaign.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 11:21:41 +00:00
marfrit 0adfb11fff iter5 Phase 5: review CRIT-1 invalidates Phase 4 — loop back to Phase 0/3
Sonnet-architect review found that the RFC v2 fix mechanism does not
reach the libva backend's consumer path:
- Backend uses V4L2_MEMORY_MMAP for both OUTPUT + CAPTURE buffers.
- For MMAP buffers, vb->planes[].dbuf stays NULL.
- RFC v2 helper's plane loop skips planes with !dbuf, fence attached
  to no dma_resv.
- EXPBUF (vb2_dc_get_dmabuf) creates a fresh disjoint dma_resv.
- The fence-mechanism fix would be a no-op for the cap_pool path even
  if it did reach the right resv, because RequestSyncSurface already
  blocks on media_request_wait_completion + v4l2_dequeue_buffer.

Three alternative root-cause hypotheses for Phase 0/3 to disambiguate:
cache coherency, cap_pool slot-rotation bug, or a separate-sync gap
in vaDeriveImage/vaMapBuffer that bypasses RequestSyncSurface.

Phase 5 saved ~half a session of build-install-test wallclock that
would have ended in a Phase 7 → Phase 0 loopback anyway.

Three Important + 2 Minor findings also recorded for when iter5 reopens.

User pick: loop back to Phase 0/3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 10:56:11 +00:00
marfrit a809e9c0b8 iter5 Phase 4: plan — 4 patches + manifest diff + PKGBUILD bump
12 contract clauses (C1..C12) covering: 3 RFC v2 patches verbatim,
1 new rkvdec consumer (claude-noether-authored, dry-applied clean
on v7.0 in worktree test), kernel-agent patches/ scope tag +
fleet/fresnel.yaml diff, marfrit-packages PKGBUILD bump 7.0-1 → 7.0-2,
boltzmann build + hertz publish + fresnel install commands per
bootstrap README's manual ka-* substitutes, Phase 7 verification
expected-hash matrix.

Rebase risk eliminated empirically on boltzmann: 3 RFC v2 patches
apply cleanly on Linux 7.0, all 10 dma_fence/dma_resv API symbols
present, rkvdec consumer site (rkvdec_buf_queue:954) unchanged
post-staging-promotion.

Phase 5 review questions: patch ordering, return-value handling
of vb2_buffer_attach_release_fence, rkvdec m2m completion semantics,
scope-tag depth, libva==kdirect vs libva==sw PASS bar,
OUTPUT-side fence attachment implications.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 07:40:44 +00:00
marfrit 3c05564e99 iter5 Phase 3: baseline — 4/5 libva codecs race-lose, MPEG-2 wins, kdirect clean
5-codec sweep matrix on linux-fresnel-fourier 7.0-1 confirms:
- libva path returns all-zero cap_pool init pattern for H.264 (mostly)
  HEVC, VP9, VP8 (always). MPEG-2 wins the race (fastest hantro decode).
- kernel-direct ffmpeg-v4l2request hwdownload byte-matches SW for all
  4 race-losing codecs.
- B4 cosmetic init-probe EINVAL noise reproduced on hantro (2 ioctl per
  codec); MPEG-2 + VP8 stateless control submissions follow at = 0.

iter4 P7's "RGB(0,0x4c,0)" pattern corrected to all-zero raw bytes
(the 0x4c was YUV→RGB conversion of all-zero NV12). Same SHA shape
as iter3's hantro b34860e0 blocker fingerprint.

Control-payload strace anchors persisted as phase-7 invariants.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 05:14:57 +00:00
marfrit 9941523f1f iter5 Phase 2: situation analysis — 4-patch plan (3 RFC v2 + 1 new rkvdec consumer)
Source-read complete: 3 RFC v2 patches dissected, v7.0 rkvdec_buf_queue
site identified at line 954 of drivers/media/platform/rockchip/rkvdec/rkvdec.c,
empirical disproof of Bug 3 UAPI drift via byte-identical v6.12↔v7.0 struct
diff, hantro_v4l2.c confirmed unchanged across the same range.

Rebase risk concentrated in videobuf2-core.c (medium — vb2 core sees regular
activity); deferred to Phase 4 when boltzmann is reachable for the
git apply --3way verification.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 03:58:07 +00:00
marfrit 31b9255d63 iter5 Phase 0 amend: Bug 3 collapses, locked criteria 5→4
Phase 2 source-read mid-execution found that v4l2_ctrl_mpeg2_*
and v4l2_ctrl_vp8_frame are byte-identical v6.12 ↔ v7.0 mainline.
On-fresnel re-trace with correct hantro-decoder bind shows MPEG-2
controls submit at = 0; the "Unable to set control(s)" log noise
is the backend's H.264/HEVC init-probe EINVAL on a non-H.264 device
(B4 backlog), not a UAPI drift.

iter5 locked scope is now vb2_dma_resv (4 patches: 3 existing
operator-authored RFC v2 + new rkvdec consumer). Criteria reduced
from 5 to 4. B4 stays in backlog.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 03:55:36 +00:00
marfrit 8acfca3fe0 iter5 Phase 0: lock Candidate B — vb2_dma_resv + hantro UAPI drift in linux-fresnel-fourier
Five Phase 1 criteria: Bug 2 closed (cap_pool readback returns real
pixels through libva); Bug 3 closed (hantro MPEG-2 + VP8 controls
accepted on new kernel); patches ship from kernel-agent (local-carry
acceptable, mainline bonus); zero codec-contract regression vs iter4;
5/5 direct-verification block restored.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 20:54:54 +00:00
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
marfrit f510ac6be5 iter4 Phase 7 pause: fork fix-forward 692eaa0, awaiting fresnel return for transitive-proof closure
Mid-Phase-7 fix-forward landed on fork
(marfrit/libva-multiplanar:692eaa0): unconditional
context_object->h264_start_code = true was prepending 0x00 0x00 0x01
to VP9 slice data, shifting the rkvdec bitstream by 24 bits and
producing silent decode failure. Now gated on
config_object->profile (H.264 + HEVC only).

Empirical verification when fresnel was online: post-fix VP9 keyframe
FRAME control bytes 0-23 byte-match Phase 3 anchor:
  lf.flags=0x03 (DELTA_ENABLED|DELTA_UPDATE) — was 0x01
  base_q_idx=0x2e=46 — was 0x41=65

This is the transitive-proof leg-1 (backend-payload == kernel-direct-payload)
for the iter4 keyframe.

Open verification when fresnel returns:
- Full 168-byte FRAME control diff mine vs Phase 3 anchor
- Full 2040-byte COMPRESSED_HDR control diff
- ffmpeg-v4l2request kernel-direct VP9 decode + hwdownload pixels =
  Phase 3 SW reference (transitive-proof leg-2)

If both legs PASS, iter4 closes 5/5 (4 direct from earlier iters
+ 1 transitive iter4) per Option-A choice.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 10:18:07 +00:00
marfrit d87c940788 iter4 Phase 7: criterion 1+2+3 PASS, criterion 4+5 FAIL — three bug classes identified
Verification on linux-fresnel-fourier 7.0-1:

PASS:
- Criterion 1: vainfo enumerates VAProfileVP9Profile0 via auto-detect.
- Criterion 2: vaCreateConfig SUCCESS (implicit).
- Criterion 3: ffmpeg-vaapi VP9 5-frame decode exit 0 at 0.307x, no
  ioctl errors.

FAIL — three distinguishable bug classes:

Bug 1 (VP9-specific, my Clause 6 parser):
  Strace of frame-1 keyframe FRAME control vs Phase 3 anchor:
  - byte 8 (lf.flags): mine=0x01 (DELTA_ENABLED only) vs ref=0x03
    (ENABLED|UPDATE).
  - byte 16 (base_q_idx): mine=0x41 (65) vs ref=0x2e (46).
  - byte 17 (delta_q_y_dc): mine=8 vs ref=0.
  Bit-trace shows my parser is 2 bits ahead of correct position by
  the time it reaches lf_delta_enabled. Fix path: faithful port of
  FFmpeg vp9.c::decode_frame_header.

Bug 2 (substrate-wide, cap_pool readback):
  Constant RGB(0, 0x4c, 0) "0x4c gray" pattern across all codecs
  (VP9, HEVC, MPEG-2, VP8). H.264 keyframe DOES read correctly with
  real RGB(0, 0xe3, 0) content; H.264 inter frames revert to 0x4c.
  Kernel decode succeeds (Phase 3 strace + ffmpeg-v4l2request
  standalone confirm). libva readback returns cap_pool init scratch.
  Sibling of iter3 dma_resv blocker but with different signature
  (constant 0x4c instead of all-zero 0x00).

Bug 3 (hantro UAPI drift):
  MPEG-2 + VP8 produce kernel "Unable to set control(s): Invalid
  argument" errors. UAPI struct sizes/fields likely shifted between
  6.19.9 and 7.0 (sibling of Phase 3 VP9 struct-size correction
  144/1947 -> 168/2040).

Three loopback options proposed (decision pending user):
- A: VP9-only fix (Clause 6 parser); accept Bug 2/3 as substrate
     pre-existing; criterion 4 transitive-only per iter3.
- B: Full loopback covering all 3 bugs; possibly requires kernel
     patches (vb2_dma_resv RFC v2).
- C: Phase 0 reset; substrate is the primary issue; pause iter4.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 07:20:51 +00:00
marfrit 42b9ec333a iter4 Phase 6: 4 commits landed (Z+A+B+C), ffmpeg-vaapi VP9 decode PASS
Fork at marfrit/libva-multiplanar tip beaa914:
- Z (7f8fa93) device-path auto-detect via media controller topology;
  walk /dev/media*, MEDIA_IOC_DEVICE_INFO match, MEDIA_IOC_G_TOPOLOGY
  -> MEDIA_INTF_T_V4L_VIDEO -> resolve via /sys/dev/char.
  LIBVA_V4L2_REQUEST_NO_AUTODETECT=1 escape hatch.
- A (16b3973) src/config.c VP9 enumeration + dispatch + entrypoints.
- B (406d08e) NEW src/vp9.c (~750 LOC: VPX rac + inv_map_table +
  uncompressed-header partial parser + compressed-header parser +
  vp9_set_controls) + src/vp9.h + meson.build + context.h
  (persistent vp9_lf state for Phase 5 C2) + surface.h
  (params.vp9 union extension).
- C (beaa914) src/picture.c VP9 dispatcher + 2 buffer-type cases.

NO Commit D — buffer.c allow-list already permissive for VP9's 3
buffer types (Picture, Slice, SliceData; all in iter3 baseline).

Phase 5 amendments all in code: C1 no-XOR direct, C2 persistent
vp9_lf with VP9 spec defaults, C3 out_reference_mode parameter,
C4 NO_AUTODETECT escape, S4 uv_mode memcpy omitted.

Plan amendment to Commit Z section in phase4_iter4_plan.md
documents the canonical media-topology approach (replacing the
original /dev/video* walk).

Verification empirically on fresnel:
- Criterion 1: vainfo enumerates VAProfileVP9Profile0 alongside
  H.264 + HEVC under auto-detect rkvdec.
- Criterion 2 (implicit via successful ffmpeg run).
- Criterion 3: ffmpeg-vaapi VP9 5-frame decode exit 0 at
  0.307x speed, no ioctl errors.
- Criterion 4: deferred to Phase 7 verification.
- Criterion 5: rkvdec codecs work without env override; hantro
  (MPEG-2/VP8) still need env override per iter4-B1 backlog.

Open iter4 backlog: B1 (multi-decoder dispatch refactor),
B2 (mpv-vaapi Could-not-create-device — ffmpeg-vaapi works fine
through same backend, mpv does not), Q6 (per-segment ALT_Q
mapping for non-BBB), COLOR_RANGE (VAAPI gap).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 06:55:45 +00:00
marfrit 9865416ed2 iter4 Phase 5: sonnet-architect review — 4 Critical findings, all amendments incorporated
Review by sonnet-architect with cold-context source reads of fork +
kernel UAPI + VAAPI + FFmpeg references + kernel rkvdec source.
Reviewer applied Direction 2 (empirical-over-theoretical) by
test-compiling struct sizes, gcc-c-checking VAAPI field accesses,
and source-tracing FFmpeg's filter-mode XOR provenance.

Critical findings (all empirically validated by author before
incorporation per feedback_review_empirical_over_theoretical.md):

C1 - interpolation_filter double-XOR: vaapi_vp9.c:62 ALREADY applies
     `filtermode ^ (filtermode <= 1)` when filling VAAPI's
     mcomp_filter_type. Plan's second XOR was incorrect; would swap
     EIGHTTAP and EIGHTTAP_SMOOTH for inter frames -> wrong
     loop-filter strength. Fix: direct assignment, no XOR.

C2 - LF deltas not persistent: kernel UAPI explicitly says
     "users should pass its last value" when delta_update=0. Plan
     memset-zeroed each frame; would send {0,0,0,0,0,0} on BBB inter
     frames instead of {1,0,-1,-1,0,0}. Fix: add persistent vp9_lf
     state to object_context, init to VP9 spec defaults, update only
     when parser sees delta_update=1, always copy to kernel control.

C3 - reference_mode out-parameter missing: reference_mode lives in
     FRAME struct, not COMPRESSED_HDR. Plan referenced
     `compressed_hdr_reference_mode` placeholder which would be an
     undefined identifier -> compile failure. Fix: add
     `uint8_t *out_reference_mode` param to vp9_fill_compressed_hdr;
     derive `allowcompinter` at call site from the 3 sign biases.

C4 - Mitigation B scope claim overstated: walk-and-pick-first always
     selects rkvdec on 7.0 (since video1 enumerates first). Hantro
     codecs (MPEG-2, VP8) at video3 still require env override.
     Fix: qualify criterion-5 trace; add LIBVA_V4L2_REQUEST_NO_
     AUTODETECT=1 escape hatch for legacy callers.

6 Suggested (S1-S6): all confirm plan correctness OR are scope-
aligned non-issues. S4 (uv_mode memcpy omission safe for rkvdec)
baked into Clause 9 amended text.

Without this review, iter4 Phase 6 would have failed first compile
(C3) + produced wrong inter-frame output (C1+C2) + caused user
confusion (C4). Estimated saving: 1 compile failure + 1 Phase 7 ->
Phase 4 loopback + 1 doc correction.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 05:49:13 +00:00
marfrit 4b36077b17 iter4 Phase 4: plan locks 12 contract clauses + Mitigation B
5-commit plan (Z, A, B, C, optional D):
- Commit Z: src/request.c — walk /dev/video* + /dev/media*, match by
  driver name in {rkvdec, hantro-vpu, cedrus, sun4i_csi}; restores
  baseline functionality on 7.0 (where /dev/video0 is rockchip-rga).
- Commit A: src/config.c — VAProfileVP9Profile0 enumeration + dispatch
  + entrypoints (~16 LOC, 1 file).
- Commit B: NEW src/vp9.c + .h + meson — 12 contract clauses; ~580 LOC
  vp9.c (50 infra + 80 VPX rac + 50 uncompressed-header partial parse +
  180 compressed-header parser + ~200 frame-fill).
- Commit C: src/picture.c + surface.h — VP9 dispatch + 2 buffer-type
  cases + union extension; NO BeginPicture reset (VP9 has no
  iqmatrix_set-style flags).
- Commit D: optional fix-forward placeholder (predicted no-op per
  feedback_runtime_enumerates_allowlists.md).

Total ~699 LOC, 7 files.

12 contract clauses include 2 NEW vs iter3:
- Clause 3: compile-time _Static_assert sizeof v4l2_ctrl_vp9_frame ==
  168 && ..._compressed_hdr == 2040 (any UAPI shift fails loudly).
- Clause 6: uncompressed-header partial parse for lf_delta_* +
  base_q_idx (VAAPI doesn't expose; BBB keyframe needs non-zero
  ref_deltas={1,0,-1,-1} per Phase 3 anchor).

7 Phase 5 review questions queued, all empirical-leaning per
feedback_review_empirical_over_theoretical.md Direction 2:
parser-vs-bitstream cross-check, FFmpeg-XOR-remap validation,
struct-size stability, mitigation B regression risk.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 23:10:47 +00:00
marfrit 56abe3d6a2 iter4 Phase 3: VP9 baseline + 4-codec regression on 7.0 substrate
Captured on linux-fresnel-fourier 7.0-1 (post 6.19 decommission).

VP9 baseline (kernel-direct via ffmpeg-v4l2request on rkvdec):
- 5-frame SW reference PNG SHA256 anchors (criterion-4)
- VIDIOC_S_EXT_CTRLS strace with full payload at -s 16384
- Empirical struct sizes 168 B (FRAME) / 2040 B (COMPRESSED_HDR)
  supersede Phase 2 estimates of 144 / 1947
- Probe pattern: count=1 (FRAME-only) then count=2 (FRAME + COMPRESSED_HDR)

Phase 2 doc fix: control IDs corrected 0xa40b2c/d -> 0xa40a2c/d.

4-codec regression (H.264, MPEG-2, HEVC, VP8): all fall back to SW on
default config because /dev/video0 is now rockchip-rga (RGB color
converter), not a codec device. Fork hardcodes /dev/video0 in
request.c:149. Env override LIBVA_V4L2_REQUEST_VIDEO_PATH /
_MEDIA_PATH restores per-driver profile enumeration; mitigation A/B/C
queued for user decision.

New contract clauses surfaced:
- Clause 11: uncompressed-header partial parse for lf_delta /
  base_q_idx (VAAPI doesn't expose these; keyframe ref_deltas non-zero
  for BBB so leave-at-zero is wrong)
- Clause 12: compile-time sizeof asserts on the two control structs
  so future UAPI shifts fail loudly

iter4_phase3.tgz: full Phase 3 artifact bundle (strace + PNG refs).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 20:31:53 +00:00
claude-noether 2651e4cfdf iter4 Phase 2: situation analysis — VP9 backend gaps + compressed-
header parser requirement

Source-read of every file the iter4 patch series will touch, plus
kernel UAPI + VAAPI + downstream FFmpeg + kernel rkvdec reference
sources. Conducted on noether against fork tip e1aca9c (iter3 close).

Critical scope-shaping finding: rkvdec on RK3399 REQUIRES
V4L2_CID_STATELESS_VP9_COMPRESSED_HDR (not optional). Per
drivers/staging/media/rkvdec/rkvdec-vp9.c::rkvdec_vp9_run_preamble
lines 752-754:

  ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
                        V4L2_CID_STATELESS_VP9_COMPRESSED_HDR);
  if (WARN_ON(!ctrl))
      return -EINVAL;

VAAPI does NOT expose compressed-header probability updates
(va_dec_vp9.h:50-192 — only frame parameters + segmentation;
vendor VAAPI drivers parse compressed header in firmware/GPU).
Therefore the libva backend MUST parse the compressed header
itself via a VPX boolean decoder + inv_map_table[]. ~150-200 LOC
of bitstream parsing logic (port from FFmpeg
v4l2_request_vp9.c::fill_compressed_hdr).

Bug enumeration (12 sites):

  B1   config.c::RequestQueryConfigProfiles    enum block missing
  B2   config.c::RequestCreateConfig           VP9 case missing
  B3   config.c::RequestQueryConfigEntrypoints VP9 case missing
  B4   src/vp9.c                               new file ~500-600 LOC
  B5   src/vp9.h                               new file ~35-45 LOC
  B6   src/vp9_rac.h                           NEW or inline (Phase 4
                                                 plan locks Option A:
                                                 inline in vp9.c)
  B7   picture.c::codec_set_controls           VP9 dispatch missing
  B8   picture.c::codec_store_buffer           2 buffer-type cases
                                                 (Picture + Slice;
                                                 NOT 4 like VP8)
  B9   picture.c::RequestBeginPicture          predicted no reset
                                                 needed (no flag-state
                                                 like VP8 iqmatrix_set)
  B10  surface.h::object_surface::params union vp9 member missing
  B11  meson.build                             vp9.c/vp9.h not in lists
  B12  buffer.c                                predicted no change
                                                 needed (VP9 uses
                                                 Picture/Slice/SliceData
                                                 — all whitelisted)

Non-bugs (intentionally untouched): context.c (no DECODE_MODE/
START_CODE menus per FFmpeg ref), video.c (CAPTURE-side format
list), v4l2.c (fourcc-agnostic), include/hevc-ctrls.h (already
includes <linux/v4l2-controls.h>).

Contract surface cited verbatim:

  V4L2_CID_STATELESS_VP9_FRAME = 0xa40b2c (~144 bytes — much
    smaller than VP8's 1232 bytes because VP9_FRAME carries no
    entropy table; that's in COMPRESSED_HDR)
  V4L2_CID_STATELESS_VP9_COMPRESSED_HDR = 0xa40b2d (~1947 bytes
    — coef[4][2][2][6][6][3] alone is 1728 bytes)
  Per-frame submission: 2 controls batched in single S_EXT_CTRLS
  v4l2_request_vp9.c references confirmed: 2-control shape,
    runtime-probed COMPRESSED_HDR availability (rkvdec advertises
    it; we MUST provide)

VAAPI buffer types: 2 per frame (Picture + Slice) vs iter3 VP8's
4. NO Probability buffer (VP9 keeps probs in compressed header).
NO IQMatrix (VP9 keeps quant in slice's per-segment seg_param[8]).

VAAPI → V4L2 mapping table: 30+ fields enumerated. Several gap
candidates identified for Phase 3 empirical resolution:

  Q1 lf.ref_deltas/mode_deltas/flags — not in VAAPI; FFmpeg reads
     from VP9Context internal. BBB likely zero.
  Q2 quant.base_q_idx + deltas — VAAPI exposes only effective
     per-segment scales. Inverse-derive needed.
  Q3 reference_mode — not in VAAPI. Default to SELECT?
  Q4 interpolation_filter mapping (FFmpeg ^ remap)
  Q5 reset_frame_context off-by-one (FFmpeg > 0 ? - 1 : 0)
  Q6 Per-segment feature_data[8][4] derivation from VAAPI's
     effective scales is non-trivial
  Q7 mpv 0.41.0 VP9 hwdec engagement (per memory feedback_hw_
     decode_engagement_check.md — known gap from iter3 VP8)
  Q8 rkvdec dma_resv issue? (predicted NO based on iter1+iter2
     successful mpv-DMA-BUF-GL on rkvdec)

Patch-shape prediction: ~580-690 LOC across 5 modified + 2 new
files (closer to iter2 HEVC's 470 than iter3 VP8's 370). Compressed-
header parser is the dominant cost.

Phase 3 baseline targets queued: cross-validator strace verbatim
S_EXT_CTRLS payloads (both controls), VAAPI consumer trace, mpv-
VP9-vaapi engagement check, rkvdec readback non-zero check.

Phase 4 plan structure anticipated: 10-clause template per
iter2/iter3, with new Clause 8 dedicated to compressed-header
parser.

Refs:
  phase0_findings_iter4.md (Phase 1 lock)
  phase8_iteration3_close.md (predecessor)
  references/ffmpeg-kwiboo/libavcodec/v4l2_request_vp9.c (V4L2 ref)
  references/ffmpeg-kwiboo/libavcodec/vaapi_vp9.c (VAAPI ref)
  /home/mfritsche/src/linux-rfc/drivers/staging/media/rkvdec/
    rkvdec-vp9.c (kernel driver — confirms COMPRESSED_HDR
    requirement at lines 752-754)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 05:20:07 +00:00
claude-noether 9a71dbf4c3 iter4 Phase 0 + Phase 1 lock: VP9 on rkvdec
Opens iter4 immediately after iter3 close (d5d4beb). Targets VP9
Profile 0 as the fifth (final) codec to pass boolean-correctness
on fresnel via libva-v4l2-request-fourier — completes the campaign
codec scope.

Locked research question:
  mpv --hwdec=vaapi bbb_720p10s_vp9.webm engages backend cleanly
  on rkvdec, and HW pixel readback yields byte-identical output
  to a software-decoded reference for the same frames.

Five Phase 1 boolean criteria:
  1. vainfo enumerates VAProfileVP9Profile0 on rkvdec env binding
  2. vaCreateConfig(VAProfileVP9Profile0, VLD) = SUCCESS
  3. ffmpeg -hwaccel vaapi VP9 5-frame decode exit 0
  4. HW=SW byte-identical with HW engagement verified per memory
     feedback_hw_decode_engagement_check.md (mpv -v log inspection
     before claiming match). If mpv falls back to SW for VP9 like
     it did for iter3 VP8, OR if rkvdec exhibits the same dma_resv
     kernel issue as hantro, fall through to transitive proof per
     memory reference_dmabuf_resv_blocker.md (libva backend
     payload == kernel-direct payload AND kernel-direct decode ==
     SW reference).
  5. FOUR-codec regression block: H.264 + MPEG-2 + HEVC + VP8
     reference hashes hold

Substrate carry-forward (re-verified):
  - fork tip e1aca9c (post-iter3-close)
  - /usr/lib/dri/v4l2_request_drv_video.so SHA256 0ab5b2ba...4ef
  - linux-eos-arm 6.19.9-99-eos-arm
  - bbb_720p10s_vp9.webm fixture on fresnel ~/fourier-test/ (3.4 MB)
  - rkvdec OUTPUT_MPLANE VP9F + 2 VP9 stateless controls
    (V4L2_CID_STATELESS_VP9_FRAME = 0xa40b2c, COMPRESSED_HDR =
    0xa40b2d)
  - cross-validator anchor confirmed: rkvdec advertises VP9 per
    Phase 0 V4L2 inventory
  - Reference sources local:
    references/ffmpeg-kwiboo/libavcodec/v4l2_request_vp9.c
    references/ffmpeg-kwiboo/libavcodec/vaapi_vp9.c
    references/linux-mainline/drivers/staging/media/rkvdec/
      rkvdec-vp9.c (verify presence at Phase 2)

Predicted scope:
  - config.c: ADD VP9 enumeration block + RequestCreateConfig case
    + RequestQueryConfigEntrypoints case (3 sites; same shape as
    iter3 VP8)
  - src/vp9.c NEW file (~250-350 LOC; 2 V4L2 controls per frame:
    FRAME + COMPRESSED_HDR; 8-entry DPB vs VP8's 3)
  - src/vp9.h NEW file
  - src/meson.build add 'vp9.c' + 'vp9.h' entries
  - picture.c codec_set_controls VP9 dispatch + codec_store_buffer
    cases for 2 VAAPI VP9 buffer types (Picture + Slice; NO
    Probability + IQMatrix unlike iter3 VP8)
  - surface.h params union extend with vp9 member
  - context.c: NO changes expected (no init-time menus per FFmpeg
    ref pattern)
  - buffer.c: predicted no Commit D needed (VP9 uses Picture +
    Slice + SliceData buffer types — all already whitelisted by
    H.264 path); plan for fix-forward if runtime miss surfaces
    per memory feedback_runtime_enumerates_allowlists.md

Predicted total: ~400-500 LOC, 3-4 commits + 0-1 fix-forwards.
Larger than iter3 VP8 (370 LOC) but comparable to iter2 HEVC
(470 LOC).

VP9 contract surface:
  - 2 controls per frame batched in single S_EXT_CTRLS:
    FRAME (struct v4l2_ctrl_vp9_frame) + COMPRESSED_HDR
    (struct v4l2_ctrl_vp9_compressed_hdr — probability updates
    from compressed header)
  - 8 reference frames in DPB (active_ref_frames[8])
  - Tile-based decoding (VP9 has 1..N tiles per frame)
  - Profile 0 only (8-bit 4:2:0); Profile 1/2/3 OUT-OF-SCOPE

Phase 2 source-read targets queued: config.c enumeration pattern,
picture.c dispatch + per-buffer-type cases, surface.h params union,
VAAPI <va/va_dec_vp9.h>, kernel UAPI v4l2_ctrl_vp9_frame +
v4l2_ctrl_vp9_compressed_hdr (lines 2696-2870), kernel rkvdec-
vp9.c driver, FFmpeg v4l2_request_vp9.c + vaapi_vp9.c.

Memory carry-forward (all 9 entries apply unchanged):
  feedback_gitea_as_claude_noether
  feedback_no_session_termination_attempts
  feedback_header_deletion_check
  feedback_runtime_enumerates_allowlists (NEW iter3)
  feedback_review_empirical_over_theoretical (BOTH directions)
  feedback_rockchip_pixel_verify_path
  feedback_fresnel_hostname (NEW iter3)
  feedback_hw_decode_engagement_check (NEW iter3)
  reference_dmabuf_resv_blocker (NEW iter3)

Open questions inherited from iter3 close (not blocking iter4
lock):
  - Does mpv 0.41.0 engage HW for VP9 hwdec=vaapi or fall back
    like it did for VP8? Phase 0+3 verifies via mpv -v log.
  - Does rkvdec exhibit the same vb2_dma_resv kernel issue as
    hantro? Likely no (different driver subsystem; iter1+iter2
    mpv-DMA-BUF-GL paths worked on rkvdec). Phase 3 baseline
    answers via ffmpeg-vaapi-hwdownload non-zero check.

iter4 = final codec in campaign scope. Clean close → 5/5 codecs
passing → campaign complete.

Refs:
  phase0_findings_iter1.md (iter1 MPEG-2 lock template)
  phase0_findings_iter2.md (iter2 HEVC lock template)
  phase0_findings_iter3.md (iter3 VP8 lock template)
  phase8_iteration3_close.md (immediate predecessor close)
  phase0_evidence/2026-05-07/v4l2_inventory_findings.md (rkvdec
    VP9 capability)
  phase0_evidence/2026-05-07/test_fixtures.md (bbb_720p10s_vp9.
    webm provenance)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 23:36:04 +00:00