fix(client): the D3D11VA AV1 rung decodes wrong pixels — the parity harness existed all along

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.
This commit is contained in:
2026-08-07 20:22:33 +02:00
parent f351eb01e9
commit 6d0a389dd2
2 changed files with 264 additions and 48 deletions
+94 -44
View File
@@ -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
);
+170 -4
View File
@@ -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=<tag>` 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<u64, String> = HashMap::new();
let mut hidden: std::collections::HashSet<u64> = 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<u64> = 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<u64, String> = 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<u64> = 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() {