diff --git a/phase8_iteration31_close.md b/phase8_iteration31_close.md new file mode 100644 index 0000000..ac179f0 --- /dev/null +++ b/phase8_iteration31_close.md @@ -0,0 +1,61 @@ +## Iteration 29/30/31 — Phase 8 (close): HEVC frame 2+ FIXED + +Closes 2026-05-14, second campaign-day session after iter27/28 partial close. Three sub-iterations: iter29 probe (refuted 40-byte inflation theory), iter30 timestamp-magnitude sweep (refuted timestamp hypothesis), **iter31 α-29 (FIX)**. + +### Final result: 3/3 PASS (anchors that go through libva single-device probe) + +| Codec | 10F result | Status | +|---|---|---| +| H.264 | byte-equal to SW (10 frames) | **PASS** (Bug 4 already fixed iter25 α-25) | +| HEVC | byte-equal to SW (10 frames) | **PASS** (Bug 5 NOW FIXED iter31 α-29) | +| VP9 | byte-equal to SW (10 frames) | **PASS** (unchanged) | +| MPEG-2| untestable through libva | pre-existing single-device probe limitation | +| VP8 | untestable through libva | same | + +### Root cause: wrong V4L2 field + +α-26 had set `decode_params->short_term_ref_pic_set_size = picture->st_rps_bits` based on the FIELD NAME. But `picture->st_rps_bits` from VAAPI is documented as the bit-count of `short_term_ref_pic_set()` in the **slice segment header** (per `/usr/include/va/va_dec_hevc.h:177-185`). The V4L2 SLICE-PARAMS field of the same name is what rkvdec reads. The V4L2 DECODE-PARAMS field with this name (where α-26 wrote it) refers to the SPS-side bit count, which rkvdec does not use. + +`rkvdec_hevc.c::assemble_sw_rps` (lines 386-389) reads `sl_params->short_term_ref_pic_set_size`: +```c +if (!(decode_params->flags & V4L2_HEVC_DECODE_PARAM_FLAG_IDR_PIC)) { + if (sl_params->short_term_ref_pic_set_size) + st_bit_offset = sl_params->short_term_ref_pic_set_size; + else if (sps->num_short_term_ref_pic_sets > 1) + st_bit_offset = fls(sps->num_short_term_ref_pic_sets - 1); +} +``` + +For BBB's `num_short_term_ref_pic_sets == 1`, the fallback (`fls(0) = 0`) is wrong — the HW reads slice-header bits from offset 0, treats the st_ref_pic_set() bytes as long-term-RPS / slice-header continuation, and the entropy decoder spins onto garbage state for every non-IDR slice. IDR is gated out by `V4L2_HEVC_DECODE_PARAM_FLAG_IDR_PIC` → frame 1 unaffected → consistent with prior iter25 "frame 1 PASS, frame 2+ FAIL" observation. + +### Fix: α-29 + +```c +// src/h265.c::h265_fill_slice_params, line 476: +- slice_params->short_term_ref_pic_set_size = 0; /* VAAPI doesn't expose */ ++ slice_params->short_term_ref_pic_set_size = picture->st_rps_bits; +``` + +Backend commit `23eb1bd`. Build, deploy, re-test → 10/10 PASS for HEVC. + +### Investigation path + +- **iter29** added env-gated dump of last 80 bytes of HEVC slice_data. Trailing 80 bytes look like real entropy — refuted the "40-byte ffmpeg-vaapi inflation" hypothesis from iter27/28 close. +- **iter30** added `LIBVA_TS_SCALE` env-gated timestamp multiplier. Tested scales 1/1000/1000000. All produced identical (wrong) frame-2 output. Refuted timestamp-magnitude hypothesis. +- **iter31** extended kernel iter27 printk to dump `dpb[2..3]` (offsets 96..128) and `sl[32..64]`. Direct libva-vs-kdirect comparison for POC=1 (display frame 2, decode #4) revealed all slice_params[0..64] bytes match EXCEPT `num_entry_point_offsets` (known unused by rkvdec). Re-reading rkvdec's assemble_sw_rps located the `sl_params->short_term_ref_pic_set_size` read site, then traced backward to ffmpeg-v4l2request's `sh->short_term_ref_pic_set_size` source. VAAPI's `picture->st_rps_bits` is the semantic equivalent. + +### Substrate state at iter31 close + +- Backend fork tip `23eb1bd` (α-25 through α-29 + iter29 dump + iter30 ts-scale env probes; α-29 is the load-bearing fix; others are env-gated and inactive by default). +- Kernel `7.0-10` with iter17 + iter20 + iter21 + iter22 + iter23 + iter27 + iter31 printks (production kernel would revert all these — outstanding cleanup item). +- 5-codec status: 3 PASS, 2 untestable. MPEG-2/VP8 reachability requires libva multi-device-probe rework (separate iter — orthogonal to Bug 4/5). + +### Campaign milestone + +Bug 4 fix landed iter25 α-25. Bug 5 frame 1 fix landed same iter25 α-25. Bug 5 remainder (frames 2+) localized over iter26-28 (correctly: rkvdec reads field X, libva sends 0), mis-fixed in α-26 (right value, wrong field), refuted in iter29-30 (rule-outs), correctly fixed in α-29 (right value to right field). Total: 1 day for full pixel correctness across the three rkvdec-routed codecs. + +### Memory entries to update + +- `feedback_rkvdec_image_fmt_pre_seed.md` — note the remainder-fix landed (no longer "deferred"). +- `feedback_libva_byte_correct_kernel_bug.md` — fully overturned now (Bug 5 is fully a libva-side fix, not kernel-side). +- New: `feedback_va_st_rps_bits_is_slice_field.md` — VAAPI's picture->st_rps_bits goes into slice_params not decode_params (the field NAME is identical, semantics differ).