α-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>
8.4 KiB
Iteration 8 — Phase 8 (close)
Closes 2026-05-13. iter8 = Bug 4 (H.264 partial-fill, originally "inter race-loss") PARTIAL close — narrowed via three eliminations, not fixed.
Summary
| Metric | Value |
|---|---|
| Iteration target | Bug 4 — H.264 libva produces 16×32 partial-fill instead of full frame |
| Hardware | RK3399 rkvdec |
| Fork tip start (iter7 close) | 6df2159 |
| Fork tip end (iter8 close) | 0226684 (4 commits: γ dump, IMP-1 pre-zero, stdlib include fix-fwd, α-2 POC strip removal) |
| LOC delta | +103 / -3 across src/surface.c, src/picture.c, src/h264.c |
| Phase 1 criteria | 5/6 PASS (C1 PARTIAL — Bug 4 narrowed not fixed) |
| Reviews | 2 (Phase 5 γ-plan review + Phase 5b/5c α candidate reviews); 1 plan killed (α-1) |
| Hypotheses eliminated | 5 (libva-readback, slot-binding, stale-residue, constraint_set_flags, POC sentinel) |
| Campaign scoreboard | Unchanged on pixel-correctness axis; +0 net codec fixes. +diagnostic instrumentation (γ dump, pre-zero gate). |
Phase-by-phase narrative
Phase 0 — Bug 4 locked
User pick: H.264 inter race-loss (carryover label from iter4 Phase 7). Lock criterion: libva_h264 == kdirect_h264 byte-identical.
Phase 2 — H.264 source-read
Walked h264.c pipeline end-to-end (994 LOC). Mapped per-frame decode flow (BeginPicture → RenderPicture → EndPicture → SyncSurface). Eliminated 6 of 13 surface-level hypotheses by source-read alone.
Phase 3 — empirical strace + byte-level YUV examination
Bug redefinition: NOT inter-race-loss — even keyframe (IDR) fails. Pattern: structured 16×32 byte patch of luma-neutral data at Y top-left, rest zero. Per-codec diff: VP9 works via libva, HEVC/H.264 don't. SPS bytes: only diff is constraint_set_flags (libva=0 vs kdirect=2). DECODE_PARAMS shows POC sentinel diff (libva strips, kdirect doesn't).
Phase 4 — γ-then-α plan
Decided: diagnostic dump γ FIRST to distinguish "kernel didn't write" from "libva mis-reads."
Phase 5 — γ plan reviewed
Sonnet-architect: 2 CRIT (logging API misuse, missing null guard) + 4 IMP + 3 MIN. All mechanical; strategy stood.
Phase 6 — γ implemented
Added env-gated diagnostic dump to surface.c::RequestSyncSurface. Built, installed.
Phase 7 — γ + IMP-1 verification
γ dump showed libva-readback path returns exactly what's in the buffer (no readback bug). IMP-1 memset experiment confirmed the 16×32 patch is deterministic (NOT stale residue). Bug pinned to kernel-side: rkvdec accepts request, writes only 512 bytes, stops without error flag.
Phase 4b/5b — α-1 SPS constraint_set_flags fix (KILLED)
Plan: derive constraint_set_flags per-profile.
Phase 5b reviewer empirically read rkvdec-h264.c end-to-end and found constraint_set_flags is NEVER accessed by the driver. CRIT-1 killed α-1 before any build. Saved ~15 LOC + a build/install/test cycle.
Reviewer redirected to DPB / POC diff.
Phase 4c/5c/6c — α-2 POC sentinel strip removal
Plan: remove h264_strip_ffmpeg_poc_sentinel (hantro-specific kludge that's not needed for rkvdec).
Phase 5c greenlit with one amendment (preserve VA_PICTURE_H264_INVALID → 0 guard).
Phase 6c implemented: ~12 LOC change in h264.c.
Phase 7c — α-2 verification
α-2 changed wire bytes (POC now matches kdirect's 0x00010000) but output unchanged. POC isn't load-bearing for Bug 4. Three more hypotheses eliminated (libva-readback, slot-binding, stale-residue, constraint_set_flags, POC sentinel — 5 total).
5-codec regression sweep: all 4 non-H.264 hashes unchanged. Zero regression.
Commits shipped
Fork (libva-v4l2-request-fourier)
| SHA | Files | LOC | Description |
|---|---|---|---|
7eae6ea |
src/surface.c | +78 / -1 | γ env-gated CAPTURE buffer diagnostic dump |
66ecbef |
src/picture.c | +23 | IMP-1 env-gated CAPTURE pre-zero diagnostic |
6f4e583 |
src/picture.c | +1 | stdlib.h include fix-forward (getenv) |
0226684 |
src/h264.c | +12 / -2 | α-2: pass H.264 POC values through unchanged |
Campaign repo (fresnel-fourier)
| Commit | Phase | Description |
|---|---|---|
e47a7ba |
P0 | Lock Bug 4 |
abd97e3 |
P2 | H.264 source-read |
4320d78 |
P3 | Strace findings + bug redefinition |
3a63076 |
P4 | γ-then-α plan |
d4c04b4 |
P5 | Architect review |
| (this) | P7 + P8 | Verification + close |
What worked
- Phase 5b's empirical source-read killed α-1 BEFORE the build cycle. Saved a build/install/test pass. Pure win for the review discipline.
- γ dump immediately ruled out libva-readback hypothesis. Without γ, iter9 would still be considering libva-side fixes.
- IMP-1 memset experiment was a one-line discriminator (env-gated) that definitively ruled out stale-residue.
- 5-codec regression sweep at every step: no anchors moved across γ, IMP-1, α-2 changes. Zero regression risk to date.
What didn't work
- α-1 (constraint_set_flags): hypothesis dead per source read.
- α-2 (POC strip): wire bytes changed; output unchanged.
Net: 5 hypotheses eliminated, Bug 4 still present.
Lessons distilled
Lesson 1: Wire-byte equivalence ≠ behavior equivalence
α-2 made libva's wire-byte output match kdirect's for POC fields. Output unchanged. A single-field wire diff is a necessary signal but not sufficient — without knowing whether the kernel actually consumes that field, matching it is theater. Phase 5b CRIT-1's source-read methodology is the right discipline.
Lesson 2: Per-driver kludges should be flagged as such
h264_strip_ffmpeg_poc_sentinel was a hantro-specific workaround embedded in shared H.264 code without per-driver gating. iter5b-β's feedback_unconditional_codec_state.md rule covers per-codec; this case suggests an analogous per-V4L2-driver rule for any kludge with implementation-specific motivation. Memory candidate.
Lesson 3: Bug carryover labels can mislead
"H.264 inter race-loss" was the label since iter4 P7. Phase 3 empirically showed the keyframe also fails — the bug isn't race-related at all. Re-validate the bug shape with empirical evidence before assuming the prior label is correct. Memory candidate.
Cross-cutting backlog status (iter8 increment)
Closed:
- (none — Bug 4 is PARTIAL, not closed)
Narrowed:
- Bug 4: 5 hypotheses eliminated. Remaining candidates: PPS field diff, DPB ordering, DECODE_PARAMS post-DPB fields, slice data encoding.
Still open (unchanged):
- iter4-B1b (multi-decoder routing)
- iter4-B2, B3, Q6, COLOR_RANGE, L3
- Bug 5 (HEVC kernel rejection)
- Bug 6 (VP8 partial output, kernel-side per iter6 narrowing)
Iter8 → iter9 handoff
Substrate at close:
- Fork tip
0226684on noether + fresnel + gitea. - Backend SHA
b6a3958a…on fresnel. - Kernel
linux-fresnel-fourier 7.0-1unchanged. - Test fixtures unchanged.
- γ + IMP-1 diagnostic infrastructure in place (env-gated off by default).
Campaign scoreboard (unchanged from iter7):
Codec | Site | Status | Notes
========|===========|===============|====================================
H.264 | rkvdec | PARTIAL | Bug 4 narrowed; PPS diff next (iter9)
HEVC | rkvdec | TRANSITIVE * | Bug 5 deferred
VP9 | rkvdec | PASS direct | iter5b-β fix
MPEG-2 | hantro | PASS (env) | iter1 PASS
VP8 | hantro | PARTIAL (env) | Bug 6 deferred
iter9 candidate ranking (per Phase 7c):
- α-3 PPS full-byte diff (lowest LOC, mechanical strace re-run + diff)
- α-6 DECODE_PARAMS post-DPB fields (strace re-run with longer -s, diff bytes 512-559)
- α-4 DPB entry ordering (~10 LOC change to h264_fill_dpb)
- α-5 Slice data encoding (deepest investigation, ~60 min)
Memory rule candidates (deferred to next memory-curation pass)
- Per-V4L2-driver kludge gating: any hantro / rkvdec / cedrus-specific workaround in shared codec code must be gated, similar to per-codec gating per
feedback_unconditional_codec_state.md. - Bug label revalidation: when picking up a carryover bug, run Phase 3-equivalent empirical reproduction first; the label may be wrong.
- Wire-byte equivalence ≠ behavior equivalence: a successful "match kdirect's bytes" change isn't proof of fix. Always run the success criterion verifier post-change.
Phase 8 commit
This document records iter8 close. Fork at 0226684. Backend SHA b6a3958a…. Bug 4 narrowed via 5 eliminations; 4 of 5 codec criteria unchanged (PASS); criterion-1 H.264 still PARTIAL.
iter8 = 5/6 PASS. iter9 has a clearer search space than iter8 opened with.