From 6d0a389dd2e7600e3fab23f09bcc2c79b35d83de Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 7 Aug 2026 20:22:33 +0200 Subject: [PATCH] =?UTF-8?q?fix(client):=20the=20D3D11VA=20AV1=20rung=20dec?= =?UTF-8?q?odes=20wrong=20pixels=20=E2=80=94=20the=20parity=20harness=20ex?= =?UTF-8?q?isted=20all=20along?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The follow-up was framed as "build the frame-hash parity harness the D3D11VA AV1 rung is missing, then flip hardware_verified to true". Both halves were wrong. The harness was never missing. `video_d3d11_native`'s `parity` module has carried `av1_every_delivered_frame_hashes_bit_identical_to_libavcodec` since M7 wired the rung — wired to the SAME libavcodec goldens the Vulkan AV1 leg passes against, with the display-order model that handles the vector's 24 hidden frames, sitting `#[ignore]`d beside the H.264/H.265/Main10 legs. It had simply never been run on a device; .173 was powered off the day it was written. What the old evidence note called a missing harness is real about pf-dxvadec the CRATE, which cannot host one — it links no D3D11 — but the device half lives here and was already done. Run on .221, it FAILS, on both GPUs, deterministically (three runs each, identical first-divergent frame and identical hashes): 186/250 diverging display frames on an RTX 3500 Ada, 245/250 on an Intel Arc. It is the decode that is wrong, not the measurement, and three independent checks say so. H.264 and H.265 pass 250/250 and HEVC Main 10 50/50 through the same harness, the same readback geometry, the same crop and the same slot map on those same two GPUs. pf-vkdecode's Vulkan AV1 leg reproduces the same golden file 250/250 on the same box. And the goldens regenerate byte-for-byte from the ffmpeg build their own header names. Two signatures, and they are not one defect wearing two faces. NVIDIA is bit-exact for display frames 0..=63 and then loses ONE 16x24 luma block — 174 pixels, max |delta| 8, chroma untouched — on the frame whose order_hint first reaches 64, after which every remaining frame is downstream of it through prediction. The stream parks the key frame (order_hint 0) in BWDREF and ALTREF2 for its whole length, so 64 is where the distance to it reaches the edge of what get_relative_dist can represent at OrderHintBits = 7. Intel is structurally wrong from display frame 4 — 47% of luma, max |delta| 242, chroma wrong too, a frame predicted from the wrong picture — and the only later frame it gets right is the one whose primary_ref_frame is PRIMARY_REF_NONE. None of this is visible on glass, which is the whole argument for goldens: the rung streams 4K60 on both parts with a clean five-minute soak at roughly ten times the Vulkan leg's decode time. The 2026-08-07 field sessions that looked clean were looking at wrong pixels. So hardware_verified stays false, and the note now says why in the strongest available terms — it prints at warn on every session that lands here, and "decodes AV1 to wrong pixels" is what a support engineer needs to read. The pair stays in `every_rung_runs_and_the_unproven_ones_are_named`'s unproven array; its note still contains NEVER, because the pair has never PASSED parity, which is now a measured statement rather than an absence. Left deliberately unchanged: `auto` on Windows can still reach this rung for AV1, and on Intel it is the arm that fires, because that vendor advertises no SAMPLED usage on any decode profile so zero-copy Vulkan Video cannot run there. Barring it trades visibly-wrong AV1 for the software rung, which cannot keep up at 4K and is itself unproven. Which way that trade goes is a product call, so it is recorded at the admission site rather than made silently here. `av1_divergence_map` is kept, cleaned up and documented: it is what turned "186 frames differ" into a lead — one line per display frame, its verdict beside the plan facts that could explain it, and an opt-in raw-NV12 dump. At a frame where one vendor hashes correctly, that vendor's bytes ARE libavcodec's bytes and so a valid reference for the other's, which is how "how badly" was answered without new goldens. The tool that would localise the rest does not exist: pf-dxvadec's libav_picparams_parity covers H.264 and HEVC only, so the AV1 conversion has never been compared against libavcodec at the picture-parameter level either. That is the next step, not another session. Also in this file, since it is the same table and the same day: the VAAPI rung's AV1 leg has now decoded 250/250 of the vendored vector on RDNA3 and its arm is split from the H.264/H.265 ones, which genuinely have still never decoded anything. It is unverified for the same reason as ever — no parity — and the D3D11VA row above is exactly why that distinction is worth keeping: a rung can decode 250 frames and still be wrong. --- crates/pf-client-core/src/video.rs | 138 +++++++++----- .../pf-client-core/src/video_d3d11_native.rs | 174 +++++++++++++++++- 2 files changed, 264 insertions(+), 48 deletions(-) diff --git a/crates/pf-client-core/src/video.rs b/crates/pf-client-core/src/video.rs index 25b2c689..51e1c384 100644 --- a/crates/pf-client-core/src/video.rs +++ b/crates/pf-client-core/src/video.rs @@ -24,7 +24,7 @@ //! **One column of that table is different, and it is the one the filter still guards.** //! On Linux, Intel and every unknown vendor id run `native-vaapi → native-vk → sw` //! ([`VulkanDecodeDevice::prefer_vulkan_first`] is true for NVIDIA and AMD only), so the -//! rung directly below the never-run pf-vaadec is native Vulkan Video — H.264 and H.265 on +//! rung directly below the unproven pf-vaadec is native Vulkan Video — H.264 and H.265 on //! three drivers plus a 92-minute soak, AV1 250/250. There, barring the unproven rung moves //! the session exactly one rung down, onto proven code, so it is barred: an unproven rung //! yields to a rung that is BOTH verified for this codec and usable on THIS device, and to @@ -48,9 +48,10 @@ //! | native Vulkan Video | [`crate::video_vk_native`] | H.264 | **yes** — bit-exact vs libavcodec, 250/250 AUs on three drivers + a 92-minute soak (M2 WP-D) | //! | native Vulkan Video | | H.265 (Main / Main10 / 4:4:4) | **yes** — same parity run + HDR chain and Deck/VanGogh legs (M3) | //! | native Vulkan Video | | AV1 | **yes** — 250/250 bit-identical to libavcodec on an RTX 5070 Ti (M7); ONE vendor, no soak | -//! | native D3D11VA | [`crate::video_d3d11_native`] | H.264, H.265 | **yes** — frame-hash parity on an RTX 4090 and an AMD iGPU + a 30-minute soak (M5) | -//! | native D3D11VA | | AV1 | **not proven, but no longer unexercised** — 4K60 on an RTX 3500 Ada AND an Intel Arc, plus a clean 5-minute Arc soak (2026-08-07), at roughly 10x the speed of the Vulkan leg on the same GPU. It stays out of the admission filter for one specific missing thing: frame-hash parity against libavcodec, which every verified pair here has and pf-dxvadec has no harness for. Its M7 wiring was right all along: what looked like a DXVA reference-mapping bug (`reference picture N holds no DPB slot`, 72 consecutive failures) was the HOST shipping half of every AV1 frame — see `pf_encode`'s `resolve_split_subframe` | -//! | native VAAPI | [`crate::video_vaapi_native`] | H.264, H.265, AV1 | **NO** — has never decoded a frame anywhere (M6/M7). Still true as of 2026-08-07: the reachable VAAPI box (`.25`, RDNA3) reports `VAProfileAV1Profile0: VAEntrypointVLD` and no Vulkan AV1 decode at all, so it is the right box to prove this rung on — it has no punktfunk checkout and 4 GB of usable RAM, which is why it has not happened | +//! | native D3D11VA | [`crate::video_d3d11_native`] | H.264, H.265 | **yes** — frame-hash parity on an RTX 4090 and an AMD iGPU + a 30-minute soak (M5), re-confirmed 250/250 (+ 50/50 Main 10) on an RTX 3500 Ada and an Intel Arc on 2026-08-07 | +//! | native D3D11VA | | AV1 | **NO — it decodes WRONG PIXELS.** The parity harness that was missing turned out to exist (`video_d3d11_native`'s `parity` module, written by M7 and never run); running it on 2026-08-07 failed on BOTH GPUs of `.221`, deterministically: 186/250 diverging frames on an RTX 3500 Ada and 245/250 on an Intel Arc. Not the environment — H.264, H.265 and HEVC Main 10 pass 250/250/50 through the SAME harness on the same two GPUs, and pf-vkdecode's Vulkan AV1 leg reproduces the SAME goldens 250/250 on the same box. Two unlike signatures: NVIDIA is bit-exact for 63 frames and then loses ONE 16x24 luma block (174 px, max |delta| 8) on the frame whose `order_hint` first reaches 64, which then propagates; Intel is structurally wrong from display frame 4 (47% of luma, max |delta| 242). It still streams — 4K60 on both, a clean 5-minute Arc soak, ~10x the Vulkan leg's speed — which is exactly why the picture looked fine and only the goldens caught it. See `av1_divergence_map` | +//! | native VAAPI | [`crate::video_vaapi_native`] | AV1 | **not proven** — but it has now DECODED: 250/250 frames of the vendored AV1 vector on `.25` (Radeon 780M, RDNA3, Mesa 26.0.3) on 2026-08-07, NV12 on a tiled AMD modifier, and `probe_this_machines_libva` reports `AV1 Profile 0: VLD decode`. Never frame-hash parity-checked: the rung exports a tiled dmabuf with no CPU-readable image, so parity needs a readback path that does not exist yet | +//! | native VAAPI | | H.264, H.265 | **NO** — these two legs have still never decoded a frame anywhere (M6/M7) | //! | software | `video_software` | H.264, AV1 | **not proven** — openh264 has never run on glass; rav1d HAS now decoded 1080p and 4K60 AV1 there (2026-08-07, .21) and recovers in-session from a mid-stream reference loss, but with no parity check and no soak. Its 4K "abort" was never about 4K: rav1d 1.1.0 kills the process on ANY decode error while it holds a single frame context, so `video_software` opens it with two — see [`crate::video_software`] | //! //! The software rung's evidence is recorded for the same reason but does not gate @@ -771,7 +772,8 @@ enum Backend { /// libavcodec's VAAPI hwaccel, and since M10 the only VAAPI rung: libva driven /// straight from pf-bitstream plans, dlopen'd, exporting the same DRM-PRIME dmabufs. /// Reachable by pin (`PUNKTFUNK_DECODER=native-vaapi`) and by `auto` in the vendor - /// order. ⚠ This rung has decoded NOTHING on hardware ([`native_evidence`]) — `auto` + /// order. ⚠ Its AV1 leg has now decoded 250/250 on RDNA3 but is unverified for want of + /// parity, and its H.264/H.265 legs have decoded NOTHING ([`native_evidence`]) — `auto` /// runs it where the alternative below it is the CPU, and yields to native Vulkan /// Video where that rung is proven for the codec and usable on the device /// ([`native_rung_admitted`], which is the Intel/unknown arm). Every session that @@ -1109,38 +1111,67 @@ pub fn native_evidence(rung: NativeRung, wire: u8) -> RungEvidence { true, "frame-hash parity on an RTX 4090 and an AMD iGPU + 30-min soak (M5)", ), - // Decoded on hardware for the first time on 2026-08-07 (4K60, RTX 3500 Ada) once the - // host stopped truncating AV1 — so the old "NEVER decoded a frame anywhere" is no - // longer true and must not be printed. + // 2026-08-07: this pair was PARITY-CHECKED for the first time, and it FAILED. // - // Re-measured the same day against a host carrying #95, and it now has more than one - // session behind it: 4K60 on the RTX 3500 Ada AND on the box's Intel Arc (which - // advertises no SAMPLED usage on any decode profile, so zero-copy Vulkan Video cannot - // work there and `auto` correctly lands here), plus a 5-minute Arc soak — 297 stats - // lines, 60 fps, decode 1.3 ms, e2e 10.9/14.8 ms p50/p95, and exactly ONE warning in - // the whole run: this very notice. No refusals, no demotions, no concealed runs, and - // the session was still alive at the end. Two vendors and a soak. It is also - // roughly 10x faster than Vulkan - // Video's AV1 leg on the SAME NVIDIA GPU (1.0-1.4 ms against 11.6-16.7 ms), which is - // the strongest argument yet for eventually admitting it. + // The harness the previous note said did not exist did exist — `video_d3d11_native`'s + // `parity` module, written by M7 against the same libavcodec goldens the Vulkan rung + // uses, `#[ignore]`d and never once run on a device. Running it on `.221` failed on + // BOTH GPUs and did so deterministically (three runs each, identical first-divergent + // frame and identical hashes): 186/250 diverging display frames on an RTX 3500 Ada, + // 245/250 on an Intel Arc. // - // Still NOT `verified`, and the missing piece is named rather than hand-waved: - // `verified` gates `native_rung_admitted` — whether `auto` may pick this rung AHEAD - // of Vulkan Video — and there is still NO frame-hash parity against libavcodec. - // Every other verified pair in this table earned it with one. pf-dxvadec has no - // `gpu_parity`-style harness at all (only `libav_picparams_parity`, which compares - // picture parameters on the CPU and never decodes), so building that harness is the - // work that would promote this, not another session. + // It is the DECODE that is wrong, not the measurement. Three things rule the harness + // and the box out: H.264 and H.265 pass 250/250 and HEVC Main 10 50/50 through the + // SAME harness, the same readback geometry and the same slot map on those same two + // GPUs; pf-vkdecode's Vulkan AV1 leg reproduces the SAME golden file 250/250 on the + // same box; and the goldens themselves reproduce byte-for-byte from ffmpeg 8.1.1. + // + // Two signatures, and they are not the same defect wearing two faces: + // * NVIDIA is bit-exact for display frames 0..=63 and then loses ONE 16x24 luma + // block — 174 pixels, max |delta| 8, chroma untouched — on the frame whose + // `order_hint` first reaches 64, after which every remaining frame is downstream + // of it. The stream parks the key frame (`order_hint` 0) in BWDREF and ALTREF2 for + // its whole length, so 64 is where the distance to it reaches the edge of + // `get_relative_dist`'s range at `OrderHintBits = 7`. + // * Intel is structurally wrong from display frame 4 — 47% of luma, max |delta| 242, + // chroma wrong too, i.e. predicted from the wrong picture — and the only later + // frame it gets right is the one whose `primary_ref_frame` is PRIMARY_REF_NONE. + // + // None of this shows on glass: the rung streams 4K60 on both parts with a clean + // 5-minute soak at ~10x the Vulkan leg's speed. That is the point of a golden. + // + // What would localise it does not exist: pf-dxvadec's `libav_picparams_parity` + // covers H.264 and HEVC only, so the AV1 conversion has never been compared against + // libavcodec at the picture-parameter level either. `av1_divergence_map` in + // `video_d3d11_native` carries the per-frame evidence. (NativeRung::D3d11va, CODEC_AV1) => ( false, - "4K60 on an RTX 3500 Ada AND an Intel Arc + a clean 5-min soak (2026-08-07), \ - and ~10x faster than the Vulkan leg on the same GPU - but has NEVER been \ - frame-hash parity-checked against libavcodec, which every verified pair \ - here has (M7)", + "streams 4K60 on an RTX 3500 Ada AND an Intel Arc with a clean 5-min soak, but \ + has NEVER passed frame-hash parity and now measurably FAILS it: 186/250 and \ + 245/250 display frames diverge from libavcodec on those two GPUs (2026-08-07), \ + while H.264/H.265/Main10 pass through the same harness - this rung decodes AV1 \ + to wrong pixels (M7)", + ), + // 2026-08-07: the VAAPI rung decoded its first frames ever — 250/250 of the vendored + // AV1 vector on `.25` (Radeon 780M, RDNA3, Mesa 26.0.3), NV12 on a tiled AMD + // modifier. So "never decoded a frame anywhere" is no longer true of AV1 and must not + // be printed for it; it is still exactly true of the other two legs, which is why + // this arm is now split. + // + // Not `verified`, and for a reason the AV1 row above makes vivid: a rung can decode + // 250 frames and still produce wrong pixels. Parity is what tells them apart, and + // this rung exports a TILED dmabuf with no CPU-readable image, so it needs a readback + // path nothing has written yet. + (NativeRung::Vaapi, CODEC_AV1) => ( + false, + "decoded 250/250 frames of the vendored AV1 vector on RDNA3 (Mesa 26.0.3, \ + 2026-08-07) - its first frames on any hardware - but has never been frame-hash \ + parity-checked: the rung exports a tiled dmabuf with no CPU-readable image (M7)", ), (NativeRung::Vaapi, _) => ( false, - "NEVER decoded a frame on any hardware - no VAAPI device was reachable (M6/M7)", + "NEVER decoded a frame on any hardware - the AV1 leg has now run on RDNA3, these \ + two have not (M6/M7)", ), // The 4K AV1 abort recorded here on 2026-08-07 is FIXED, and it was never about 4K. // rav1d 1.1.0 aborts the process on ANY decode error while it holds a single frame @@ -1192,7 +1223,7 @@ pub fn native_vulkan_usable(wire: u8, video_decode: bool, decode_video_caps: u32 /// Where the rule bites, and where it deliberately does not: /// /// * **Linux, Intel and every unknown vendor id.** The order is `native-vaapi → -/// native-vk → sw`, so the rung under the never-run pf-vaadec is native Vulkan Video, +/// native-vk → sw`, so the rung under the unproven pf-vaadec is native Vulkan Video, /// proven for all three codecs. Barring VAAPI there moves the session ONE rung down onto /// proven code, so it is barred — and it stays reachable below Vulkan (the same ladder /// reaches it again if Vulkan can't be built) and by pin. @@ -1462,8 +1493,11 @@ pub fn decodable_codecs() -> u8 { /// * the presenter's Vulkan device advertises `DECODE_AV1` in its decode queue /// family's codec operations, or /// * (Windows) the presenter can import D3D11 textures — the native DXVA rung then decodes -/// AV1 Profile 0 through the adapter's profile GUID, and `auto` reaches it. ⚠ That leg -/// has decoded nothing on hardware ([`native_evidence`]); the session says so at `warn`. +/// AV1 Profile 0 through the adapter's profile GUID, and `auto` reaches it. ⚠⚠ That leg +/// decodes WRONG PIXELS — measured against libavcodec on two GPUs on 2026-08-07 +/// ([`native_evidence`]); the session says so at `warn`. Advertising AV1 is still answered +/// from device facts here, deliberately: withdrawing the codec would be a product call +/// about what a Windows Intel box streams instead, not a fact about the device. /// Before M10 this arm was conditional, because the leg was kept out of `auto` while /// libavcodec's DXVA rung was still below it — with that rung deleted there is no /// condition left to write. @@ -1568,7 +1602,8 @@ fn report_au_fault_env(native_rung: bool) { /// a frame through it for this codec. /// /// This is the program's honesty surface, and M10 is where it earns its keep: every rung -/// is now native, two of them have never decoded anything anywhere, and there is no +/// is now native, one of them still has legs that have never decoded anything anywhere, +/// another has a leg that decodes wrong pixels, and there is no /// libavcodec twin left underneath to catch a session that lands wrong. A field report of /// the form "M10 broke my stream" is only actionable if the log distinguishes *the rung /// with three drivers and a 92-minute soak behind it* from *the rung nothing has ever @@ -1646,10 +1681,10 @@ impl Decoder { /// Intel/unknown (Intel's driver advertises Vulkan Video, but Vulkan decode on it /// strobed/overran the budget — B580 field report). /// - /// On top of that order sits the evidence filter ([`native_rung_admitted`]): a rung - /// that has never decoded a frame does not go FIRST when the rung directly below it is - /// proven for this codec and usable on this device. That is the Linux Intel/unknown - /// arm and only that arm — everywhere else what is below is the CPU. + /// On top of that order sits the evidence filter ([`native_rung_admitted`]): an + /// UNPROVEN rung does not go FIRST when the rung directly below it is proven for this + /// codec and usable on this device. That is the Linux Intel/unknown arm and only that + /// arm — everywhere else what is below is the CPU. /// /// Whatever it lands on, the session logs `decode rung active` with the rung's name /// and its evidence state, and that line is a WARNING when no hardware has ever @@ -1994,6 +2029,20 @@ impl Decoder { // so what is really below the DXVA AV1 leg is the CPU — and its H.264/H.265 // legs are verified anyway, which is what the first clause of // [`native_rung_admitted`] answers. + // + // ⚠⚠ OPEN, 2026-08-07: for AV1 this now admits a rung MEASURED to decode wrong + // pixels (186/250 and 245/250 diverging frames — [`native_evidence`]), and on + // Intel it is the arm that actually fires, because that vendor advertises no + // SAMPLED usage on any decode profile so zero-copy Vulkan Video cannot run + // there. The filter is behaving as written — "unproven yields only to PROVEN + // code below", and below here is the CPU — but the premise has changed: this is + // no longer a rung with no evidence, it is a rung with BAD evidence, and the + // rule was never asked that question. Deliberately NOT changed here: barring it + // trades visibly-wrong AV1 for the software rung, which cannot keep up at 4K + // (see [`crate::video_software`]) and is itself unproven, so which way that + // trade should go is a product call and not this function's to make silently. + // Every such session says so at `warn` via [`log_rung`], and the note now names + // the parity failure rather than merely the absence of a check. && native_rung_admitted(NativeRung::D3d11va, wire, None) { d3d11_tried = true; @@ -3282,22 +3331,22 @@ mod tests { ( NativeRung::D3d11va, CODEC_AV1, - "the DXVA AV1 leg never ran (M7)", + "the DXVA AV1 leg FAILS parity on two GPUs (M7)", ), ( NativeRung::Vaapi, CODEC_H264, - "no VAAPI device was reachable", + "no VAAPI device has run this leg", ), ( NativeRung::Vaapi, CODEC_HEVC, - "no VAAPI device was reachable", + "no VAAPI device has run this leg", ), ( NativeRung::Vaapi, CODEC_AV1, - "no VAAPI device was reachable", + "VAAPI decoded AV1 on RDNA3 but has no parity check", ), ( NativeRung::Software, @@ -3375,8 +3424,9 @@ mod tests { e.note.contains("NEVER") || e.note.contains("never"), "{} / {codec:#x}: the note is what the session log prints at warn — it \ must name plainly what this pair has NEVER had, whether that is a \ - hardware run at all (the VAAPI rows) or the parity check that would \ - promote it (D3D11VA/AV1), got {:?}", + hardware run at all (VAAPI H.264/H.265) or the parity check that would \ + promote it (VAAPI AV1, which HAS decoded, and D3D11VA AV1, which has \ + decoded and then FAILED that check on two GPUs), got {:?}", rung.name(), e.note ); diff --git a/crates/pf-client-core/src/video_d3d11_native.rs b/crates/pf-client-core/src/video_d3d11_native.rs index 04ad598e..c1f0ee48 100644 --- a/crates/pf-client-core/src/video_d3d11_native.rs +++ b/crates/pf-client-core/src/video_d3d11_native.rs @@ -18,10 +18,19 @@ //! (`video::native_evidence`, and the table in `video`'s module docs): //! //! * **H.264 and H.265** — frame-hash parity against libavcodec on an RTX 4090 and an AMD -//! iGPU plus a 30-minute soak (M5). -//! * **AV1** — wired in M7, has decoded nothing on any hardware. Until M10 `auto` skipped it -//! in favour of the libavcodec rung below; with that gone the alternative is the CPU, so it -//! runs and the session log says so at `warn`. +//! iGPU plus a 30-minute soak (M5), re-confirmed on an RTX 3500 Ada and an Intel Arc on +//! 2026-08-07 (250/250 both codecs, plus 50/50 HEVC Main 10 on both). +//! * **AV1** — wired in M7. It streams: 4K60 on an RTX 3500 Ada and on an Intel Arc, with a +//! clean 5-minute soak. But it **fails frame-hash parity on both of those GPUs**, measured +//! 2026-08-07 — 186/250 diverging frames on the NVIDIA part and 245/250 on the Intel one, +//! deterministically, against the same libavcodec goldens the Vulkan rung reproduces +//! 250/250 on the SAME box. So this rung's AV1 leg produces wrong pixels and the session +//! log says so at `warn`. `av1_divergence_map` (below) carries the two signatures; the +//! tool that would localise it — an AV1 leg for pf-dxvadec's `libav_picparams_parity`, +//! which covers only H.264 and HEVC — does not exist yet. +//! +//! Until M10 `auto` skipped AV1 here in favour of the libavcodec rung below; with that +//! gone the alternative is the CPU, so it still runs. //! //! A refusal or an init failure logs and falls through to the standard ladder, so neither the //! pin nor the `auto` admission can cost a session its decoder. @@ -2227,6 +2236,163 @@ mod parity { ); } + /// The AV1 leg's post-mortem: one line per DISPLAY frame, its verdict against the + /// goldens beside the plan facts that could explain it. + /// + /// Not a gate — it asserts nothing and always "passes". It exists because + /// [`av1_every_delivered_frame_hashes_bit_identical_to_libavcodec`] FAILS on + /// every device tried so far, and a count of diverging frames is not a lead. This + /// is what turned that count into one, on 2026-08-07: + /// + /// * **NVIDIA RTX 3500 Ada** — display frames 0..=63 bit-identical, then every one + /// of the remaining 186 diverges. The first bad frame is the one whose + /// `order_hint` first reaches **64**, and its error is 174 luma pixels in a + /// single 16x24 block (max |delta| 8, chroma untouched) which then propagates + /// through prediction. The stream keeps the key frame (`order_hint` 0) in the + /// BWDREF and ALTREF2 slots for its whole length, so 64 is where the distance to + /// it reaches the edge of what `get_relative_dist` can represent at + /// `OrderHintBits = 7`. + /// * **Intel Arc** — only display frames 0, 1, 2, 3 and 10 are bit-identical, and + /// the divergence is STRUCTURAL rather than marginal (47% of luma at the first + /// bad frame, max |delta| 242, chroma wrong too): a frame predicted from the + /// wrong picture, not a filter rounding. + /// + /// Both are deterministic — three runs each, identical first-divergent frame and + /// identical hashes — so neither is a race against the decode queue. + /// + /// Set `PF_AV1_DUMP=` to also write a few frames' raw NV12 to the temp + /// directory. That is how "how badly" was answered: at a frame where ONE vendor + /// hashes correctly, that vendor's bytes are libavcodec's bytes and so a valid + /// reference for the other's, and `ffmpeg -f rawvideo -pix_fmt nv12` regenerates + /// the rest (the golden file's header carries the exact command). + #[test] + #[ignore = "diagnostic, needs a Windows D3D11 video device (see module docs)"] + fn av1_divergence_map() { + let units = split_ivf(TEST_25FPS_AV1); + let order = order_av1(&units); + let goldens = golden_hashes(GOLDENS_AV1); + + // Plan facts per PicId, from a planner run alongside the decoder's own. + let mut facts: HashMap = HashMap::new(); + let mut hidden: std::collections::HashSet = std::collections::HashSet::new(); + { + let mut planner = pf_dxvadec::Av1Planner::new(); + for unit in &units { + for plan in planner.plan_au(unit).expect("the clean vector plans") { + let Some(id) = plan.dpb.stored else { continue }; + let h = &*plan.header; + if !h.show_frame { + hidden.insert(id); + } + let mut refs = String::new(); + for r in plan.refs.iter() { + match r { + Some(r) => { + refs.push_str(&format!("{}/{} ", r.slot, r.id)); + } + None => refs.push_str("-/- "), + } + } + facts.insert( + id, + format!( + "ft={} show={} oh={:3} pri={} refresh={:#06x} grain={} seg={} \ + sr={} warp={} refmvs={} skip={} refsel={} tiles={}x{} \ + lf={:?} lfsharp={} lfdelta={}{} refd={:?} moded={:?} \ + cdefbits={} lr={:?} refs=[{}]", + h.frame_type as u8, + u8::from(h.show_frame), + h.order_hint, + h.primary_ref_frame, + h.refresh_frame_flags, + u8::from(h.film_grain_params.apply_grain), + u8::from(h.segmentation_params.segmentation_enabled), + u8::from(h.use_superres), + u8::from(h.allow_warped_motion), + u8::from(h.use_ref_frame_mvs), + u8::from(h.skip_mode_present), + u8::from(h.reference_select), + h.tile_info.tile_cols, + h.tile_info.tile_rows, + h.loop_filter_params.loop_filter_level, + h.loop_filter_params.loop_filter_sharpness, + u8::from(h.loop_filter_params.loop_filter_delta_enabled), + u8::from(h.loop_filter_params.loop_filter_delta_update), + h.loop_filter_params.loop_filter_ref_deltas, + h.loop_filter_params.loop_filter_mode_deltas, + h.cdef_params.cdef_bits, + h.loop_restoration_params.frame_restoration_type, + refs.trim_end(), + ), + ); + } + } + } + + let luid = pinned_adapter(); + let mut decoder = NativeD3d11Decoder::new(Codec::Av1, StreamFormat::SDR_420_8, luid, false) + .expect("the box must host AV1 Profile 0"); + let mut readback = Readback { + ctx: decoder.context.clone(), + staging: None, + }; + // Raw NV12 for a few display frames is kept as well as its hash, so a + // divergence can be classified by plane and magnitude against a vendor whose + // hash at that same frame MATCHES the golden. It has to be captured inside + // the loop: surfaces are recycled, so by the end of the run the slot that + // held an early picture holds someone else's pixels. + let dump_tag = std::env::var("PF_AV1_DUMP").ok(); + let wanted: Vec = if dump_tag.is_some() { + [3usize, 4, 10, 63, 64] + .iter() + .filter_map(|&n| order.display.get(n).copied()) + .collect() + } else { + Vec::new() + }; + let mut by_id: HashMap = HashMap::new(); + for (index, unit) in units.iter().enumerate() { + decoder.decode_av1(unit).expect("decode"); + for &id in &order.per_unit[index] { + let (slot, f, pool) = { + let session = decoder.session.as_ref().expect("session"); + let slot = session.slots.slot_of(id).expect("slot"); + let f = session.held[usize::from(slot)].expect("facts"); + (slot, f, session.pool.clone()) + }; + let bytes = + readback.read(&decoder.device, &pool, u32::from(slot), (f.width, f.height)); + if wanted.contains(&id) { + let tag = dump_tag.as_deref().unwrap_or("x"); + let path = std::env::temp_dir().join(format!("pf-nv12-{tag}-pic{id}.bin")); + std::fs::write(&path, &bytes).expect("write the dump"); + eprintln!("dumped pic {id} -> {}", path.display()); + } + by_id.insert(id, sha256_hex(&bytes)); + } + } + + eprintln!("=== MAP BEGIN ==="); + for (n, (id, golden)) in order.display.iter().zip(goldens.iter()).enumerate() { + let got = by_id.get(id).expect("decoded"); + eprintln!( + "disp {n:3} pic {id:3} {} | {}", + if got == golden { "OK " } else { "BAD" }, + facts.get(id).map(String::as_str).unwrap_or("?") + ); + } + eprintln!("=== HIDDEN ==="); + let mut h: Vec = hidden.into_iter().collect(); + h.sort_unstable(); + for id in h { + eprintln!( + "hidden pic {id:3} | {}", + facts.get(&id).map(String::as_str).unwrap_or("?") + ); + } + eprintln!("=== MAP END ==="); + } + #[test] #[ignore = "needs a Windows D3D11 video device (see module docs)"] fn av1_every_delivered_frame_hashes_bit_identical_to_libavcodec() {