# Iteration 2 — Phase 7 (verification measurements)

Phase 7 verification of the iter2 HEVC fix, executed 2026-05-08 against fork tip `8d71e20` on `git.reauktion.de/marfrit/libva-v4l2-request-fourier`. **All five Phase 1 criteria green.** Bonus byte-compare confirms structural match against Baseline B with two field-value divergences in informational bitstream parameters that don't affect decode correctness (Criterion 4's byte-identical HW=SW pixel verification proves it).

Per `feedback_dev_process.md` Phase 7: verbatim raw output is the artifact; this document is the index. Raw captures in [`phase0_evidence/2026-05-08/iter2_phase7/`](phase0_evidence/2026-05-08/iter2_phase7/).

## Pre-flight: rig state

```
$ ssh fresnel 'git -C ~/src/libva-v4l2-request-fourier log --oneline -3'
8d71e20 fresnel-fourier iter2 Phase 6 commit B: rewrite h265.c against new V4L2 stateless HEVC API
cca539d fresnel-fourier iter2 Phase 6 commit A: config.c break for HEVCMain case
229d6d1 fresnel-fourier iter1 Phase 6 commit D: drop missed mpeg2-ctrls.h include from context.c

$ sha256sum /usr/lib/dri/v4l2_request_drv_video.so build/src/v4l2_request_drv_video.so
9e27043847998c197a46a1a26b2f77f22880bb7b3a62aa4d60d8fcaec0ae6258  /usr/lib/dri/v4l2_request_drv_video.so
9e27043847998c197a46a1a26b2f77f22880bb7b3a62aa4d60d8fcaec0ae6258  build/src/v4l2_request_drv_video.so

device map (this boot):
  /dev/video0  rockchip-rga
  /dev/video1  rkvdec                       (HEVC + H.264 + VP9)
  /dev/video2  rockchip,rk3399-vpu-enc
  /dev/video3  rockchip,rk3399-vpu-dec      (MPEG-2 + VP8)
  /dev/video4  Camera (USB)
  /dev/video5  Camera (USB)
```

Same device numbering as iter1 Phase 7 boot. Substrate matches iter1 close + `pacman -Syu` userland refresh + iter2 Phase 6 commits A + B. `eos-reboot-recommended` marker still present from the userland upgrade; mfritsche Plasma session active throughout (no SDDM regression).

## Criterion 1 — vainfo lists VAProfileHEVCMain on rkvdec bind

**Verbatim** ([`criterion1_vainfo.txt`](phase0_evidence/2026-05-08/iter2_phase7/criterion1_vainfo.txt)):

```
$ LIBVA_DRIVER_NAME=v4l2_request \
  LIBVA_V4L2_REQUEST_VIDEO_PATH=/dev/video1 \
  LIBVA_V4L2_REQUEST_MEDIA_PATH=/dev/media0 \
  vainfo --display drm --device /dev/dri/renderD128

vainfo: VA-API version: 1.23 (libva 2.22.0)
vainfo: Driver version: v4l2-request
vainfo: Supported profile and entrypoints
      VAProfileH264Main               :	VAEntrypointVLD
      VAProfileH264High               :	VAEntrypointVLD
      VAProfileH264ConstrainedBaseline:	VAEntrypointVLD
      VAProfileH264MultiviewHigh      :	VAEntrypointVLD
      VAProfileH264StereoHigh         :	VAEntrypointVLD
      VAProfileHEVCMain               :	VAEntrypointVLD
```

**Result: ✅ PASS.** VAProfileHEVCMain enumerated; H.264 family unchanged; no regression.

## Criterion 2 — vaCreateConfig succeeds for VAProfileHEVCMain

**Verbatim libva trace** (`criterion2_3/libva.trace.135940.thd-0x000052f4`):

```
[60492.576649] vaQueryConfigProfiles ret = VA_STATUS_SUCCESS
[60492.576680] va_TraceCreateConfig
[60492.576684]   profile = 17, VAProfileHEVCMain
[60492.576687]   entrypoint = 1, VAEntrypointVLD
[60492.576690]   num_attribs = 0
[60492.576730] vaCreateConfig ret = VA_STATUS_SUCCESS, success (no error)
```

**Result: ✅ PASS.** `vaCreateConfig(VAProfileHEVCMain, VAEntrypointVLD)` returns `VA_STATUS_SUCCESS` (was `VA_STATUS_ERROR_UNSUPPORTED_PROFILE = 12` pre-iter2).

## Criterion 3 — ffmpeg-direct HEVC decode engages backend, exits 0

**Verbatim** ([`criterion2_3/ffmpeg.stdout`](phase0_evidence/2026-05-08/iter2_phase7/criterion2_3/ffmpeg.stdout)):

```
$ ffmpeg -hide_banner -loglevel info -hwaccel vaapi \
    -i ~/fourier-test/bbb_720p10s_hevc.mp4 -frames:v 5 -f null -

Press [q] to stop, [?] for help
v4l2-request: cap_pool_init: 24 slots ready (v4l2_index=0..23, 1 plane(s) per slot)
Output #0, null, to 'pipe:':
  Stream #0:0(eng): Video: wrapped_avframe, nv12(tv, bt709, progressive), 1280x720,
                   q=2-31, 200 kb/s, 24 fps, 24 tbn (default)
frame=    5 fps=0.0 q=-0.0 Lsize=N/A time=00:00:00.20 bitrate=N/A speed=0.253x
[ffmpeg exit 0]
```

**Greps**:
- `Failed to create decode configuration`: 0 hits.
- `S_EXT_CTRLS.*EINVAL` in libva trace: 0 hits.
- `EINVAL` in ffmpeg stdout: 0 hits.

Notably **no auxiliary "Unable to set control(s)" line** for HEVC (vs MPEG-2/H.264 cases where it appears from the H.264 device-init falling on hantro). On rkvdec both H.264 and HEVC device-init batches succeed; both batches set DECODE_MODE + START_CODE without EINVAL.

**Result: ✅ PASS.** ffmpeg processed 5 HEVC frames cleanly via the libva backend; cap_pool_init confirms our backend was engaged.

## Criterion 4 — DMA-BUF GL HEVC HW=SW byte-identical at +02s

**Verbatim hashes** ([`criterion4/hashes.txt`](phase0_evidence/2026-05-08/iter2_phase7/criterion4/hashes.txt)):

```
47a5f3850df5d8c732767a227830c2272ff78402a7b6adeea329e29838808be5  /tmp/iter2_phase7/criterion4/image_hw/00000001.jpg
a467b3bc9d7b6374b6786ecfac46932d6c7bb932ab11d311edaa233d7863e656  /tmp/iter2_phase7/criterion4/image_hw/00000002.jpg
47a5f3850df5d8c732767a227830c2272ff78402a7b6adeea329e29838808be5  /tmp/iter2_phase7/criterion4/image_sw/00000001.jpg
a467b3bc9d7b6374b6786ecfac46932d6c7bb932ab11d311edaa233d7863e656  /tmp/iter2_phase7/criterion4/image_sw/00000002.jpg
```

| Check | Result |
|---|---|
| HW frame 1 == SW frame 1 (`47a5f3850df5...`) | ✅ PASS |
| HW frame 2 == SW frame 2 (`a467b3bc9d7b...`) | ✅ PASS |
| frame 1 != frame 2 (real motion) | ✅ PASS |

**Result: ✅ PASS.** HEVC hardware decode on RK3399 / rkvdec / libva-v4l2-request-fourier @ iter2 produces pixels bit-exact identical to software reference, when read via the cache-coherency-safe DMA-BUF GL import path.

## Criterion 5 — iter1 MPEG-2 + T4 H.264 references both still match

**Verbatim hashes** ([`criterion5/hashes.txt`](phase0_evidence/2026-05-08/iter2_phase7/criterion5/hashes.txt)):

```
f623d5f7a41697f67dd227275c6f1b21ffc257f65626d32fde8229357f8764c9  /tmp/iter2_phase7/criterion5/h264_hw/00000001.jpg
7d7bc6f2146dda8b2d223bba622c4b9fbe9674181ff1e02afe286b620342e0a8  /tmp/iter2_phase7/criterion5/h264_hw/00000002.jpg
6e7873030dbf0403c67f35dd106ebef3c7909a0fd12433b82ad758e7fee9f092  /tmp/iter2_phase7/criterion5/mpeg2_hw/00000001.jpg
ccc7ce08810d4a96e9ba7a19f4f95bbf6cc861bda9337604b5c668ad52bef7de  /tmp/iter2_phase7/criterion5/mpeg2_hw/00000002.jpg
```

| Cell | Reference | Match |
|---|---|---|
| H.264 +30s frame 1 | T4 `f623d5f7...` | ✅ |
| H.264 +30s frame 2 | T4 `7d7bc6f2...` | ✅ |
| MPEG-2 +02s frame 1 | iter1 `6e7873030dbf...` | ✅ |
| MPEG-2 +02s frame 2 | iter1 `ccc7ce08810d...` | ✅ |

**Result: ✅ PASS.** Iter2's HEVC fix doesn't regress H.264 or MPEG-2. All four reference hashes hold byte-identical.

## Bonus — byte-compare post-fix VIDIOC_S_EXT_CTRLS payload vs Baseline B

**Verbatim post-fix call** (frame 1, from [`bonus/postfix_frame1.txt`](phase0_evidence/2026-05-08/iter2_phase7/bonus/postfix_frame1.txt)):

```
ioctl(/dev/video1, VIDIOC_S_EXT_CTRLS,
  {ctrl_class=0xf010000 /* V4L2_CTRL_CLASS_CODEC_STATELESS */,
   count=5,
   controls=[
     {id=0xa40a90 /* SPS */,            size=40,   ...},
     {id=0xa40a91 /* PPS */,            size=64,   ...},
     {id=0xa40a92 /* SLICE_PARAMS */,   size=280,  ...},  /* 1 slice × sizeof(struct v4l2_ctrl_hevc_slice_params)=280 */
     {id=0xa40a93 /* SCALING_MATRIX */, size=1000, ...},  /* sizeof(struct v4l2_ctrl_hevc_scaling_matrix) = 1000 */
     {id=0xa40a94 /* DECODE_PARAMS */,  size=328,  ...}
   ]}) = 0
```

**Structural match**: same shape as Phase 3 Baseline B verbatim — count=5, ctrl_class=`V4L2_CTRL_CLASS_CODEC_STATELESS`, three CIDs in order (90, 91, 92, 93, 94), all sizes correct.

(Note: my Phase 4 plan body claimed `sizeof(struct v4l2_ctrl_hevc_scaling_matrix) = 1296` but empirical sizeof is **1000 bytes** = 96 (4×4) + 384 (8×8) + 384 (16×16) + 128 (32×32) + 6 (DC 16×16) + 2 (DC 32×32). Plan's 1296 was a typo from earlier draft; actual struct is 1000.)

### Field-decoded SPS (40 bytes, BBB I-frame frame 1):

```
post-fix:    00000005d002000004040000010100030000fffffd00000001000000000000008001000000000000
Baseline B:  00000005d00200000404020401010003 0000fffffd00000001000000000000008001000000000000
```

(Bytes 8-11 highlighted: `00 00 00 00` post-fix vs `02 04 00 00` Baseline B.)

| Byte offset | Field | Post-fix | Baseline B | Match |
|---|---|---|---|---|
| 0 | `video_parameter_set_id` | 0 | 0 | ✅ |
| 1 | `seq_parameter_set_id` | 0 | 0 | ✅ |
| 2-3 | `pic_width_in_luma_samples` | 1280 | 1280 | ✅ |
| 4-5 | `pic_height_in_luma_samples` | 720 | 720 | ✅ |
| 6 | `bit_depth_luma_minus8` | 0 | 0 | ✅ |
| 7 | `bit_depth_chroma_minus8` | 0 | 0 | ✅ |
| 8 | `log2_max_pic_order_cnt_lsb_minus4` | 4 | 4 | ✅ |
| 9 | `sps_max_dec_pic_buffering_minus1` | 4 | 4 | ✅ |
| **10** | **`sps_max_num_reorder_pics`** | **0** | **2** | **❌ DIVERGE** |
| **11** | **`sps_max_latency_increase_plus1`** | **0** | **4** | **❌ DIVERGE** |
| 12+ | (rest matches) | ... | ... | ✅ |
| 32-39 | `flags` | `0x180` (SAO\|STRONG_INTRA_SMOOTHING) | `0x180` | ✅ |

**Two field divergences** in SPS bytes 10-11. Per Phase 4 plan Clause 2: "VAAPI doesn't expose `sps_max_num_reorder_pics` or `sps_max_latency_increase_plus1`; iter2 hardcodes 0." FFmpeg parses these from the bitstream's SPS header directly. The libva backend reads them via VAAPI's `VAPictureParameterBufferHEVC` which doesn't expose them, so they default to 0.

**Operational impact**: nil. Criterion 4 confirms HW=SW byte-identical pixels — kernel decoded correctly with these fields zero. The kernel HEVC handler may use them for DPB sizing hints, but the rkvdec driver tolerated 0 values for the BBB fixture.

### PPS field divergence (informational)

PPS bytes also show a small divergence in the `flags` u64:

- Post-fix: bits set are 6 (`CU_QP_DELTA_ENABLED`) + 14 (`PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED`) → flags = 0x4040.
- Baseline B (per Phase 3 raw): includes one additional bit, possibly bit 12 (`ENTROPY_CODING_SYNC_ENABLED`) → flags = 0x5040.

VAAPI source for bit 12 is `picture->pic_fields.bits.entropy_coding_sync_enabled_flag`. The post-fix code DOES check that field and set the flag. Either:

(a) BBB doesn't actually have entropy_coding_sync_enabled (field is 0 in VAAPI's struct on libva path; FFmpeg may have parsed something different from bitstream), OR
(b) Two distinct VAAPI consumers (mpv-vaapi vs ffmpeg-v4l2request) feed different data to the libva backend.

**Operational impact**: nil. Criterion 4 byte-identical pass.

### Net byte-compare verdict

The libva backend's V4L2 control payload differs from the cross-validator on a small number of informational bitstream-parsed fields that VAAPI doesn't expose to the libva backend. **Pixel correctness verified independently via Criterion 4 byte-identical HW=SW** — kernel HEVC decoder accepts the libva-supplied controls and produces correct output regardless of these field divergences.

**No Phase 7 → Phase 4 loopback triggered.** Field divergences logged for Phase 8 backlog (low-priority polish — improve VAAPI-to-V4L2 fidelity by parsing additional SPS fields from slice bitstream, similar to the existing NAL-unit-type extraction).

## Phase 1 → Phase 7 scoreboard

| Criterion | Pre-iter2 (Phase 3 baseline) | Post-iter2 (Phase 7 verification) | Verdict |
|---|---|---|---|
| 1: vainfo VAProfileHEVCMain | ✓ already | ✓ unchanged | ✅ PASS |
| 2: vaCreateConfig SUCCESS | ✗ ret = 12 UNSUPPORTED_PROFILE | ✓ ret = VA_STATUS_SUCCESS | ✅ PASS |
| 3: ffmpeg-direct exit 0 | ✗ Failed setup for format vaapi | ✓ frame=5, exit 0, cap_pool engaged | ✅ PASS |
| 4: DMA-BUF GL HW=SW byte-identical at +02s | n/a (decode never reached) | ✓ HW1=SW1=`47a5f3850df5...`, HW2=SW2=`a467b3bc9d7b...`, frames differ | ✅ PASS |
| 5: iter1 MPEG-2 + T4 H.264 reference hashes | ✓ baseline | ✓ all 4 reference hashes hold byte-identical | ✅ PASS |

**All five criteria green. Phase 7 → Phase 8: proceed (no Phase 7 → Phase 4 loopback needed).**

## Notable Phase 7 observations for Phase 8 memory

1. **Phase 5 review value**: 3 Critical findings (data_byte_offset rename, dpb.rps→index-arrays semantics, pic_order_cnt_val rename) caught at Phase 5 — prevented Phase 6 compile failures + at least 1-2 Phase 7 → Phase 4 loopback cycles. Per `feedback_review_empirical_over_theoretical.md`: every Critical and Should-fix verified against fresnel kernel UAPI before responding; no source-read rebuttals attempted. Lesson held throughout iter2.

2. **One Phase 5 reviewer suggestion was empirically wrong**: S1 mapping suggested `picture->pic_fields.bits.uniform_spacing_flag` exists in VAAPI; gcc test-compile rejected it. Both PPS bits 19 (`DEBLOCKING_FILTER_CONTROL_PRESENT`) and 20 (`UNIFORM_SPACING`) stay zero — VAAPI doesn't expose either. Documented inline in `src/h265.c::h265_fill_pps`. Functionally fine for BBB (no tiles, no explicit deblocking control). Lesson: even reviewer-cited mappings can be wrong on subtle field availability; the rule "verify empirically" applies to amendment proposals too, not just original-author claims.

3. **Phase 4 plan body had a typo** on `sizeof(struct v4l2_ctrl_hevc_scaling_matrix)`: claimed 1296 bytes, actual is 1000. Reviewer didn't catch this (focused on field-name correctness, not size arithmetic). Phase 7 byte-compare caught it — the structural-shape check confirmed 1000 was right. Phase 4 amendment-by-side-channel: the value in Phase 4 plan body is wrong; the actual code uses `sizeof()` so produces correct bytes.

4. **VAAPI ↔ V4L2 field-fidelity gaps**: the bonus byte-compare surfaced 2 field divergences (sps_max_num_reorder_pics, sps_max_latency_increase_plus1, possibly PPS bit 12) where the libva backend's output differs from FFmpeg's because VAAPI's `VAPictureParameterBufferHEVC` doesn't expose all SPS-parsed fields. Operational impact nil (Criterion 4 byte-identical pixel pass). Phase 8 polish-backlog candidate: add SPS bitstream parsing to extract these fields when VAAPI doesn't supply them.

5. **No mpv-vaapi-MPEG-2-style filter for HEVC**: mpv `--hwdec=vaapi` engages HEVC cleanly (unlike its silent fallback for MPEG-2 vaapi-copy in iter1). Confirms Phase 5 Q3: mpv-vaapi DOES send VAPictureParameterBufferType per frame for HEVC — the per-frame iqmatrix-set-true reset masks the latent B3 bug for HEVC same as it does for iter1 MPEG-2.

6. **Slice count for BBB HEVC fixture**: 1 slice per frame (size=280 = 1 × `sizeof(slice_params)`), confirming Phase 4 prediction. Multi-slice support in iter2 is untested but coded.

## Phase 7 close

Phase 7 → Phase 8 transition. **iter2 advances to memory-update phase.** Third-codec passing on the campaign-level scoreboard: 2/5 → **3/5** (H.264 in T4, MPEG-2 in iter1, HEVC in iter2).
