Commit Graph

58 Commits

Author SHA1 Message Date
claude-noether b9625af278 iter1 Phase 3: baseline measurements — Phase 2 confirmed empirically
Four Phase 3 baselines captured on fresnel post-reboot 2026-05-08
00:39 CEST. SDDM watchpoint condition stayed green (greeter passed
cleanly on the new boot). All four baselines confirm Phase 2's
situation analysis empirically; one Phase 1 criterion needs minor
adjustment (Phase 3 → Phase 1 loopback per feedback_dev_process.md).

Baseline A — pre-patch failure mode (master tip 65969da):

  ffmpeg -hwaccel vaapi -i bbb_720p10s_mpeg2.ts ... under strace +
  LIBVA_TRACE captures the chain:

  vaInitialize ret = SUCCESS
  vaQueryConfigProfiles ret = SUCCESS
  vaCreateConfig(profile=VAProfileMPEG2Main, entrypoint=VLD)
    ret = VA_STATUS_ERROR_UNSUPPORTED_PROFILE

  No V4L2 ioctls beyond ENUM_FMT probes from RequestQueryConfigProfiles.
  Confirms Phase 2 Bug 1 (config.c:55-69 fall-through to default).

Baseline B — post Bug 1 scratch patch (the missing break added):

  vaCreateConfig now returns SUCCESS. V4L2 setup proceeds:
  CREATE_BUFS, QUERYBUF (40), REQBUFS, STREAMON, S_FMT, etc.
  Then VIDIOC_S_EXT_CTRLS fails:

    ioctl(/dev/video5, VIDIOC_S_EXT_CTRLS,
          {ctrl_class=0xf010000,
           count=1,
           controls=[
             {id=V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS,
              size=56, ...}
           ]})
        = -1 EINVAL

  CID 0x9909fa (V4L2_CID_MPEG_BASE+250) doesn't exist on this kernel —
  mainline removed it in favor of the split V4L2_CID_STATELESS_MPEG2_*
  CIDs. Size 56 = sizeof(combined v4l2_ctrl_mpeg2_slice_params) from
  the fork's local include/mpeg2-ctrls.h. Confirms Phase 2 Bug 2.

  Auxiliary EINVAL: src/context.c:142-155 unconditionally sets H.264
  device-wide controls (H264_DECODE_MODE, H264_START_CODE) on every
  CreateContext, regardless of profile. EINVALs on hantro-vpu-dec
  (no H.264 controls there). Intentional best-effort behavior —
  return value is cast to (void) and discarded. Auxiliary, not iter1
  scope.

Baseline C — cross-validator verbatim contract anchor:

  ffmpeg -hwaccel v4l2request strace shows ONE batched call per frame:

    ioctl(/dev/video5, VIDIOC_S_EXT_CTRLS,
          {ctrl_class=0xf010000,    // V4L2_CTRL_CLASS_CODEC_STATELESS
           count=3,
           controls=[
             {id=0xa409dc, size=12,  ...},   // SEQUENCE
             {id=0xa409dd, size=32,  ...},   // PICTURE
             {id=0xa409de, size=256, ...}    // QUANTISATION
           ]}) = 0

  Field-by-field decode of frame 1 (I-picture):
    SEQUENCE: 1280×720, vbv=0x151800, profile_level=0,
              chroma_format=1, flags=PROGRESSIVE
    PICTURE: back/fwd_ref_ts=0/0, flags=0x82
             (FRAME_PRED_DCT|PROGRESSIVE), f_code=0xF×4 (I-frame
             default), P_C_T=1 (I), structure=3 (FRAME),
             intra_dc_precision=0
    QUANTISATION: starts [8, 16, 16, 19, 16, 19, 22, 22, ...] —
                  canonical MPEG-2 default intra matrix in zigzag
                  scanning order.

  Frame 2 (P-picture) shows real f_code values {{1,1},{15,15}}
  and forward_ref_ts pointing to frame 1's timestamp. Confirms
  Phase 2's claim that matrices arrive in zigzag order;
  no permutation needed in the libva backend (kernel's
  hantro_mpeg2_dec_copy_qtable handles zigzag-to-raster).

  This is the iter1 contract anchor: every Phase 4 implementation
  diff must produce a structurally indistinguishable
  VIDIOC_S_EXT_CTRLS call.

Baseline D — H.264 regression check (Phase 1 criterion #5):

  T4 reference hashes match exactly with scratch Bug 1 fix installed:
    HW frame 1: f623d5f7a41697f67dd227275c6f1b21ffc257f65626d32fde8229357f8764c9
    SW frame 1: f623d5f7a41697f67dd227275c6f1b21ffc257f65626d32fde8229357f8764c9
    HW frame 2: 7d7bc6f2146dda8b2d223bba622c4b9fbe9674181ff1e02afe286b620342e0a8
    SW frame 2: 7d7bc6f2146dda8b2d223bba622c4b9fbe9674181ff1e02afe286b620342e0a8

  Bug 1 fix in isolation does not regress H.264.

Phase 1 criterion #3 needs adjustment (Phase 3 → Phase 1 loopback):

  Original wording: "mpv --hwdec=vaapi-copy ... engages the backend"
  Reality: mpv-vaapi-copy never loads libva for MPEG-2. mpv's hwdec
  policy filters MPEG-2 out before libva is touched. Zero V4L2
  ioctls, zero libva trace, silent SW fallback. Independent of
  Bug 1 fix state.

  Adjusted criterion #3 (proposed; locks alongside Phase 4 plan):
    "ffmpeg -hwaccel vaapi -i bbb_720p10s_mpeg2.ts -frames:v 2
     -f null - shows vaCreateConfig SUCCESS, no Failed to create
     decode configuration lines, no EINVAL from VIDIOC_S_EXT_CTRLS,
     exits 0 cleanly."

  mpv-driven testing moves to a follow-up task (mpv hwdec-codecs
  filter override), separate from iter1.

Other 4 Phase 1 criteria (vainfo regression, vaCreateConfig SUCCESS,
DMA-BUF GL pixel verify HW=SW, T4 H.264 regression) hold as locked.

Scratch state cleanup: scratch patch reverted, master backend
reinstalled, MPEG-2 fails again with vaCreateConfig=12 — back to
Baseline A state, no leak.

Phase 4 plan inputs:

  - Diff scope: src/config.c (1 break), src/mpeg2.c (rewrite to
    new API), include/mpeg2-ctrls.h (delete or empty). picture.c
    + context.c unchanged.
  - Contract anchor: cite verbatim from
    linux/v4l2-controls.h:1985-2105, FFmpeg
    libavcodec/v4l2_request_mpeg2.c:130-155, kernel
    drivers/media/platform/verisilicon/hantro_mpeg2.c, AND this
    document's Baseline C verbatim payload.
  - Phase 7 verification: re-run all 5 Phase 1 criteria
    (with #3 adjusted), byte-by-byte compare post-fix
    VIDIOC_S_EXT_CTRLS payload against Baseline C.

Evidence files:

  Tracked (text):
    phase3_iter1_baseline.md (writeup with verbatim raw output)
    phase0_evidence/2026-05-07/iter1_phase3/baseline_A_ffmpeg/ffmpeg.stdout
    phase0_evidence/2026-05-07/iter1_phase3/baseline_B_postbug1/ffmpeg.stdout
    phase0_evidence/2026-05-07/iter1_phase3/baseline_C_xvalidator/ffmpeg.stdout

  Gitignored (regenerable from re-run incantations in the writeup):
    *.strace.*  *.txt (ftrace) libva.trace.* (added the latter pattern)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 04:04:25 +00:00
claude-noether cc55a6e60a iter1 Phase 2: situation analysis — three bugs in MPEG-2 path
Phase 2 source-read of the libva-v4l2-request-fourier MPEG-2 path
on master tip 65969da identifies three independent bugs, all in
the libva backend (kernel + driver path proven solid by Phase 0
cross-validator sweep).

Bug 1 — fall-through to default in RequestCreateConfig
(src/config.c:55-69):

  case VAProfileH264*:
      // FIXME
      break;
  case VAProfileMPEG2Simple:
  case VAProfileMPEG2Main:
  case VAProfileHEVCMain:
  default:
      return VA_STATUS_ERROR_UNSUPPORTED_PROFILE;

H.264 cases have a break, MPEG-2 + HEVC fall through to default.
This explains the vaCreateConfig: 12 (UNSUPPORTED_PROFILE) error
observed in Phase 0 cross-validator sweep for both codecs.

Likely history: H.264 was libva-multiplanar focus iter1-iter5;
the FIXME comment suggests profile-specific validation logic was
expected but never landed. MPEG-2 stayed in fall-through bucket.

Fix shape: add break for MPEG-2 cases. HEVC stays in fall-through
(h265.c excluded from build per Phase 0 finding F-C; honest
UNSUPPORTED_PROFILE is correct until h265.c is reinstated in a
later iteration).

Bug 2 — staging-era UAPI in mpeg2.c; mainline kernel removed it:

src/mpeg2.c uses:
  V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS  (V4L2_CID_MPEG_BASE+250 = 0x9909fa)
  V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION  (V4L2_CID_MPEG_BASE+251 = 0x9909fb)

Mainline kernel UAPI (include/uapi/linux/v4l2-controls.h:1985-2105):
  V4L2_CID_STATELESS_MPEG2_SEQUENCE      (CODEC_STATELESS_BASE+220 = 0xa409dc)
  V4L2_CID_STATELESS_MPEG2_PICTURE       (CODEC_STATELESS_BASE+221 = 0xa409dd)
  V4L2_CID_STATELESS_MPEG2_QUANTISATION  (CODEC_STATELESS_BASE+222 = 0xa409de)

Fresnel V4L2 inventory confirms kernel exposes the new IDs only.
The fork's local include/mpeg2-ctrls.h is the staging-era header
that masks the kernel's modern definitions.

Six structural changes from old to new API:

1. Slice header parsing moved to kernel — bit_size, data_bit_offset,
   quantiser_scale_code GONE from new structs.
2. Reference timestamps moved from slice to picture
   (forward_ref_ts, backward_ref_ts now in v4l2_ctrl_mpeg2_picture).
3. Boolean fields collapsed into v4l2_ctrl_mpeg2_picture.flags
   bitmask (TOP_FIELD_FIRST, FRAME_PRED_DCT, CONCEALMENT_MV,
   Q_SCALE_TYPE, INTRA_VLC, ALT_SCAN, REPEAT_FIRST, PROGRESSIVE).
4. progressive_sequence collapsed into
   v4l2_ctrl_mpeg2_sequence.flags & V4L2_MPEG2_SEQ_FLAG_PROGRESSIVE.
5. PICTURE_CODING_TYPE renamed to PIC_CODING_TYPE
   (V4L2_MPEG2_PICTURE_CODING_TYPE_X → V4L2_MPEG2_PIC_CODING_TYPE_X).
6. Quantisation load_* flags removed; matrices always present;
   British spelling — quantiSation not quantiZation.

Quantisation matrix order: kernel doc says zigzag scanning order;
VAAPI VAIQMatrixBufferMPEG2 also stores in zigzag scanning order;
direct memcpy works. Kernel hantro_mpeg2.c does the
zigzag-to-raster permutation kernel-side
(hantro_mpeg2_dec_copy_qtable lines 12-26). No userspace
permutation needed in the libva backend (unlike FFmpeg, which
unwinds its internal idsp.idct_permutation order).

Per-frame submission: FFmpeg reference (libavcodec/
v4l2_request_mpeg2.c:130-155) batches 3 controls in single
VIDIOC_S_EXT_CTRLS. Backend's v4l2_set_controls (src/v4l2.c:475)
already supports batching — used by iter6/7/8 H.264
(src/h264.c:986). MPEG-2 rewrite follows H.264's batched pattern.

Bug 3 — include/mpeg2-ctrls.h is the staging-era local header:

The fork's local include/mpeg2-ctrls.h is the staging-era header
that defines the old (removed) API. config.c:37 + mpeg2.c:38
include it via meson's include_directories('../include'). Should
be deleted (or emptied); rely on kernel <linux/v4l2-controls.h>
pulled transitively via <linux/videodev2.h>.

Things verified NOT to be bugs:

- src/picture.c MPEG-2 dispatch is fully wired:
  - codec_store_buffer handles VAPictureParameterBuffer + VAIQMatrix
  - codec_set_controls dispatches MPEG-2 to mpeg2_set_controls
  - HEVC explicitly UNSUPPORTED_PROFILE (correct for build state)
- src/picture.c:287 unconditional h264.matrix_set=false reset is
  benign for MPEG-2 (union aliasing puts it in mpeg2.picture or
  .slice region; RenderPicture overwrites that byte before
  mpeg2_set_controls reads anything).
- src/mpeg2.c field extraction from VAAPI structs is sound; only
  the destination control IDs and struct shape need rewiring.
- src/v4l2.c batching API (v4l2_set_controls) is in place.

Open questions tabled for Phase 3 baseline:

1. Live ftrace of failing libva MPEG-2 attempt post Bug-1-fix
   (verify expected EINVAL on VIDIOC_S_EXT_CTRLS for old CID).
2. VAAPI VAIQMatrixBufferMPEG2 matrix order from real mpv decode
   (verify zigzag, no pre-permutation).
3. Cross-reference verbatim VIDIOC_S_EXT_CTRLS payload from
   ffmpeg-v4l2request cross-validator anchor strace dump.
4. SDDM watchpoint resolution — fresnel SSH No route to host at
   Phase 2 start (network event, SDDM regression, or operator
   power-state). Resolve before Phase 3.

Predicted iter1 outcome: small mechanical diff (config.c break
+ mpeg2.c rewrite + drop local mpeg2-ctrls.h). Phase 7 verification
should land all 5 Phase 1 boolean checks green on first or second
try. Likely Phase 7 → Phase 4 loopback triggers if any: forgotten
struct padding zero, garbage timestamps on first I-frame, or
device-state precondition we missed in hantro_mpeg2.c.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 22:24:50 +00:00
claude-noether f720c7784b iter1 Phase 0 + Phase 1 lock: MPEG-2 boolean correctness on hantro
Iteration 1 of the campaign 8(+1)-phase loop opens following the
campaign Phase 0 close (b74551b). Per the suggested order in
phase0_evidence/2026-05-07/cross_validator_traces.md, iter1 attacks
MPEG-2 — the cheapest fix in the codec status sweep (mpeg2.c is
already compiled, src/config.c has the case statements, but
vaCreateConfig returns 12 / UNSUPPORTED_PROFILE).

Locked research question:

  Make MPEG-2 the second codec to pass boolean-correctness on
  fresnel via the libva-v4l2-request-fourier path —
  mpv --hwdec=vaapi-copy bbb_720p10s_mpeg2.ts engages the backend
  cleanly and DMA-BUF GL import yields HW pixels byte-identical
  to a software-decoded reference for the same frames.

Phase 1 success criterion (5 boolean checks, all must be green):

  1. vainfo: VAProfileMPEG2Simple + VAProfileMPEG2Main still
     enumerated on the hantro env binding (regression check).
  2. vaCreateConfig(VAProfileMPEG2Main, VLD): VA_STATUS_SUCCESS.
  3. mpv --hwdec=vaapi-copy --frames=2 --vo=null on
     bbb_720p10s_mpeg2.ts: engages backend, exit 0, no
     "Failed to create decode configuration" lines.
  4. mpv --hwdec=vaapi --vo=image (DMA-BUF GL import) at +02s
     seek: 2 distinct frames hash-equal to SW reference frames
     and hash-differ from each other.
  5. T4 H.264 regression: re-run T4 incantation, hashes match
     f623d5f7... and 7d7bc6f2... reference values.

Mechanism the question targets:

The kernel + driver path is solid (cross_validator_traces.md —
ffmpeg-v4l2request decodes the same fixture exit 0). vaCreateConfig
rejection must be downstream of src/config.c:64-65's case match
(both VAProfileMPEG2Simple and VAProfileMPEG2Main are present in
the validation switch) but upstream of return-success. Plausible
suspects for Phase 2 source-read:

  - V4L2 capability probe (e.g., VIDIOC_TRY_FMT against MG2S) that
    fails because the libva backend was bound to /dev/video5 but
    is checking format against the wrong codec list.
  - Device-discovery routing reaches a default-reject because
    bound device didn't match an expected codec-to-device map.
  - media_request allocation step fails on /dev/media2 for some
    MPEG-2-specific reason.
  - iter6/iter7 regression in the dispatch-by-profile path that
    broke MPEG-2 silently because nobody on libva-multiplanar
    tested it (iter5 close: "MPEG-2 was iter1 backlog, dropped
    at iter6 close because A55 CPU handles it fine" — fresnel
    runs A53 so the disposition doesn't transfer).

Phase 4 plan must cite the contract before patching, per
feedback_dev_process.md Phase 6 contract-before-code: read kernel
drivers/media/platform/verisilicon/hantro_mpeg2.c, read FFmpeg
downstream libavcodec/v4l2_request_mpeg2.c, state the MPEG-2
control-submission contract explicitly before any code lands.

Predecessor carry-over (state vs data) explicit per
feedback_dev_process.md Phase 0 + feedback_replicate_baseline_first.md:

  Carries forward (re-verified in campaign Phase 0):
    - iter8 master fork (65969da) installed on fresnel,
      vainfo enumeration confirmed
    - bbb_720p10s_mpeg2.ts fixture (5.3 MB, MPEG-2 Main, 720p,
      10s, MPEG-TS) on fresnel ~/fourier-test/, provenance
      documented in test_fixtures.md
    - hantro-vpu-dec /dev/video5 + /dev/media2 binding
    - Cross-validator anchor: ffmpeg-v4l2request mpeg2 trace
      captured (5 S_EXT_CTRLS, 4 REQUEST_ALLOC, 4 DMA_BUF_SYNC)
    - T4 reference hashes for H.264 regression check

  Does NOT carry forward (re-acquire if needed):
    - ohm/RK3568 hantro MPEG-2 behaviour — different kernel
      driver variant inside drivers/media/platform/verisilicon/
    - Pre-iter6 libva-multiplanar MPEG-2 trace data (untested
      at iter6 close)

  Open questions inherited:
    - Cache-stale vaDeriveImage bug class on RK3399 (T4) —
      iter1 uses DMA-BUF GL import for verify; Phase 4 cross-
      cutting fix is not iter1-scoped
    - Architectural divergence ffmpeg vs our backend (EXPBUF +
      DMA_BUF_SYNC vs cap_pool + vaDeriveImage) — Phase 4
      design decision, not iter1-blocking

Out-of-scope (LOCKED): HEVC/VP9/VP8 (later iterations); vaDerive
Image cache-stale fix (Phase 4 cross-cutting); chromium-fourier
149 install (Phase 0 follow-up to iter1 substrate, non-gating);
performance metrics (Phase 1+ separate iteration); long-duration
stress (>10s); other MPEG-2 containers beyond MPEG-TS;
upstream engagement.

Iter1 Phase 2 source-read targets:
  - src/config.c::RequestCreateConfig (rejection site)
  - src/picture.c MPEG-2 dispatch path
  - src/mpeg2.c set-controls path
  - kernel drivers/media/platform/verisilicon/hantro_mpeg2.c
  - FFmpeg downstream libavcodec/v4l2_request_mpeg2.c

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 22:04:52 +00:00
claude-noether b74551bc56 phase 0 close: deliverables 5 + 6 — fixtures + cross-validator anchor
Closes Phase 0 for fresnel-fourier. Per-codec test fixtures and
cross-validator contract traces complete the campaign-locked
boolean-correctness baseline.

Deliverable #5 — per-codec test fixtures (test_fixtures.md):

Generated 4 new fixtures on fresnel from the bbb_1080p30_h264.mp4
master via stock ffmpeg (libx265 ultrafast, libvpx-vp9 speed 5,
mpeg2video, libvpx vp8). All 720p 10s 8-bit yuv420p — matching the
silicon-supported profile/pixfmt for each codec on RK3399:

  bbb_720p10s_hevc.mp4   620 KB  (HEVC Main, rkvdec target)
  bbb_720p10s_vp9.webm   3.4 MB  (VP9 Profile 0, rkvdec target)
  bbb_720p10s_mpeg2.ts   5.3 MB  (MPEG-2 Main, hantro-vpu-dec target)
  bbb_720p10s_vp8.webm   2.4 MB  (VP8, hantro-vpu-dec target)

Encode wall times on fresnel: HEVC 13s, VP9 93s, MPEG-2 6s, VP8 26s.
H.264 master is 725 MB carryover from libva-multiplanar / fourier_attribution.

Deliverable #6 — cross-validator anchor (cross_validator_traces.md):

phase0_findings.md named chromium-fourier 149 as the cross-validator;
that package isn't installed on fresnel and marfrit-packages isn't
configured (no auto-install path tonight). Substituted ffmpeg
-hwaccel v4l2request as a better-fit cross-validator: it's an
independent V4L2 client (uses no libva at all, lives in
libavcodec/v4l2_request*.c), already on the box (stock
ffmpeg n8.1-13-gb57fbbe50c, the Kwiboo v4l2-request-n8.1 branch),
and implements all 5 codecs the campaign locked.

Headline finding: ALL 5 CODECS WORK end-to-end via the kernel
direct path on RK3399.

  ffmpeg -hwaccel v4l2request -i bbb_<codec>.<ext> -frames:v 2 -f null -
  H.264:  exit 0
  HEVC:   exit 0
  VP9:    exit 0
  MPEG-2: exit 0
  VP8:    exit 0

The Linux kernel + rkvdec + hantro-vpu drivers are solid for the
entire campaign codec scope. Phase 6 work scope is purely libva-
backend code — no kernel patches, no upstream Linux engagement.

Per-codec libva (iter8) vs ffmpeg-v4l2request status sweep:

  H.264   libva: PASS (T4 PASS + bit-exact pixel verify) | ffmpeg-v4l2req: PASS
  HEVC    libva: vaCreateConfig=12 (UNSUPPORTED_PROFILE) | ffmpeg-v4l2req: PASS
          → src/h265.c is excluded in src/meson.build but src/config.c:151
            enumerates HEVCMain via V4L2_PIX_FMT_HEVC_SLICE probe;
            vaCreateConfig fails downstream of the case match.
  VP9     libva: profile not enumerated                  | ffmpeg-v4l2req: PASS
          → no vp9.c in fork
  MPEG-2  libva: vaCreateConfig=12 (UNSUPPORTED_PROFILE) | ffmpeg-v4l2req: PASS
          → mpeg2.c IS compiled, config.c:64-65 has the case statements,
            yet vaCreateConfig rejects. Phase 2 source-read needed.
  VP8     libva: profile not enumerated                  | ffmpeg-v4l2req: PASS
          → no vp8.c in fork

Suggested Phase 6 iteration order (subject to Phase 1 lock):
  iter1: MPEG-2 — likely cheapest (config.c-level path; mpeg2.c
                  already compiled)
  iter2: HEVC   — re-enable h265.c in build, audit against rkvdec
  iter3: VP8    — implement vp8.c on hantro
  iter4: VP9    — implement vp9.c on rkvdec (largest control surface)

Per-codec ioctl frequency anchor (2-frame ffmpeg -hwaccel v4l2request):

  ioctl                  H.264 HEVC  VP9  MPEG-2 VP8
  VIDIOC_DQBUF              45   49   40    26   49
  VIDIOC_QBUF               22   24   20    10   20
  VIDIOC_CREATE_BUFS        17   17   17    12   17
  VIDIOC_QUERYBUF           15   15   15    10   15
  VIDIOC_S_EXT_CTRLS        13   14   11     5   10
  VIDIOC_EXPBUF             11   11   11     6   11
  VIDIOC_QUERY_EXT_CTRL      0    5    0     0    0
  MEDIA_IOC_REQUEST_ALLOC    4    4    4     4    4
  DMA_BUF_IOCTL_SYNC         0    0    0     4    0
  MEDIA_REQUEST_IOC_REINIT   0    0    0     0    3

Architectural divergence ffmpeg-v4l2request vs libva-v4l2-request-fourier:

  - ffmpeg uses VIDIOC_EXPBUF + DMA-BUF for downstream readback.
    Our libva backend uses cached mmap via vaDeriveImage — the
    iter1 patch-0011 cache-stale bug class. Phase 4 work item
    consistent with T4's finding: adding VIDIOC_EXPBUF + DMA-BUF-
    backed image export to the libva backend would fix the
    cache-coherency issue identified in T4's H.264 readback.
  - ffmpeg uses 4 request_fds pooled. Our backend uses 16 (iter6
    per-OUTPUT-slot binding). Both valid; different pool depth.
  - HEVC alone needs VIDIOC_QUERY_EXT_CTRL for hevc_slice_params
    dynamic-array introspection — unique among the 5 codecs.

Substrate change deferred (not a Phase 0 blocker): chromium-fourier
149 install on fresnel is Phase 1+ work. When done, a follow-up
trace pass per codec will cross-check ffmpeg-v4l2request and
chromium contracts. For Phase 0 baseline, ffmpeg-v4l2request is
the anchor.

Phase 0 fully closed. Six deliverables landed. Phase 1 lock can proceed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 21:52:21 +00:00
claude-noether d8a9903ef4 phase 0 deliverable 4: H.264 baseline trace — PASS boolean correctness
H.264 hardware decode on RK3399 / rkvdec / libva-v4l2-request-fourier
@ master tip 65969da (iter8 Phase 4) verified bit-exact correct against
software reference, when read via the cache-safe DMA-BUF GL import path.

Test method:

  - mpv --hwdec=vaapi --vo=image (DMA-BUF + EGL_EXT_image_dma_buf_import
    + glReadPixels + JPEG encode — cache-coherency-safe per the iter1
    patch-0011 lesson).
  - Decoded 2 frames at +30s seek (mid-content bunny motion, not BBB
    intro fade-in) so size + content variation is genuine.
  - Compared HW JPEGs vs SW reference JPEGs (same mpv invocation with
    --hwdec=no).

Result:

  HW frame 1 sha256 = f623d5f7...  (651,726 bytes)  byte-identical
  SW frame 1 sha256 = f623d5f7...  (651,726 bytes)  to SW reference
  HW frame 2 sha256 = 7d7bc6f2...  (630,433 bytes)  byte-identical
  SW frame 2 sha256 = 7d7bc6f2...  (630,433 bytes)  to SW reference

  Frames 1 vs 2 differ in size — real content change captured.

Phase 0 boolean-correctness criterion for H.264: PASS.

Contract trace:

The V4L2 + media-request ioctl sequence per H.264 frame is the
canonical iter6/iter7 pattern:

  S_EXT_CTRLS (CODEC_STATELESS class, request_fd=N)
  QBUF CAPTURE_MPLANE  index=K
  QBUF OUTPUT_MPLANE   index=K  (compressed slice)
  MEDIA_REQUEST_IOC_QUEUE   (request_fd=N)
  MEDIA_REQUEST_IOC_REINIT  (request_fd=N)  ← per-OUTPUT-slot reuse
  DQBUF OUTPUT_MPLANE  index=K
  DQBUF CAPTURE_MPLANE index=K

REINIT-before-DQBUF works because the kernel completes decode in
~0.6 ms (request → COMPLETE state), and mainline media_request_
ioctl_reinit accepts both IDLE and COMPLETE. iter7 cap_pool
instantiates 24 slots cleanly: "v4l2-request: cap_pool_init: 24
slots ready" in mpv stdout.

No EINVAL, no EBUSY, no errors observed across 5 frames. iter4's
frame-11 EINVAL bug from libva-multiplanar does not reproduce on
RK3399 in this short window (longer-run repro is Phase 1+ work).

Side finding — cache-stale readback bug present in libva-backend's
vaDeriveImage path on RK3399:

When pixels are read via the cached-mmap path (libva's vaDeriveImage
+ vaMapBuffer, used by ffmpeg -hwaccel vaapi -hwaccel_output_format
nv12), readback is corrupted in exactly the iter1 patch-0011 pattern:

  size=6,220,800 bytes (correct: 2 × 1920×1080×1.5 NV12)
  non-zero=544 (0.009%)
  pattern: 16 consecutive non-zero bytes at every 1920-byte row stride,
           rest of buffer reads as zero
  diff vs SW reference: 100% of bytes differ, MAE=53.3 per byte

This is the canonical stale-cached-mmap pattern. Kernel writes real
pixels (proven by DMA-BUF GL import readback succeeding), but the
libva backend's image-export path returns a cached pointer without
the correct cache-invalidation incantation. Userspace reads stale
all-zero memory punctuated by whichever cache lines happened to fetch
post-write.

Phase 4 work item: audit whether the iter1 patch-0011 cache-flush
fix is present, effective, or RK3399-routing-bypassed. Three
possibilities: (a) fix landed for RK3568 but cache topology differs
on RK3399, (b) fix is gated on something that's not true on RK3399,
or (c) RK3399 V4L2_MEMORY_MMAP page protection bypasses the flush.
Not gating Phase 0 — kernel-side decode is correct.

Phase 1+ binding cells must use the DMA-BUF GL import path for pixel
verification, not vaDeriveImage / cached-mmap. The iter1 lesson
restated: cached-mmap readback is unreliable on this hardware family.

Evidence files (under phase0_evidence/2026-05-07/h264_baseline_trace.md
and h264_baseline/):

  - mpv.stdout — libva log, vaapi-copy engaged, cap_pool_init
  - h264_baseline_trace.md — full writeup with re-run incantations
  - mpv.strace.* (gitignored) — 19 per-thread ioctl/openat traces
  - ftrace_v4l2.txt (gitignored) — kernel qbuf/dqbuf events
  - merged_ioctls.tsv (gitignored) — time-sorted V4L2/MEDIA/DRM
    ioctls across all threads
  - *.jpg (gitignored) — HW vs SW JPEG comparison artefacts
  - frames_hw_cached_readback.nv12 (gitignored) — broken nv12
    readback for forensic reference

gitignore: extended extension list (jpg, png, nv12, yuv, tsv, strace*).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 21:32:36 +00:00
claude-noether 60e62da666 phase 0 corrections: hantro RK3399 has no H.264; substrate is iter8 master
Two empirical corrections to the morning-of-2026-05-07 phase 0 lock,
based on V4L2 inventory + iter8 fork build smoke captured this evening
on fresnel (kernel 6.19.9-99-eos-arm).

Correction 1 — hantro-vpu-dec on RK3399 does not advertise H.264.

phase0_findings.md morning lock claimed both rkvdec (/dev/video3)
and hantro-vpu-dec (/dev/video5) advertise H.264. Empirical
v4l2-ctl --list-formats-out shows hantro-vpu-dec exposes only
MG2S (MPEG-2) and VP8F (VP8) — no S264. Likely carryover from
RK3568 (ohm) hantro, which does support H.264; the RK3399 hantro
kernel variant in drivers/media/platform/verisilicon/ registers
a different codec list. Fix:

  - README.md hardware-target table: drop "+ H.264" from Decoder
    block 2.
  - README.md decode-side surface-area paragraph: note hantro is
    MPEG-2 + VP8 only and that there is exactly one bind for H.264
    (rkvdec).
  - phase0_findings.md mechanism table: drop H.264 from /dev/video5
    row; correct DT compatible to rockchip,rk3399-vpu (the actual
    parent device compatible — sysfs reports rockchip,rk3399-vpu,
    not rockchip,rk3399-vpu-dec which is just the v4l2 card type
    string).
  - phase0_findings.md "H.264 lands on both blocks" sentence:
    inverted to "H.264 lands only on rkvdec".
  - phase0_findings.md Open Question #2 (two-block H.264 routing):
    marked RESOLVED 2026-05-07 evening (null). Single bind, no
    routing decision, one test cell per codec.

Empirical evidence: phase0_evidence/2026-05-07/v4l2_inventory_findings.md
(distilled from v4l2_inventory.txt — the latter is gitignored as
raw data, regenerable via the v4l2-ctl invocation documented in
the findings file).

Correction 2 — substrate is iter8 master (65969da), not iter5-end.

phase0_findings.md morning lock framed the substrate as "iter5-end
fork." That was true on 2026-05-05 (iter5 close); between then and
the 2026-05-07 fresnel-fourier scaffold libva-multiplanar continued
through iter6 (per-OUTPUT-slot REINIT request_fd binding), iter7
(slot-leak fix, cap_pool harness, msync verify harness, OUTPUT-pool
teardown), and iter8 (perf binding cell harness, RK3566/3568 doc
fix). Building from master tip 65969da inherits all the iter6-iter8
hardening at zero cost. Fix:

  - phase0_findings.md substrate paragraph: strikethrough the
    "iter5-end" framing, add corrected paragraph naming master
    tip 65969da and listing what iter6/7/8 added.
  - phase0_findings.md top-of-doc: add an "Empirical corrections
    2026-05-07 evening" callout linking to the evidence files,
    so a reader spotting the locked-vs-corrected mismatch knows
    where the empirical update came from.

Empirical evidence: phase0_evidence/2026-05-07/iter8_build_smoke.md
(clean build, vainfo profile enumeration, HEVC anomaly write-up).

What's preserved on purpose:

The strikethrough rendering in phase0_findings.md keeps the original
locked text visible alongside the correction — campaign convention
treats locks as historical record, not editable state. A reader
landing on the file from a deep link sees both the morning's
intent and the evening's empirical update. Git history has the
clean diff if anyone wants the original without strikethrough.

What's not changed:

The codec scope in the locked research question stays correct in
count — five codecs (H.264 + HEVC + VP9 + MPEG-2 + VP8). The
routing table changes (H.264 → rkvdec only; MPEG-2 → hantro only;
no shared block) but the boolean-correctness pass/fail criterion
per codec is unaffected. Phase 1 lock can proceed on the corrected
map without re-opening scope.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:57:40 +00:00
claude-noether e83298f0da phase 0 deliverables 1-3: SDDM watchpoint, V4L2 inventory, iter8 fork smoke
Three Phase 0 deliverables, three findings worth flagging.

Deliverable #1 — SDDM recovery (phase0_recovery_2026-05-07.md):

Closed as watchpoint, not as root-caused fix. Greeter is green on
this boot — mfritsche has been in Plasma Wayland on tty1 since 20:32.
The "Process crashed (exit code 1)" in journalctl -u sddm is the
post-login greeter teardown, not the pre-login crash described in
~/.claude/plans/dynamic-forging-piglet.md. No coredumps, no qFatal
strings. No package changed since the 2026-04-28 Syyuu; the only
difference between the failing boot and this one is a reboot —
likely a flaky panfrost/GBM cold-init that happened to succeed.
Plan procedures + /var/cache/pacman/pkg rollback candidates remain
ready if regression fires.

Deliverable #2 — V4L2 inventory (phase0_evidence/2026-05-07/
v4l2_inventory_findings.md, raw capture in v4l2_inventory.txt
which is gitignored as raw data):

Full v4l2-ctl --all + --list-ctrls-menus + --list-formats-out per
node, plus media-ctl topology, plus DT compatibles. Authoritative
codec map on running kernel 6.19.9-99-eos-arm:

  /dev/video3 (rkvdec, rockchip,rk3399-vdec):
    OUTPUT_MPLANE: S265 (HEVC), S264 (H.264), VP9F (VP9)
    CAPTURE_MPLANE: NV12

  /dev/video5 (hantro-vpu-dec, rockchip,rk3399-vpu):
    OUTPUT_MPLANE: MG2S (MPEG-2), VP8F (VP8)
    CAPTURE_MPLANE: NV12

This contradicts phase0_findings.md (and README.md), which both
claim hantro-vpu-dec on RK3399 also does H.264. It does not on
this kernel. Open Question #2 from phase0_findings.md ("two-block
H.264 routing") is null on RK3399. Correction commit follows.

Deliverable #3 — iter8 fork build + vainfo (phase0_evidence/
2026-05-07/iter8_build_smoke.md):

Built libva-v4l2-request-fourier master tip 65969da on fresnel
directly (no distcc per locked precedent), gcc 15.2.1, meson 1.11.1,
ninja 1.13.2, libva 1.23.0, libdrm 2.4.131. Clean build, 302 KB
.so, two harmless v4l2.h forward-decl warnings. Installed to
/usr/lib/dri/v4l2_request_drv_video.so. vainfo enumerates:

  rkvdec bind: H.264 {Main, High, ConstrainedBaseline, MultiviewHigh,
                      StereoHigh}, HEVCMain
  hantro-vpu-dec bind: MPEG-2 {Simple, Main}

Substrate is iter8 master, not iter5 as the campaign docs frame —
libva-multiplanar continued past iter5 into iter6-iter8 between the
2026-05-05 iter5 close and the 2026-05-07 fresnel-fourier scaffold.
Building from master inherits per-OUTPUT-slot REINIT, slot-leak
fix, cap_pool harness, msync verify harness, OUTPUT-pool teardown.
Correction commit follows.

HEVC anomaly worth flagging: src/config.c:146-151 probes
V4L2_PIX_FMT_HEVC_SLICE before adding VAProfileHEVCMain. rkvdec
advertises S265 so the probe succeeds; HEVCMain gets enumerated.
But src/meson.build excludes h265.c from the build. A consumer
that calls vaCreateConfig(VAProfileHEVCMain) will succeed (config.c
validation list includes HEVCMain) but actual decode will fault
at dispatch since no h265 symbols are linked. Phase 4 decision:
re-enable h265.c, gate enumeration on a compile-time #ifdef, or
strip enumeration honestly.

Per-codec routing confirmed one-env-var-per-process: request.c:149
reads LIBVA_V4L2_REQUEST_VIDEO_PATH once at init. A single backend
instance binds to either rkvdec or hantro-vpu-dec, not both. Phase 4
will need either a wrapper-script-per-consumer hack or a backend
probe-loop change to route by VAProfile across both decode nodes.

Build infrastructure (gitignore):

Switched from blanket phase*_evidence/ exclude to extension-based
allow-list — track narrative .md, ignore raw .txt/.log/.trace/.pcap/
.bin/.gz/.zst/.json/.dat/.ftrace/.strace. Keeps the V4L2 inventory
text untracked (reproducible from the v4l2-ctl invocation) while
preserving the findings narrative in-repo.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:54:38 +00:00
marfrit c9a96cbb38 fresnel-fourier campaign scaffold: peer to libva-multiplanar, RK3399 target
Roll out libva-v4l2-request-fourier on fresnel (Pinebook Pro / RK3399)
with broader codec scope than ohm-side libva-multiplanar:

- H.264 + HEVC + VP9 via the rkvdec block (/dev/video3)
- MPEG-2 + VP8 via the hantro-vpu-dec block (/dev/video5)

Topology: peer campaign, independent 8(+1) loop. fresnel-fourier
results do not gate libva-multiplanar Phase 8 closes. Code-side work
in the shared fork (../libva-multiplanar/libva-v4l2-request-fourier/)
lands per Phase 2 source-read of each iteration.

Phase 0 task 1: recover fresnel from the SDDM greeter crash-loop
(per ~/.claude/plans/dynamic-forging-piglet.md). Recovery is bookkept
inside this campaign's Phase 0, not as an out-of-band prereq.

Verified 2026-05-07 via SSH:
- linux-eos-arm 6.19.9-99 has CONFIG_FTRACE=y + CONFIG_FUNCTION_TRACER=y
  + CONFIG_DYNAMIC_FTRACE=y + CONFIG_TRACING=y. /sys/kernel/tracing/
  populated. No kernel rebuild needed for trace work.
- /dev/video3 = rkvdec (NV12 capture, MPLANE).
- /dev/video5 = hantro-vpu, card type rockchip,rk3399-vpu-dec.

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