Files
fresnel-fourier/phase8_iteration31_close.md
marfrit c1f9738368 iter31 α-29 close: HEVC Bug 5 remainder FIXED — 3/3 PASS
Fix: slice_params.short_term_ref_pic_set_size = picture->st_rps_bits
(was 0, mis-routed via α-26 into decode_params with same field name).

Final 5-codec state:
- H.264 10F: PASS (byte-equal SW)
- HEVC 10F: PASS (byte-equal SW)  ← THIS ITER
- VP9 10F: PASS (byte-equal SW)
- MPEG-2 / VP8: untestable through libva single-device probe
  (pre-existing limitation, orthogonal to Bug 4/5)

Backend fork tip: 23eb1bd. Kernel: 7.0-10 (diagnostic printks still in,
production cleanup outstanding).
2026-05-14 15:30:48 +00:00

4.6 KiB
Raw Permalink Blame History

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:

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

// 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).