From 669176982dd447f8e747010c47684af9b733e772 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 7 Aug 2026 19:15:53 +0200 Subject: [PATCH 01/21] fix(h264): name the DPB cliff #96 left standing in the other codec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit H.264 derives its DPB size the same way HEVC did before #96 — from a level ceiling that says what a stream MAY use, not what it needs — and the ceiling saturates at 16 frames, which is 17 hardware slots with the picture in flight. That is the exact arithmetic that cost 720p and 1080p their HEVC. Measured on real encoders (2026-08-07) rather than assumed: H.264 escapes it twice over, and both escapes belong to the encoders, not to the format. encoder level picked VUI restriction NVENC (RTX 5070 Ti, 610.57.04) 3.2/4.2/5.1/5.2 present, buffering 3 VAAPI via libavcodec (RDNA3, 26.0.3) 4.1/4.2/5.1/5.2 present, buffering 1 openh264 (the software rung) 3.2/4.2/5.1/5.2 present, buffering 1 Every one picks a level proportionate to the picture AND states its real need in the VUI bitstream restriction, so the ceiling is never reached and never consulted. Nothing is broken today, and clamping would be wrong: with the restriction present the number IS the stream's own statement, and a stream that genuinely asked for a deep DPB would decode wrong if we shrank it. So this does not change what any stream decodes. It gives the arithmetic one named home (`dpb_limit`, the twin of `h265::dpb_limit`) carrying the evidence and the reasoning, and it adds the signal that was missing: when an SPS carries no restriction AND its level ceiling would demand more slots than mainstream hardware provides, the plan now says so with `PlanWarning::LevelDerivedDpb` instead of a user silently losing the codec the way #96's users silently lost HEVC. It is not an integrity warning — the picture is intact; what fails is opening a session — so `is_integrity_warning` classifies it false. One thing the sweep corrects about how the follow-up was framed: it is SMALL pictures that saturate the ceiling most easily, not 720p specifically. 640x360 at level 3.1 computes 16 as readily as 720p at level 5.0, because the ceiling is MaxDpbMbs divided by the picture's macroblocks. The authored 64x64 test fixtures land there too, which is why they now assert through `picture_warnings`. Guards, as the missing consumer-end half of pf-encode's `rfi_dpb_fits_a_mainstream_vulkan_decoder`: * every_reachable_h264_stream_fits_a_mainstream_slot_pool — the measured (picture, level, declaration) pairs, asserting slots <= 16 * the_level_ceiling_alone_would_reproduce_96_and_is_warned_about — the same resolutions at levels that saturate, pinned WITH the warning * a_proportionate_level_fits_even_without_a_vui_restriction — so neither escape looks like it is doing all the work alone Gates: fmt + clippy -D warnings clean; pf-client-core 167/167; pf-bitstream 84/84; and gpu_parity 8/8 bit-identical to libavcodec on the RTX 5070 Ti, which is the gate that matters for anything touching the bitstream layer. --- crates/pf-bitstream/src/h264.rs | 265 ++++++++++++++++++++++++++-- crates/pf-vkdecode/src/integrity.rs | 8 +- 2 files changed, 259 insertions(+), 14 deletions(-) diff --git a/crates/pf-bitstream/src/h264.rs b/crates/pf-bitstream/src/h264.rs index 17314b818..146555c90 100644 --- a/crates/pf-bitstream/src/h264.rs +++ b/crates/pf-bitstream/src/h264.rs @@ -240,6 +240,16 @@ pub enum PlanWarning { /// hosts never emit MMCO 5, so this warning is the field signal if that /// assumption ever breaks. Mmco5Rebase, + /// The SPS carried no VUI `bitstream_restriction`, so the DPB had to be sized from + /// A.3.1's LEVEL ceiling — and that ceiling demands more hardware slots than a + /// mainstream decoder provides. See [`dpb_limit`] for why this is the H.264 shape + /// of the defect #96 fixed for HEVC, and why it is a warning here rather than a + /// clamp. Every H.264 encoder a punktfunk host can reach writes the restriction + /// (measured 2026-08-07), so this warning is the field signal if that ever breaks. + LevelDerivedDpb { + max_dpb_frames: usize, + level_idc: u8, + }, } /// The AU cannot be planned at all. @@ -296,12 +306,72 @@ impl From<&Sps> for NegotiationInfo { bit_depth_luma_minus8: sps.bit_depth_luma_minus8, bit_depth_chroma_minus8: sps.bit_depth_chroma_minus8, chroma_format_idc: sps.chroma_format_idc, - max_dpb_frames: sps.max_dpb_frames(), + max_dpb_frames: dpb_limit(sps), interlaced: !sps.frame_mbs_only_flag, } } } +/// The hardware DPB slot count a mainstream decoder provides. NVIDIA's Vulkan Video +/// reports `maxDpbSlots = 16` (RADV 17), and DXVA/D3D11VA's H.264 picture-parameter +/// format indexes the DPB with a 16-entry array. Backends allocate one slot per DPB +/// frame plus one for the picture in flight, so a stream whose DPB is sized at 16 +/// demands 17 and is refused outright — losing the codec, not merely a slower path. +const MAINSTREAM_MAX_DPB_SLOTS: usize = 16; + +/// DPB size in frames — the same question [`crate::h265::dpb_limit`] answers, and the +/// same trap, but H.264 gets a different answer and it is worth writing down why. +/// +/// HEVC's SPS states its own requirement outright (`sps_max_dec_pic_buffering_minus1`), +/// so #96 could simply stop consulting equation A-2's level ceiling. H.264 has no such +/// unconditional field: the stream declares its need ONLY in the VUI's +/// `bitstream_restriction` (`max_dec_frame_buffering`, E.2.1). Absent that, A.3.1's +/// level ceiling — `min(MaxDpbMbs / (PicWidthInMbs * FrameHeightInMbs), 16)` — is all +/// the spec leaves, and it is genuinely the correct inference, not a bug. It is also +/// what the stream MAY use rather than what it needs, which is exactly the shape that +/// killed HEVC at 720p and 1080p. +/// +/// The ceiling saturates at 16 — thus [`MAINSTREAM_MAX_DPB_SLOTS`] + 1 hardware slots, +/// the fatal value — whenever the picture is small relative to its level's `MaxDpbMbs`: +/// +/// | picture | ceiling by level | +/// |---|---| +/// | 720p (3600 MBs) | L3.2 → 5, L4.2 → 9, **L5.0+ → 16** | +/// | 1080p (8160 MBs) | L4.2 → 4, L5.0 → 13, **L5.1+ → 16** | +/// | 1440p (14400 MBs) | L5.1 → 12, **L6.0+ → 16** | +/// | 2160p (32400 MBs) | L5.2 → 5, **L6.0+ → 16** | +/// +/// So H.264 escaped #96 twice over rather than by one piece of luck, and BOTH escapes +/// are properties of the encoders, not of the format. Measured 2026-08-07 by reading +/// the SPS each encoder actually emitted, at 720p/1080p/1440p/2160p: +/// +/// | encoder | level picked | `bitstream_restriction` | `max_dec_frame_buffering` | +/// |---|---|---|---| +/// | NVENC (RTX 5070 Ti, 610.57.04) | 3.2 / 4.2 / 5.1 / 5.2 | present | 3 | +/// | VAAPI via libavcodec (RDNA3, Mesa 26.0.3) | 4.1 / 4.2 / 5.1 / 5.2 | present | 1 | +/// | openh264 (software rung) | 3.2 / 4.2 / 5.1 / 5.2 | present | 1 | +/// +/// Every one of them picks a level proportionate to the picture AND states its real +/// need in the VUI, so the ceiling is never reached and never consulted. That is why +/// this function does NOT clamp: with the restriction present the value below IS the +/// stream's own statement, and clamping a stream that genuinely asked for a deep DPB +/// would corrupt its output. Absent the restriction there is no honest smaller number +/// to substitute — [`PlanWarning::LevelDerivedDpb`] names the situation instead, so the +/// field tells us if a driver ever stops writing the VUI, rather than a user silently +/// losing H.264 the way #96's users silently lost HEVC. +fn dpb_limit(sps: &Sps) -> usize { + // A.3.1's cap, the VUI override and the `max_num_ref_frames` floor all live in the + // vendored parser; this is the one named place the RESULT is interpreted. + sps.max_dpb_frames() +} + +/// Whether [`dpb_limit`] had to fall back to A.3.1's level ceiling because the SPS +/// carried no VUI `bitstream_restriction` — i.e. whether the number is the stream's own +/// statement of need or merely the largest DPB its level permits. +fn dpb_is_level_derived(sps: &Sps) -> bool { + !(sps.vui_parameters_present_flag && sps.vui_parameters.bitstream_restriction_flag) +} + #[derive(Copy, Clone, Debug)] enum RefPicList { RefPicList0, @@ -606,7 +676,7 @@ impl H264Planner { // uncapped. No hardware decoder implements a deeper DPB — a larger value is a // corrupt (or hostile) VUI, not a feature request — and backends size real // slot pools from this number, so it is gated here, at SPS activation. - if sps.max_dpb_frames() > 16 { + if dpb_limit(sps) > MAINSTREAM_MAX_DPB_SLOTS { return Err(PlanError::OutsideEnvelope( "DPB deeper than 16 frames (max_dec_frame_buffering)", )); @@ -1279,10 +1349,19 @@ impl H264Planner { } // Apply the parameters of `sps` to the planning state. - fn apply_sps(&mut self, sps: &Sps) { + fn apply_sps(&mut self, sps: &Sps, warnings: &mut Vec) { self.negotiation_info = NegotiationInfo::from(sps); - let max_dpb_frames = sps.max_dpb_frames(); + let max_dpb_frames = dpb_limit(sps); + // Sized from the level ceiling rather than the stream's own declaration, and + // large enough that backends will ask for more slots than they can get. See + // [`dpb_limit`]: warned, not clamped, because there is no honest smaller number. + if dpb_is_level_derived(sps) && max_dpb_frames + 1 > MAINSTREAM_MAX_DPB_SLOTS { + warnings.push(PlanWarning::LevelDerivedDpb { + max_dpb_frames, + level_idc: sps.level_idc as u8, + }); + } let interlaced = !sps.frame_mbs_only_flag; let max_num_order_frames = sps.max_num_order_frames() as usize; let max_num_reorder_frames = if max_num_order_frames > max_dpb_frames { @@ -1300,13 +1379,17 @@ impl H264Planner { *old_negotiation_info != negotiation_info } - fn renegotiate_if_needed(&mut self, sps: &Sps) -> Result<(), PlanError> { + fn renegotiate_if_needed( + &mut self, + sps: &Sps, + warnings: &mut Vec, + ) -> Result<(), PlanError> { if Self::negotiation_possible(sps, &self.negotiation_info) { Self::check_envelope(sps)?; // Make sure all the frames planned so far are display-ready before the // stream parameters change under them. self.drain_dpb(); - self.apply_sps(sps); + self.apply_sps(sps, warnings); } Ok(()) @@ -1412,7 +1495,7 @@ impl H264Planner { )?); // A picture's SPS may require renegotiation. - self.renegotiate_if_needed(&pps.sps)?; + self.renegotiate_if_needed(&pps.sps, warnings)?; let first_field = self.find_first_field(hdr).map_err(PlanError::Parse)?; @@ -1706,7 +1789,7 @@ impl H264Planner { bit_depth_luma_minus8: sps.bit_depth_luma_minus8, bit_depth_chroma_minus8: sps.bit_depth_chroma_minus8, chroma_format_idc: sps.chroma_format_idc, - max_dpb_frames: sps.max_dpb_frames(), + max_dpb_frames: dpb_limit(sps), recovery_point, } } @@ -1900,6 +1983,21 @@ mod tests { (sps, pps) } + /// The warnings a plan carries, minus the DPB-sizing signal. + /// + /// [`base_sps`]'s fixtures are 64x64 with no VUI bitstream restriction, so A.3.1's + /// ceiling saturates (`MaxDpbMbs(L1) = 396` over 16 macroblocks) and every plan + /// built on them carries [`PlanWarning::LevelDerivedDpb`]. That is the arithmetic + /// `the_level_ceiling_alone_would_reproduce_96_and_is_warned_about` exists to pin — + /// SMALL pictures saturate the ceiling most easily, not large ones — and it says + /// nothing about the picture, which is what the tests below are checking. + fn picture_warnings(plan: &AuPlan) -> Vec<&PlanWarning> { + plan.warnings + .iter() + .filter(|w| !matches!(w, PlanWarning::LevelDerivedDpb { .. })) + .collect() + } + fn param_set_au(sps: &Sps, pps: &Pps) -> Vec { let mut au = Vec::new(); Synthesizer::<'_, Sps, _>::synthesize(3, sps, &mut au, true).unwrap(); @@ -2022,7 +2120,7 @@ mod tests { let p4 = planner.plan_au(&au4).unwrap(); for plan in [&p0, &p1, &p2, &p3, &p4] { assert!( - plan.warnings.is_empty(), + picture_warnings(plan).is_empty(), "authored stream must plan clean: {plan:?}" ); } @@ -2101,7 +2199,10 @@ mod tests { let p1 = planner.plan_au(&au1).unwrap(); let p2 = planner.plan_au(&au2).unwrap(); for plan in [&p0, &p1, &p2] { - assert!(plan.warnings.is_empty(), "must plan clean: {plan:?}"); + assert!( + picture_warnings(plan).is_empty(), + "must plan clean: {plan:?}" + ); } let idr_id = p0.dpb.stored.unwrap(); let lt_id = p1.dpb.stored.unwrap(); @@ -2364,6 +2465,144 @@ mod tests { ); } + /// An SPS at a given picture size and level, with the VUI bitstream restriction + /// either absent (so [`dpb_limit`] must fall back to A.3.1's level ceiling) or + /// present with an explicit `max_dec_frame_buffering`. + fn sps_at(width: u32, height: u32, level: Level, declared: Option) -> Sps { + Sps { + profile_idc: Profile::Main as u8, + level_idc: level, + frame_mbs_only_flag: true, + direct_8x8_inference_flag: true, + // Every H.264 encoder measured below sits at or under this; it is the + // A.3.1 floor `max_dpb_frames` applies, never the value under test. + max_num_ref_frames: 3, + pic_width_in_mbs_minus1: (width / 16 - 1) as u16, + pic_height_in_map_units_minus1: (height / 16 - 1) as u16, + vui_parameters_present_flag: declared.is_some(), + vui_parameters: VuiParams { + bitstream_restriction_flag: declared.is_some(), + max_dec_frame_buffering: declared.unwrap_or(0), + ..Default::default() + }, + ..Default::default() + } + } + + /// The H.264 half of `pf-encode`'s `rfi_dpb_fits_a_mainstream_vulkan_decoder`. + /// + /// That test guards the PRODUCER end for HEVC — that `RFI_DPB` never grows past + /// what a client can allocate. This is the CONSUMER end for H.264, where the + /// number is not ours to choose: the client derives it from whatever SPS arrives, + /// and #96 proved that deriving a DPB from a level's ceiling instead of a stream's + /// need costs the codec outright. + /// + /// Pinned here are the (picture, level) pairs a punktfunk host can actually reach, + /// with the level each shipping encoder was MEASURED to pick on 2026-08-07 (see + /// [`dpb_limit`] for the full table and the hardware). The property that has to + /// hold is the one the backends enforce: `dpb_limit + 1 <= 16` slots. + #[test] + fn every_reachable_h264_stream_fits_a_mainstream_slot_pool() { + // (picture, level measured on NVENC / VAAPI / openh264, their VUI declaration) + let measured = [ + ((1280, 720), Level::L3_2, 3), // NVENC, openh264 + ((1280, 720), Level::L4_1, 1), // VAAPI via libavcodec + ((1920, 1080), Level::L4_2, 3), // all three + ((2560, 1440), Level::L5_1, 3), + ((3840, 2160), Level::L5_2, 3), + ]; + for ((w, h), level, declared) in measured { + let sps = sps_at(w, h, level, Some(declared)); + assert!( + !dpb_is_level_derived(&sps), + "{w}x{h} L{:?}: the measured encoders all write the VUI restriction — \ + an SPS that carries it must never be treated as level-derived", + level + ); + let slots = dpb_limit(&sps) + 1; + assert!( + slots <= MAINSTREAM_MAX_DPB_SLOTS, + "{w}x{h} L{level:?} declaring {declared} needs {slots} DPB slots, \ + mainstream hardware caps at {MAINSTREAM_MAX_DPB_SLOTS}" + ); + } + } + + /// The cliff the measured encoders walk past, pinned so it stays visible. + /// + /// These are the SAME resolutions, at levels a host could legally pick, with no + /// VUI restriction to state the real need. Each computes a 16-frame DPB — 17 + /// hardware slots — which is precisely the arithmetic that killed HEVC at 720p and + /// 1080p before #96. H.264 escapes it because every encoder both picks a + /// proportionate level AND writes the restriction, not because the format is safe. + #[test] + fn the_level_ceiling_alone_would_reproduce_96_and_is_warned_about() { + // (picture, a level that saturates A.3.1's ceiling for it) + let cliff = [ + ((1280, 720), Level::L5), + ((1280, 720), Level::L5_2), + ((1920, 1080), Level::L5_1), + ((2560, 1440), Level::L6), + ((3840, 2160), Level::L6_2), + ]; + for ((w, h), level) in cliff { + let sps = sps_at(w, h, level, None); + assert!(dpb_is_level_derived(&sps), "{w}x{h} L{level:?}"); + assert_eq!( + dpb_limit(&sps), + 16, + "{w}x{h} L{level:?} should saturate A.3.1's 16-frame ceiling" + ); + assert!( + dpb_limit(&sps) + 1 > MAINSTREAM_MAX_DPB_SLOTS, + "{w}x{h} L{level:?} is the #96 arithmetic and must be recognised as such" + ); + + // ...and the planner must NAME it rather than let a user silently lose the + // codec. The SPS activates on the first slice, so the warning rides out on + // the IDR's plan. + let sps = Rc::new(sps); + let pps = PpsBuilder::new(Rc::clone(&sps)) + .pic_parameter_set_id(0) + .pic_init_qp(26) + .build(); + let mut au = param_set_au(&sps, &pps); + au.extend(write_idr_slice()); + + let plan = H264Planner::new() + .plan_au(&au) + .unwrap_or_else(|e| panic!("{w}x{h} L{level:?} should plan, got {e:?}")); + assert!( + plan.warnings.contains(&PlanWarning::LevelDerivedDpb { + max_dpb_frames: 16, + level_idc: level as u8, + }), + "{w}x{h} L{level:?}: expected LevelDerivedDpb, got {:?}", + plan.warnings + ); + } + } + + /// A proportionate level is the other half of the escape: at the levels the + /// encoders actually pick, the ceiling is small enough that even a stream with no + /// VUI at all fits — so neither escape is doing all the work alone. + #[test] + fn a_proportionate_level_fits_even_without_a_vui_restriction() { + let proportionate = [ + ((1280, 720), Level::L3_2, 5), + ((1280, 720), Level::L4_1, 9), + ((1920, 1080), Level::L4_2, 4), + ((2560, 1440), Level::L5_1, 12), + ((3840, 2160), Level::L5_2, 5), + ]; + for ((w, h), level, expected) in proportionate { + let sps = sps_at(w, h, level, None); + assert_eq!(dpb_limit(&sps), expected, "{w}x{h} L{level:?}"); + let slots = dpb_limit(&sps) + 1; // + the picture in flight + assert!(slots <= MAINSTREAM_MAX_DPB_SLOTS, "{w}x{h} L{level:?}"); + } + } + /// MMCO 5 writer: op 5 takes NO argument (Table 7-9), so the generic /// [`write_p_slice`] — whose supported ops all take exactly one — cannot author /// it. @@ -2688,11 +2927,11 @@ mod tests { let plan = planner.plan_au(&write_idr_slice()).unwrap(); assert!(plan.picture.is_idr); assert_eq!(plan.picture.pic_order_cnt, 0); - assert!(plan.warnings.is_empty()); + assert!(picture_warnings(&plan).is_empty()); // And the stream continues cleanly on the reset state. let plan = planner.plan_au(&write_p_slice(1, 2, 1, 1, None)).unwrap(); - assert!(plan.warnings.is_empty()); + assert!(picture_warnings(&plan).is_empty()); assert_eq!(plan.slices[0].ref_list0.len(), 1); } @@ -2725,7 +2964,7 @@ mod tests { au.extend(write_idr_slice_at(8, 1)); let plan = H264Planner::new().plan_au(&au).unwrap(); - assert!(plan.warnings.is_empty()); + assert!(picture_warnings(&plan).is_empty()); assert_eq!(plan.slices.len(), 2); // The picture parameters come from the FIRST slice's PPS (the uncropped // SPS 0); they must not drift to the last slice's. diff --git a/crates/pf-vkdecode/src/integrity.rs b/crates/pf-vkdecode/src/integrity.rs index 35892e85b..a5f493a38 100644 --- a/crates/pf-vkdecode/src/integrity.rs +++ b/crates/pf-vkdecode/src/integrity.rs @@ -32,6 +32,12 @@ use crate::{Av1PlanWarning, H265PlanWarning, PlanWarning}; /// full (the plan holds the pre-rebase 8.2.1 values; later AUs reference the /// rebased ones). /// +/// `LevelDerivedDpb` does not either: the picture is intact and fully planned. It +/// reports that the SPS never declared its DPB depth, so the plan had to size from +/// A.3.1's level ceiling and the result will not fit a mainstream slot pool — a +/// property of the STREAM's signalling, which the decoder answers by failing to open +/// a session, not by showing a damaged frame. +/// /// Written as an EXHAUSTIVE match with no wildcard, deliberately. A `matches!` (or /// a `_ => false`) makes "damage" the opt-in and silence the default, so a /// `PlanWarning` added later — by definition one nobody here has classified — @@ -43,7 +49,7 @@ pub fn is_integrity_warning(w: &PlanWarning) -> bool { PlanWarning::FrameNumGap { .. } | PlanWarning::MissingReference { .. } | PlanWarning::TruncatedAu { .. } => true, - PlanWarning::Mmco5Rebase => false, + PlanWarning::Mmco5Rebase | PlanWarning::LevelDerivedDpb { .. } => false, } } From 6b4be28d24aec40d1d77111d2c7381e01f5d4229 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 7 Aug 2026 19:25:22 +0200 Subject: [PATCH 02/21] docs(client): write down why the CPU rung is not process-isolated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #97's frame-context floor closes the one rav1d abort we hit and can prove. It does not make the rung panic-proof and nothing at that call site can, because rav1d's public surface is dav1d's C ABI: any reachable panic crosses `extern "C"` as `panic_cannot_unwind` and becomes `abort()`, past every `catch_unwind`, rung demotion and typed refusal we have. Counted across rav1d 1.1.0's 60 source files: 285 `unwrap()`, 214 `assert!`, 19 `unreachable!`, 11 `expect()`, 10 `panic!`. 539 sites that end the client if a stream can reach them. #97 fixed one of them. Process isolation is the only defence that actually works, and this records the decision NOT to build it, with the reasoning, so it is not re-argued from scratch each time someone reads that number: * the defect is upstream's and is one line (memorysafety/rav1d#1497, filed 2026-08-07 with the fix and a reproducer; still open, no PR, as of today); * 539 is an unbounded number, not a risk estimate — none of those sites is known reachable from a punktfunk stream, and the honest next step is to fuzz the rung and find out, which is cheap, rather than buy insurance, which is not; * the cost lands on the video path across Linux, Windows and Android (the Apple clients decode through VideoToolbox and never reach this code), each needing its own shared-memory frame transport, child lifecycle and backpressure, and it adds a scheduling boundary to the slowest rung on the ladder while zero-copy is a hard requirement; * an abort here costs a session that was already degraded — this rung exists because the GPU rungs failed first. The trigger to revisit is named as an event rather than a feeling: a SECOND distinct abort in the field, or a fuzzer finding a reachable panic. Either makes it a class of bugs instead of one, and a class is what would justify the architecture. Documentation only — no behaviour change. --- crates/pf-client-core/src/video_software.rs | 40 +++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/crates/pf-client-core/src/video_software.rs b/crates/pf-client-core/src/video_software.rs index bd37a6dc9..718f7729c 100644 --- a/crates/pf-client-core/src/video_software.rs +++ b/crates/pf-client-core/src/video_software.rs @@ -45,6 +45,46 @@ //! no equivalent here. rav1d gets the machine's cores, and **at least two frame contexts**; //! [`Av1Software::new`] carries the whole argument, because "at least two" is not a //! performance choice but the difference between an error and `abort()`. +//! +//! # Why this rung is NOT process-isolated +//! +//! The frame-context floor closes the one abort we hit and can prove. It does not make the +//! rung panic-proof, and nothing at this call site can: rav1d exposes dav1d's C ABI, every +//! internal `rav1d_*` entry point is `pub(crate)`, so any reachable panic crosses +//! `extern "C"` as `panic_cannot_unwind` → `abort()`. No `catch_unwind`, no rung demotion +//! and no [`NoSoftwareRung`] refusal can contain it. Counted in rav1d 1.1.0's 60 source +//! files: 285 `unwrap()`, 214 `assert!`, 19 `unreachable!`, 11 `expect()`, 10 `panic!` — +//! 539 sites that are an `abort()` if a stream can reach them. #97 fixed ONE. +//! +//! Isolating the decoder in its own process is the only defence that actually works, and +//! it is deliberately NOT taken. The decision, so it is not re-litigated from scratch: +//! +//! * **The defect is a dependency's, and it is one line.** memorysafety/rav1d#1497 was +//! filed 2026-08-07 with the fix (`is_some_and` for the `unwrap`) and a reproducer. +//! Paying a permanent architectural tax to route around a bug that costs upstream one +//! line is the wrong trade while that line is still plausibly coming. +//! * **The residual risk is real but unquantified.** 539 panic sites is a scary number +//! and a meaningless one: not one of them is known to be reachable from a punktfunk +//! stream. The honest next step is to MEASURE reachability — fuzz this rung with +//! truncated, reordered and bit-flipped AUs and see whether any input aborts — not to +//! buy insurance against a number nobody has bounded. That is cheap; this is not. +//! * **The cost lands on the video path, and on three platforms.** pf-client-core builds +//! into the Linux, Windows and Android clients (the Apple clients decode through +//! VideoToolbox and never reach here). Each needs its own shared-memory transport for +//! `CpuPlanarFrame`s, its own child lifecycle, crash detection and restart, and its own +//! backpressure — and it adds a scheduling boundary to the rung that is ALREADY the +//! slowest one on the ladder. Zero-copy is a hard requirement here; an IPC hop that +//! copies frames would be rejected on its own terms. +//! * **What an abort actually costs is bounded.** This rung is reached because the GPU +//! rungs already failed, so the session is degraded before rav1d sees a byte. Losing +//! the process loses a session the user was going to have a bad time in regardless. +//! That is bad, and it is not the same as losing a working session. +//! +//! **Revisit when the calculus changes, which is a specific event, not a feeling:** a +//! SECOND distinct abort observed in the field, or a fuzzer finding a reachable panic. +//! Either turns this from one upstream bug into a class of them, and a class is what +//! justifies isolation. Until then the floor plus the upstream fix is the proportionate +//! answer, and the fuzzing is the work that would tell us we were wrong. use crate::video::{CpuPlanarFrame, RungLoss}; use crate::video_color::ColorDesc; From c64cdc4ef7a76121ca57834dcda5d50ee124b07d Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 7 Aug 2026 19:26:32 +0200 Subject: [PATCH 03/21] =?UTF-8?q?docs(encode):=20close=20out=20the=20tile-?= =?UTF-8?q?aware=20AV1=20sub-frame=20reader=20=E2=80=94=20measured,=20not?= =?UTF-8?q?=20worth=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #95 disarmed sub-frame readback for AV1, which means AV1 forgoes the latency win HEVC gets from shipping slice 1 while slice 2 encodes. The follow-up was to teach the reader AV1's units: cut on OBU boundaries rather than byte counts and arm from the driver's reported unit count. Measured on .21 (RTX 5070 Ti, av1_nvenc) before writing any of it, and the measurement closes it rather than scoping it. Reading the frame headers av1_nvenc actually emits at 4K: width_in_sbs_minus_1[0] = 59 one tile column, the full 3840 height_in_sbs_minus_1[0..1] = 16, 16 two tile rows tile_start_and_end_present_flag = 0 BOTH TILES IN ONE TILE GROUP OBU That last flag is the finding. "Cut on OBU boundaries" presumes the tiles are separate OBUs and they are not — there is no boundary between them to cut on. Shipping tile 1 early would need the HOST to re-author AV1 syntax per chunk, synthesising a fresh Tile Group OBU header with tile_start_and_end_present_flag = 1 and its own tg_start/tg_end. That is bitstream surgery on the encode path, not the reader change it was assumed to be. And the prize would be small even then, because split encode already spent it. The two tile rows go to two split-encode engines that run CONCURRENTLY, so they complete at nearly the same moment — the win is bounded by the skew between engines, not by half a frame. Whole-frame encode measures 3.3-3.6 ms at 4K60 against a 16.7 ms p50 end-to-end, so even the sequential-tiles fantasy caps near 1.7 ms and the real number is a fraction of it. HEVC's win is bigger for a structural reason that does not transfer: forced split and sub-frame are mutually unsupported, so HEVC's slices genuinely are produced one after another. 1080p settles it further: tile_cols_log2 = tile_rows_log2 = 0, a single tile, so there is nothing to pipeline at the commonest streaming resolution at all. Recorded next to the disarm with the reopen condition named — NVENC emitting one OBU per tile, or setting tile_start_and_end_present_flag = 1 — so this is closed on evidence rather than left as an open maybe. Documentation only — no behaviour change. --- crates/pf-encode/src/enc/nvenc_core.rs | 30 ++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/crates/pf-encode/src/enc/nvenc_core.rs b/crates/pf-encode/src/enc/nvenc_core.rs index a20cd47db..242230f9e 100644 --- a/crates/pf-encode/src/enc/nvenc_core.rs +++ b/crates/pf-encode/src/enc/nvenc_core.rs @@ -137,6 +137,36 @@ pub(super) fn subframe_env_forced() -> bool { /// drop-in alternative — `poll_chunk` cuts at `bitstreamSizeInBytes` on the reasoning that /// "slices are contiguous Annex-B", which AV1's OBUs are not. /// +/// # A tile-aware chunk reader was considered and is CLOSED, not deferred +/// +/// The obvious follow-up is to teach the reader AV1's units — cut on OBU boundaries instead +/// of byte counts and arm from the driver's reported unit count — so AV1 gets the sub-frame +/// latency win HEVC gets (ship tile 1 while tile 2 encodes). Measured on `.21` (RTX 5070 Ti, +/// `av1_nvenc`, 2026-08-07) before writing any of it, and the measurement closes it: +/// +/// * **4K carries two tiles, and they share ONE Tile Group OBU.** The frame header reads +/// `width_in_sbs_minus_1[0] = 59` (one tile column, the full 3840) and +/// `height_in_sbs_minus_1[] = {16, 16}` (two tile rows) — but +/// `tile_start_and_end_present_flag = 0`, which puts both tiles in a single Tile Group +/// OBU. There is no OBU boundary between them to cut on. Shipping tile 1 early would mean +/// the HOST re-authoring AV1 syntax per chunk — synthesising a fresh Tile Group OBU header +/// with `tile_start_and_end_present_flag = 1` and its own `tg_start`/`tg_end` — which is +/// bitstream surgery on the encode path, not a reader change. +/// * **1080p carries one tile** (`tile_cols_log2 = tile_rows_log2 = 0`), so there is nothing +/// to pipeline at the commonest streaming resolution regardless. +/// * **The prize is small even at 4K, because split encode already spent it.** The two tile +/// rows go to two split-encode engines that run CONCURRENTLY, so they finish at nearly the +/// same moment: the win is bounded by the skew between engines, not by half the frame. +/// Whole-frame encode measures 3.3–3.6 ms at 4K60 against a 16.7 ms p50 end-to-end, so +/// even the sequential-tiles fantasy caps out near 1.7 ms and the real figure is a +/// fraction of that. HEVC's sub-frame win is larger for a structural reason that does not +/// transfer: forced split and sub-frame are mutually unsupported (below), so HEVC's slices +/// really are produced one after another. +/// +/// Reopen only if NVENC starts emitting one OBU per tile, or sets +/// `tile_start_and_end_present_flag = 1` — at that point the cut points exist and the reader +/// change becomes the small piece it was assumed to be. +/// /// Returns the `(split_mode, subframe)` to ACTUALLY configure. The caller must store BOTH back /// (the chunked-poll latch and `CeilingKey` key on them) — a silent in-params drop would leave /// `poll_chunk` busy-polling its full budget every AU (`numSlices` stays 0 without From 19c9165d4b4db8e8f4efecaea01a3e8aae5c6200 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 7 Aug 2026 19:29:30 +0200 Subject: [PATCH 04/21] =?UTF-8?q?docs(client):=20the=20D3D11VA=20AV1=20run?= =?UTF-8?q?g=20has=20two=20vendors=20and=20a=20soak=20now=20=E2=80=94=20an?= =?UTF-8?q?d=20still=20no=20parity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-measured against a host carrying #95, from .21 (RTX 5070 Ti, av1_nvenc) to .221, on glass: Intel Arc, auto -> native-d3d11va 4K60, decode 1.4 ms, e2e 16.7 ms p50 RTX 3500 Ada, pinned native-d3d11va 4K60, decode 1.0 ms RTX 3500 Ada, pinned native-vulkan 4K60, decode 11.6-16.7 ms Plus a 5-minute Arc soak: 297 stats lines, 60 fps, decode 1.3 ms, e2e 10.9/14.8 ms p50, and exactly one WARN in the whole run — the hardware_verified=false notice itself. No refusals, no demotions, no concealed runs. Three things that follow. The rung is no longer a one-session curiosity: it decodes 4K60 AV1 on TWO vendors and survives a soak. The Arc leg matters twice over, because the Arc advertises no SAMPLED usage on any decode profile — zero-copy Vulkan Video cannot work there — so `auto` demoting to D3D11VA and then decoding is the whole demotion path working as designed. It is roughly 10x faster than the Vulkan AV1 leg on the SAME NVIDIA GPU. That is the strongest argument yet for eventually letting `auto` pick it ahead of Vulkan Video, which is exactly what `verified` gates. And it stays `verified = false` anyway, because the missing piece is specific: there is no frame-hash parity against libavcodec. Every other verified pair in that table earned it with one, and pf-dxvadec has no harness that could produce one — `libav_picparams_parity` compares picture parameters on the CPU and never decodes a frame. Building that harness is the work that promotes this rung; a fourth session is not. The evidence string now says so, so the next reader does not have to rediscover which half is missing. The VAAPI row is corrected in the same spirit rather than left as a bare "NO": the reachable VAAPI box (.25, RDNA3) reports VAProfileAV1Profile0 / VAEntrypointVLD and advertises no Vulkan AV1 decode at all, which makes it the right box to prove that rung on and an unambiguous oracle when it happens. What stopped it is recorded too — no punktfunk checkout there and 4 GB of usable RAM. Documentation only — no behaviour change, and no flag flipped. --- crates/pf-client-core/src/video.rs | 38 +++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/crates/pf-client-core/src/video.rs b/crates/pf-client-core/src/video.rs index f29547e70..25b2c6892 100644 --- a/crates/pf-client-core/src/video.rs +++ b/crates/pf-client-core/src/video.rs @@ -49,8 +49,8 @@ //! | 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** — it HAS now decoded (4K60, RTX 3500 Ada, 2026-08-07), but with no parity check and no soak it stays out of the admission filter. 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; no VAAPI hardware was reachable) | +//! | 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 | //! | 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 @@ -1111,14 +1111,32 @@ pub fn native_evidence(rung: NativeRung, wire: u8) -> RungEvidence { ), // 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. Still NOT `verified`: `verified` gates - // `native_rung_admitted`, i.e. whether `auto` may pick this rung AHEAD of Vulkan - // Video, and one 25-second session with no frame-hash parity and no soak does not - // buy that. Promoting it wants a `gpu_parity`-style run, deliberately. + // longer true and must not be printed. + // + // 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. + // + // 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. (NativeRung::D3d11va, CODEC_AV1) => ( false, - "decoded 4K60 once on an RTX 3500 Ada (2026-08-07) but has NEVER been \ - parity-checked or soaked (M7)", + "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)", ), (NativeRung::Vaapi, _) => ( false, @@ -3356,7 +3374,9 @@ mod tests { assert!( e.note.contains("NEVER") || e.note.contains("never"), "{} / {codec:#x}: the note is what the session log prints at warn — it \ - must say plainly that nothing has run it, got {:?}", + 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 {:?}", rung.name(), e.note ); From f351eb01e985ba7fe78226c0c725758d88f32b70 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 7 Aug 2026 20:04:33 +0200 Subject: [PATCH 05/21] =?UTF-8?q?feat(vaapi):=20VAAPI=20decodes=20AV1=20?= =?UTF-8?q?=E2=80=94=20the=20rung's=20first=20frame=20on=20any=20hardware?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The evidence table has said "native VAAPI: has never decoded a frame anywhere (M6/M7)" since the rung was written. That is no longer true. Measured on `.25` (Radeon 780M / Phoenix1 RDNA3, radeonsi, Mesa 26.0.3, VA-API 1.23, Ubuntu 26.04 — headless, no display server needed): VAAPI AV1 rung constructed: native-vaapi av1 VAAPI AV1: 250 frames delivered, first 320x240 fourcc="NV12" modifier=0x200000010401b04 250 of 250 displayed frames, first try, on the same vendored vector the Vulkan and D3D11VA AV1 legs walk. The count matters as more than a smoke test: the vector carries 274 coded frames in 250 temporal units — 24 units carry two, and those extras are HIDDEN (decoded, referenced, never shown) — so 250 delivered is this rung agreeing with the other two about which frames are output. A tiled AMD DRM modifier rather than a linear one says the surface is a real decode target, not a fallback. Two changes, both in the rung's own file. **The probe never asked about AV1.** `probe_this_machines_libva` walked H.264 High, HEVC Main and HEVC Main 10 and stopped there, which is part of why "never decoded a frame" could stand so long without anyone noticing what had not been asked. It now covers both AV1 profiles, and this box answers: H.264 High: VLD decode AV1 Profile 0: VLD decode HEVC Main: VLD decode AV1 Profile 1: no (VAProfile not supported) Profile 1 being refused is correct — 4:4:4 AV1, which radeonsi does not do — and it is the negative case that proves the probe reports rather than assumes. **`av1_decodes_the_vendored_vector_on_this_machines_vaapi`** is the decode itself, `#[ignore]`d beside the probe. It is deliberately WEAKER than the Vulkan and D3D11VA AV1 legs, and the docs say so rather than letting the name imply parity: those two hash every frame against libavcodec's goldens because both can read their decoded surface back. This rung hands out a DRM-PRIME dmabuf whose memory the driver tiles, so there is no CPU-readable image to hash without adding a vaDeriveImage/vaGetImage path that production neither uses nor wants. So it asserts what can be asserted honestly — every temporal unit accepted, the right number of frames back, each a real exported surface of the right shape, the first flagged as a keyframe — and it is NOT frame-hash parity. Promoting this rung to `verified` still wants parity, and parity wants a readback path first. It fails loudly rather than skipping when the device has no AV1 entry point. It is `#[ignore]`d, so it only runs when someone points it at a box that is supposed to have one, and a silent pass there is exactly the invisible-failure mode this program exists to end. Gates: on `.25`, fmt clean, `clippy -p pf-client-core --all-targets -D warnings` green under the Linux cfg where this rung actually compiles, the whole lib suite 167/167, and all 11 VAAPI tests green with `--include-ignored`. Workspace fmt + clippy + lib suite also green in the Linux container. ⚠ Not touched here on purpose: the evidence table in `video.rs`. Its VAAPI row still reads "never decoded a frame anywhere" and now understates what is known — but a parallel agent is editing that same file for the D3D11VA AV1 row, so the row is left for whoever lands second to update once, rather than conflicting. Note for anyone reproducing on `.25`: it has no system SDL3 and no passwordless sudo, so the test binary links only with `--features sdl3/build-from-source` (SDL3 is gamepads, irrelevant to decode; production Linux still links the system one). Its disk sits at ~99% full, and the tree there is a `git archive` export with no `.git`, so `git apply`/`git checkout --` silently do nothing. --- .../pf-client-core/src/video_vaapi_native.rs | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/crates/pf-client-core/src/video_vaapi_native.rs b/crates/pf-client-core/src/video_vaapi_native.rs index bc8cb0e0a..cd0a725a3 100644 --- a/crates/pf-client-core/src/video_vaapi_native.rs +++ b/crates/pf-client-core/src/video_vaapi_native.rs @@ -2423,6 +2423,12 @@ mod tests { ("H.264 High", pf_vaadec::config::VA_PROFILE_H264_HIGH), ("HEVC Main", pf_vaadec::config::VA_PROFILE_HEVC_MAIN), ("HEVC Main 10", pf_vaadec::config::VA_PROFILE_HEVC_MAIN10), + // AV1 was MISSING from this loop until 2026-08-07, which is part + // of why the rung's evidence row could say "never decoded a frame" + // for so long without anyone noticing what had not been asked. + // `profile_for` maps both 8- and 10-bit AV1 4:2:0 onto Profile 0. + ("AV1 Profile 0", pf_vaadec::config::VA_PROFILE_AV1_PROFILE0), + ("AV1 Profile 1", pf_vaadec::config::VA_PROFILE_AV1_PROFILE1), ] { match d.require_entrypoint(profile) { Ok(()) => eprintln!(" {name}: VLD decode"), @@ -2438,4 +2444,108 @@ mod tests { nodes.len() ); } + + /// The vendored AV1 vector, as IVF: 320x240 Main 4:2:0 8-bit, 250 temporal units + /// carrying 274 coded frames (24 units carry two, and those extras are HIDDEN — + /// decoded, referenced, never shown), so **250 frames are displayed**. The same + /// file `pf-vkdecode`'s Vulkan parity leg and `video_d3d11_native`'s D3D11VA leg + /// walk, so a count that disagrees with 250 is this rung's problem, not the + /// vector's. + const AV1_25FPS: &[u8] = include_bytes!( + "../../pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1" + ); + + /// One temporal unit per IVF packet: 32 bytes of `DKIF` header, then + /// `[u32 size][u64 pts][size bytes]`. Hand-rolled because `pf-client-core` does + /// not depend on the vendored parser crate — the same reason and the same walk as + /// `video_d3d11_native`'s `split_ivf`, and kept honest by the unit count asserted + /// at the top of the test below. + fn split_ivf(stream: &[u8]) -> Vec<&[u8]> { + assert_eq!(&stream[0..4], b"DKIF", "the AV1 vector must be an IVF file"); + let header = usize::from(u16::from_le_bytes([stream[6], stream[7]])); + let mut out = Vec::new(); + let mut at = header; + while at + 12 <= stream.len() { + let size = + u32::from_le_bytes(stream[at..at + 4].try_into().expect("four bytes")) as usize; + at += 12; + assert!( + at + size <= stream.len(), + "an IVF frame header claims {size} bytes past the end of the file" + ); + out.push(&stream[at..at + size]); + at += size; + } + out + } + + /// Does this machine's VAAPI actually DECODE AV1 — the question the evidence table + /// has answered "no hardware has ever tried" since M6. + /// + /// This is deliberately weaker than the Vulkan and D3D11VA AV1 legs, and the + /// difference is worth stating rather than hiding: those two hash every decoded + /// frame against libavcodec's goldens, because both can read their decoded surface + /// back. This rung hands out a **DRM-PRIME dmabuf** whose memory is tiled by the + /// driver, so there is no CPU-readable image to hash without adding a + /// `vaDeriveImage`/`vaGetImage` path that production does not use and does not + /// want. So this asserts what CAN be asserted honestly — that every temporal unit + /// is accepted, that the expected number of frames comes back, and that each one + /// is a real exported surface of the right shape — and it is NOT frame-hash parity. + /// It is what turns "never decoded a frame anywhere" into a measurement; promoting + /// the rung to `verified` still wants parity, and that wants a readback path first. + /// + /// Fails loudly rather than skipping when the device has no AV1 entry point: it is + /// `#[ignore]`d, so it only runs when someone deliberately points it at a box that + /// is supposed to have one, and a silent pass there is the invisible-failure mode + /// this whole program exists to end. + #[test] + #[ignore = "needs a machine with a libva runtime and an AV1 VLD entry point"] + fn av1_decodes_the_vendored_vector_on_this_machines_vaapi() { + let units = split_ivf(AV1_25FPS); + assert_eq!( + units.len(), + 250, + "the vendored AV1 vector is 250 temporal units" + ); + + let mut decoder = NativeVaapiDecoder::new(pf_vaadec::Codec::Av1, StreamFormat::SDR_420_8) + .expect("this box is supposed to have a VAAPI AV1 decode entry point"); + eprintln!("VAAPI AV1 rung constructed: {}", decoder.name()); + + let mut delivered = 0usize; + let mut first: Option<(u32, u32, u32, u64)> = None; + for (index, unit) in units.iter().enumerate() { + match decoder.decode(unit) { + Ok(Some(frame)) => { + assert!( + !frame.planes.is_empty(), + "unit {index}: a delivered frame exported no dmabuf planes" + ); + if first.is_none() { + assert!( + frame.keyframe, + "the vector opens on a keyframe, so the first delivered \ + frame must be flagged as one" + ); + first = Some((frame.width, frame.height, frame.fourcc, frame.modifier)); + } + delivered += 1; + } + Ok(None) => {} + Err(e) => panic!("unit {index}: VAAPI AV1 decode failed: {e:#}"), + } + } + + let (w, h, fourcc, modifier) = first.expect("not one frame came back"); + eprintln!( + "VAAPI AV1: {delivered} frames delivered, first {w}x{h} \ + fourcc={:?} modifier={modifier:#x}", + std::str::from_utf8(&fourcc.to_le_bytes()).unwrap_or("?") + ); + assert_eq!((w, h), (320, 240), "the vector is 320x240"); + assert_eq!( + delivered, 250, + "the vector displays 250 frames (274 coded, 24 hidden)" + ); + } } From 6d0a389dd2e7600e3fab23f09bcc2c79b35d83de Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 7 Aug 2026 20:22:33 +0200 Subject: [PATCH 06/21] =?UTF-8?q?fix(client):=20the=20D3D11VA=20AV1=20rung?= =?UTF-8?q?=20decodes=20wrong=20pixels=20=E2=80=94=20the=20parity=20harnes?= =?UTF-8?q?s=20existed=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 25b2c6892..51e1c3841 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 04ad598e9..c1f0ee48b 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() { From 1c54d0999b25bc9cc0042f88ee30aa834d9c1801 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 7 Aug 2026 20:52:00 +0200 Subject: [PATCH 07/21] fix(client): the D3D11VA AV1 rung decoded every inter frame into a surface it was predicting from MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AV1 applies `refresh_frame_flags` AFTER the frame is decoded (7.20), so a frame that reads a reference slot and then overwrites it is the ORDINARY case, not an exotic one: 268 of the vendored vector's 274 frames do it, first at frame 6. `plan_to_dxva_av1` released every displaced picture inside the conversion — which is what the H.264 and H.265 siblings do with their whole `removed` list — and then assigned the decode target a slot. `SlotMap::assign` takes the lowest free slot, and the lowest free slot is the one just vacated. So the submission said `CurrPicTextureIndex = N` and `RefFrameMapTextureIndex[k] = N` in the same breath, on 268 of 274 frames: decode into the surface you predict from. Neither vendored H.264 nor H.265 vector ever produces that shape (measured: zero on 250 AUs), which is why an eager release survived two hardware-proven codecs and opened on the first AV1 frame past the key frame's neighbourhood. HEVC even has the invariant under test already — `the_current_picture_is_named_by_curr_pic_and_ never_aliases_a_reference` — and AV1 had nothing. The Vulkan rung already carries the fix; this is the same contract, and the DXVA constraint is the STRICTER of the two: Vulkan binds only the references a frame names, while `RefFrameMapTextureIndex` declares the whole store, so every picture the store still names has to survive the conversion. `DecodePlanDxvaAv1` grows `release_after_decode` and `frame_av1` applies it once the decode op is issued — next to the `refresh_frame_flags == 0` release that already waits for the same reason. Peak surfaces held goes 7 of the 9 the pool allocates, so the spare slot `SlotMap::new` adds is doing exactly the job it exists for. Measured on hardware before the fix: Intel Arc got 245 of 250 delivered frames wrong — 47% of luma at the first bad frame, max |delta| 242, chroma wrong too, a frame predicted from the wrong picture — and the only late frame it got right was the one intra frame, which names no reference and so could not alias. That reads as a `primary_ref_frame` defect and is not one: PRIMARY_REF_NONE and "has no references to alias" are the same frames. --- .../pf-client-core/src/video_d3d11_native.rs | 40 ++++ crates/pf-dxvadec/src/pic_av1.rs | 180 +++++++++++++++++- 2 files changed, 211 insertions(+), 9 deletions(-) diff --git a/crates/pf-client-core/src/video_d3d11_native.rs b/crates/pf-client-core/src/video_d3d11_native.rs index c1f0ee48b..84da49c14 100644 --- a/crates/pf-client-core/src/video_d3d11_native.rs +++ b/crates/pf-client-core/src/video_d3d11_native.rs @@ -184,6 +184,15 @@ struct Submission { /// other two codecs do not (see [`NativeD3d11Decoder::frame_av1`]). All three /// conversions produce it; dropping it here made the AV1 leak invisible. setup_id: u64, + /// AV1 only: surfaces this frame's own refresh displaces while the submission + /// still NAMES them, released once the decode op has been issued. + /// + /// See [`pf_dxvadec::DecodePlanDxvaAv1::release_after_decode`] — this is the + /// caller's half of that contract, and dropping it decodes 268 of the vendored + /// vector's 274 frames into a surface they predict from. Always empty on + /// H.264 and H.265, whose conversions release their whole `removed` list + /// themselves (neither vendored vector ever produces the shape). + release_after_decode: Vec, /// Which codec's slice-control record the packer's locations become. codec: Codec, /// What the hand-off needs to blit this picture. @@ -595,6 +604,26 @@ impl NativeD3d11Decoder { } }; + // The surfaces this frame's own refresh displaced while its submission still + // NAMED them (fn docs). Released here for the same reason the block below + // waits: the decode op has been issued, so nothing can be assigned them + // until the next frame — and on the `damaged` path there is no op at all, + // where dropping the release would leak a surface just the same. + if let Some(session) = self.session.as_mut() { + for &id in &sub.release_after_decode { + if !session.slots.release(id) { + // Never fatal, and never silent: a deferred id that holds no + // slot means the conversion and the ledger disagree about the + // store, which is a bug in one of them rather than a stream + // this frame can do anything about. + tracing::warn!( + id, + "AV1 deferred release named a picture holding no surface" + ); + } + } + } + // The slot nothing will ever ask for again (fn docs). Released AFTER the // blit above, so the surface is read before anything can be assigned it. if plan.header.refresh_frame_flags == 0 { @@ -642,6 +671,7 @@ impl NativeD3d11Decoder { slice_ranges: Vec::new(), setup_slot: dxva.setup_slot, setup_id: dxva.setup_id, + release_after_decode: dxva.release_after_decode, codec: Codec::Av1, facts: PictureFacts { colour: colour_of(plan.picture.colour), @@ -745,6 +775,12 @@ impl NativeD3d11Decoder { slice_ranges: dxva.slice_ranges, setup_slot: dxva.setup_slot, setup_id: dxva.setup_id, + // H.264's conversion releases its whole `removed` list itself: + // neither vendored vector ever has a picture the slices read + // being displaced by the same access unit (measured: zero on + // 250 AUs), and the sliding window evicts only pictures no + // slice names. + release_after_decode: Vec::new(), codec: Codec::H264, facts: PictureFacts { colour: colour_of(plan.picture.colour), @@ -804,6 +840,10 @@ impl NativeD3d11Decoder { slice_ranges: dxva.slice_ranges, setup_slot: dxva.setup_slot, setup_id: dxva.setup_id, + // As H.264 above: an HEVC picture's RPS is resolved against the + // DPB before any removal, and a picture the RPS names is by + // construction still in it. + release_after_decode: Vec::new(), codec: Codec::H265, facts: PictureFacts { colour: colour_of(plan.picture.colour), diff --git a/crates/pf-dxvadec/src/pic_av1.rs b/crates/pf-dxvadec/src/pic_av1.rs index d9614f480..66a693db4 100644 --- a/crates/pf-dxvadec/src/pic_av1.rs +++ b/crates/pf-dxvadec/src/pic_av1.rs @@ -122,6 +122,37 @@ pub struct DecodePlanDxvaAv1 { pub bitstream: Av1Bitstream, pub setup_slot: u8, pub setup_id: PicId, + /// Pictures this frame's own `refresh_frame_flags` displaces from the store + /// while THIS submission still NAMES them — their surfaces may not be recycled + /// until the decode op has been issued, and the caller owes exactly that. + /// + /// AV1 applies `refresh_frame_flags` AFTER the frame is decoded (7.20), so + /// `ref_frame_idx` resolves against the store as it stood BEFORE this frame and + /// a frame that reads a slot it then overwrites is the ORDINARY case, not an + /// exotic one: **268 of the vendored vector's 274 frames** do it, first at frame + /// 6. Releasing such a picture inside this conversion — which is what the H.264 + /// and H.265 siblings do with their whole `removed` list, and what this one did + /// until the parity harness caught it — hands its surface straight back to + /// [`Self::setup_slot`], because [`SlotMap::assign`] takes the lowest free slot + /// and the lowest free slot is the one just vacated. The submission then says + /// `CurrPicTextureIndex = N` and `RefFrameMapTextureIndex[k] = N` in the same + /// breath: decode into the surface you are predicting from. + /// + /// Neither vendored H.264 nor H.265 vector ever produces that shape (measured: + /// zero on the 250-AU clips), which is why the eager release survived two + /// hardware-proven codecs and opened on the first AV1 frame past the key frame's + /// neighbourhood. The Vulkan rung carries the same contract for the same reason + /// (`pf_vkdecode::pic_av1::DecodePlanVkAv1::release_after_decode`), and this + /// rung's constraint is the STRICTER of the two: Vulkan binds only the + /// references the frame names, while `RefFrameMapTextureIndex` declares the + /// whole store, so every picture the store still names has to survive — not just + /// the seven the frame reads. + /// + /// The ids are always a subset of the plan's `dpb.removed`, so applying them + /// completes that plan's bookkeeping and never invents a removal. Empty on the + /// overwhelming minority of frames that displace nothing they name; a caller + /// that drops them leaks a surface per frame and runs the ledger dry within ten. + pub release_after_decode: Vec, } /// Why a plan cannot be expressed as DXVA AV1 buffers. @@ -629,8 +660,22 @@ pub fn plan_to_dxva_av1( } // --- mutations, after every fallible step ----------------------------- + // ⚠ A picture this submission NAMES may be displaced by this same frame's + // refresh. Its surface is still in `ref_frame_map` above, so releasing it here + // would hand that very surface to `setup_slot` below and the frame would decode + // into a picture it predicts from. Held back for the caller instead — see + // `DecodePlanDxvaAv1::release_after_decode` for the measurement and for why + // this rung's test is `dpb_refs` (the whole store `RefFrameMapTextureIndex` + // declares) rather than the Vulkan rung's narrower `refs`. + let release_after_decode: Vec = plan + .dpb + .removed + .iter() + .copied() + .filter(|id| *id != setup_id && plan.dpb_refs.iter().any(|r| r.id == *id)) + .collect(); for &id in &plan.dpb.removed { - if id == setup_id { + if id == setup_id || release_after_decode.contains(&id) { continue; } let _ = slots.release(id); @@ -758,6 +803,7 @@ pub fn plan_to_dxva_av1( bitstream, setup_slot, setup_id, + release_after_decode, }) } @@ -781,6 +827,123 @@ mod tests { "../../pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1" ); + /// Convert one plan the way the RUNG must: the conversion, then the releases it + /// defers past the decode op ([`DecodePlanDxvaAv1::release_after_decode`]). + /// + /// Not a convenience — it is the caller's half of the contract, and the same + /// helper the Vulkan rung's tests carry for the same reason. A loop that + /// converts without it holds a surface on 268 of this vector's 274 frames and + /// runs the nine-slot ledger dry inside ten. + fn convert(au: &[u8], plan: &AuPlan, slots: &mut SlotMap) -> DecodePlanDxvaAv1 { + let dx = plan_to_dxva_av1(au, plan, slots).expect("the clean vector converts"); + for &id in &dx.release_after_decode { + assert!( + slots.release(id), + "a deferred release named picture {id}, which holds no surface" + ); + } + dx + } + + /// The decode target never shares a surface with a picture the submission names. + /// + /// The defect this pins is the one the Windows parity harness caught and nothing + /// on the CPU could see. AV1 applies `refresh_frame_flags` AFTER the frame is + /// decoded (7.20), so a frame that reads a slot it then overwrites is ordinary — + /// **268 of this vector's 274 frames**, first at frame 6 — and releasing the + /// displaced picture inside the conversion handed its surface straight to + /// `setup_slot`, because [`SlotMap::assign`] takes the lowest free slot and the + /// lowest free slot is the one just vacated. The submission then said + /// `CurrPicTextureIndex = N` and `RefFrameMapTextureIndex[k] = N` at once. + /// + /// Measured on hardware before the fix: Intel Arc decoded 245 of 250 delivered + /// frames wrong (47% of luma at the first bad frame, max |delta| 242, chroma + /// wrong too — a frame predicted from the wrong picture), and the only late + /// frame it got right was the one intra frame, which names no reference and so + /// could not alias. NVIDIA tolerated it. + /// + /// The assertion is against the WHOLE STORE, not just the seven names this frame + /// reads: `RefFrameMapTextureIndex` declares every occupied slot, so a driver is + /// entitled to consult one the frame never names. + #[test] + fn the_decode_target_never_aliases_a_surface_the_submission_names() { + let mut planner = Av1Planner::new(); + let mut slots = SlotMap::new(NUM_REF_SLOTS); + let (mut frames, mut deferring, mut deferred) = (0u32, 0u32, 0u32); + let mut peak = 0usize; + + for packet in IvfIterator::new(AV1_25FPS) { + for plan in planner.plan_au(packet).expect("the clean vector plans") { + if plan.dpb.stored.is_none() { + continue; + } + // Deliberately NOT `convert` — this test applies the deferred + // releases itself, after checking each one. + let dx = plan_to_dxva_av1(packet, &plan, &mut slots).expect("the vector converts"); + frames += 1; + peak = peak.max(slots.active()); + + // `#[repr(packed)]` — copy the fields out before reading them. + let curr = dx.pic_params.curr_pic_texture_index; + let store = dx.pic_params.ref_frame_map_texture_index; + assert!( + store.iter().all(|surface| *surface != curr), + "frame {frames}: surface {curr} is both CurrPicTextureIndex and a \ + RefFrameMapTextureIndex entry — the frame decodes into a picture \ + it predicts from" + ); + // Every deferred id is one this plan really removed AND the store + // really names — never an invented removal, never a live picture. + for &id in &dx.release_after_decode { + assert!( + plan.dpb.removed.contains(&id), + "frame {frames}: deferred picture {id} is not in this plan's \ + removed list" + ); + assert!( + plan.dpb_refs.iter().any(|r| r.id == id), + "frame {frames}: picture {id} is deferred without being named \ + by the store — only a picture the submission points at earns \ + the reprieve" + ); + assert!( + slots.slot_of(id).is_some(), + "frame {frames}: a deferred picture must still hold its surface" + ); + } + if !dx.release_after_decode.is_empty() { + deferring += 1; + deferred += dx.release_after_decode.len() as u32; + } + for &id in &dx.release_after_decode { + assert!(slots.release(id)); + } + } + } + + assert_eq!(frames, 274); + assert_eq!( + deferring, 268, + "268 of 274 frames of this vector displace a picture their own submission \ + names; at zero this test compares an empty list against itself and the \ + deferral could be deleted without a single assertion noticing" + ); + assert_eq!(deferred, 268, "one displaced picture per frame here"); + // The nine-slot ledger is `NUM_REF_SLOTS + 1`, and holding a displaced + // picture one frame longer is exactly what that spare is for — the pool is + // allocated `SlotMap::capacity()` surfaces (`pf_dxvadec::pool_size`), so a + // peak above it would be a submission naming a surface that does not exist. + assert!( + peak <= slots.capacity(), + "peak {peak} surfaces held exceeds the {} the pool allocates", + slots.capacity() + ); + eprintln!( + "frames {frames} · deferring {deferring} · peak surfaces held {peak}/{}", + slots.capacity() + ); + } + /// The whole vector, converted **and packed** — the closest a CPU gate gets to /// the hardware leg, and the test that would have caught the defect this /// module shipped with. @@ -815,7 +978,7 @@ mod tests { if plan.dpb.stored.is_none() { continue; } - let dx = plan_to_dxva_av1(packet, &plan, &mut slots).expect("converts"); + let dx = convert(packet, &plan, &mut slots); frames += 1; // Poison the mapping so a record can only be "right" by pointing // at bytes this pack actually wrote. @@ -949,8 +1112,7 @@ mod tests { index_by_surface_would_differ += 1; } } - let dx = - plan_to_dxva_av1(packet, &plan, &mut slots).expect("the clean vector converts"); + let dx = convert(packet, &plan, &mut slots); frames += 1; // Tile records must describe TILE PAYLOAD ranges inside the access @@ -1153,7 +1315,7 @@ mod tests { if plan.dpb.stored.is_none() { continue; } - let dx = plan_to_dxva_av1(packet, &plan, &mut slots).expect("converts"); + let dx = convert(packet, &plan, &mut slots); frames += 1; let lf = &plan.header.loop_filter_params; // `#[repr(packed)]` — copy the block out before reading its fields. @@ -1244,7 +1406,7 @@ mod tests { if plan.dpb.stored.is_none() { continue; } - let dx = plan_to_dxva_av1(packet, &plan, &mut slots).expect("converts"); + let dx = convert(packet, &plan, &mut slots); frames += 1; let raw = &plan.header.cdef_params; // `#[repr(packed)]` — copy the arrays out before indexing them. @@ -1335,7 +1497,7 @@ mod tests { if plan.dpb.stored.is_none() { continue; } - let dx = plan_to_dxva_av1(packet, &plan, &mut slots).expect("converts"); + let dx = convert(packet, &plan, &mut slots); frames += 1; let t = &plan.header.tile_info; // `#[repr(packed)]` — copy the block out before reading its arrays. @@ -1398,7 +1560,7 @@ mod tests { if plan.dpb.stored.is_none() { continue; } - let dx = plan_to_dxva_av1(packet, &plan, &mut slots).expect("converts"); + let dx = convert(packet, &plan, &mut slots); frames += 1; let pp = &dx.pic_params; let status = pp.status_report_feedback_number; @@ -1478,7 +1640,7 @@ mod tests { if plan.dpb.stored.is_none() { continue; } - let dx = plan_to_dxva_av1(packet, plan, &mut slots).expect("converts"); + let dx = convert(packet, plan, &mut slots); decoded.push((dx.setup_id, dx.setup_slot)); } if decoded.len() > 1 { From f4dda9074b1d677f15c0ff77c24d2c93b3c09bb9 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 7 Aug 2026 21:07:23 +0200 Subject: [PATCH 08/21] feat(dxvadec): the AV1 picparams harness AV1 forgot, and the D3D11VA AV1 rung is promoted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two halves. **The harness.** `libav_picparams_parity` covered H.264 and HEVC only, which is exactly the gap that let a wrong AV1 submission ship. It now plans, converts and packs all 274 frames of the vendored AV1 vector and checks what needs no capture: the three-buffer descriptor set with no quantization matrix (AV1's matrices are selected by index, so `dxva2_av1_end_frame` passes NULL/0 and there is no buffer to submit), no macroblock count anywhere, the 912-byte picture-parameter buffer, and the tile records — which unlike H.264/HEVC slice records do NOT abut, because a `DXVA_Tile_AV1` addresses a tile PAYLOAD and consecutive payloads are separated by their `tile_size_minus_1` fields. The one that matters most is `no_av1_submission_names_its_decode_surface_in_the_ reference_store`: the invariant the previous commit fixed, over the submitted BYTES rather than over the plan. libavcodec cannot produce that shape — it fills `RefFrameMapTextureIndex` from the pre-refresh store and takes `CurrPicTextureIndex` from a frame the reference update has not run on — which is the argument for calling it a defect rather than a convention. `AV1_FIELDS` reaches into the eight nested blocks (`tiles.widths`, `segmentation.feature_data`, …) so a future capture reports a field and not "260 bytes of tiles differ"; `field_table!` grew nested-path support for it. The `#[ignore]`d `our_av1_picture_parameters_match_libavcodecs` and the capture recipe are in place, and `the_dump_and_the_parser_agree…` now self-compares AV1 too. ⚠ NO libavcodec AV1 capture was taken and the module docs say so rather than leaving an absent result to be read as a pass: `.221` has no MSYS2, no gcc and no make, so a patched FFmpeg there is a toolchain bring-up, not a build. Everything this file claims about libavcodec's AV1 side is READ out of `dxva2_av1.c` (n8.1). That reading did turn up one live divergence, recorded at `pic_av1.rs`'s `pp.width` and deliberately NOT changed: libavcodec sends `avctx->width`, which is FrameWidth (pre-superres), where this crate sends UpscaledWidth. The two are equal whenever superres is off, which is every stream that exists here, so the 250/250 result says nothing either way and a blind change would be unmeasured. **The promotion.** `(D3d11va, CODEC_AV1)` is `verified` — 250/250 delivered frames bit-identical to libavcodec on an RTX 3500 Ada AND an Intel Arc. All three places move together: the evidence arm, the module table and `every_rung_runs_and_the_unproven_ones_are_named`, whose `unproven` array loses the pair and whose proven list gains it. ⚠ This changes rung SELECTION, not just a label. `verified` is what lets `auto` pick D3D11VA ahead of Vulkan Video, so Windows Intel and unknown-vendor boxes — where the ladder is `native-d3d11va → native-vk → sw` — now decode AV1 on D3D11VA where they previously fell to Vulkan. Taken deliberately: ~10x the Vulkan leg's speed, and the parity that promoted it was measured on an Intel Arc, which is the vendor family the change moves. Still no soak on the goldens, and the notes say so. Also: `frame_av1` holds the decode's `Result` instead of `?`-ing it, so both slot releases run on the failure path. `decode_av1` notes an error and keeps the session rather than rebuilding the slot map, so an early return leaked a surface per failed frame and hit `SlotError::Full` after nine. --- crates/pf-client-core/src/video.rs | 106 ++- .../pf-client-core/src/video_d3d11_native.rs | 143 ++-- crates/pf-dxvadec/src/pic_av1.rs | 14 + .../tests/libav_picparams_parity.rs | 618 +++++++++++++++++- 4 files changed, 763 insertions(+), 118 deletions(-) diff --git a/crates/pf-client-core/src/video.rs b/crates/pf-client-core/src/video.rs index 51e1c3841..40c1ddc75 100644 --- a/crates/pf-client-core/src/video.rs +++ b/crates/pf-client-core/src/video.rs @@ -49,7 +49,7 @@ //! | 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), 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 D3D11VA | | AV1 | **yes** — 250/250 delivered frames bit-identical to libavcodec on an RTX 3500 Ada AND an Intel Arc (2026-08-07). It got there from 186/250 and 245/250 DIVERGING frames on those same two GPUs: `plan_to_dxva_av1` released the picture this frame's own refresh displaces before assigning the decode target its slot, and `SlotMap::assign` hands back the slot just vacated — so 268 of the vector's 274 frames named one surface as both `CurrPicTextureIndex` and a `RefFrameMapTextureIndex` entry. Intel followed the aliased surface (structurally wrong from display frame 4); NVIDIA tolerated it until the `order_hint` wrap at 64 made one 16x24 luma block depend on it. ONE defect, two driver tolerances — the two unlike signatures were not two bugs. TWO vendors, still NO soak on the goldens: the 5-minute 4K60 soak this row used to cite measured throughput, and "streams cleanly" was true throughout the failure | //! | 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`] | @@ -1111,46 +1111,38 @@ 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)", ), - // 2026-08-07: this pair was PARITY-CHECKED for the first time, and it FAILED. + // 2026-08-07: this pair failed its first parity check and now PASSES it, on both of + // the box's GPUs, after one defect was fixed. // - // 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. + // The failure was 186/250 diverging display frames on an RTX 3500 Ada and 245/250 on + // an Intel Arc, deterministic on three runs each. The two signatures looked like two + // defects — NVIDIA bit-exact through display frame 63 and then one 16x24 luma block + // (max |delta| 8, chroma untouched) at the frame whose `order_hint` first reaches 64; + // Intel structurally wrong from display frame 4 (47% of luma, max |delta| 242, chroma + // wrong too) with only its one PRIMARY_REF_NONE frame right. They were ONE defect and + // two driver tolerances. // - // 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. + // `plan_to_dxva_av1` released the pictures this frame's own `refresh_frame_flags` + // displaces INSIDE the conversion, then assigned the decode target a slot — and + // `SlotMap::assign` takes the lowest free slot, which is the one just vacated. So the + // submission named the same surface as `CurrPicTextureIndex` and as a + // `RefFrameMapTextureIndex` entry, on 268 of the vector's 274 frames: decode into the + // surface you are predicting from. AV1 applies `refresh_frame_flags` AFTER the frame + // is decoded (7.20), so that shape is ordinary rather than exotic; neither vendored + // H.264 nor H.265 vector ever produces it, which is why an eager release survived two + // hardware-proven codecs. Intel followed the aliased surface, NVIDIA tolerated it + // until the order-hint wrap made one block's prediction depend on it. `pf_dxvadec`'s + // `the_decode_target_never_aliases_a_surface_the_submission_names` is the CPU guard. // - // 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. + // ⚠ What this pair still does NOT have, unlike the H.264/HEVC one above: a soak on + // the goldens. The 5-minute 4K60 soak this note used to lean on measured throughput, + // not pixels, and "streams cleanly" is exactly what was true while 186 frames were + // wrong. (NativeRung::D3d11va, CODEC_AV1) => ( - false, - "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)", + true, + "250/250 delivered frames bit-identical to libavcodec on an RTX 3500 Ada AND an \ + Intel Arc (2026-08-07), after fixing a decode target that aliased a reference \ + surface on 268 of 274 frames - two vendors, no soak (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 @@ -3397,20 +3389,20 @@ mod tests { /// read against. Which of them `auto` may pick FIRST is /// [`native_rung_admitted`]'s decision, asserted in the test after this one. /// - /// `(D3d11va, AV1)` stays here after 2026-08-07 even though it has now decoded on - /// hardware, and its warn line is why: it named the rung as unproven moments before that - /// rung failed 72 access units running, which is exactly the job this test protects. (The - /// cause was the HOST shipping half of every AV1 frame — `pf_encode`'s - /// `resolve_split_subframe` — not the rung.) Unproven is about EVIDENCE, not about whether - /// it has ever worked: one 25-second session with no parity check and no soak must not - /// promote a rung past Vulkan Video in the admission filter. + /// `(D3d11va, AV1)` LEFT this list on 2026-08-07, and the bar it had to clear is the + /// point. It had already decoded on hardware twice over — a 25-second session and a + /// 5-minute 4K60 soak on two GPUs — and it stayed unproven through both, because + /// unproven is about EVIDENCE and neither run looked at a pixel. What moved it was the + /// frame-hash parity check: 250/250 delivered frames bit-identical to libavcodec on an + /// RTX 3500 Ada AND an Intel Arc. Its first run of that check FAILED on both (186/250 and + /// 245/250 diverging), while the rung streamed 4K60 the whole time — so the warn line + /// this test protects was telling the truth right up to the run that retired it. #[test] fn every_rung_runs_and_the_unproven_ones_are_named() { let unproven = [ (NativeRung::Vaapi, CODEC_H264), (NativeRung::Vaapi, CODEC_HEVC), (NativeRung::Vaapi, CODEC_AV1), - (NativeRung::D3d11va, CODEC_AV1), ]; for (rung, codec) in unproven { let e = native_evidence(rung, codec); @@ -3425,8 +3417,7 @@ mod tests { "{} / {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 (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 {:?}", + promote it (VAAPI AV1, which HAS decoded), got {:?}", rung.name(), e.note ); @@ -3439,6 +3430,7 @@ mod tests { (NativeRung::Vulkan, CODEC_AV1), (NativeRung::D3d11va, CODEC_H264), (NativeRung::D3d11va, CODEC_HEVC), + (NativeRung::D3d11va, CODEC_AV1), ] { assert!(native_evidence(rung, codec).verified, "{}", rung.name()); } @@ -3491,6 +3483,15 @@ mod tests { (NativeRung::Vulkan, CODEC_AV1), (NativeRung::D3d11va, CODEC_H264), (NativeRung::D3d11va, CODEC_HEVC), + // Joined this list on 2026-08-07 with 250/250 on two vendors. ⚠ It is a + // BEHAVIOUR change on real machines and not only a label: this pair was the + // one the filter barred under `Some(Vulkan)`, so on Windows Intel and + // unknown-vendor boxes — where the ladder is `native-d3d11va → native-vk → sw` + // — `auto` now decodes AV1 on D3D11VA where it previously fell to Vulkan + // Video. Taken deliberately: the rung is ~10x the Vulkan leg's speed, and the + // parity that promoted it was measured on an Intel Arc, which is exactly the + // vendor family that change moves. + (NativeRung::D3d11va, CODEC_AV1), ] { for below in [ None, @@ -3505,17 +3506,6 @@ mod tests { ); } } - // Windows, Intel/unknown auto: the DXVA AV1 leg has never run, and the ladder - // passes `None` there on purpose — that vendor family is the one with a measured - // wrong-pixel report against Vulkan decode, so what is really below it is the CPU. - // This asserts the ARGUMENT the call site passes, which is where the judgement - // lives; `Some(Vulkan)` would bar it, and that is deliberately not what it passes. - assert!(native_rung_admitted(NativeRung::D3d11va, CODEC_AV1, None)); - assert!(!native_rung_admitted( - NativeRung::D3d11va, - CODEC_AV1, - Some(NativeRung::Vulkan) - )); // The CPU rung is last everywhere, so nothing is ever below it and it always runs // — including for a codec it has no decoder for, which is `last_rung_verdict`'s // problem and not the filter's. diff --git a/crates/pf-client-core/src/video_d3d11_native.rs b/crates/pf-client-core/src/video_d3d11_native.rs index 84da49c14..2380b7633 100644 --- a/crates/pf-client-core/src/video_d3d11_native.rs +++ b/crates/pf-client-core/src/video_d3d11_native.rs @@ -20,17 +20,21 @@ //! * **H.264 and H.265** — frame-hash parity against libavcodec on an RTX 4090 and an AMD //! 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. +//! * **AV1** — wired in M7, and frame-hash parity on the SAME two GPUs since 2026-08-07: +//! 250/250 delivered frames bit-identical to libavcodec on the RTX 3500 Ada and on the +//! Intel Arc. It streams 4K60 on both with a clean 5-minute soak, but that is throughput +//! and not pixels — the leg streamed exactly as cleanly while 186 and 245 of those 250 +//! frames were WRONG, which is what the first run of this harness measured on 2026-08-07 +//! and what `av1_divergence_map` (below) records. The defect was one line of DPB +//! bookkeeping in [`pf_dxvadec::plan_to_dxva_av1`]: it released the picture this frame's +//! own `refresh_frame_flags` displaces before assigning the decode target a slot, and +//! `SlotMap::assign` hands back the slot just vacated, so 268 of the vector's 274 frames +//! named one surface as both `CurrPicTextureIndex` and a `RefFrameMapTextureIndex` entry. +//! [`NativeD3d11Decoder::frame_av1`] now applies the conversion's +//! `release_after_decode` once the decode op is issued. //! -//! 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. +//! ⚠ Still no SOAK on the goldens, so this leg's evidence is one 250-frame vector on two +//! vendors — narrower than the H.264/H.265 legs above. //! //! 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. @@ -572,43 +576,19 @@ impl NativeD3d11Decoder { return self.show_existing_av1(plan); } let sub = self.plan_frame_av1(au, plan)?; - let shown = if damaged { - // Converted (so the slot map stayed in step with the planner's store), - // deliberately not submitted (fn docs). - // - // ⚠ And the surface's `held` entry is CLEARED rather than left. The slot - // map now says this slot holds THIS picture, while the surface still - // carries whatever the previous occupant decoded; a later - // `show_existing_frame` naming it would find the old picture's facts and - // blit the old picture's pixels. `None` makes that path return - // `Ok(None)` — nothing shown — which is what the unit's concealment - // already asked for. - if let Some(session) = self.session.as_mut() { - if let Some(held) = session.held.get_mut(usize::from(sub.setup_slot)) { - *held = None; - } - } - None - } else { - self.decode_into(au, &sub)?; - if let Some(session) = self.session.as_mut() { - // What this surface now holds, for a later `show_existing_frame`. - if let Some(held) = session.held.get_mut(usize::from(sub.setup_slot)) { - *held = Some(sub.facts); - } - } - if sub.show { - Some(self.present(sub.setup_slot, sub.facts)?) - } else { - None - } - }; + // ⚠ The decode's `Result` is held rather than `?`-ed, so that the two slot + // releases below run on the FAILURE path too. `decode_av1` treats an error + // here as a health note and keeps the session — it does not rebuild the slot + // map — so an early return would leak a surface per failed frame and reach + // `SlotError::Full` after nine, which is a session that dies of an error it + // had already recovered from. + let shown = self.decode_and_present_av1(au, &sub, damaged); // The surfaces this frame's own refresh displaced while its submission still // NAMED them (fn docs). Released here for the same reason the block below // waits: the decode op has been issued, so nothing can be assigned them - // until the next frame — and on the `damaged` path there is no op at all, - // where dropping the release would leak a surface just the same. + // until the next frame — and on the `damaged` and failed paths there is no + // op at all, where dropping the release would leak a surface just the same. if let Some(session) = self.session.as_mut() { for &id in &sub.release_after_decode { if !session.slots.release(id) { @@ -640,6 +620,49 @@ impl NativeD3d11Decoder { } } } + shown + } + + /// Submit one converted AV1 frame and blit it if it displays — the part of + /// [`Self::frame_av1`] that can fail, split out so its caller can run the slot + /// releases on the failure path as well as on the two clean ones. + fn decode_and_present_av1( + &mut self, + au: &[u8], + sub: &Submission, + damaged: bool, + ) -> Result> { + let shown = if damaged { + // Converted (so the slot map stayed in step with the planner's store), + // deliberately not submitted (fn docs). + // + // ⚠ And the surface's `held` entry is CLEARED rather than left. The slot + // map now says this slot holds THIS picture, while the surface still + // carries whatever the previous occupant decoded; a later + // `show_existing_frame` naming it would find the old picture's facts and + // blit the old picture's pixels. `None` makes that path return + // `Ok(None)` — nothing shown — which is what the unit's concealment + // already asked for. + if let Some(session) = self.session.as_mut() { + if let Some(held) = session.held.get_mut(usize::from(sub.setup_slot)) { + *held = None; + } + } + None + } else { + self.decode_into(au, sub)?; + if let Some(session) = self.session.as_mut() { + // What this surface now holds, for a later `show_existing_frame`. + if let Some(held) = session.held.get_mut(usize::from(sub.setup_slot)) { + *held = Some(sub.facts); + } + } + if sub.show { + Some(self.present(sub.setup_slot, sub.facts)?) + } else { + None + } + }; Ok(shown) } @@ -2280,25 +2303,39 @@ mod parity { /// 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: + /// [`av1_every_delivered_frame_hashes_bit_identical_to_libavcodec`] FAILED on both + /// GPUs of `.221` the first time it was ever run, 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 + /// of the remaining 186 diverged. The first bad frame was the one whose + /// `order_hint` first reaches **64**, and its error was 174 luma pixels in a + /// single 16x24 block (max |delta| 8, chroma untouched) which then propagated /// 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 + /// * **Intel Arc** — only display frames 0, 1, 2, 3 and 10 were bit-identical, and + /// the divergence was 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. + /// Both were deterministic — three runs each, identical first-divergent frame and + /// identical hashes — so neither was a race against the decode queue. + /// + /// **⚠ Both were ONE defect, and the two unlike signatures argued for two.** The + /// submission named a single surface as `CurrPicTextureIndex` and as a + /// `RefFrameMapTextureIndex` entry on 268 of the vector's 274 frames — decode into + /// the picture you predict from — because [`pf_dxvadec::plan_to_dxva_av1`] released + /// the displaced reference before assigning the decode target its slot. Intel + /// followed the aliased surface immediately; NVIDIA tolerated it until the order-hint + /// wrap put one block's prediction on the far side of it. Fixing that one thing took + /// BOTH vendors to 250/250. Two readings this map invited and that were wrong: + /// "`primary_ref_frame` or its resolution" (Intel's one correct late frame is + /// PRIMARY_REF_NONE **because** it is the intra frame, which names no reference and + /// so cannot alias) and "motion-field projection at the `get_relative_dist` sign + /// flip" (the wrap is where an already-aliased surface first mattered on NVIDIA, not + /// what was wrong). Read a signature as evidence about WHERE, not about WHAT. /// /// 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 diff --git a/crates/pf-dxvadec/src/pic_av1.rs b/crates/pf-dxvadec/src/pic_av1.rs index 66a693db4..923bd848c 100644 --- a/crates/pf-dxvadec/src/pic_av1.rs +++ b/crates/pf-dxvadec/src/pic_av1.rs @@ -687,6 +687,20 @@ pub fn plan_to_dxva_av1( let color = &seq.color_config; let mut pic_params = PicParamsAv1::zeroed(); + // ⚠ UPSCALED width, where libavcodec sends the CODED one — a divergence that is + // inert on every stream that exists here and is written down rather than + // "fixed" because nothing can measure it. + // + // `dxva2_av1.c` sends `avctx->width`, and `update_context_with_frame_header` + // sets that from `frame_width_minus_1 + 1` — FrameWidth, the pre-superres coded + // width. The same goes for `frame_refs[i].width`, which libav reads off the + // reference's `AVFrame`. With superres OFF the two are equal by definition + // (7.20: `UpscaledWidth = FrameWidth` when `use_superres` is 0), which is every + // frame of both vendored vectors and every frame a punktfunk host emits — no + // encoder in this program codes superres. So the 250/250 parity result on two + // vendors says nothing either way about which is right, and changing it would + // be an unmeasured change to a rung that is finally proven. Revisit with a + // superres vector and a driver-by-driver measurement, not by reading. pic_params.width = h.upscaled_width; pic_params.height = h.frame_height; pic_params.max_width = u32::from(seq.max_frame_width_minus_1) + 1; diff --git a/crates/pf-dxvadec/tests/libav_picparams_parity.rs b/crates/pf-dxvadec/tests/libav_picparams_parity.rs index f5b770e95..e063c7ec4 100644 --- a/crates/pf-dxvadec/tests/libav_picparams_parity.rs +++ b/crates/pf-dxvadec/tests/libav_picparams_parity.rs @@ -145,6 +145,47 @@ //! while every other byte still looks right. If the macro is spelled differently in the tree, //! any expression yielding the negotiated config's `ConfigBitstreamRaw` will do. //! +//! **5b. AV1.** The identical `PFPP` block goes at the very END of +//! `ff_dxva2_av1_fill_picture_parameters` (`dxva2_av1.c:60`), with `h264` replaced by `av1` — +//! after the film-grain block, so every field is final. Three things differ from the two codecs +//! above and each of them changes what a capture MEANS: +//! +//! * **The AU index is a FRAME, not a temporal unit.** `ff_dxva2_common_end_frame` runs once per +//! submitted picture and an AV1 temporal unit may decode several, so `pf_au_index` walks +//! decoded frames. This crate's [`our_av1_submissions`] emits one entry per decoded frame for +//! the same reason, and the vendored vector is **274** frames in 250 units — a capture with +//! 250 `PFPP av1` lines is a capture of something else. (A `show_existing_frame` unit submits +//! nothing on either side; this vector has none.) +//! * **No `PFQM` line and no matrix buffer.** `dxva2_av1_end_frame` passes `NULL, 0` for the qm +//! pair, so the `qm_size > 0` branch of the block in step 4 logs `absent` on every frame. That +//! is the expected reading, not a missed patch site. +//! * **No `PFCFG` check.** [`preflight`]'s `ConfigBitstreamRaw` assertion is about the two short +//! slice-control formats; AV1's slice-control record is `DXVA_Tile_AV1` and has no short/long +//! pair, so a captured `PFCFG av1` line is ignored rather than compared. +//! +//! The stream is the vendored IVF at +//! `crates/pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1`: +//! +//! ```text +//! ffmpeg -hwaccel d3d11va -hwaccel_output_format d3d11 -i test-25fps.ivf.av1 -f null - 2> av1.log +//! grep -oE 'PF(PP|QM|BD|CFG) .*' av1.log > libav-av1.capture +//! ``` +//! +//! ⚠ Add `-export_side_data +film_grain` to NOTHING: `apply_grain` and `pp->coding.film_grain` +//! both turn OFF when film grain is exported as side data, and this crate always applies it in +//! the decoder. The vendored vector codes no grain either way. +//! +//! **No such capture has been taken.** As of 2026-08-07 the AV1 comparison +//! (`our_av1_picture_parameters_match_libavcodecs`) has never run against libavcodec's bytes, +//! and the reason is stated rather than left as an absent result: `.221`, the only box in this +//! fleet with a D3D11VA GPU to spare, has no MSYS2, no gcc and no make, so producing a patched +//! FFmpeg there is a toolchain bring-up rather than a build. What DID localise the AV1 defect +//! of 2026-08-07 was `video_d3d11_native`'s frame-hash parity harness plus a CPU invariant +//! (`no_av1_submission_names_its_decode_surface_in_the_reference_store`, below) — so this file's +//! AV1 half is currently the no-capture half only, and every claim it makes about libavcodec's +//! AV1 side is READ out of `dxva2_av1.c` (n8.1) rather than measured. Tier one, in the +//! provenance section's terms, for all of it. +//! //! **6. Run it.** `--enable-d3d11va` is on by default on Windows. Decode the SAME elementary //! streams this test plans — the vendored vectors, in the repository at //! `crates/pf-bitstream/vendor/cros-codecs/src/codec/{h264,h265}/test_data/test-25fps.{h264,h265}`: @@ -167,6 +208,7 @@ //! //! ```text //! PF_LIBAV_CAPTURE_H264=libav-h264.capture PF_LIBAV_CAPTURE_HEVC=libav-hevc.capture \ +//! PF_LIBAV_CAPTURE_AV1=libav-av1.capture \ //! cargo test -p pf-dxvadec --test libav_picparams_parity -- --ignored --nocapture //! ``` //! @@ -352,13 +394,19 @@ use pf_dxvadec::dxva::QmatrixHevc; use pf_dxvadec::dxva::SliceH264Short; use pf_dxvadec::dxva::SliceHevcShort; use pf_dxvadec::dxva::UNUSED_ENTRY; +use pf_dxvadec::dxva_av1::PicEntryAv1; +use pf_dxvadec::dxva_av1::UNUSED_INDEX; use pf_dxvadec::AuPlan; +use pf_dxvadec::Av1Planner; use pf_dxvadec::BufferDescriptor; use pf_dxvadec::Codec; use pf_dxvadec::H264Planner; use pf_dxvadec::H265Planner; +use pf_dxvadec::PicParamsAv1; use pf_dxvadec::SliceRecord; use pf_dxvadec::SlotMap; +use pf_dxvadec::TileAv1; +use pf_dxvadec::NUM_REF_SLOTS; const TEST_25FPS_H264: &[u8] = include_bytes!( "../../pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264" @@ -366,6 +414,11 @@ const TEST_25FPS_H264: &[u8] = include_bytes!( const TEST_25FPS_H265: &[u8] = include_bytes!( "../../pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265" ); +/// The AV1 vector is an IVF container, and its unit of comparison is the TEMPORAL UNIT rather +/// than the access unit: one IVF packet may decode several frames, of which at most one shows. +const TEST_25FPS_AV1: &[u8] = include_bytes!( + "../../pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1" +); /// Both vendored vectors carry exactly this many access units — pf-bitstream's own golden, and /// the number of `PFPP` lines a valid capture holds. @@ -453,8 +506,12 @@ struct OurSubmission { qmatrix: Option>, /// The descriptor set, in submission order. descriptors: Vec, - /// The packer's slice records, for the internal-consistency checks. + /// The packer's slice records, for the internal-consistency checks. Empty on AV1, whose + /// slice-control buffer holds [`Self::tiles`] instead. records: Vec, + /// AV1's slice-control records — one `DXVA_Tile_AV1` per TILE, not per tile group. Empty + /// on H.264 and H.265. + tiles: Vec, /// Bytes the packer wrote BEFORE the tail padding. unpadded: u32, /// `mb_width * mb_height` (H.264) or 0 (HEVC) — the value the descriptors must carry. @@ -491,6 +548,7 @@ fn our_h264_submissions() -> Vec { qmatrix: Some(pf_dxvadec::as_bytes(&dxva.qmatrix).to_vec()), descriptors: pf_dxvadec::descriptors_h264(&dxva, &packed), records: packed.records, + tiles: Vec::new(), unpadded, mb_count: dxva.mb_count, }); @@ -527,6 +585,7 @@ fn our_hevc_submissions() -> Vec { .map(|qm| pf_dxvadec::as_bytes(qm).to_vec()), descriptors: pf_dxvadec::descriptors_h265(&dxva, &packed), records: packed.records, + tiles: Vec::new(), unpadded, mb_count: 0, }); @@ -535,6 +594,85 @@ fn our_hevc_submissions() -> Vec { out } +/// Every AV1 FRAME the vendored vector decodes — 274, of which 250 are displayed. +/// +/// The unit of comparison is the frame and not the temporal unit, because that is what +/// libavcodec's hwaccel counts: `ff_dxva2_common_end_frame` runs once per submitted PICTURE, so +/// a capture's AU index walks decoded frames. A `show_existing_frame` unit submits nothing and +/// appears on neither side; this vector has none. +const VENDORED_AV1_FRAMES: usize = 274; + +/// Plan, convert and pack the whole vendored AV1 vector, one entry per decoded FRAME. +/// +/// ⚠ This is the only one of the three that has to speak the conversion's DEFERRED RELEASE +/// contract ([`pf_dxvadec::DecodePlanDxvaAv1::release_after_decode`]). A loop that converts +/// without it holds a surface on 268 of these 274 frames and runs the nine-slot ledger dry +/// inside ten — and, worse for a harness, it would compare a submission built by a caller that +/// is not the rung. +fn our_av1_submissions() -> Vec { + let mut planner = Av1Planner::new(); + let mut slots = SlotMap::new(NUM_REF_SLOTS); + let mut mapping = vec![0u8; MAPPING_BYTES]; + let mut out = Vec::new(); + for (i, unit) in split_ivf(TEST_25FPS_AV1).into_iter().enumerate() { + let plans = planner + .plan_au(unit) + .unwrap_or_else(|e| panic!("unit {i} of the vendored AV1 vector must plan: {e}")); + for plan in &plans { + if plan.dpb.stored.is_none() { + continue; // `show_existing_frame`: no submission at all + } + let dxva = pf_dxvadec::plan_to_dxva_av1(unit, plan, &mut slots) + .unwrap_or_else(|e| panic!("unit {i} must convert: {e}")); + let packed = pf_dxvadec::pack_av1(unit, &dxva.bitstream, &dxva.tiles, &mut mapping) + .unwrap_or_else(|e| panic!("unit {i} must pack: {e}")); + let unpadded = pf_dxvadec::packed_size_av1(&dxva.bitstream) as u32; + out.push(OurSubmission { + pic_params: pf_dxvadec::as_bytes(&dxva.pic_params).to_vec(), + // AV1 transmits no quantization matrix at all: its matrices are SELECTED by + // index out of tables the decoder already has, and `dxva2_av1_end_frame` + // passes `NULL, 0` for the pair. `None` here is a fact about the codec, not a + // condition on the stream the way HEVC's is. + qmatrix: None, + descriptors: pf_dxvadec::descriptors_av1(&packed), + // AV1's slice-control records are `DXVA_Tile_AV1`, a different struct with a + // different size; the shared `SliceRecord` checks do not apply to them, and + // the tile records get their own test rather than a coerced one. + records: Vec::new(), + tiles: packed.tiles.clone(), + unpadded, + mb_count: 0, + }); + for &id in &dxva.release_after_decode { + assert!( + slots.release(id), + "unit {i}: a deferred release named a picture holding no surface" + ); + } + } + } + assert_eq!(out.len(), VENDORED_AV1_FRAMES); + out +} + +/// The IVF frame walk — the same one `video_d3d11_native`'s parity module and every AV1 test in +/// this program use: a 32-byte file header, then a 12-byte header per packet carrying its size. +fn split_ivf(stream: &[u8]) -> Vec<&[u8]> { + let mut out = Vec::new(); + let mut at = 32usize; + while at + 12 <= stream.len() { + let size = u32::from_le_bytes([stream[at], stream[at + 1], stream[at + 2], stream[at + 3]]) + as usize; + at += 12; + if at + size > stream.len() { + break; + } + out.push(&stream[at..at + size]); + at += size; + } + out +} + // --------------------------------------------------------------------------- // Offset → field name // --------------------------------------------------------------------------- @@ -543,9 +681,12 @@ fn our_hevc_submissions() -> Vec { /// order, built from the field IDENTIFIERS so a name and the offset it reports cannot drift /// apart — the whole point of the table is to turn a differing byte into a field name, and a /// table with a copy-pasted mismatch would name the wrong one. +/// Nested paths (`tiles.cols`) are accepted as well as plain identifiers, and are named by +/// the whole path — AV1's picture parameters are eight nested blocks, and a table that could +/// only reach the outer members would report "segmentation differs" for a 140-byte struct. macro_rules! field_table { - ($ty:ty, $($field:ident),+ $(,)?) => { - &[$((stringify!($field), offset_of!($ty, $field))),+] + ($ty:ty, $($($field:ident).+),+ $(,)?) => { + &[$((stringify!($($field).+), offset_of!($ty, $($field).+))),+] }; } @@ -670,6 +811,87 @@ const HEVC_QMATRIX_FIELDS: &[(&str, usize)] = field_table!( ucScalingListDCCoefSizeID3, ); +/// Every field of `DXVA_PicParams_AV1`, same construction — but reaching INTO the eight nested +/// blocks, because they are where AV1 keeps almost the whole frame header. `tiles` alone is 260 +/// bytes and `segmentation` 140; a table stopping at the outer members would turn every finding +/// in them into one useless name. +/// +/// Three members stay whole on purpose. `frame_refs` is seven 36-byte entries which — unlike +/// the other two codecs' reference arrays — carry NO surface index and so compare byte for +/// byte: `Index` is `ref_frame_idx[name]`, an AV1 SLOT both sides read out of the same frame +/// header. `ref_frame_map_texture_index` is the surface array, which cannot be compared by +/// value at all ([`av1_reference_store`]). And `film_grain` is 158 bytes neither vendored +/// vector codes. +const AV1_FIELDS: &[(&str, usize)] = field_table!( + PicParamsAv1, + width, + height, + max_width, + max_height, + curr_pic_texture_index, + superres_denom, + bitdepth, + seq_profile, + tiles.cols, + tiles.rows, + tiles.context_update_id, + tiles.widths, + tiles.heights, + coding, + format, + primary_ref_frame, + order_hint, + order_hint_bits, + frame_refs, + ref_frame_map_texture_index, + loop_filter.filter_level, + loop_filter.filter_level_u, + loop_filter.filter_level_v, + loop_filter.sharpness_level, + loop_filter.control_flags, + loop_filter.ref_deltas, + loop_filter.mode_deltas, + loop_filter.delta_lf_res, + loop_filter.frame_restoration_type, + loop_filter.log2_restoration_unit_size, + loop_filter.reserved16, + quantization.control_flags, + quantization.base_qindex, + quantization.y_dc_delta_q, + quantization.u_dc_delta_q, + quantization.v_dc_delta_q, + quantization.u_ac_delta_q, + quantization.v_ac_delta_q, + quantization.qm_y, + quantization.qm_u, + quantization.qm_v, + quantization.reserved16, + cdef.control_flags, + cdef.y_strengths, + cdef.uv_strengths, + interp_filter, + segmentation.control_flags, + segmentation.reserved24, + segmentation.feature_mask, + segmentation.feature_data, + film_grain, + reserved32, + status_report_feedback_number, +); + +/// `DXVA_Tile_AV1` — AV1's slice-control record, and the one this crate had to derive rather +/// than measure (`dxva.h` declares it; the SIZE is what the descriptor states). +const AV1_TILE_FIELDS: &[(&str, usize)] = field_table!( + TileAv1, + data_offset, + data_size, + row, + column, + reserved16, + anchor_frame, + reserved8, +); + /// Turn a field table into `(name, byte range)`, the last field running to `total`. fn field_ranges( fields: &[(&'static str, usize)], @@ -1038,6 +1260,37 @@ fn hevc_ref_entries(pp: &[u8]) -> Vec<(u8, RefEntry)> { .collect() } +/// One side's AV1 reference store, read out of the SUBMITTED BYTES: `(CurrPicTextureIndex, +/// RefFrameMapTextureIndex[8], frame_refs[name].Index for the seven names)`. +/// +/// AV1's reference numbering is two arrays that mean different things at once and the split is +/// exactly what a comparison has to respect. `frame_refs[i].Index` is an AV1 reference SLOT — +/// `ref_frame_idx[i]`, which both sides read out of the same frame header — so it is a VALUE +/// that must match libavcodec's exactly, and it is compared as part of the `frame_refs` field. +/// `RefFrameMapTextureIndex[slot]` and `CurrPicTextureIndex` are SURFACES, which come from each +/// side's own pool and are only ever a bijection. +/// +/// So the store is compared as a SHAPE: which slots are occupied, and whether the decode target +/// collides with any of them. +fn av1_reference_store(pp: &[u8]) -> (u8, [u8; 8], [u8; 7]) { + let curr = pp[offset_of!(PicParamsAv1, curr_pic_texture_index)]; + let mut store = [UNUSED_INDEX; 8]; + let base = offset_of!(PicParamsAv1, ref_frame_map_texture_index); + store.copy_from_slice(&pp[base..base + 8]); + let mut names = [UNUSED_INDEX; 7]; + // The stride and the member offset come from the TYPE, never from the two numbers + // `dxva_av1.rs` measured (36 and 33). Those are pinned there as compile-time assertions + // against the Windows SDK's own header, and re-typing them here would be a second copy that + // can drift from the first — which for a reader of this array is the difference between a + // reference slot and a warp coefficient. + for (name, slot) in names.iter_mut().enumerate() { + *slot = pp[offset_of!(PicParamsAv1, frame_refs) + + name * size_of::() + + offset_of!(PicEntryAv1, index)]; + } + (curr, store, names) +} + /// The surface mapping between the two sides, tracked per PICTURE. /// /// A global index-to-index bijection over a whole stream is the wrong model: both sides reuse a @@ -1287,10 +1540,17 @@ fn preflight(capture: &Capture, ours: usize, codec: &str, reserved16: Option Codec::H264, - _ => Codec::H265, - }); + // + // ⚠ AV1 is exempt, and not because the check is inconvenient: `ConfigBitstreamRaw`'s short + // format is a property of the two SHORT SLICE-CONTROL structs, and AV1's slice-control + // record is `DXVA_Tile_AV1`, which has no short/long pair for a config to select between. + // Comparing an AV1 capture's number against HEVC's 1 — which a `_ =>` arm would do — is a + // check of nothing that fails on anything. + let want = match codec { + "h264" => pf_dxvadec::short_slice_config(Codec::H264), + "hevc" => pf_dxvadec::short_slice_config(Codec::H265), + _ => return, + }; for (au, &raw) in &capture.config_bitstream_raw { assert_eq!( raw, want, @@ -1568,6 +1828,108 @@ fn hevc_rps_pictures(pp: &[u8], array: usize, entries: &[(u8, RefEntry)]) -> Vec .collect() } +/// The whole AV1 picture-parameter comparison. +/// +/// Structurally simpler than the other two and the reason is worth stating: AV1 puts NO surface +/// index in its reference entries. `frame_refs[i].Index` is `ref_frame_idx[i]`, an AV1 SLOT both +/// sides read out of the same frame header, so the seven 36-byte entries — sizes, warp +/// parameters, warp type and slot alike — compare byte for byte with no re-indexing, no set +/// comparison and no allowance. Only two members carry surfaces, and they are handled as the +/// SHAPE of the store rather than by value ([`av1_reference_store`]). +/// +/// There is no POC base to derive either: AV1's `order_hint` is a coded field, not a decoder's +/// running count, so libavcodec has nothing to seed it with. +/// +/// ⚠ **`width`/`height` is a divergence waiting to be measured, and this comparison will +/// report it rather than absorb it.** libavcodec sends `avctx->width`, which +/// `update_context_with_frame_header` sets from `frame_width_minus_1 + 1` — FrameWidth, the +/// PRE-superres coded width — and the same for `frame_refs[i].width` off the reference's +/// `AVFrame`; this crate sends `UpscaledWidth`. With superres off the two are equal by +/// definition (7.20), which is every frame of the vendored vector and every frame a punktfunk +/// host emits, so a capture made from this vector cannot tell them apart. Deliberately given no +/// allowance: if a superres capture ever reaches this harness, the difference must be a finding +/// somebody reads, not a line somebody already excused. See `pic_av1.rs`'s note at `pp.width`. +fn compare_av1_picparams(ours: &[OurSubmission], capture: &Capture) -> Findings { + let ranges = field_ranges(AV1_FIELDS, size_of::()); + // The two surface arrays, and nothing else: every other byte of this struct is a fact about + // the bitstream that both sides derive from the same frame header. + let structural = ["curr_pic_texture_index", "ref_frame_map_texture_index"]; + let mut findings = Findings::default(); + for (au, sub) in ours.iter().enumerate() { + let Some(theirs) = capture.pic_params.get(&au) else { + findings.note( + "", + au, + "the capture holds no PFPP line for this frame", + ); + continue; + }; + if theirs.len() != sub.pic_params.len() { + findings.note( + "", + au, + format!( + "the capture's picture parameters are {} bytes and ours are {}", + theirs.len(), + sub.pic_params.len() + ), + ); + continue; + } + compare_scalars( + au, + &sub.pic_params, + theirs, + &ranges, + &structural, + no_allowance, + &mut findings, + ); + + // The store, as a shape. Which SLOTS hold a picture is a fact about the bitstream and + // must agree; which SURFACE each holds is each side's own pool and never can. + let (our_curr, our_store, _) = av1_reference_store(&sub.pic_params); + let (their_curr, their_store, _) = av1_reference_store(theirs); + for slot in 0..8 { + let ours_occupied = our_store[slot] != UNUSED_INDEX; + let theirs_occupied = their_store[slot] != UNUSED_INDEX; + if ours_occupied != theirs_occupied { + findings.note( + format!("ref_frame_map_texture_index[{slot}][occupied]"), + au, + format!("ours {ours_occupied}, libav {theirs_occupied}"), + ); + } + } + // The decode target must hold no store entry's surface. libavcodec cannot produce a + // collision — it fills the store from `h->ref[i]`, which the reference update has not + // run on yet, and takes `CurrPicTextureIndex` from `h->cur_frame.f` — so a collision on + // our side is a defect however the surfaces are numbered. This is the check that names + // the 2026-08-07 defect. + // + // ⚠ Note what is deliberately NOT checked: that two slots hold different surfaces. One + // picture in several reference slots is ordinary AV1 and this very vector does it — + // the key frame sits in BWDREF and ALTREF2 for the stream's whole length — so a + // "duplicate surface" check would fire on 273 of 274 frames of a correct conversion. + for (label, curr, store) in [ + ("ours", our_curr, our_store), + ("libav", their_curr, their_store), + ] { + if store.iter().any(|surface| *surface == curr) { + findings.note( + "curr_pic_texture_index[aliases the store]", + au, + format!( + "{label}: surface {curr} is both the decode target and a reference \ + store entry — the frame decodes into a picture it predicts from" + ), + ); + } + } + } + findings +} + /// The whole HEVC picture-parameter comparison. fn compare_hevc_picparams(ours: &[OurSubmission], capture: &Capture) -> Findings { let ranges = field_ranges(HEVC_FIELDS, size_of::()); @@ -1970,6 +2332,13 @@ fn every_hand_declared_dxva_struct_is_tiled_exactly_by_its_fields() { HEVC_SLICE_FIELDS, size_of::(), ), + // AV1's two. `PicParamsAv1` is the one struct in this crate whose offsets were + // MEASURED rather than mirrored — `layout-probe-av1.c` compiled with MSVC against the + // Windows SDK's own `dxva.h` — and `dxva_av1.rs` pins every one at compile time. What + // this adds is the other half: that the TABLE above reaches all 912 bytes, so a + // capture comparison can name every one of them. + ("PicParamsAv1", AV1_FIELDS, size_of::()), + ("TileAv1", AV1_TILE_FIELDS, size_of::()), ] { assert_eq!(fields[0].1, 0, "{what}: the first field must start at 0"); let ranges = field_ranges(fields, total); @@ -2006,6 +2375,196 @@ fn every_h264_au_submits_four_buffers_in_libavcodecs_order() { } } +/// **No AV1 submission names its decode surface anywhere in the reference store**, and every +/// reference NAME resolves through a slot that holds one. +/// +/// This is the defect the Windows parity harness caught on 2026-08-07 and the one nothing on +/// the CPU could see, stated over the SUBMITTED BYTES — which is where a libavcodec capture +/// would see it too, and the reason it belongs in this file as well as in `pic_av1`'s own +/// tests. `plan_to_dxva_av1` released the picture this frame's own `refresh_frame_flags` +/// displaces before assigning the decode target a slot, and `SlotMap::assign` hands back the +/// slot just vacated — so `CurrPicTextureIndex` and one `RefFrameMapTextureIndex` entry were +/// the same surface on 268 of these 274 frames: decode into the picture you predict from. +/// Intel Arc followed the aliased surface and got 245 of 250 delivered frames wrong; NVIDIA +/// tolerated it for 63 frames and then lost one 16x24 luma block at the `order_hint` wrap. +/// +/// libavcodec cannot produce this shape and that is the whole argument for calling it a defect +/// rather than a convention: `ff_dxva2_av1_fill_picture_parameters` fills +/// `RefFrameMapTextureIndex` from `h->ref[i]`, the pre-refresh store, and takes +/// `CurrPicTextureIndex` from `h->cur_frame.f`, a frame the reference-frame update has not run +/// on yet. The two cannot be one surface. +/// +/// The counts are asserted, not printed. At zero references this test would pass against a +/// conversion that named nothing at all. +#[test] +fn no_av1_submission_names_its_decode_surface_in_the_reference_store() { + let subs = our_av1_submissions(); + let (mut with_store, mut named_refs) = (0usize, 0usize); + for (frame, sub) in subs.iter().enumerate() { + let (curr, store, names) = av1_reference_store(&sub.pic_params); + assert!( + store.iter().all(|surface| *surface != curr), + "frame {frame}: surface {curr} is both CurrPicTextureIndex and a \ + RefFrameMapTextureIndex entry" + ); + if store.iter().any(|s| *s != UNUSED_INDEX) { + with_store += 1; + } + for (name, slot) in names.iter().enumerate() { + if *slot == UNUSED_INDEX { + continue; + } + named_refs += 1; + assert!( + usize::from(*slot) < store.len(), + "frame {frame}, reference name {name}: slot {slot} is outside the eight-entry \ + store — `Index` is an AV1 reference SLOT, not a surface" + ); + assert_ne!( + store[usize::from(*slot)], + UNUSED_INDEX, + "frame {frame}, reference name {name}: slot {slot} holds no surface, so the \ + driver would follow `Index` into an empty entry" + ); + } + } + assert_eq!( + with_store, 273, + "every frame but the opening key frame carries a populated reference store" + ); + assert!( + named_refs > 0, + "no frame named a reference, so every check above was skipped" + ); +} + +/// AV1 submits THREE buffers and never a quantization matrix, on every frame of the vector. +/// +/// The codec asymmetry the H.264 and HEVC tests above are about, taken to its third case. +/// H.264 submits the matrix unconditionally, HEVC only under `scaling_list_enabled_flag`, and +/// AV1 has no matrix BUFFER at all: its quantiser matrices are SELECTED by index +/// (`qm_y`/`qm_u`/`qm_v`) out of tables the decoder already holds, and `dxva2_av1_end_frame` +/// passes `NULL, 0` for the qm pair so the generic layer submits nothing. A fourth descriptor +/// here would be a buffer the driver has no `DXVA_Qmatrix_AV1` to read it as. +#[test] +fn every_av1_frame_submits_three_buffers_and_never_a_quantization_matrix() { + for (frame, sub) in our_av1_submissions().iter().enumerate() { + assert_eq!( + sub.descriptors + .iter() + .map(|d| d.buffer_type) + .collect::>(), + vec![ + BUFFER_PICTURE_PARAMETERS, + BUFFER_BITSTREAM, + BUFFER_SLICE_CONTROL, + ], + "frame {frame}" + ); + assert!(sub.qmatrix.is_none(), "frame {frame}"); + } +} + +/// No AV1 descriptor carries a macroblock count. AV1 has no macroblocks and `dxva2_av1.c` +/// never touches the field — the same statement `no_hevc_descriptor_ever_carries_a_macroblock_count` +/// makes, and the same defect class review 13 found on the H.264 side in the other direction. +#[test] +fn no_av1_descriptor_ever_carries_a_macroblock_count() { + for (frame, sub) in our_av1_submissions().iter().enumerate() { + assert_eq!(sub.mb_count, 0, "frame {frame}"); + for desc in &sub.descriptors { + assert_eq!( + desc.num_mbs_in_buffer, + 0, + "frame {frame}, {}", + buffer_name(desc.buffer_type) + ); + } + } +} + +/// AV1's slice-control buffer is `16 * tile count`, its bitstream descriptor is the packer's +/// PADDED size, and the tile records tile the unpadded window exactly — in order, without gaps +/// and without overlaps. +/// +/// The last part is what distinguishes AV1 from the other two codecs here and is the reason +/// this cannot reuse `the_bitstream_descriptor_is_the_packers_padded_size_and_the_slice_records_tile_it_exactly`: +/// a `DXVA_Tile_AV1` addresses a TILE PAYLOAD, which is the bytes after that tile's +/// `tile_size_minus_1` field — so consecutive records are separated by those size fields and do +/// NOT abut, unlike H.264/HEVC slice records which tile their buffer with no gaps. What must +/// hold is weaker and still exact: strictly increasing, non-overlapping, inside the unpadded +/// window, and never starting at a tile-group OBU's first byte (which would hand the driver an +/// OBU header as entropy-coded tile data). +/// +/// ⚠ The padding is charged to NO record. H.264 and HEVC add the tail padding to their last +/// slice record's `SliceBytesInBuffer`; `pack_av1` does not, because a tile's size is the +/// tile's, and the descriptor is the only place AV1's padding is accounted at all. +#[test] +fn the_av1_bitstream_descriptor_is_padded_and_the_tile_records_tile_it_without_overlapping() { + let mut frames_with_padding = 0usize; + for (frame, sub) in our_av1_submissions().iter().enumerate() { + let bitstream = sub + .descriptors + .iter() + .find(|d| d.buffer_type == BUFFER_BITSTREAM) + .unwrap_or_else(|| panic!("frame {frame} submits no bitstream buffer")); + assert_eq!( + bitstream.data_size % 128, + 0, + "frame {frame}: the bitstream descriptor states the PADDED size" + ); + // ⚠ `1..=128`, not `0..128`. `pack_av1` writes libavcodec's expression verbatim — + // `BITSTREAM_ALIGN - (cursor % BITSTREAM_ALIGN)` — so data that is ALREADY on the + // granule gets a whole 128-byte block rather than none (`pack_av1`'s + // `data_already_on_the_granule_still_gets_a_full_padding_block`). This vector never + // lands on the granule, which is exactly why the bound has to come from the rule and + // not from the measurement. + let padding = bitstream.data_size - sub.unpadded; + assert!( + (1..=128).contains(&padding), + "frame {frame}: {padding} bytes of padding" + ); + frames_with_padding += 1; + + let slice_control = sub + .descriptors + .iter() + .find(|d| d.buffer_type == BUFFER_SLICE_CONTROL) + .unwrap_or_else(|| panic!("frame {frame} submits no slice-control buffer")); + assert_eq!( + slice_control.data_size as usize, + size_of::() * sub.tiles.len(), + "frame {frame}: sixteen bytes per TILE" + ); + assert!(!sub.tiles.is_empty(), "frame {frame}: a frame has tiles"); + + let mut previous_end = 0u32; + for (i, tile) in sub.tiles.iter().enumerate() { + // `#[repr(packed)]` — copy the fields out before using them. + let (offset, size) = (tile.data_offset, tile.data_size); + assert!(size > 0, "frame {frame}, tile {i}: an empty tile payload"); + assert!( + offset >= previous_end, + "frame {frame}, tile {i}: starts at {offset}, inside the previous tile which \ + ends at {previous_end}" + ); + assert!( + offset + size <= sub.unpadded, + "frame {frame}, tile {i}: runs past the bytes the packer wrote" + ); + previous_end = offset + size; + assert_eq!( + tile.anchor_frame, UNUSED_INDEX, + "frame {frame}, tile {i}: large-scale-tile anchors are libavcodec's 0xFF" + ); + } + } + assert_eq!( + frames_with_padding, VENDORED_AV1_FRAMES, + "every frame is padded — the rule is unconditional" + ); +} + #[test] fn every_h264_bitstream_and_slice_control_descriptor_carries_mb_width_times_mb_height() { // Review 13's defect, over the whole vector: `NumMBsInBuffer` was 0 where libavcodec's @@ -2281,6 +2840,7 @@ fn the_picture_parameter_buffer_is_the_whole_hand_declared_struct_for_both_codec for (codec, subs, size) in [ ("h264", our_h264_submissions(), size_of::()), ("hevc", our_hevc_submissions(), size_of::()), + ("av1", our_av1_submissions(), size_of::()), ] { for (au, sub) in subs.iter().enumerate() { assert_eq!(sub.pic_params.len(), size, "{codec} AU {au}"); @@ -2347,6 +2907,7 @@ fn hevc_case(enabled: bool, sps_coded: Option, pps_coded: Option) -> Our .map(|qm| pf_dxvadec::as_bytes(qm).to_vec()), descriptors: pf_dxvadec::descriptors_h265(&dxva, &packed), records: packed.records, + tiles: Vec::new(), unpadded, mb_count: 0, } @@ -2508,6 +3069,35 @@ fn the_dump_and_the_parser_agree_and_the_comparison_finds_nothing_against_oursel // The HEVC matrices are `absent` on this vector, and the parser must carry that fact rather // than losing it — the whole of review 13's defect is the difference between the two. assert!(capture.qmatrix.values().all(Option::is_none)); + + // AV1, on all 274 FRAMES. Nothing here has ever been run against libavcodec's own bytes + // (module docs say why), so this self-comparison is the only thing standing between + // `compare_av1_picparams` and a first capture: it proves the 912-byte field table reaches + // every byte, that `av1_reference_store` reads `CurrPicTextureIndex` and the eight-entry + // store from the offsets it thinks it does — a wrong one would report a false alias on a + // correct submission — and that the comparison invents nothing on identical input. + let ours = our_av1_submissions(); + let capture = parse_capture(&dump("av1", &ours), "av1"); + preflight(&capture, ours.len(), "av1", None); + assert_eq!(capture.pic_params.len(), VENDORED_AV1_FRAMES); + for findings in [ + compare_av1_picparams(&ours, &capture), + compare_descriptors(&ours, &capture), + ] { + assert!( + findings.is_empty(), + "comparing our own AV1 bytes against themselves must find nothing, got {:?}", + findings.fields() + ); + assert!( + findings.documented_fields().is_empty(), + "identical bytes documented a divergence: {:?}", + findings.documented_fields() + ); + } + // AV1 reports `absent` on every frame — it has no matrix BUFFER at all, unlike HEVC where + // the same spelling is a per-sequence decision. + assert!(capture.qmatrix.values().all(Option::is_none)); } /// A submission holding only what [`compare_descriptors`] reads, for the bitstream-size @@ -2527,6 +3117,7 @@ fn descriptor_only_submission(unpadded: u32, padded: u32, slices: usize) -> OurS OurSubmission { pic_params: vec![0u8; size_of::()], qmatrix: None, + tiles: Vec::new(), descriptors: vec![ BufferDescriptor { buffer_type: BUFFER_BITSTREAM, @@ -2698,6 +3289,7 @@ fn the_hevc_tiles_flag_allowance_is_exactly_bit_ten_with_tiles_disabled_and_noth qmatrix: sub.qmatrix.clone(), descriptors: sub.descriptors.clone(), records: sub.records.clone(), + tiles: sub.tiles.clone(), unpadded: sub.unpadded, mb_count: sub.mb_count, } @@ -3080,6 +3672,18 @@ fn renumber_h264_surfaces(pp: &[u8], f: impl Fn(u8) -> u8) -> Vec { /// Emit this crate's whole submission — both codecs — in the capture's own format, so the two /// files can be diffed by any tool without a capture at all. +#[test] +#[ignore = "needs a libavcodec capture: PF_LIBAV_CAPTURE_AV1= (see the module docs)"] +fn our_av1_picture_parameters_match_libavcodecs() { + let capture = capture_from_env("PF_LIBAV_CAPTURE_AV1", "av1") + .expect("PF_LIBAV_CAPTURE_AV1= names a capture (see the module docs)"); + let ours = our_av1_submissions(); + // No `Reserved16Bits` preflight: both libavcodec workarounds are H.264-only + // (`dxva2_h264.c`), and `DXVA_PicParams_AV1` has no such field to test. + preflight(&capture, ours.len(), "av1", None); + compare_av1_picparams(&ours, &capture).verdict("AV1 picture parameters", ours.len()); +} + #[test] #[ignore = "writes a dump: PF_DXVA_DUMP="] fn dump_our_submission_in_the_captures_own_format() { From a29e366b3ef7401d1cba1cf9c010e707fde12f0a Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 7 Aug 2026 21:07:48 +0200 Subject: [PATCH 09/21] =?UTF-8?q?feat(vaapi):=20VAAPI=20decodes=20H.264,?= =?UTF-8?q?=20H.265=20and=20Main=2010=20=E2=80=94=20their=20first=20frames?= =?UTF-8?q?=20on=20any=20hardware?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The evidence table said these legs "have still never decoded a frame anywhere", and VAAPI is the rung every Linux AMD/Intel client lands on. They have now decoded, on `.25` (Radeon 780M / Phoenix1, RDNA3, radeonsi, Mesa 26.0.3, VA-API 1.23, /dev/dri/renderD128): H.264 250/250 access units accepted, 225 frames delivered, NV12 H.265 250/250 accepted, 204 delivered, NV12 HEVC Main 10 50/50 accepted, 45 delivered, P010 (AV1, unchanged: 250/250 accepted, 250 delivered, NV12) all on the same tiled AMD modifier (0x200000010401b04). Not one access unit of any vector was refused. Three `#[ignore]`d legs modelled on the AV1 one, plus the Annex-B access-unit splitters they need — ported verbatim from `video_d3d11_native`'s test module so the two platform rungs are driven over the same access units rather than over two splitters free to disagree. Main 10 earns a third leg rather than a variation on the second: ten bits is a different VAAPI profile, a different render-target format and a different surface fourcc, and that leg's fourcc assertion is the only thing that would catch a driver quietly handing back NV12 for a ten-bit stream. This is NOT frame-hash parity, and the doc comments say so rather than letting the test names imply it. The Vulkan and D3D11VA legs hash every frame against libavcodec because both can read their decoded surface back; this rung exports a DRM-PRIME dmabuf whose memory the driver tiles, so there is no CPU-readable image to hash without a `vaDeriveImage`/`vaGetImage` path production neither uses nor wants. What these legs prove is that every access unit is accepted, that the expected number of frames comes back, and that each one is a real exported surface of the right shape and fourcc — enough to turn "never decoded a frame anywhere" into a measurement, not enough to promote the rung to `verified`. Two findings the run surfaced, neither of which bites punktfunk's own streams: * The delivered counts are 225/204/45, not 250/250/50, and that is the RUNG, not the driver. `finish` shows `outputs.last()` and never more, so an access unit whose plan bumps several pictures out of the DPB displays the last and drops the rest — 18 dropped at the H.264 vector's three draining IDRs, 45 on the H.265 vector's 45 two-picture bumps — and there is no end-of-stream flush. Hosts emit zero-reorder low-delay output with no B pictures, so `outputs` never holds more than one picture in the field. A CPU-only test derives all three counts from the planner alone, on any Linux box with no GPU, so they stay explanations rather than recordings. * `DmabufFrame::keyframe` labels the ACCESS UNIT, not the picture delivered: `finish` is handed the current AU's `is_idr`. On a reordering stream the IDR is bumped out several access units after it decoded and arrives flagged `false`, while the access unit that drains the DPB at a later IDR flags whichever old picture it displays as a keyframe. That flag is `DecodedImage::is_keyframe`, the pump's post-loss re-anchor signal. Asserted so that fixing it is noticed, not so that it is preserved. Gates, all run on `.25` (this rung only compiles on Linux): `cargo fmt --all -- --check`; `cargo clippy -p pf-client-core --all-targets --features sdl3/build-from-source -- -D warnings`; `cargo test -p pf-client-core --lib --features sdl3/build-from-source` (169 passed); the same filtered to video_vaapi_native with `--include-ignored` (16 passed). Plus the pf-lxcheck2 container's workspace-wide `cargo fmt --all -- --check` and `cargo clippy --workspace --all-targets -- -D warnings`, both clean. The evidence table in `video.rs` still says these legs have never decoded a frame. It is being edited concurrently, so its replacement row is handed over rather than raced for here. --- .../pf-client-core/src/video_vaapi_native.rs | 468 ++++++++++++++++++ 1 file changed, 468 insertions(+) diff --git a/crates/pf-client-core/src/video_vaapi_native.rs b/crates/pf-client-core/src/video_vaapi_native.rs index cd0a725a3..ee09fe6df 100644 --- a/crates/pf-client-core/src/video_vaapi_native.rs +++ b/crates/pf-client-core/src/video_vaapi_native.rs @@ -2548,4 +2548,472 @@ mod tests { "the vector displays 250 frames (274 coded, 24 hidden)" ); } + + // --------------------------------------------------------------------- + // H.264 / H.265 — the two legs that had never decoded a frame anywhere + // --------------------------------------------------------------------- + + /// The vendored H.264 vector: **250 access units** of 320x240 High 4:2:0 8-bit, + /// TWO slice NALUs per picture (500 slice NALs over 250 AUs, 4 IDRs). The same + /// file, at the same relative path, that `pf-vkdecode`'s Vulkan legs and + /// `video_d3d11_native`'s D3D11VA leg decode — so a count that disagrees with + /// theirs is this rung's problem rather than the vector's. + const H264_25FPS: &[u8] = include_bytes!( + "../../pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264" + ); + + /// The vendored H.265 twin: 250 access units, 320x240 Main 8-bit 4:2:0, ONE slice + /// per picture, one `IDR_N_LP` then 249 TRAIL pictures. + const H265_25FPS: &[u8] = include_bytes!( + "../../pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265" + ); + + /// HEVC **Main 10**: 50 access units of 320x240 4:2:0 ten-bit, from libx265. + /// + /// Worth a third leg rather than a variation on the second because ten bits is a + /// different VAAPI PROFILE (`VAProfileHEVCMain10`), a different render-target + /// format (`VA_RT_FORMAT_YUV420_10`) and a different surface fourcc (**P010**, not + /// NV12) — three branches of `Session::build` that no 8-bit leg reaches, on the + /// path every HDR session takes. `finish` refuses a surface whose exported fourcc + /// is not the one the pool was created with, so this leg is also the only thing + /// that would catch a driver quietly handing back NV12 for a ten-bit stream. + const MAIN10_H265: &[u8] = include_bytes!("../../pf-vkdecode/tests/data/test-main10.h265"); + + /// Both 8-bit vectors are 250 access units. + const H26X_AU_COUNT: usize = 250; + /// The Main 10 vector is 50. + const MAIN10_AU_COUNT: usize = 50; + + /// How many frames each vector can yield THROUGH THIS RUNG — which is not how many + /// frames it contains, and the gap is a property of the rung worth stating once + /// here rather than three times below. + /// + /// [`finish`] shows `outputs.last()` and never more: one frame per access unit, at + /// most. So an access unit whose plan bumps SEVERAL pictures out of the DPB — which + /// is what an IDR with `no_output_of_prior_pics_flag` clear does, and what ordinary + /// B-pyramid reordering does at every other picture — displays the last of them and + /// drops the rest, and an access unit whose plan outputs nothing yet displays + /// nothing. There is no end-of-stream flush either, so whatever is still in the DPB + /// when the vector ends never comes out. + /// + /// Measured on `.25` and reproduced exactly by + /// [`the_planner_already_says_how_many_frames_these_legs_can_deliver`], which is + /// what keeps these three numbers explanations rather than recordings: + /// + /// | vector | pictures the planner outputs | this rung delivers | dropped | + /// |---|---|---|---| + /// | H.264 | 243 (7 stranded in the DPB) | **225** | 18, at the 3 IDRs that drain the DPB | + /// | H.265 | 249 (1 stranded) | **204** | 45, one on each of the 45 AUs that bump two | + /// | Main 10 | 48 (2 stranded) | **45** | 3, likewise | + /// + /// ⚠ This does NOT bite punktfunk's own streams and is not what these legs exist to + /// find: hosts emit zero-reorder low-delay output with no B pictures, so `outputs` + /// never holds more than one picture and the rung is exact. It is the same + /// divergence `video_d3d11_native`'s parity module records for the D3D11VA rung, + /// and it is written down here for the same reason — a conformance vector reorders, + /// punktfunk does not, and a reader comparing 225 against "250 frames" needs to + /// know which of the two they are looking at. + const H264_DELIVERED: usize = 225; + const H265_DELIVERED: usize = 204; + const MAIN10_DELIVERED: usize = 45; + + /// Byte offsets of every Annex-B NAL header in `stream`, in order. + /// + /// Emulation prevention guarantees `00 00 01` cannot appear inside a NAL payload, + /// so scanning for it finds start codes and nothing else; the header begins on the + /// byte after. Hand-rolled for the same reason [`split_ivf`] is — `pf-client-core` + /// does not depend on the vendored parser crate — and a VERBATIM port of + /// `video_d3d11_native`'s, so the two platform rungs are driven over the same + /// access units rather than over two splitters free to disagree. Kept honest by + /// the AU counts [`the_annex_b_splitters_still_cut_the_vendored_vectors`] asserts + /// on every ordinary Linux test run, which no plausible splitter bug survives. + fn nal_headers(stream: &[u8]) -> Vec { + let mut out = Vec::new(); + let mut i = 0usize; + while i + 3 <= stream.len() { + if stream[i..i + 3] == [0x00, 0x00, 0x01] { + out.push(i + 3); + i += 3; + } else { + i += 1; + } + } + out + } + + /// Split `stream` into access units, given a per-NAL `(is_slice, starts_a_picture)` + /// rule. A new AU begins at a non-VCL NALU following slices, or at a slice that + /// declares itself the first of a picture when the current AU already has slices — + /// the same rule pf-bitstream applies, spelled once for both codecs. + fn split_aus(stream: &[u8], classify: impl Fn(&[u8], usize) -> (bool, bool)) -> Vec<&[u8]> { + let mut aus = Vec::new(); + let mut au_start = 0usize; + let mut au_has_slice = false; + for header in nal_headers(stream) { + let (is_slice, first_in_picture) = classify(stream, header); + // The start code owning this header: three bytes, plus the optional + // leading zero byte of the four-byte form. + let mut start = header - 3; + if start > 0 && stream[start - 1] == 0x00 { + start -= 1; + } + if au_has_slice && (!is_slice || first_in_picture) { + aus.push(&stream[au_start..start]); + au_start = start; + au_has_slice = false; + } + au_has_slice |= is_slice; + } + aus.push(&stream[au_start..]); + aus + } + + /// H.264: one-byte NAL header, `nal_unit_type` in the low 5 bits (1 = non-IDR + /// slice, 5 = IDR slice), and `first_mb_in_slice == 0` is the top bit of the byte + /// after it. Load-bearing rather than decorative on this vector: it codes two + /// slices per picture, so without the flag every picture would be split in two. + fn split_h264_aus(stream: &[u8]) -> Vec<&[u8]> { + split_aus(stream, |s, h| { + let is_slice = matches!(s[h] & 0x1f, 1 | 5); + let first = is_slice && s.get(h + 1).is_some_and(|b| b & 0x80 != 0); + (is_slice, first) + }) + } + + /// H.265: TWO-byte NAL header, `nal_unit_type` in bits 1..7 of the first byte and + /// "is a slice" the numeric range `< 32`, so `first_slice_segment_in_pic_flag` is + /// the top bit of the byte at `+2` where H.264 reads `+1`. Getting either wrong + /// silently merges or splits AUs, which surfaces as a frame-count mismatch a long + /// way from its cause. + fn split_h265_aus(stream: &[u8]) -> Vec<&[u8]> { + split_aus(stream, |s, h| { + let is_slice = (s[h] >> 1) & 0x3f < 32; + let first = is_slice && s.get(h + 2).is_some_and(|b| b & 0x80 != 0); + (is_slice, first) + }) + } + + /// The splitters cut both vendored vectors into the access units every other rung's + /// legs count, and the Main 10 vector really is ten-bit. + /// + /// NOT `#[ignore]`d, unlike everything below it: the splitters are pure CPU, they + /// are a hand-rolled copy of code that lives in two other crates, and a drift in + /// them would reach the hardware legs as a frame-count mismatch on a box someone + /// had to walk to. Ordinary `cargo test -p pf-client-core --lib` catches it here + /// instead. + /// + /// The ten-bit check is the same guard `video_d3d11_native` carries and for the + /// same reason: a regenerated vector that came out 8-bit would turn + /// [`hevc_main10_decodes_the_ten_bit_vector_on_this_machines_vaapi`] into a second + /// run of the 8-bit path wearing a ten-bit name, and it would pass. + #[test] + fn the_annex_b_splitters_still_cut_the_vendored_vectors() { + assert_eq!( + split_h264_aus(H264_25FPS).len(), + H26X_AU_COUNT, + "H.264 vector access units" + ); + assert_eq!( + split_h265_aus(H265_25FPS).len(), + H26X_AU_COUNT, + "H.265 vector access units" + ); + + let main10 = split_h265_aus(MAIN10_H265); + assert_eq!(main10.len(), MAIN10_AU_COUNT, "Main 10 vector access units"); + + let mut planner = pf_vaadec::H265Planner::new(); + let plan = planner + .plan_au(main10[0]) + .expect("the Main 10 vector's first access unit must plan"); + assert_eq!( + ( + plan.picture.chroma_format_idc, + plan.picture.bit_depth_luma_minus8 + ), + (1, 2), + "the Main 10 vector must be 4:2:0 at ten bits" + ); + } + + /// Access units whose plan outputs at least one picture — one delivered frame each, + /// and the ONLY thing that separates [`H264_DELIVERED`] and friends from the + /// vectors' frame counts. + /// + /// Two small walks rather than one generic one because the two planners share no + /// trait: `AuPlan` and `AuPlanH265` are different types with the same `dpb.outputs` + /// field, which is exactly the shape a macro would obscure for six saved lines. + fn output_bearing_aus_h264(aus: &[&[u8]]) -> usize { + let mut planner = pf_vaadec::H264Planner::new(); + aus.iter() + .filter(|au| { + !planner + .plan_au(au) + .expect("the vendored H.264 vector plans") + .dpb + .outputs + .is_empty() + }) + .count() + } + + /// [`output_bearing_aus_h264`] for HEVC. A skipped RASL picture counts as no + /// output, which is what the rung does with it too ([`NativeVaapiDecoder::decode`]). + fn output_bearing_aus_h265(aus: &[&[u8]]) -> usize { + let mut planner = pf_vaadec::H265Planner::new(); + aus.iter() + .filter(|au| match planner.plan_au(au) { + Ok(plan) => !plan.dpb.outputs.is_empty(), + Err(pf_vaadec::PlanErrorH265::RaslSkipped { .. }) => false, + Err(e) => panic!("the vendored HEVC vector must plan: {e:?}"), + }) + .count() + } + + /// The three delivered-frame counts the hardware legs assert are what the PLANNER + /// implies, not what a hardware run happened to print. + /// + /// This is the difference between a number that explains itself and a number + /// somebody wrote down: it runs on any Linux box, with no GPU and no libva, and it + /// fails the moment a vector is regenerated or the planner's bumping changes — + /// which would otherwise show up as three mysterious hardware failures on a machine + /// somebody had to walk to. See [`H264_DELIVERED`] for why the counts are below the + /// vectors' frame counts at all. + #[test] + fn the_planner_already_says_how_many_frames_these_legs_can_deliver() { + assert_eq!( + output_bearing_aus_h264(&split_h264_aus(H264_25FPS)), + H264_DELIVERED, + "H.264: access units whose plan outputs a picture" + ); + assert_eq!( + output_bearing_aus_h265(&split_h265_aus(H265_25FPS)), + H265_DELIVERED, + "H.265: access units whose plan outputs a picture" + ); + assert_eq!( + output_bearing_aus_h265(&split_h265_aus(MAIN10_H265)), + MAIN10_DELIVERED, + "Main 10: access units whose plan outputs a picture" + ); + } + + /// The first frame a leg got back — enough to say the rung exported a real surface + /// of the right shape and pixel format, which is the most it can honestly claim. + #[derive(Clone, Copy)] + struct FirstFrame { + width: u32, + height: u32, + fourcc: u32, + modifier: u64, + keyframe: bool, + } + + /// Drive one Annex-B vector's access units through a freshly built rung and report + /// how many frames came back and what the first one was. + /// + /// Shared by all three H.26x legs so that "the H.265 leg proves the same thing the + /// H.264 leg does" is a fact about one function rather than a claim about three + /// hand-copied ones — the same reasoning `pf-vkdecode`'s `common` module records + /// for binding its three decoders to one driver. + /// + /// # What these legs prove, and what they do not + /// + /// Deliberately weaker than the Vulkan and D3D11VA H.26x legs, and the difference + /// is worth stating rather than hiding behind a test name: those hash every decoded + /// frame against libavcodec's goldens, because both can read their decoded surface + /// back. This rung hands out a **DRM-PRIME dmabuf** whose memory the driver tiles, + /// so there is no CPU-readable image to hash without adding a + /// `vaDeriveImage`/`vaGetImage` path that production does not use and does not + /// want. So this asserts what CAN be asserted honestly — that every access unit is + /// accepted, that the expected number of frames comes back, and that each one is a + /// real exported surface of the right shape and fourcc — and it is **NOT frame-hash + /// parity**. It is what turns "never decoded a frame anywhere" into a measurement; + /// promoting these legs to `verified` still wants parity, and parity wants a + /// readback path that does not exist yet. + /// + /// Fails loudly rather than skipping when the device has no entry point for the + /// profile: these legs are `#[ignore]`d, so they only run when someone deliberately + /// points them at a box that is supposed to have one, and a silent pass there is + /// the invisible-failure mode this whole program exists to end. + fn run_annex_b( + codec: pf_vaadec::Codec, + stream: StreamFormat, + aus: &[&[u8]], + label: &str, + ) -> (usize, FirstFrame) { + let mut decoder = NativeVaapiDecoder::new(codec, stream).unwrap_or_else(|e| { + panic!( + "{label}: this box is supposed to have a VAAPI {label} decode entry point: {e:#}" + ) + }); + eprintln!("VAAPI {label} rung constructed: {}", decoder.name()); + + let mut delivered = 0usize; + let mut first: Option = None; + for (index, au) in aus.iter().enumerate() { + match decoder.decode(au) { + Ok(Some(frame)) => { + assert!( + !frame.planes.is_empty(), + "{label} AU {index}: a delivered frame exported no dmabuf planes" + ); + if first.is_none() { + first = Some(FirstFrame { + width: frame.width, + height: frame.height, + fourcc: frame.fourcc, + modifier: frame.modifier, + keyframe: frame.keyframe, + }); + } + delivered += 1; + } + Ok(None) => {} + Err(e) => panic!("{label} AU {index}: VAAPI decode failed: {e:#}"), + } + } + + let first = first.unwrap_or_else(|| panic!("{label}: not one frame came back")); + let FirstFrame { + width, + height, + fourcc, + modifier, + keyframe, + } = first; + eprintln!( + "VAAPI {label}: {delivered} of {} access units delivered a frame, first \ + {width}x{height} fourcc={:?} modifier={modifier:#x} keyframe={keyframe}", + aus.len(), + std::str::from_utf8(&fourcc.to_le_bytes()).unwrap_or("?"), + ); + (delivered, first) + } + + /// Does this machine's VAAPI actually DECODE H.264 — the question the evidence + /// table has answered "no hardware has ever tried" since M6. + /// + /// See [`run_annex_b`] for what this proves and, more importantly, what it does + /// not: it is a decode measurement, not frame-hash parity. + #[test] + #[ignore = "needs a machine with a libva runtime and an H.264 VLD entry point"] + fn h264_decodes_the_vendored_vector_on_this_machines_vaapi() { + let aus = split_h264_aus(H264_25FPS); + assert_eq!(aus.len(), H26X_AU_COUNT, "the H.264 vector is 250 AUs"); + + let (delivered, first) = run_annex_b( + pf_vaadec::Codec::H264, + StreamFormat::SDR_420_8, + &aus, + "H.264", + ); + assert_eq!((first.width, first.height), (320, 240), "320x240"); + assert_eq!( + first.fourcc, + pf_vaadec::VA_FOURCC_NV12, + "an 8-bit pool exports NV12" + ); + assert_eq!( + delivered, + output_bearing_aus_h264(&aus), + "every access unit whose plan outputs a picture must deliver one" + ); + assert_eq!( + delivered, H264_DELIVERED, + "see H264_DELIVERED for why this is 225 and not 250" + ); + + // ⚠ A DEFECT this leg found, asserted so that fixing it is noticed rather than + // so that it is preserved. `finish` is handed the CURRENT access unit's + // `is_idr`, not the flag of the picture it is about to display — and on a + // reordering stream those are different pictures. The first frame delivered + // here IS the IDR, bumped out several access units after it decoded, and it + // arrives flagged `keyframe: false`; conversely the AU that drains the DPB at a + // later IDR flags whichever OLD picture it displays as a keyframe. The flag is + // `DecodedImage::is_keyframe`, the pump's post-loss re-anchor signal, so a rung + // that mislabels it would keep asking for a keyframe it has already been sent. + // It cannot bite punktfunk today for the same reason the frame count cannot: + // hosts emit zero-reorder output, so the decoded picture and the displayed one + // are always the same picture. Fix it and this line is the one to delete. + assert!( + !first.keyframe, + "the rung labels the ACCESS UNIT, not the picture it delivers — if this \ + now passes the label was fixed, which is good; delete this assertion" + ); + } + + /// The same question for H.265, whose leg has never decoded a frame either. + /// + /// Not a redundant copy of the H.264 leg: HEVC reaches an entirely different + /// conversion in `pf-vaadec` (its own picture parameters, its own reference-picture + /// set, its own slice header) and a different arm of [`NativeVaapiDecoder::decode`] + /// — including the `RaslSkipped` Ok-skip no other codec has. + #[test] + #[ignore = "needs a machine with a libva runtime and an HEVC Main VLD entry point"] + fn h265_decodes_the_vendored_vector_on_this_machines_vaapi() { + let aus = split_h265_aus(H265_25FPS); + assert_eq!(aus.len(), H26X_AU_COUNT, "the H.265 vector is 250 AUs"); + + let (delivered, first) = run_annex_b( + pf_vaadec::Codec::H265, + StreamFormat::SDR_420_8, + &aus, + "H.265", + ); + assert_eq!((first.width, first.height), (320, 240), "320x240"); + assert_eq!( + first.fourcc, + pf_vaadec::VA_FOURCC_NV12, + "an 8-bit pool exports NV12" + ); + assert_eq!( + delivered, + output_bearing_aus_h265(&aus), + "every access unit whose plan outputs a picture must deliver one" + ); + assert_eq!( + delivered, H265_DELIVERED, + "see H264_DELIVERED for why this is 204 and not 250" + ); + assert!(!first.keyframe, "the same mislabel the H.264 leg documents"); + } + + /// And the ten-bit leg, which is the one every HDR session lands on. + /// + /// The fourcc assertion is the point of running it at all: `Session::build` picks + /// P010 from the SPS's bit depth, and a pool that came out NV12 would be a ten-bit + /// stream decoded into an 8-bit surface. + #[test] + #[ignore = "needs a machine with a libva runtime and an HEVC Main 10 VLD entry point"] + fn hevc_main10_decodes_the_ten_bit_vector_on_this_machines_vaapi() { + let aus = split_h265_aus(MAIN10_H265); + assert_eq!(aus.len(), MAIN10_AU_COUNT, "the Main 10 vector is 50 AUs"); + + let (delivered, first) = run_annex_b( + pf_vaadec::Codec::H265, + StreamFormat { + bit_depth: 10, + ..StreamFormat::SDR_420_8 + }, + &aus, + "HEVC Main 10", + ); + assert_eq!((first.width, first.height), (320, 240), "320x240"); + assert_eq!( + first.fourcc, + pf_vaadec::VA_FOURCC_P010, + "a ten-bit stream must build a P010 pool, not an 8-bit one" + ); + assert_eq!( + delivered, + output_bearing_aus_h265(&aus), + "every access unit whose plan outputs a picture must deliver one" + ); + assert_eq!( + delivered, MAIN10_DELIVERED, + "see H264_DELIVERED for why this is 45 and not 50" + ); + assert!(!first.keyframe, "the same mislabel the H.264 leg documents"); + } } From af4d265168c9abc571e8734f8aba6208c2bebb5e Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 7 Aug 2026 21:10:03 +0200 Subject: [PATCH 10/21] fix(client): the fourth site that swore the DXVA AV1 leg fails parity, and a clippy lint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `the_evidence_table_says_exactly_which_rungs_have_run_on_hardware` asserts the same fact a third way — a proven list and a NOT-proven list, both spelled out — so promoting the rung in the three places the handoff named still left a test saying "the DXVA AV1 leg FAILS parity on two GPUs — claiming otherwise is the dishonesty this program must not ship". It was right to fail; the pair moves lists here. Three prose sites that still described the leg as decoding wrong pixels move with it: `native_supports_av1`'s device-facts note, `log_rung`'s honesty-surface docs, and the OPEN question in the Windows Intel arm of `pick_native` — that last one is marked CLOSED rather than deleted, because the question it raised (the evidence filter asks "any evidence", and has no answer for BAD evidence) is a real gap in the rule that outlived this particular leg. --- crates/pf-client-core/src/video.rs | 57 ++++++++++--------- .../tests/libav_picparams_parity.rs | 2 +- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/crates/pf-client-core/src/video.rs b/crates/pf-client-core/src/video.rs index 40c1ddc75..abb4314e4 100644 --- a/crates/pf-client-core/src/video.rs +++ b/crates/pf-client-core/src/video.rs @@ -1485,11 +1485,13 @@ 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 -/// 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. +/// AV1 Profile 0 through the adapter's profile GUID, and `auto` reaches it. That leg is +/// frame-hash parity-proven on two vendors since 2026-08-07 ([`native_evidence`]); until +/// that run it was measured decoding WRONG PIXELS on both, and advertising AV1 was still +/// answered from device facts here — deliberately, because withdrawing the codec would be +/// a product call about what a Windows Intel box streams instead, not a fact about the +/// device. That reasoning is kept rather than deleted: it is the shape this arm has to +/// hold the next time a leg's evidence goes bad. /// 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. @@ -1594,9 +1596,9 @@ 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, 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 +/// is now native, one of them still has legs that have never decoded anything anywhere, one +/// leg that was measured decoding wrong pixels has since been fixed and proven, 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 /// run*, and the `stats:` decode-path tag — which is a machine interface and stays @@ -2022,19 +2024,18 @@ impl Decoder { // 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. + // CLOSED, 2026-08-07 (opened and closed the same day): for a few hours this + // admitted a rung MEASURED to decode wrong pixels — 186/250 and 245/250 + // diverging frames — 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 question it raised is worth keeping even + // though the answer expired: the filter asks "has this rung any evidence", and + // a rung with BAD evidence is a case the rule was never posed. It was left + // admitted rather than barred, because barring it trades visibly-wrong AV1 for + // the software rung, which cannot keep up at 4K (see [`crate::video_software`]) + // and is itself unproven. The AV1 leg is now parity-proven on both vendors + // ([`native_evidence`]) so this arm admits a PROVEN rung today, and + // `native_rung_admitted`'s first clause would admit it whatever were below. && native_rung_admitted(NativeRung::D3d11va, wire, None) { d3d11_tried = true; @@ -3293,8 +3294,8 @@ mod tests { /// /// This test is the reason the table can be trusted a milestone from now. M9 turned /// native rungs on by default and M10 deleted every libavcodec rung beneath them; the - /// argument for doing that honestly rests entirely on the claim "these five pairs are - /// proven and these six are not", and on the session log saying so. A + /// argument for doing that honestly rests entirely on the claim "these six pairs are + /// proven and these five are not", and on the session log saying so. A /// table nobody checks drifts into a table that says everything is fine — which is /// the exact failure this whole program exists to end, one layer up. #[test] @@ -3313,6 +3314,11 @@ mod tests { ), (NativeRung::D3d11va, CODEC_H264, "native D3D11VA H.264 (M5)"), (NativeRung::D3d11va, CODEC_HEVC, "native D3D11VA H.265 (M5)"), + ( + NativeRung::D3d11va, + CODEC_AV1, + "native D3D11VA AV1 (M7, RTX 3500 Ada + Intel Arc, 2026-08-07)", + ), ] { assert!( native_evidence(rung, codec).verified, @@ -3320,11 +3326,6 @@ mod tests { ); } for (rung, codec, why) in [ - ( - NativeRung::D3d11va, - CODEC_AV1, - "the DXVA AV1 leg FAILS parity on two GPUs (M7)", - ), ( NativeRung::Vaapi, CODEC_H264, diff --git a/crates/pf-dxvadec/tests/libav_picparams_parity.rs b/crates/pf-dxvadec/tests/libav_picparams_parity.rs index e063c7ec4..c3520e3c0 100644 --- a/crates/pf-dxvadec/tests/libav_picparams_parity.rs +++ b/crates/pf-dxvadec/tests/libav_picparams_parity.rs @@ -1915,7 +1915,7 @@ fn compare_av1_picparams(ours: &[OurSubmission], capture: &Capture) -> Findings ("ours", our_curr, our_store), ("libav", their_curr, their_store), ] { - if store.iter().any(|surface| *surface == curr) { + if store.contains(&curr) { findings.note( "curr_pic_texture_index[aliases the store]", au, From 3a4c94ad79837a656f8e294d7aa98519345d33b5 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 7 Aug 2026 21:34:59 +0200 Subject: [PATCH 11/21] =?UTF-8?q?fix(dxvadec):=20the=20review=20round=20?= =?UTF-8?q?=E2=80=94=20a=20vacuous=20predicate,=20an=20overstated=20claim,?= =?UTF-8?q?=20and=20the=20H.264=20twin=20of=20this=20defect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Five findings from the adversarial pass, all real. **The deferral predicate was vacuous.** `plan.dpb.removed` is ALWAYS a subset of `plan.dpb_refs`: `Av1Planner::plan_frame` snapshots `dpb_refs` before any mutation and `refresh_slots` can only report a picture that was in `self.slots` at that moment. So `filter(|id| dpb_refs.contains(id))` was a condition that is never false, the eager-release loop beside it could never release anything, and the test assertion "only a picture the submission points at earns the reprieve" could never fire. Now: defer every removal, say why in terms of the planner, and assert the PLANNER's property (`removed ⊆ dpb_refs`) — which is falsifiable, and whose failure would mean the conversion is releasing a surface `ref_frame_map` points at. **The failure-path claim was overstated.** Holding the decode's `Result` closes this frame's leak, not the unit's: `decode_av1` returns on the first failing frame and abandons the rest of the temporal unit's plans, so their removals are never released. 24 of 250 units carry a second frame. Named rather than fixed — what to do with the frames after a failure is the pump's question. **⚠⚠ The H.264 leg plausibly has the same defect, and the comment this change added said it could not.** `pic.rs` builds `RefFrameList` from `plan.dpb_refs`, and `H264Planner` snapshots that in `begin_picture` — BEFORE 8.2.5 marking and the DPB bump. The vendored bump drops a picture the sliding window just unmarked once it has been output, so a picture can land in both `RefFrameList` and `dpb.removed`: the AV1 aliasing shape exactly. Measured zero on the vendored vector — but that vector REORDERS, which is precisely what keeps an unmarked picture alive past the AU that unmarked it. A punktfunk host emits LOW-DELAY H.264, where output happens as each picture is decoded, which is the condition that makes eviction and unmarking land in the same access unit. Traced end to end in source, not reproduced (no low-delay vector). NOT fixed: changing a hardware-proven codec on an unreproduced suspicion is the worse risk two commits before a release. Instead `no_au_removes_a_picture_its_own_reference_list_names` makes the assumption falsifiable, and its message says what to do when it fires. HEVC is structurally safe and now says why: `H265Planner` snapshots `dpb_refs` AFTER `decode_rps`. **Four more stale promotion sites**, past the four already fixed: `Backend:: NativeD3d11va`'s variant doc, `Decoder::new`'s Windows rung comment, `lib.rs`'s module note and `clients/session/README.md`. Two sites that used the AV1 leg as the live EXAMPLE of an unproven rung are marked as expired rather than deleted — the reasoning is what the next bad-evidence leg will need. **The AV1 dump was missing.** `PF_DXVA_DUMP` wrote h264 and hevc only, for the one codec whose libavcodec capture has never been taken and where the dump is therefore the only tool. --- clients/session/README.md | 4 +- crates/pf-client-core/src/lib.rs | 4 +- crates/pf-client-core/src/video.rs | 26 +++-- .../pf-client-core/src/video_d3d11_native.rs | 59 +++++++++--- crates/pf-dxvadec/src/pic.rs | 63 ++++++++++++ crates/pf-dxvadec/src/pic_av1.rs | 95 ++++++++++++------- .../tests/libav_picparams_parity.rs | 4 + 7 files changed, 198 insertions(+), 57 deletions(-) diff --git a/clients/session/README.md b/clients/session/README.md index 3e7555caf..9bf2c77f9 100644 --- a/clients/session/README.md +++ b/clients/session/README.md @@ -69,8 +69,8 @@ Video, then the platform's own (pf-dxvadec on Windows, pf-vaadec on Linux), then rung (openh264/rav1d). The libavcodec rungs that used to sit under each of them are deleted, along with `pf-ffvk` and the `ffmpeg-next` dependency. -Two of the native rungs have never decoded a frame on real hardware (native VAAPI at all; -native D3D11VA's AV1 leg). They run anyway — with the libavcodec twins gone, the only +One of the native rungs has never decoded a frame on real hardware (native VAAPI's H.264 and +H.265 legs; its AV1 leg has decoded but has never been parity-checked). It runs anyway — with the libavcodec twins gone, the only thing below them is the CPU, so barring them would cost the session hardware decode outright rather than move it one rung down. What replaces the safety net is the log: every session names the rung it landed on with its evidence state, diff --git a/crates/pf-client-core/src/lib.rs b/crates/pf-client-core/src/lib.rs index 2926c6c0b..3acbb5be8 100644 --- a/crates/pf-client-core/src/lib.rs +++ b/crates/pf-client-core/src/lib.rs @@ -105,8 +105,8 @@ pub mod clipboard; pub mod video_d3d11; // Native D3D11VA (M5): `ID3D11VideoDecoder` driven from pf-bitstream plans, filling the // hand-off ring `video_d3d11` owns. Since M10 it is the only DXVA rung there is. In `auto` -// for the codecs that have hardware evidence (H.264/H.265) and, with nothing proven left -// below it, for AV1 too — see `video`'s evidence table; `PUNKTFUNK_DECODER=native-d3d11va` +// for all three codecs, each of which now has hardware evidence — H.264/H.265 since M5, AV1 +// since 2026-08-07 — see `video`'s evidence table; `PUNKTFUNK_DECODER=native-d3d11va` // reaches every leg by pin. #[cfg(windows)] pub mod video_d3d11_native; diff --git a/crates/pf-client-core/src/video.rs b/crates/pf-client-core/src/video.rs index abb4314e4..f936eed8e 100644 --- a/crates/pf-client-core/src/video.rs +++ b/crates/pf-client-core/src/video.rs @@ -16,10 +16,11 @@ //! M9's evidence FILTER survives, narrowed to the one thing it can still protect //! ([`native_rung_admitted`]). The filter kept a rung that had never decoded on real //! hardware out of `auto` while its proven libavcodec twin was one step below. With the -//! twins deleted that is usually no longer the situation: below native-d3d11va's AV1 leg, -//! and below native-vaapi on NVIDIA/AMD, there is nothing proven left to fall onto, so -//! barring the rung would not move a session one rung DOWN — it would take hardware decode -//! away from it entirely, which is the worse answer. +//! twins deleted that is usually no longer the situation: below native-vaapi on NVIDIA/AMD +//! there is nothing proven left to fall onto, so barring the rung would not move a session +//! one rung DOWN — it would take hardware decode away from it entirely, which is the worse +//! answer. (native-d3d11va's AV1 leg was the other standing example until 2026-08-07, when +//! it earned parity on two vendors and stopped being an unproven rung at all.) //! //! **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` @@ -788,8 +789,9 @@ enum Backend { /// `ID3D11VideoDecoder` driven from pf-bitstream plans, filling the shareable-RGBA /// hand-off ring in `crate::video_d3d11`. /// Reachable by pin (`PUNKTFUNK_DECODER=native-d3d11va`) and by `auto` in the vendor - /// order: its H.264/H.265 legs have hardware parity + a soak (M5); its AV1 leg has - /// decoded nothing anywhere and runs with the warning [`log_rung`] emits. Errors + /// order: its H.264/H.265 legs have hardware parity + a soak (M5), and its AV1 leg has + /// frame-hash parity on two vendors since 2026-08-07 (M7) — 250/250 after a decode + /// target that aliased a reference surface was fixed. Errors /// ride the SAME streak/demotion machinery as every other hardware rung. /// Boxed: the decoder (two planners plus a session) dwarfs the other variants. #[cfg(windows)] @@ -1221,11 +1223,14 @@ pub fn native_vulkan_usable(wire: u8, video_decode: bool, decode_video_caps: u32 /// reaches it again if Vulkan can't be built) and by pin. /// * **Everything else.** Below the unproven rung is the CPU. Trading hardware decode for /// software decode to avoid an unproven decoder is the worse answer, so those rungs run, -/// with the warning [`log_rung`] emits. That includes Windows Intel/unknown, where the +/// with the warning [`log_rung`] emits. That included Windows Intel/unknown, where the /// rung below native-d3d11va IS native Vulkan Video on paper: that vendor family is the /// one thing in this program with a MEASURED wrong-pixel report against Vulkan decode /// (the B580, see [`Decoder::new`]), and "has never run" is not a reason to move a -/// session onto "known to strobe here". Callers say so where they pass `None`. +/// session onto "known to strobe here". Callers say so where they pass `None`. ⚠ Since +/// 2026-08-07 no D3D11VA leg is unproven, so that arm no longer exercises this clause — +/// the reasoning is kept because the `None` those callers pass is still what decides the +/// answer if any leg's evidence ever goes bad again. /// /// ⚠ This governs `auto` ONLY. An explicit `PUNKTFUNK_DECODER=` pin bypasses it exactly as /// it bypasses the vendor order — a pin is how a lab run reaches a rung `auto` will not @@ -1965,8 +1970,9 @@ impl Decoder { // // Windows' D3D11VA RUNG: native D3D11VA (pf-dxvadec). Its H.264/H.265 legs HAVE // hardware evidence (parity on an RTX 4090 and an AMD iGPU plus a 30-minute soak, - // M5); its AV1 leg has none and runs with the warning `done` logs — until M10 that - // leg was skipped in `auto` in favour of libavcodec's DXVA rung, which no longer + // M5), and since 2026-08-07 so does its AV1 leg (250/250 on an RTX 3500 Ada and an + // Intel Arc, no soak) — until M10 that leg was skipped in `auto` in favour of + // libavcodec's DXVA rung, which no longer // exists. The rung needs the presenter's win32 import path or its frames could // never reach the screen — that check is first, once. #[cfg(windows)] diff --git a/crates/pf-client-core/src/video_d3d11_native.rs b/crates/pf-client-core/src/video_d3d11_native.rs index 2380b7633..10582a8db 100644 --- a/crates/pf-client-core/src/video_d3d11_native.rs +++ b/crates/pf-client-core/src/video_d3d11_native.rs @@ -564,6 +564,21 @@ impl NativeD3d11Decoder { /// rung closes it in `pf_vkdecode::decoder_av1`; this is the same close, and it /// runs on the concealed path too, because a converted-but-unsubmitted frame /// took a slot just the same. + /// + /// # The surfaces the conversion refuses to release + /// + /// The second of the two slot releases below, and the caller's half of + /// [`pf_dxvadec::DecodePlanDxvaAv1::release_after_decode`]. AV1 applies + /// `refresh_frame_flags` AFTER the frame is decoded (7.20), so a frame that reads + /// a slot its own refresh overwrites is ordinary — 268 of the vendored vector's + /// 274 frames — and `plan_to_dxva_av1` therefore hands those pictures back rather + /// than releasing them, because `SlotMap::assign` would return the surface just + /// vacated to `setup_slot` and the submission would name one surface as both + /// `CurrPicTextureIndex` and a `RefFrameMapTextureIndex` entry. Releasing them + /// HERE is safe for the same reason the `refresh_frame_flags == 0` release below + /// is: the decode op has been issued, so nothing can be assigned them before the + /// next frame. Dropping them instead holds a surface per frame and exhausts the + /// nine-slot ledger within ten. fn frame_av1( &mut self, au: &[u8], @@ -579,9 +594,18 @@ impl NativeD3d11Decoder { // ⚠ The decode's `Result` is held rather than `?`-ed, so that the two slot // releases below run on the FAILURE path too. `decode_av1` treats an error // here as a health note and keeps the session — it does not rebuild the slot - // map — so an early return would leak a surface per failed frame and reach - // `SlotError::Full` after nine, which is a session that dies of an error it - // had already recovered from. + // map — so an early return leaked a surface per failed frame and would reach + // `SlotError::Full` after nine, a session dying of an error it had already + // recovered from. + // + // ⚠⚠ That closes THIS frame's leak and not the unit's: `decode_av1` returns on + // the first failing frame and abandons the rest of the temporal unit's plans, + // whose removals are then never released and whose stored ids are never + // assigned — the ledger and the planner's store desynchronise. 24 of the + // vendored vector's 250 units carry a second frame, so it is not hypothetical. + // Left as it is: recovering a partly-decoded unit means deciding what to do + // with the frames after the failure, which is the pump's question and not this + // function's, and the failure already ends in a keyframe request. let shown = self.decode_and_present_av1(au, &sub, damaged); // The surfaces this frame's own refresh displaced while its submission still @@ -798,11 +822,20 @@ impl NativeD3d11Decoder { slice_ranges: dxva.slice_ranges, setup_slot: dxva.setup_slot, setup_id: dxva.setup_id, - // H.264's conversion releases its whole `removed` list itself: - // neither vendored vector ever has a picture the slices read - // being displaced by the same access unit (measured: zero on - // 250 AUs), and the sliding window evicts only pictures no - // slice names. + // ⚠ H.264's conversion still releases its whole `removed` list + // inside itself, and that is a MEASUREMENT rather than a proof. + // `pf_dxvadec::pic`'s `no_au_removes_a_picture_its_own_reference_ + // list_names` pins `removed ∩ dpb_refs` at zero over the vendored + // vector — but that vector REORDERS, and reordering is what keeps + // an unmarked picture in the DPB past the AU that unmarked it. On + // a low-delay stream, which is what a punktfunk host emits, the + // sliding window can unmark an already-output picture and + // `bump_as_needed` evict it in the same access unit, putting it in + // both `RefFrameList` and `removed` — the AV1 aliasing shape, + // exactly. Left alone deliberately: the AV1 defect is what this + // change fixes and proves, and giving two hardware-proven codecs a + // deferral no vector exercises would be an unmeasured change to + // working code. The tripwire is the test named above. release_after_decode: Vec::new(), codec: Codec::H264, facts: PictureFacts { @@ -863,9 +896,13 @@ impl NativeD3d11Decoder { slice_ranges: dxva.slice_ranges, setup_slot: dxva.setup_slot, setup_id: dxva.setup_id, - // As H.264 above: an HEVC picture's RPS is resolved against the - // DPB before any removal, and a picture the RPS names is by - // construction still in it. + // HEVC is the one of the three where this IS structural rather + // than measured: `H265Planner` snapshots `dpb_refs` AFTER + // `decode_rps` has updated the DPB, so a picture this AU's RPS + // dropped is never in the snapshot `RefPicList` is built from, and + // the aliasing shape cannot arise. (The AV1 planner snapshots + // BEFORE, which is why that codec needed the deferral, and the + // H.264 one snapshots before marking too — see the note above.) release_after_decode: Vec::new(), codec: Codec::H265, facts: PictureFacts { diff --git a/crates/pf-dxvadec/src/pic.rs b/crates/pf-dxvadec/src/pic.rs index 6546c0cdb..e9a28730f 100644 --- a/crates/pf-dxvadec/src/pic.rs +++ b/crates/pf-dxvadec/src/pic.rs @@ -881,6 +881,69 @@ mod tests { assert_eq!(converted.len(), 250); } + /// ⚠⚠ **TRIPWIRE, not a proof — and the thing it watches for is UNRESOLVED.** + /// + /// This conversion releases the whole of `plan.dpb.removed` and then assigns the + /// decode target a slot. [`SlotMap::assign`] takes the LOWEST FREE slot, which is + /// the one just released — so if a picture is ever in BOTH `dpb_refs` (which is + /// what `RefFrameList` is built from, above) and `dpb.removed`, the submission + /// names one surface as `CurrPic` and as a `RefFrameList` entry at once, and the + /// frame decodes into a picture it predicts from. That is precisely the defect + /// measured on the AV1 leg on 2026-08-07: 245 of 250 delivered frames wrong on an + /// Intel Arc, invisible on NVIDIA for 63 frames, and invisible on glass entirely. + /// + /// For AV1 it was fixed by deferring the release past the decode op. For H.264 it + /// was NOT, because the intersection is empty on the vendored vector and changing + /// a hardware-proven codec on an unreproduced suspicion is the worse risk. What + /// this test does is make the assumption falsifiable instead of tacit. + /// + /// **Why zero here is weak evidence.** `H264Planner` snapshots `dpb_refs` in + /// `begin_picture`, BEFORE `finish_picture` runs 8.2.5 marking and then bumps the + /// DPB — and the vendored bump drops a picture the sliding window just unmarked + /// only once it has been OUTPUT. This vector reorders (it has B-frames), so an + /// unmarked picture is still awaiting output and lingers past the AU that unmarked + /// it, which is exactly what keeps the intersection empty. **A punktfunk host emits + /// low-delay H.264 with no reordering**, where a picture is output the moment it is + /// decoded — the condition that puts eviction and unmarking in the same access + /// unit. So the shape is plausibly live in the field and merely unreachable here. + /// + /// HEVC does not need this test and cannot be given one: `H265Planner` snapshots + /// `dpb_refs` AFTER `decode_rps` has updated the DPB, so an RPS-dropped picture is + /// structurally never in the snapshot `RefPicList` is built from. + /// + /// If this ever fires, the fix is `pic_av1.rs`'s: hand the removals back to the + /// caller as `release_after_decode` and let it release them once the decode op is + /// issued. Do not "fix" it by relaxing the count. + #[test] + fn no_au_removes_a_picture_its_own_reference_list_names() { + let mut both = 0usize; + let mut aus_with_removals = 0usize; + for (plan, _) in convert_stream() { + if !plan.dpb.removed.is_empty() { + aus_with_removals += 1; + } + for id in &plan.dpb.removed { + if plan.dpb_refs.iter().any(|r| r.id == *id) { + both += 1; + } + } + } + assert!( + aus_with_removals > 0, + "no access unit of this vector removed anything, so the intersection below \ + is empty for a reason that has nothing to do with the hazard" + ); + assert_eq!( + both, 0, + "{both} picture(s) are in this AU's reference list AND removed by it — the \ + conversion releases them before assigning the decode target a slot, so \ + `CurrPic` and a `RefFrameList` entry now name one surface and the frame \ + predicts from the picture it is writing. This is the AV1 defect of \ + 2026-08-07 on the H.264 leg; fix it the same way (a deferred \ + `release_after_decode`), never by changing this number" + ); + } + #[test] fn the_setup_surface_is_the_current_picture_entry_and_is_never_also_a_reference_entry() { for (_, dxva) in convert_stream() { diff --git a/crates/pf-dxvadec/src/pic_av1.rs b/crates/pf-dxvadec/src/pic_av1.rs index 923bd848c..6cfe6e0b9 100644 --- a/crates/pf-dxvadec/src/pic_av1.rs +++ b/crates/pf-dxvadec/src/pic_av1.rs @@ -139,19 +139,23 @@ pub struct DecodePlanDxvaAv1 { /// breath: decode into the surface you are predicting from. /// /// Neither vendored H.264 nor H.265 vector ever produces that shape (measured: - /// zero on the 250-AU clips), which is why the eager release survived two - /// hardware-proven codecs and opened on the first AV1 frame past the key frame's - /// neighbourhood. The Vulkan rung carries the same contract for the same reason + /// zero on the 250-AU clips — ⚠ but see `plan_to_dxva`'s note, because that is a + /// measurement of two REORDERING vectors and not a proof about those codecs), + /// which is why the eager release survived two hardware-proven codecs and opened + /// on the first AV1 frame past the key frame's neighbourhood. The Vulkan rung + /// carries the same contract for the same reason /// (`pf_vkdecode::pic_av1::DecodePlanVkAv1::release_after_decode`), and this - /// rung's constraint is the STRICTER of the two: Vulkan binds only the - /// references the frame names, while `RefFrameMapTextureIndex` declares the - /// whole store, so every picture the store still names has to survive — not just - /// the seven the frame reads. + /// rung's constraint is the STRICTER of the two: Vulkan binds only the references + /// the frame names, while `RefFrameMapTextureIndex` declares the whole store, so + /// every picture the store still names has to survive — not just the seven the + /// frame reads. /// - /// The ids are always a subset of the plan's `dpb.removed`, so applying them - /// completes that plan's bookkeeping and never invents a removal. Empty on the - /// overwhelming minority of frames that displace nothing they name; a caller - /// that drops them leaks a surface per frame and runs the ledger dry within ten. + /// This is exactly `dpb.removed` less the picture being stored, and that is not a + /// coincidence to be tidied into a filter: the planner snapshots `dpb_refs` before + /// any mutation, so every removal is by construction a picture the store named + /// (see the conversion's own comment). Applying the list completes the plan's + /// bookkeeping and never invents a removal. A caller that drops it holds a surface + /// on nearly every frame and runs the ledger dry within ten. pub release_after_decode: Vec, } @@ -660,26 +664,32 @@ pub fn plan_to_dxva_av1( } // --- mutations, after every fallible step ----------------------------- - // ⚠ A picture this submission NAMES may be displaced by this same frame's - // refresh. Its surface is still in `ref_frame_map` above, so releasing it here - // would hand that very surface to `setup_slot` below and the frame would decode - // into a picture it predicts from. Held back for the caller instead — see - // `DecodePlanDxvaAv1::release_after_decode` for the measurement and for why - // this rung's test is `dpb_refs` (the whole store `RefFrameMapTextureIndex` - // declares) rather than the Vulkan rung's narrower `refs`. + // ⚠ EVERY removed picture is held back, and nothing is released here at all. + // + // A picture this submission NAMES may be displaced by this same frame's refresh; + // its surface is still in `ref_frame_map` above, so releasing it would hand that + // very surface to `setup_slot` below and the frame would decode into a picture it + // predicts from. What makes the rule "every removal" rather than "the removals + // the store names" is a property of the PLANNER: `Av1Planner::plan_frame` + // snapshots `dpb_refs` before any mutation, and `refresh_slots` can only report a + // picture that was in `self.slots` at that moment, so `dpb.removed` is always a + // SUBSET of the store `ref_frame_map` was built from. Filtering on `dpb_refs` + // here would be a condition that is never false wearing the clothes of a + // decision; the subset relation is asserted in + // `the_decode_target_never_aliases_a_surface_the_submission_names` instead, where + // a planner change that broke it fails loudly. + // + // `setup_id` is excluded as a safety property rather than as a live case: a + // picture this frame stores cannot also be one its own refresh displaced, because + // `refresh_slots` retains out any displaced id still held anywhere. Releasing it + // would return the surface being decoded into. let release_after_decode: Vec = plan .dpb .removed .iter() .copied() - .filter(|id| *id != setup_id && plan.dpb_refs.iter().any(|r| r.id == *id)) + .filter(|id| *id != setup_id) .collect(); - for &id in &plan.dpb.removed { - if id == setup_id || release_after_decode.contains(&id) { - continue; - } - let _ = slots.release(id); - } let setup_slot = match slots.slot_of(setup_id) { Some(existing) => existing, None => slots.assign(setup_id)?, @@ -906,19 +916,40 @@ mod tests { RefFrameMapTextureIndex entry — the frame decodes into a picture \ it predicts from" ); - // Every deferred id is one this plan really removed AND the store - // really names — never an invented removal, never a live picture. + // ⚠ THE PROPERTY THE DEFERRAL RESTS ON, asserted about the PLANNER + // rather than about the conversion's own output. + // + // The conversion holds back every removal, which is only safe-and- + // sufficient because `Av1Planner` snapshots `dpb_refs` before any + // mutation and `refresh_slots` can only report a picture that was in + // it — so `dpb.removed` is a subset of the store `ref_frame_map` was + // built from. Asserting instead that each DEFERRED id is in + // `dpb_refs` would be vacuous: the deferred list is filtered out of + // `removed`, so that check compares an expression with itself. This + // one can fail, and if a planner change ever makes it fail the + // conversion is releasing a surface the submission points at. + for &id in &plan.dpb.removed { + assert!( + plan.dpb_refs.iter().any(|r| r.id == id), + "frame {frames}: the planner removed picture {id}, which the \ + pre-decode store never held — `ref_frame_map` is built from \ + that store, so a removal outside it is a picture this \ + conversion could release without aliasing, and the blanket \ + deferral above stops being justified" + ); + } + // Every deferred id is one this plan really removed, and it still + // holds the surface the caller is being asked to give back. for &id in &dx.release_after_decode { assert!( plan.dpb.removed.contains(&id), "frame {frames}: deferred picture {id} is not in this plan's \ removed list" ); - assert!( - plan.dpb_refs.iter().any(|r| r.id == id), - "frame {frames}: picture {id} is deferred without being named \ - by the store — only a picture the submission points at earns \ - the reprieve" + assert_ne!( + id, dx.setup_id, + "frame {frames}: the picture being decoded must never be \ + deferred — releasing it returns the surface being written" ); assert!( slots.slot_of(id).is_some(), diff --git a/crates/pf-dxvadec/tests/libav_picparams_parity.rs b/crates/pf-dxvadec/tests/libav_picparams_parity.rs index c3520e3c0..59e874299 100644 --- a/crates/pf-dxvadec/tests/libav_picparams_parity.rs +++ b/crates/pf-dxvadec/tests/libav_picparams_parity.rs @@ -3690,6 +3690,10 @@ fn dump_our_submission_in_the_captures_own_format() { let path = std::env::var("PF_DXVA_DUMP").expect("PF_DXVA_DUMP= names the output file"); let mut text = dump("h264", &our_h264_submissions()); text.push_str(&dump("hevc", &our_hevc_submissions())); + // AV1 too, and it is the codec that needs this most: no libavcodec AV1 capture has + // ever been taken (module docs say why), so for that codec this dump is the only + // way to read what the driver is being handed at all. + text.push_str(&dump("av1", &our_av1_submissions())); std::fs::write(&path, text).expect("write the dump"); println!("wrote {path}"); } From 5aeb8d255269b39c9cd3d11bf717ed3719527c39 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 7 Aug 2026 21:36:16 +0200 Subject: [PATCH 12/21] fix(client): a failed AV1 decode left the surface's facts saying it holds the last picture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `damaged` path has cleared `Session::held[setup_slot]` since M7, for a reason that now applies to the failure path too: the slot map says the surface holds THIS picture while the surface still carries whatever the previous occupant decoded, so a later `show_existing_frame` naming it blits the old picture's pixels with the old picture's geometry and colour. The failure path never reached that far before — `decode_into`'s error returned straight out of `frame_av1` — and the previous commit made it continue so the slot releases could run. --- crates/pf-client-core/src/video_d3d11_native.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/pf-client-core/src/video_d3d11_native.rs b/crates/pf-client-core/src/video_d3d11_native.rs index 10582a8db..78759d3f5 100644 --- a/crates/pf-client-core/src/video_d3d11_native.rs +++ b/crates/pf-client-core/src/video_d3d11_native.rs @@ -607,6 +607,20 @@ impl NativeD3d11Decoder { // with the frames after the failure, which is the pump's question and not this // function's, and the failure already ends in a keyframe request. let shown = self.decode_and_present_av1(au, &sub, damaged); + if shown.is_err() { + // The surface's `held` entry, on the path that now CONTINUES rather than + // returning early. The slot map says this surface holds THIS picture while + // the surface still carries whatever the previous occupant decoded, so a + // later `show_existing_frame` naming it would blit the old picture's pixels + // with the old picture's geometry and colour. The `damaged` path has + // cleared it for that reason since M7; the failure path never reached this + // far before. + if let Some(session) = self.session.as_mut() { + if let Some(held) = session.held.get_mut(usize::from(sub.setup_slot)) { + *held = None; + } + } + } // The surfaces this frame's own refresh displaced while its submission still // NAMED them (fn docs). Released here for the same reason the block below From 834b24430101ad2db29f18c483bd42e4d6e8bd6a Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 7 Aug 2026 22:09:44 +0200 Subject: [PATCH 13/21] =?UTF-8?q?fix(client):=20the=20H.264=20twin=20was?= =?UTF-8?q?=20real=20=E2=80=94=20every=20low-delay=20picture=20decoded=20i?= =?UTF-8?q?nto=20a=20surface=20it=20predicted=20from?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AV1 review round flagged the H.264 leg as "plausibly the same defect, traced in source, not reproduced" and deliberately did not touch it. It is reproduced now, and it is worse than the AV1 one: it fires on 297 of 300 access units of every stream a punktfunk host emits, at 720p, 1080p and 2160p alike, on BOTH the DXVA rung and the Vulkan one. **Decided on the CPU, no GPU needed.** `H264Planner` snapshots `dpb_refs` in `begin_picture`, BEFORE `finish_picture` runs 8.2.5's marking and C.4.5.3's bump, so a picture the sliding window unmarks and the bump then evicts lands in both `dpb_refs` (which `RefFrameList` is built from) and `dpb.removed`. The conversion released the whole `removed` list and then assigned the decode target a slot; `SlotMap::assign` takes the lowest free slot, which is the one just vacated. `CurrPic = N` and `RefFrameList[k] = N`, in one submission. The two conditions have to coincide in ONE access unit, and low-delay H.264 is exactly what makes them: `max_num_reorder_frames = 0` means the evicted picture has already been output, which is what makes it evictable at all. NVENC seals it by writing `max_num_ref_frames = 3` ALONGSIDE `max_dec_frame_buffering = 3` — a DPB exactly as deep as its reference count — so the window unmarks the oldest reference in the very unit whose bump drops it. The aliased picture is `ref_idx 2` of a three-entry `num_ref_idx_l0_active` list: addressable by any macroblock, not a spare. **Why two hardware-proven codecs and four GPUs never saw it.** `test-25fps.h264` is level 1.3 with no VUI `bitstream_restriction`, so `dpb_limit` falls back to A.3.1's level ceiling and gives a 7-frame DPB against 2 reference frames — the window unmarks two units before the bump can evict — and it REORDERS, which keeps an unmarked picture alive past the unit that unmarked it. Two independent reasons, both properties of that vector rather than of H.264. It measured zero and passed 250/250 throughout. `data/lowdelay-640x480.h264` is vendored to close exactly that: our own host's output, 120 pictures, goldens from libavcodec cross-checked bit-identical across two ffmpeg builds on two architectures. **The fix is the AV1 fix.** `DecodePlanDxva` and `DecodePlanVk` grow `release_after_decode`, the conversions hand the removals back instead of applying them, and the callers release them once the decode op is issued. It costs no slot the map does not have: `SlotMap::new` allocates `max_dpb_frames + 1` and the DPB never exceeds `max_dpb_frames`, so a free slot always exists with the whole `removed` list still held — measured, peak 4 of 4 on the stream that defers on 117 of 120 units. The Vulkan rung breaks on it in both DPB modes and neither loudly: DISTINCT hands the aliased reference the same array layer the setup writes; COINCIDE clears `slot_image[setup]` in the binding sync and the reference then resolves to no bound image, dropping out of `pReferenceSlots` with a `trace!`. Its deferred release runs on the FAILURE paths too — the fallible region's Result is held rather than `?`-ed, because seven exits sat between the conversion and the release and each would have leaked a slot. `a_full_dpb_bump_reuses_the_slot_but_the_pool_model_binds_a_fresh_image` asserted the aliasing as "the planner's normal behaviour": an authored depth-1 stream whose AU1 references the picture it evicts. It now asserts the opposite, which is the defect in two lines. New evidence, all of it runnable: the CPU proof pins BOTH numbers (0 on the vector, 117 of 120 on the low-delay stream) so neither can drift silently; the ledger-pressure test measures the peak; and a low-delay parity leg is added to `pf-vkdecode`'s `gpu_parity` and `pf-client-core`'s `video_d3d11_native::parity` so both rungs are held to what they stream rather than only to what they conform to. --- .../pf-client-core/src/video_d3d11_native.rs | 178 ++++++--- crates/pf-dxvadec/src/descriptors.rs | 1 + crates/pf-dxvadec/src/pic.rs | 358 ++++++++++++++---- .../tests/libav_picparams_parity.rs | 13 + crates/pf-vkdecode/src/decoder.rs | 335 ++++++++-------- crates/pf-vkdecode/src/pic.rs | 168 ++++++-- .../tests/data/lowdelay-640x480.h264 | Bin 0 -> 282407 bytes .../tests/data/lowdelay-640x480.nv12.sha256 | 149 ++++++++ crates/pf-vkdecode/tests/gpu_parity.rs | 133 ++++++- 9 files changed, 1022 insertions(+), 313 deletions(-) create mode 100644 crates/pf-vkdecode/tests/data/lowdelay-640x480.h264 create mode 100644 crates/pf-vkdecode/tests/data/lowdelay-640x480.nv12.sha256 diff --git a/crates/pf-client-core/src/video_d3d11_native.rs b/crates/pf-client-core/src/video_d3d11_native.rs index 78759d3f5..6829aa93d 100644 --- a/crates/pf-client-core/src/video_d3d11_native.rs +++ b/crates/pf-client-core/src/video_d3d11_native.rs @@ -20,6 +20,19 @@ //! * **H.264 and H.265** — frame-hash parity against libavcodec on an RTX 4090 and an AMD //! 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). +//! +//! ⚠⚠ **All of that was against ONE vendored vector per codec, and for H.264 the vector +//! was blind to a defect present on 99% of the frames we actually stream.** It reorders +//! and carries a 7-frame DPB against 2 reference frames; a punktfunk host emits +//! low-delay IPPP whose DPB is exactly as deep as its 3 reference frames, so 8.2.5's +//! sliding window unmarks a picture in the very access unit whose C.4.5.3 bump evicts +//! it. `plan_to_dxva` released that surface before assigning the decode target one, and +//! `SlotMap::assign` handed it straight back — `CurrPic` and a `RefFrameList` entry +//! naming one surface, on 117 of 120 access units. Found 2026-08-07 by planning our own +//! host's output on the CPU, fixed with the same deferral the AV1 rung got +//! ([`pf_dxvadec::DecodePlanDxva::release_after_decode`]), and the stream is now +//! vendored so `low_delay_host_h264_every_frame_hashes_bit_identical_to_libavcodec` +//! holds the rung to what it streams rather than only to what it conforms to. //! * **AV1** — wired in M7, and frame-hash parity on the SAME two GPUs since 2026-08-07: //! 250/250 delivered frames bit-identical to libavcodec on the RTX 3500 Ada and on the //! Intel Arc. It streams 4K60 on both with a clean 5-minute soak, but that is throughput @@ -188,14 +201,19 @@ struct Submission { /// other two codecs do not (see [`NativeD3d11Decoder::frame_av1`]). All three /// conversions produce it; dropping it here made the AV1 leak invisible. setup_id: u64, - /// AV1 only: surfaces this frame's own refresh displaces while the submission - /// still NAMES them, released once the decode op has been issued. + /// Surfaces this picture's own end-of-picture bookkeeping retires while the + /// submission still NAMES them, released once the decode op has been issued + /// ([`NativeD3d11Decoder::release_deferred`]). /// - /// See [`pf_dxvadec::DecodePlanDxvaAv1::release_after_decode`] — this is the - /// caller's half of that contract, and dropping it decodes 268 of the vendored - /// vector's 274 frames into a surface they predict from. Always empty on - /// H.264 and H.265, whose conversions release their whole `removed` list - /// themselves (neither vendored vector ever produces the shape). + /// The caller's half of [`pf_dxvadec::DecodePlanDxvaAv1::release_after_decode`] + /// and [`pf_dxvadec::DecodePlanDxva::release_after_decode`]. Dropping it decodes + /// the picture into a surface it predicts from — 268 of the vendored AV1 vector's + /// 274 frames, and 297 of every 300 access units of low-delay H.264, which is what + /// every punktfunk host emits. + /// + /// Empty on H.265 alone, and that is structural rather than lucky: `H265Planner` + /// snapshots `dpb_refs` AFTER `decode_rps`, so a picture this AU's RPS dropped is + /// never in the set `RefPicList` is built from. release_after_decode: Vec, /// Which codec's slice-control record the packer's locations become. codec: Codec, @@ -458,11 +476,21 @@ impl NativeD3d11Decoder { // The plan needed a substitute for something lost. Fold it, ask for recovery, // and do NOT submit: a concealed picture is not fit to present, and submitting // it would put a wrong reference in the DPB for every AU after it. + // + // The deferred releases still run: they are the planner's verdict on + // pictures that left the DPB, and a converted-but-unsubmitted AU took its + // slot just the same. + self.release_deferred(&submission); self.health.note(true, false, 0); self.want_recovery = true; return Ok(None); } - let frame = match self.submit(au, &submission) { + let submitted = self.submit(au, &submission); + // The surfaces the conversion refused to release, freed now that the decode op + // has been issued (or has failed, where dropping them would leak just the + // same) — see [`Self::release_deferred`]. + self.release_deferred(&submission); + let frame = match submitted { Ok(frame) => frame, Err(e) => { self.health.note(false, true, 0); @@ -474,6 +502,32 @@ impl NativeD3d11Decoder { Ok(Some(frame)) } + /// Apply a submission's [`Submission::release_after_decode`] — the surfaces its + /// conversion held back because the submission still NAMED them. + /// + /// Safe here and nowhere earlier: the decode op has been issued (or will never be), + /// so nothing can be assigned these surfaces before the next access unit is + /// converted. Dropping the list instead holds a surface per AU and reaches + /// `SlotError::Full` within the ledger's depth — which is why every exit of + /// [`Self::decode`] runs it, the concealed and failed ones included. + /// + /// Empty on H.265, whose planner cannot produce the shape; populated on nearly + /// every H.264 and AV1 picture. + fn release_deferred(&mut self, sub: &Submission) { + let Some(session) = self.session.as_mut() else { + return; + }; + for &id in &sub.release_after_decode { + if !session.slots.release(id) { + // Never fatal, and never silent: a deferred id that holds no slot + // means the conversion and the ledger disagree about the DPB, which + // is a bug in one of them rather than a stream this AU can do + // anything about. + tracing::warn!(id, "a deferred release named a picture holding no surface"); + } + } + } + /// One AV1 **temporal unit**: decode every frame in it, present at most one. /// /// This is the whole of what AV1 adds to this rung's contract, and it is the @@ -627,20 +681,7 @@ impl NativeD3d11Decoder { // waits: the decode op has been issued, so nothing can be assigned them // until the next frame — and on the `damaged` and failed paths there is no // op at all, where dropping the release would leak a surface just the same. - if let Some(session) = self.session.as_mut() { - for &id in &sub.release_after_decode { - if !session.slots.release(id) { - // Never fatal, and never silent: a deferred id that holds no - // slot means the conversion and the ledger disagree about the - // store, which is a bug in one of them rather than a stream - // this frame can do anything about. - tracing::warn!( - id, - "AV1 deferred release named a picture holding no surface" - ); - } - } - } + self.release_deferred(&sub); // The slot nothing will ever ask for again (fn docs). Released AFTER the // blit above, so the surface is read before anything can be assigned it. @@ -836,21 +877,20 @@ impl NativeD3d11Decoder { slice_ranges: dxva.slice_ranges, setup_slot: dxva.setup_slot, setup_id: dxva.setup_id, - // ⚠ H.264's conversion still releases its whole `removed` list - // inside itself, and that is a MEASUREMENT rather than a proof. - // `pf_dxvadec::pic`'s `no_au_removes_a_picture_its_own_reference_ - // list_names` pins `removed ∩ dpb_refs` at zero over the vendored - // vector — but that vector REORDERS, and reordering is what keeps - // an unmarked picture in the DPB past the AU that unmarked it. On - // a low-delay stream, which is what a punktfunk host emits, the - // sliding window can unmark an already-output picture and - // `bump_as_needed` evict it in the same access unit, putting it in - // both `RefFrameList` and `removed` — the AV1 aliasing shape, - // exactly. Left alone deliberately: the AV1 defect is what this - // change fixes and proves, and giving two hardware-proven codecs a - // deferral no vector exercises would be an unmeasured change to - // working code. The tripwire is the test named above. - release_after_decode: Vec::new(), + // ⚠⚠ The suspicion of 2026-08-07 was RIGHT, and the shape is the + // ordinary case rather than a corner: on every stream a punktfunk + // host emits, 297 of 300 access units name one surface as both + // `CurrPic` and a `RefFrameList` entry. `H264Planner` snapshots + // `dpb_refs` before 8.2.5's marking, and low-delay H.264 — + // `max_num_reorder_frames = 0`, so a picture is output the moment + // it decodes — puts the unmarking and the eviction in one AU. + // NVENC seals it by writing `max_num_ref_frames = 3` AND + // `max_dec_frame_buffering = 3`: a DPB exactly as deep as the + // reference count. The vendored vector cannot reach the shape (a + // level-derived DPB of 7 against 2 reference frames, and it + // reorders), which is why it measured zero for two milestones. + // See `pf_dxvadec::DecodePlanDxva::release_after_decode`. + release_after_decode: dxva.release_after_decode, codec: Codec::H264, facts: PictureFacts { colour: colour_of(plan.picture.colour), @@ -910,13 +950,15 @@ impl NativeD3d11Decoder { slice_ranges: dxva.slice_ranges, setup_slot: dxva.setup_slot, setup_id: dxva.setup_id, - // HEVC is the one of the three where this IS structural rather - // than measured: `H265Planner` snapshots `dpb_refs` AFTER - // `decode_rps` has updated the DPB, so a picture this AU's RPS - // dropped is never in the snapshot `RefPicList` is built from, and - // the aliasing shape cannot arise. (The AV1 planner snapshots - // BEFORE, which is why that codec needed the deferral, and the - // H.264 one snapshots before marking too — see the note above.) + // HEVC is the one of the three that needs no deferral, and it is + // STRUCTURAL rather than measured: `H265Planner` snapshots + // `dpb_refs` AFTER `decode_rps` has updated the DPB, so a picture + // this AU's RPS dropped is never in the snapshot `RefPicList` is + // built from, and nothing later in the AU unmarks anything. Both + // other codecs snapshot BEFORE their marking, and both needed the + // deferral. Now measured as well as argued: a low-delay HEVC + // stream from the same host that aliases 297 of 300 H.264 access + // units aliases 0 of 300 here. release_after_decode: Vec::new(), codec: Codec::H265, facts: PictureFacts { @@ -1701,6 +1743,23 @@ mod parity { /// rungs measured against two copies of a golden set is two measurements, and the /// point of this file is that they are one. const GOLDENS_H264: &str = include_str!("../../pf-vkdecode/tests/data/test-25fps.nv12.sha256"); + + /// **Our own host's low-delay H.264** and its goldens — the stream the vendored + /// vector cannot be. 120 pictures of 640x480 IPPP with `max_num_reorder_frames = 0` + /// and a DPB exactly as deep as its 3 reference frames, so 8.2.5's sliding window + /// unmarks the oldest reference in the very access unit whose C.4.5.3 bump evicts + /// it: `dpb.removed` and `dpb_refs` intersect on 117 of the 120, and the conversion + /// used to release those surfaces before assigning the decode target one. + /// + /// The vendored vector passed 250/250 on four GPUs across two milestones while + /// that was true of every stream this program ships. Provenance, the + /// `punktfunk-host spike` command and the ffmpeg cross-check are in the golden + /// file's header. + const LOWDELAY_H264: &[u8] = + include_bytes!("../../pf-vkdecode/tests/data/lowdelay-640x480.h264"); + const GOLDENS_LOWDELAY: &str = + include_str!("../../pf-vkdecode/tests/data/lowdelay-640x480.nv12.sha256"); + const LOWDELAY_FRAME_COUNT: usize = 120; const GOLDENS_H265: &str = include_str!("../../pf-vkdecode/tests/data/test-25fps-h265.nv12.sha256"); @@ -2175,6 +2234,11 @@ mod parity { decoder .submit(au, &sub) .unwrap_or_else(|e| panic!("AU {index}: submit failed — {e:#}")); + // This harness drives `plan` + `submit` rather than `decode`, so it owes + // the deferred releases `decode` would have applied. Not optional + // bookkeeping: on a low-delay stream nearly every AU defers, and a loop + // that drops them exhausts the ledger within the DPB's depth. + decoder.release_deferred(&sub); let session = decoder.session.as_ref().expect("submit built a session"); let pool = session.pool.clone(); let bytes = readback.read(&decoder.device, &pool, slice, display); @@ -2545,6 +2609,32 @@ mod parity { ); } + /// The leg that would have caught this rung's H.264 defect, and the only one that + /// could: **our own host's output** rather than a conformance vector. + /// + /// `h264_every_frame_hashes_bit_identical_to_libavcodec` above passed 250/250 on an + /// RTX 4090, an AMD iGPU, an RTX 3500 Ada and an Intel Arc while this rung was + /// naming one surface as both `CurrPic` and a `RefFrameList` entry on 99% of the + /// access units of every stream punktfunk actually streams. The vector cannot reach + /// the shape — see [`LOWDELAY_H264`] — so no amount of running it harder would have + /// found this. That is the lesson worth keeping: a conformance vector proves + /// conformance to ITSELF, and the encoder we ship behind is a different stream. + #[test] + #[ignore = "needs a Windows D3D11 video device (see module docs)"] + fn low_delay_host_h264_every_frame_hashes_bit_identical_to_libavcodec() { + let aus = split_h264_aus(LOWDELAY_H264); + let order = order_h264(&aus); + parity_run( + Codec::H264, + StreamFormat::SDR_420_8, + &aus, + &order, + &golden_hashes(GOLDENS_LOWDELAY), + LOWDELAY_FRAME_COUNT, + "H.264 (low-delay host stream)", + ); + } + #[test] #[ignore = "needs a Windows D3D11 video device (see module docs)"] fn h265_every_frame_hashes_bit_identical_to_libavcodec() { diff --git a/crates/pf-dxvadec/src/descriptors.rs b/crates/pf-dxvadec/src/descriptors.rs index 7d5ed39e8..41d2eab4d 100644 --- a/crates/pf-dxvadec/src/descriptors.rs +++ b/crates/pf-dxvadec/src/descriptors.rs @@ -304,6 +304,7 @@ mod tests { setup_slot: 0, setup_id: 1, setup_is_reference: true, + release_after_decode: Vec::new(), refs: Vec::::new(), mb_count, } diff --git a/crates/pf-dxvadec/src/pic.rs b/crates/pf-dxvadec/src/pic.rs index e9a28730f..06d650b9c 100644 --- a/crates/pf-dxvadec/src/pic.rs +++ b/crates/pf-dxvadec/src/pic.rs @@ -133,6 +133,60 @@ pub struct DecodePlanDxva { /// surface exists for the decode itself plus any remaining DPB residency, /// and may already have been released by this very AU's `removed`. pub setup_is_reference: bool, + /// Surfaces this access unit's own end-of-picture bookkeeping retires while the + /// submission still NAMES them. Release them once the decode op is issued — + /// never inside the conversion, and never dropped. + /// + /// # Why the conversion cannot release them + /// + /// [`SlotMap::assign`] takes the LOWEST FREE slot. Release a picture here and the + /// setup assignment two lines later hands its surface straight back, so the + /// submission says `CurrPic = N` and `RefFrameList[k] = N` in one breath: the + /// picture decodes into a surface it predicts from. That is the AV1 defect of + /// 2026-08-07 ([`crate::pic_av1::DecodePlanDxvaAv1::release_after_decode`]) on this + /// codec, and on H.264 it is not exotic at all. + /// + /// [`AuPlan::dpb_refs`] — which `RefFrameList` is built from — is snapshotted in + /// `H264Planner::begin_picture`, BEFORE `finish_picture` runs 8.2.5's marking and + /// C.4.5.3's bump. So a picture the sliding window unmarks and the bump then evicts + /// lands in both `dpb_refs` and `dpb.removed` for the same AU. It needs the two to + /// coincide, which needs the evicted picture to be already OUTPUT — and that is + /// precisely low-delay H.264: `max_num_reorder_frames = 0`, a picture output the + /// moment it decodes. + /// + /// **Measured 2026-08-07, and it is the ordinary case, not a corner.** Every stream + /// a punktfunk host emits does it on 297 of 300 access units — 720p, 1080p and + /// 2160p alike, on both this rung and [`pf_vkdecode::pic::DecodePlanVk`]'s. NVENC + /// writes `max_num_ref_frames = 3` AND `max_dec_frame_buffering = 3`: the DPB is + /// exactly as deep as the reference count, so the window unmarks the oldest + /// reference in the very AU whose bump evicts it. The aliased picture is + /// `ref_idx 2` of a three-entry `num_ref_idx_l0_active` list — addressable by any + /// macroblock, not a spare the hardware could ignore. + /// + /// `test-25fps.h264` measures ZERO and that is why this survived to here: it is + /// level 1.3 with no VUI `bitstream_restriction`, so its DPB is the level-derived 7 + /// against 2 reference frames, and it REORDERS, which keeps an unmarked picture + /// alive past the AU that unmarked it. `data/lowdelay-640x480.h264` is vendored to + /// close exactly that gap. + /// + /// # Why the caller can release them safely + /// + /// The surfaces must outlive the CONVERSION, not the decode. One AU is planned, + /// converted and submitted before the next is planned, so once the decode op is + /// issued nothing can be assigned them before the next conversion — the same + /// argument the AV1 rung's deferral rests on. + /// + /// # Deferring the whole `removed` list rather than a filtered part + /// + /// Some removals are pictures no `RefFrameList` entry names (a non-reference + /// picture bumped long after it was unmarked), and those could be released here. + /// They are not, for three reasons: `refs` is built from the SLICE LISTS as well as + /// the snapshot, so a filter on `dpb_refs` would still miss a concealment + /// substitute the lists name; deferring costs nothing, because + /// [`SlotMap::new`]'s spare slot means `assign` always has a free slot while every + /// removal is still held (the DPB never exceeds `max_dpb_frames`, and the map holds + /// `max_dpb_frames + 1`); and one unconditional rule is a thing a reader can check. + pub release_after_decode: Vec, /// The marked DPB, resolved to surfaces — the AU's own references first, then /// every other marked picture (module docs). Laid out in exactly this order in /// `pic_params.RefFrameList`. @@ -268,7 +322,10 @@ impl From for PlanToDxvaError { /// 2. references resolve against the PRE-removal state (read-only) — this AU's /// own end-of-picture marking can evict a picture its slices legitimately /// reference; -/// 3. `removed` is applied, then the setup slot is assigned last. +/// 3. the setup slot is assigned last, and `removed` is NOT applied at all: it +/// leaves as [`DecodePlanDxva::release_after_decode`] for the caller to apply +/// once the decode op is issued. Applying it here would give the assignment +/// back a surface this submission still names — see that field's docs. pub fn plan_to_dxva( plan: &AuPlan, slots: &mut SlotMap, @@ -480,24 +537,29 @@ pub fn plan_to_dxva( let slice_ranges: Vec> = plan.slices.iter().map(|s| s.data.clone()).collect(); - // Mutations LAST, after every fallible step above (fn docs). Removals first — - // they were real regardless of this AU's fate — then the setup assignment. + // Mutations LAST, after every fallible step above (fn docs). + // + // The removals are NOT applied here — they are handed back as + // `release_after_decode` for the caller to apply once the decode op is issued, + // because releasing them now would return their surfaces to the setup + // assignment below and alias `CurrPic` with a `RefFrameList` entry. The field's + // docs carry the measurement; this is the ordinary case on every stream a + // punktfunk host emits. // // The AU's own picture can itself appear in `removed`: a non-reference // picture with no free frame buffer bypasses the DPB and is stored-and- // evicted within one plan. Its surface must still exist for the decode - // itself, so it is assigned here and released right after. + // itself, so it is assigned here and released right after — the one removal + // that cannot be deferred, since deferring it would hand the caller the + // surface being decoded into. let setup_evicted = plan.dpb.removed.contains(&setup_id); - for &id in &plan.dpb.removed { - if id == setup_id { - continue; - } - if !slots.release(id) { - // Tolerated but never silent: reachable only when the caller skipped - // feeding an AU's plan through this map. - trace!(id, "DpbUpdate removed an id this SlotMap never assigned"); - } - } + let release_after_decode: Vec = plan + .dpb + .removed + .iter() + .copied() + .filter(|id| *id != setup_id) + .collect(); let setup_slot = slots.assign(setup_id)?; if setup_evicted { slots.release(setup_id); @@ -523,6 +585,7 @@ pub fn plan_to_dxva( setup_slot, setup_id, setup_is_reference: pic.is_reference, + release_after_decode, refs, mb_count: width_mbs * height_mbs, }) @@ -570,6 +633,17 @@ mod tests { "../../pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264" ); + /// **Our own host's output**, and the only stream in this repository that reaches + /// the shape `release_after_decode` exists for: low-delay IPPP, 120 pictures, + /// `max_num_reorder_frames = 0`, and a DPB exactly as deep as its reference count. + /// + /// Vendored beside the goldens the GPU legs decode it against (that file's header + /// carries the `punktfunk-host spike` command and the ffmpeg cross-check), because + /// three crates need it: this one for the CPU proof, `pf-vkdecode`'s `gpu_parity` + /// and `pf-client-core`'s `video_d3d11_native::parity` for the hardware one. + const LOWDELAY_640X480: &[u8] = + include_bytes!("../../pf-vkdecode/tests/data/lowdelay-640x480.h264"); + /// Test-only AU splitter, the same shape pf-vkdecode's `pic` tests use /// (which in turn mirrors pf-bitstream's `#[cfg(test)]`-private helper): a /// new AU starts at a non-slice NALU following a slice, or at a slice whose @@ -602,10 +676,22 @@ mod tests { /// Plan the vendored stream and convert every AU, returning the plans paired /// with their conversions. fn convert_stream() -> Vec<(AuPlan, DecodePlanDxva)> { + convert(TEST_25FPS) + } + + /// The same over [`LOWDELAY_640X480`]. + fn convert_low_delay() -> Vec<(AuPlan, DecodePlanDxva)> { + convert(LOWDELAY_640X480) + } + + /// Plan and convert a whole stream the way a caller does — including applying + /// `release_after_decode` once the (notional) decode op is issued, which is what + /// keeps the ledger from filling up over 120 access units. + fn convert(stream: &[u8]) -> Vec<(AuPlan, DecodePlanDxva)> { let mut planner = H264Planner::new(); let mut slots: Option = None; let mut out = Vec::new(); - for (i, au) in split_into_aus(TEST_25FPS).into_iter().enumerate() { + for (i, au) in split_into_aus(stream).into_iter().enumerate() { let Ok(plan) = planner.plan_au(au) else { continue; }; @@ -614,6 +700,9 @@ mod tests { *map = SlotMap::new(plan.picture.max_dpb_frames); } let dxva = plan_to_dxva(&plan, map, i as u32 + 1).expect("conversion"); + for &id in &dxva.release_after_decode { + assert!(map.release(id), "AU {i}: deferred id {id} held no slot"); + } out.push((plan, dxva)); } out @@ -881,66 +970,153 @@ mod tests { assert_eq!(converted.len(), 250); } - /// ⚠⚠ **TRIPWIRE, not a proof — and the thing it watches for is UNRESOLVED.** + /// The hazard the vendored vector CANNOT see, on a stream that can. /// - /// This conversion releases the whole of `plan.dpb.removed` and then assigns the - /// decode target a slot. [`SlotMap::assign`] takes the LOWEST FREE slot, which is - /// the one just released — so if a picture is ever in BOTH `dpb_refs` (which is - /// what `RefFrameList` is built from, above) and `dpb.removed`, the submission - /// names one surface as `CurrPic` and as a `RefFrameList` entry at once, and the - /// frame decodes into a picture it predicts from. That is precisely the defect - /// measured on the AV1 leg on 2026-08-07: 245 of 250 delivered frames wrong on an - /// Intel Arc, invisible on NVIDIA for 63 frames, and invisible on glass entirely. + /// `removed ∩ dpb_refs` is the aliasing precondition: a picture this AU's own + /// end-of-picture bookkeeping retires while `RefFrameList` still names it. Release + /// it inside the conversion and [`SlotMap::assign`] hands its surface straight back + /// to `CurrPic`, so the picture decodes into one it predicts from. /// - /// For AV1 it was fixed by deferring the release past the decode op. For H.264 it - /// was NOT, because the intersection is empty on the vendored vector and changing - /// a hardware-proven codec on an unreproduced suspicion is the worse risk. What - /// this test does is make the assumption falsifiable instead of tacit. + /// On `test-25fps.h264` the intersection is **zero**, and for two independent + /// reasons that both happen to be properties of that vector rather than of H.264: + /// it is level 1.3 with no VUI `bitstream_restriction`, so `dpb_limit` falls back to + /// A.3.1's level ceiling and gives a 7-frame DPB against `max_num_ref_frames = 2` + /// (the sliding window unmarks two AUs before the bump can evict); and it REORDERS, + /// which keeps an unmarked picture alive for output past the AU that unmarked it. + /// That zero is what let the eager release survive two milestones. /// - /// **Why zero here is weak evidence.** `H264Planner` snapshots `dpb_refs` in - /// `begin_picture`, BEFORE `finish_picture` runs 8.2.5 marking and then bumps the - /// DPB — and the vendored bump drops a picture the sliding window just unmarked - /// only once it has been OUTPUT. This vector reorders (it has B-frames), so an - /// unmarked picture is still awaiting output and lingers past the AU that unmarked - /// it, which is exactly what keeps the intersection empty. **A punktfunk host emits - /// low-delay H.264 with no reordering**, where a picture is output the moment it is - /// decoded — the condition that puts eviction and unmarking in the same access - /// unit. So the shape is plausibly live in the field and merely unreachable here. + /// On `lowdelay-640x480.h264` — OUR host's output, vendored for exactly this — it is + /// **117 of 120 access units**, measured the same way at 720p, 1080p and 2160p. The + /// difference is the encoder, not the resolution: NVENC writes + /// `max_num_ref_frames = 3` AND `max_dec_frame_buffering = 3`, a DPB exactly as deep + /// as the reference count, so 8.2.5's window unmarks the oldest reference in the very + /// AU whose C.4.5.3 bump evicts it — and `max_num_reorder_frames = 0` means it has + /// already been output, which is what makes it evictable at all. /// - /// HEVC does not need this test and cannot be given one: `H265Planner` snapshots - /// `dpb_refs` AFTER `decode_rps` has updated the DPB, so an RPS-dropped picture is - /// structurally never in the snapshot `RefPicList` is built from. + /// So this is not a tripwire any more: it pins BOTH numbers, and the second one is + /// what makes `release_after_decode` a fixed defect rather than a precaution. /// - /// If this ever fires, the fix is `pic_av1.rs`'s: hand the removals back to the - /// caller as `release_after_decode` and let it release them once the decode op is - /// issued. Do not "fix" it by relaxing the count. + /// HEVC needs no such test: `H265Planner` snapshots `dpb_refs` AFTER `decode_rps` + /// has updated the DPB, so an RPS-dropped picture is structurally never in the + /// snapshot `RefPicList` is built from — and a low-delay HEVC stream from the same + /// host measures 0 of 300, which is the argument confirmed rather than assumed. #[test] - fn no_au_removes_a_picture_its_own_reference_list_names() { - let mut both = 0usize; - let mut aus_with_removals = 0usize; - for (plan, _) in convert_stream() { - if !plan.dpb.removed.is_empty() { - aus_with_removals += 1; - } - for id in &plan.dpb.removed { - if plan.dpb_refs.iter().any(|r| r.id == *id) { - both += 1; + fn the_low_delay_stream_removes_pictures_its_own_reference_list_names_and_the_vector_never_does( + ) { + fn intersections(stream: &[u8]) -> (usize, usize, usize) { + let mut planner = H264Planner::new(); + let (mut both, mut with_removals, mut planned) = (0usize, 0usize, 0usize); + for au in split_into_aus(stream) { + let Ok(plan) = planner.plan_au(au) else { + continue; + }; + planned += 1; + if !plan.dpb.removed.is_empty() { + with_removals += 1; } + both += plan + .dpb + .removed + .iter() + .filter(|id| plan.dpb_refs.iter().any(|r| r.id == **id)) + .count(); } + (planned, with_removals, both) } + + let (planned, with_removals, both) = intersections(TEST_25FPS); + assert_eq!(planned, 250); assert!( - aus_with_removals > 0, - "no access unit of this vector removed anything, so the intersection below \ - is empty for a reason that has nothing to do with the hazard" + with_removals > 0, + "no access unit of the vendored vector removed anything, so the zero below \ + would be empty for a reason that has nothing to do with the hazard" ); assert_eq!( both, 0, - "{both} picture(s) are in this AU's reference list AND removed by it — the \ - conversion releases them before assigning the decode target a slot, so \ - `CurrPic` and a `RefFrameList` entry now name one surface and the frame \ - predicts from the picture it is writing. This is the AV1 defect of \ - 2026-08-07 on the H.264 leg; fix it the same way (a deferred \ - `release_after_decode`), never by changing this number" + "the vendored vector is supposed to be BLIND to this shape — a non-zero \ + here means the reordering/DPB-depth reasoning above is wrong, and the \ + low-delay numbers below need re-deriving before they mean anything" + ); + + let (planned, with_removals, both) = intersections(LOWDELAY_640X480); + assert_eq!(planned, 120); + assert_eq!(with_removals, 117); + assert_eq!( + both, 117, + "the low-delay stream must still exercise the aliasing precondition on \ + nearly every access unit — if this ever drops to zero the deferral below \ + is no longer being TESTED by anything, whatever else still passes" + ); + } + + /// The fix itself: no submission names its decode surface as a reference. + /// + /// [`the_setup_surface_is_the_current_picture_entry_and_is_never_also_a_reference_entry`] + /// asserts this over the vendored vector, where it held even before + /// `release_after_decode` existed. This is the same invariant over the stream that + /// BREAKS it — 117 of 120 access units before the deferral, every one of them + /// decoding into a surface it predicts from. + #[test] + fn the_low_delay_stream_never_aliases_its_decode_surface_with_a_reference() { + let converted = convert_low_delay(); + assert_eq!(converted.len(), 120); + let mut deferred_total = 0usize; + for (i, (_, dxva)) in converted.iter().enumerate() { + assert_eq!(dxva.pic_params.CurrPic.index(), dxva.setup_slot); + deferred_total += dxva.release_after_decode.len(); + for r in &dxva.refs { + assert_ne!( + r.slot, dxva.setup_slot, + "AU {i}: reference picture {} shares surface {} with the decode \ + target — the deferral is not holding", + r.id, r.slot + ); + } + } + assert_eq!( + deferred_total, 117, + "every access unit that removes a picture must defer it; a zero here with \ + the assertions above still passing would mean the stream stopped \ + exercising the shape" + ); + } + + /// The deferral costs no slot the map does not have. + /// + /// Holding every removal through the setup assignment is only free because + /// [`SlotMap::new`] allocates `max_dpb_frames + 1` and the DPB never exceeds + /// `max_dpb_frames` — so a free slot always exists even with the whole `removed` + /// list still held. Measured rather than argued: the deepest the ledger ever gets + /// on the stream that defers on 117 of 120 access units. + #[test] + fn deferring_every_removal_still_fits_the_ledger() { + let mut planner = H264Planner::new(); + let mut slots: Option = None; + let mut peak = 0usize; + let mut capacity = 0usize; + for (i, au) in split_into_aus(LOWDELAY_640X480).into_iter().enumerate() { + let Ok(plan) = planner.plan_au(au) else { + continue; + }; + let map = slots.get_or_insert_with(|| SlotMap::new(plan.picture.max_dpb_frames)); + if map.capacity() != plan.picture.max_dpb_frames + 1 { + *map = SlotMap::new(plan.picture.max_dpb_frames); + } + let dxva = plan_to_dxva(&plan, map, i as u32 + 1).expect("conversion"); + // The peak is measured BEFORE the deferred releases are applied: that is + // the moment the map is fullest, and the moment `assign` had to find a + // free slot in. + peak = peak.max(map.held().count()); + capacity = map.capacity(); + for &id in &dxva.release_after_decode { + assert!(map.release(id), "AU {i}: deferred id {id} held no slot"); + } + } + assert_eq!(capacity, 4, "max_dec_frame_buffering 3 + the spare slot"); + assert_eq!( + peak, 4, + "the deferral is expected to USE the spare slot — a peak of 3 would mean \ + the removals are being released early after all" ); } @@ -1164,27 +1340,47 @@ mod tests { assert_eq!(&bytes[10..14], &40u32.to_le_bytes()); } + /// The whole-stream churn check: no two live pictures may share a surface index, + /// which for DXVA is the difference between a decode and a corrupted reference. + /// + /// Run over BOTH streams, because they stress opposite halves of the ledger: the + /// vendored vector has a DPB (7) far deeper than its reference count (2) and so + /// never has to reuse a surface promptly, while the low-delay stream's DPB is + /// exactly its reference count and cycles all four slots every four pictures. + /// + /// The loop applies `release_after_decode` because the CALLER does; a loop that + /// drops it holds a surface per access unit and dies of `SlotError::Full` — which + /// is what this test did the moment the deferral landed, and is the cheapest + /// possible demonstration that the deferral is real rather than decorative. #[test] fn a_slot_is_reused_only_after_its_picture_leaves_the_dpb() { - // The whole-stream churn check: no two live pictures may share a surface - // index, which for DXVA is the difference between a decode and a - // corrupted reference. - let mut planner = H264Planner::new(); - let mut slots: Option = None; - let mut live: Vec<(PicId, u8)> = Vec::new(); - for (i, au) in split_into_aus(TEST_25FPS).into_iter().enumerate() { - let Ok(plan) = planner.plan_au(au) else { - continue; - }; - let map = slots.get_or_insert_with(|| SlotMap::new(plan.picture.max_dpb_frames)); - let removed = plan.dpb.removed.clone(); - let dxva = plan_to_dxva(&plan, map, i as u32 + 1).expect("conversion"); - live.retain(|&(id, _)| !removed.contains(&id)); - assert!( - live.iter().all(|&(_, slot)| slot != dxva.setup_slot), - "AU {i} decodes into a surface a live picture still holds" - ); - live.push((dxva.setup_id, dxva.setup_slot)); + for (label, stream) in [("vendored", TEST_25FPS), ("low-delay", LOWDELAY_640X480)] { + let mut planner = H264Planner::new(); + let mut slots: Option = None; + let mut live: Vec<(PicId, u8)> = Vec::new(); + for (i, au) in split_into_aus(stream).into_iter().enumerate() { + let Ok(plan) = planner.plan_au(au) else { + continue; + }; + let map = slots.get_or_insert_with(|| SlotMap::new(plan.picture.max_dpb_frames)); + let removed = plan.dpb.removed.clone(); + let dxva = plan_to_dxva(&plan, map, i as u32 + 1).expect("conversion"); + // The check runs BEFORE the deferred releases: the aliasing this + // guards against is a property of the SUBMISSION, and at submission + // time every removed picture is still live by construction. + assert!( + live.iter().all(|&(_, slot)| slot != dxva.setup_slot), + "{label} AU {i} decodes into a surface a live picture still holds" + ); + for &id in &dxva.release_after_decode { + assert!( + map.release(id), + "{label} AU {i}: deferred id {id} held no slot" + ); + } + live.retain(|&(id, _)| !removed.contains(&id)); + live.push((dxva.setup_id, dxva.setup_slot)); + } } } } diff --git a/crates/pf-dxvadec/tests/libav_picparams_parity.rs b/crates/pf-dxvadec/tests/libav_picparams_parity.rs index 59e874299..ff56a630b 100644 --- a/crates/pf-dxvadec/tests/libav_picparams_parity.rs +++ b/crates/pf-dxvadec/tests/libav_picparams_parity.rs @@ -540,6 +540,19 @@ fn our_h264_submissions() -> Vec { // libavcodec's `1 + report_id++` produces for a decoder that saw only this stream. let dxva = pf_dxvadec::plan_to_dxva(&plan, map, out.len() as u32 + 1) .unwrap_or_else(|e| panic!("AU {i} must convert: {e}")); + // The conversion's half of the deferral contract + // ([`pf_dxvadec::DecodePlanDxva::release_after_decode`]): a loop that converts + // AU after AU without applying it holds a surface per AU and runs the ledger + // dry. The vendored vector never puts a picture in both `RefFrameList` and + // `removed`, so this list is always empty HERE — applied anyway, because a + // harness that mirrors the caller only on the streams where it does not matter + // is a harness that would not notice the caller being wrong. + for &id in &dxva.release_after_decode { + assert!( + map.release(id), + "AU {i}: a deferred release named a picture holding no surface" + ); + } let packed = pf_dxvadec::pack(au, &dxva.slice_ranges, &mut mapping) .unwrap_or_else(|e| panic!("AU {i} must pack: {e}")); let unpadded = pf_dxvadec::packed_size(au, &dxva.slice_ranges).expect("packed size") as u32; diff --git a/crates/pf-vkdecode/src/decoder.rs b/crates/pf-vkdecode/src/decoder.rs index ac417aa5c..d5fafc0bb 100644 --- a/crates/pf-vkdecode/src/decoder.rs +++ b/crates/pf-vkdecode/src/decoder.rs @@ -822,175 +822,204 @@ impl VkH264Decoder { } let vk_plan = vk_plan.expect("the rebuilt session matches its own plan"); - let state = self.state.as_mut().expect("ensured above"); - // The per-AU active-reference gate: the session was created with - // maxActiveReferencePictures; binding more in one decode op would be a - // silent VUID violation on the drivers that matter most. - let max_active = state.session.config.max_active_references as usize; - if vk_plan.refs.len() > max_active { - return Err(VkDecodeError::Unsupported(format!( - "AU references {} pictures, session allows {max_active} active references", - vk_plan.refs.len() - ))); - } - - // Coincide binding sync: slots the planner released no longer bind their - // images (the pictures may still be pending/held — untouched), and the - // setup slot's PREVIOUS binding is cleared before it binds fresh. - let setup = usize::from(vk_plan.setup_slot); - if state.dpb.is_none() { - let mut held = vec![false; state.slot_image.len()]; - for (slot, _id) in state.slots.held() { - held[usize::from(slot)] = true; + // Everything from here to the deferred release below is ONE unit of ledger + // work. `plan_to_vk` has already committed this AU's setup assignment and + // handed back the removals it deliberately did NOT apply + // (`DecodePlanVk::release_after_decode`); until those are applied the slot + // map holds one picture too many. A `?` anywhere in the region would skip + // them and leak a slot per failed AU — four `?`s and three early `return`s + // could — so the region's Result is HELD and the release runs either way. + let submitted = (|| -> Result<(), VkDecodeError> { + let state = self.state.as_mut().expect("ensured above"); + // The per-AU active-reference gate: the session was created with + // maxActiveReferencePictures; binding more in one decode op would be a + // silent VUID violation on the drivers that matter most. + let max_active = state.session.config.max_active_references as usize; + if vk_plan.refs.len() > max_active { + return Err(VkDecodeError::Unsupported(format!( + "AU references {} pictures, session allows {max_active} active references", + vk_plan.refs.len() + ))); } - for (slot, binding) in state.slot_image.iter_mut().enumerate() { - if let Some(picture) = *binding { - if !held[slot] || slot == setup { - state.pool.pictures[picture].bound = false; - *binding = None; + + // Coincide binding sync: slots the planner released no longer bind their + // images (the pictures may still be pending/held — untouched), and the + // setup slot's PREVIOUS binding is cleared before it binds fresh. + let setup = usize::from(vk_plan.setup_slot); + if state.dpb.is_none() { + let mut held = vec![false; state.slot_image.len()]; + for (slot, _id) in state.slots.held() { + held[usize::from(slot)] = true; + } + for (slot, binding) in state.slot_image.iter_mut().enumerate() { + if let Some(picture) = *binding { + if !held[slot] || slot == setup { + state.pool.pictures[picture].bound = false; + *binding = None; + } } } } - } - // The decode target: a FREE pool image (never one a consumer holds — the - // whole point of the pool model). Exhaustion means the consumer owes - // more than HOLD_HEADROOM releases; no wait can free an image here. - let Some(dst) = state.pool.free_index() else { - debug!( - held = state.pool.held_total(), - "picture pool exhausted — release_frame owed" - ); - return Err(VkDecodeError::NoFreeSlot); - }; + // The decode target: a FREE pool image (never one a consumer holds — the + // whole point of the pool model). Exhaustion means the consumer owes + // more than HOLD_HEADROOM releases; no wait can free an image here. + let Some(dst) = state.pool.free_index() else { + debug!( + held = state.pool.held_total(), + "picture pool exhausted — release_frame owed" + ); + return Err(VkDecodeError::NoFreeSlot); + }; - // Cross-queue waits (the AVVkFrame contract): the dst image's last known - // timeline value (covers a presenter write-back after release), plus — - // coincide mode — every referenced image's value, so reference reads - // order after any presenter layout restore already reported back. - let mut waits: Vec<(vk::Semaphore, u64)> = Vec::new(); - { - let dst_pic = &state.pool.pictures[dst]; - if dst_pic.value > 0 { - waits.push((dst_pic.semaphore, dst_pic.value)); + // Cross-queue waits (the AVVkFrame contract): the dst image's last known + // timeline value (covers a presenter write-back after release), plus — + // coincide mode — every referenced image's value, so reference reads + // order after any presenter layout restore already reported back. + let mut waits: Vec<(vk::Semaphore, u64)> = Vec::new(); + { + let dst_pic = &state.pool.pictures[dst]; + if dst_pic.value > 0 { + waits.push((dst_pic.semaphore, dst_pic.value)); + } } - } - if state.dpb.is_none() { - for r in &vk_plan.refs { - if let Some(picture) = state.slot_image[usize::from(r.slot)] { - let pic = &state.pool.pictures[picture]; - if pic.value > 0 && !waits.iter().any(|(sem, _)| *sem == pic.semaphore) { - waits.push((pic.semaphore, pic.value)); + if state.dpb.is_none() { + for r in &vk_plan.refs { + if let Some(picture) = state.slot_image[usize::from(r.slot)] { + let pic = &state.pool.pictures[picture]; + if pic.value > 0 && !waits.iter().any(|(sem, _)| *sem == pic.semaphore) { + waits.push((pic.semaphore, pic.value)); + } } } } - } - let signal_value = state.pool.pictures[dst].value + 1; + let signal_value = state.pool.pictures[dst].value + 1; - // Command buffer + query slot for this submission. - let submission = state.submitted; - let cmd_index = (submission % state.ops.cmds.len() as u64) as usize; - if let Some((sem, value)) = state.cmd_marks[cmd_index] { - // SAFETY: live device; the token is a pool image's semaphore. - unsafe { wait_timeline(self.dev.ash(), sem, value, "command buffer reuse")? }; - } - let query_index = (submission % u64::from(state.ops.query_count)) as u32; + // Command buffer + query slot for this submission. + let submission = state.submitted; + let cmd_index = (submission % state.ops.cmds.len() as u64) as usize; + if let Some((sem, value)) = state.cmd_marks[cmd_index] { + // SAFETY: live device; the token is a pool image's semaphore. + unsafe { wait_timeline(self.dev.ash(), sem, value, "command buffer reuse")? }; + } + let query_index = (submission % u64::from(state.ops.query_count)) as u32; - // Upload the AU (recycles/grows against submission-completion tokens). - let device = self.dev.ash().clone(); - let mut poll = |token: &(vk::Semaphore, u64)| -> Result { - // SAFETY: live device; the token's semaphore is a pool semaphore. - let current = unsafe { device.get_semaphore_counter_value(token.0) } - .map_err(VkDecodeError::from)?; - Ok(current >= token.1) - }; - let device2 = self.dev.ash().clone(); - let mut wait = |token: &(vk::Semaphore, u64)| -> Result<(), VkDecodeError> { - // SAFETY: as above. - unsafe { wait_timeline(&device2, token.0, token.1, "bitstream slot drain") } - }; - // The bitstream buffer carries the SLICE NALUs only, concatenated — a - // real AU opens with AUD/SEI (and, at IDRs, SPS/PPS) NALUs, and feeding - // those to the VCN firmware inside the decode range HANGS it (the .25 - // `vcn_unified_0 ring timeout`; FFmpeg feeds slices-only for the same - // reason). `pack_slices` rebases the offsets into the packed buffer AND - // normalises each slice's Annex-B prefix to three bytes — the two go - // together by construction, see `crate::ring::three_byte_prefix`. - let plan_segments: Vec> = - plan.slices.iter().map(|s| s.data.clone()).collect(); - let Some(packed) = pack_slices(au, &plan_segments) else { - return Err(VkDecodeError::Unsupported( - "packed slice data exceeds the u32 offsets Vulkan submits".into(), - )); - }; - let slice_offsets = packed.offsets; - // SAFETY: live device; the segments are the plan's own in-bounds slice - // ranges (narrowed by the prefix normalisation, so still in bounds); every - // pending token is the completion signal of the submission that consumed - // the slot. - let upload = unsafe { + // Upload the AU (recycles/grows against submission-completion tokens). + let device = self.dev.ash().clone(); + let mut poll = |token: &(vk::Semaphore, u64)| -> Result { + // SAFETY: live device; the token's semaphore is a pool semaphore. + let current = unsafe { device.get_semaphore_counter_value(token.0) } + .map_err(VkDecodeError::from)?; + Ok(current >= token.1) + }; + let device2 = self.dev.ash().clone(); + let mut wait = |token: &(vk::Semaphore, u64)| -> Result<(), VkDecodeError> { + // SAFETY: as above. + unsafe { wait_timeline(&device2, token.0, token.1, "bitstream slot drain") } + }; + // The bitstream buffer carries the SLICE NALUs only, concatenated — a + // real AU opens with AUD/SEI (and, at IDRs, SPS/PPS) NALUs, and feeding + // those to the VCN firmware inside the decode range HANGS it (the .25 + // `vcn_unified_0 ring timeout`; FFmpeg feeds slices-only for the same + // reason). `pack_slices` rebases the offsets into the packed buffer AND + // normalises each slice's Annex-B prefix to three bytes — the two go + // together by construction, see `crate::ring::three_byte_prefix`. + let plan_segments: Vec> = + plan.slices.iter().map(|s| s.data.clone()).collect(); + let Some(packed) = pack_slices(au, &plan_segments) else { + return Err(VkDecodeError::Unsupported( + "packed slice data exceeds the u32 offsets Vulkan submits".into(), + )); + }; + let slice_offsets = packed.offsets; + // SAFETY: live device; the segments are the plan's own in-bounds slice + // ranges (narrowed by the prefix normalisation, so still in bounds); every + // pending token is the completion signal of the submission that consumed + // the slot. + let upload = unsafe { + state + .ring + .upload(&self.dev, au, &packed.segments, &mut poll, &mut wait)? + }; + + // Record + submit, signalling the dst image's next timeline value. + // SAFETY: live device; every handle recorded below belongs to this + // session generation, and the packed slices sit uploaded in the ring slot. + unsafe { + record_and_submit( + &self.dev, + &*self.lock, + state, + &vk_plan, + &slice_offsets, + &upload, + dst, + cmd_index, + query_index, + &waits, + signal_value, + )?; + } + + // Post-submit bookkeeping. + let dst_sem = state.pool.pictures[dst].semaphore; + state.pool.pictures[dst].value = signal_value; + state.pool.pictures[dst].pending = true; + if state.dpb.is_none() { + state.pool.pictures[dst].bound = true; + state.slot_image[setup] = Some(dst); + } + state.cmd_marks[cmd_index] = Some((dst_sem, signal_value)); + state.query_marks[query_index as usize] = submission; + state.submitted += 1; + state.last_submit = Some((dst_sem, signal_value)); state .ring - .upload(&self.dev, au, &packed.segments, &mut poll, &mut wait)? - }; + .pending + .set_pending(upload.slot, (dst_sem, signal_value)); - // Record + submit, signalling the dst image's next timeline value. - // SAFETY: live device; every handle recorded below belongs to this - // session generation, and the packed slices sit uploaded in the ring slot. - unsafe { - record_and_submit( - &self.dev, - &*self.lock, - state, - &vk_plan, - &slice_offsets, - &upload, - dst, - cmd_index, - query_index, - &waits, - signal_value, - )?; + // Refresh the per-slot reference cache from this AU's facts. + state.slot_refs[setup] = Some(vk_plan.setup_ref); + for r in &vk_plan.refs { + state.slot_refs[usize::from(r.slot)] = Some(r.std); + } + + self.pending.insert( + vk_plan.setup_id, + PendingPic { + image: dst, + submission, + query_slot: query_index, + timeline_value: signal_value, + crop: plan.picture.display_crop, + colour: plan.picture.colour, + poc: plan.picture.pic_order_cnt, + is_idr: plan.picture.is_idr, + recovery, + decode_order, + }, + ); + Ok(()) + })(); + + // The slots this AU's own 8.2.5 marking retired while the decode op still + // BOUND them (`DecodePlanVk::release_after_decode`). Held through the + // conversion, the coincide binding sync and the submission, so none of the + // three could take them; freed now that the op is recorded, so the next AU + // may have them. Their images stay pinned by `bound` until that AU's sync, + // the same one-frame grace every other released slot's image gets. + // + // This runs on the failure paths too, and must: the removals are the + // planner's verdict on pictures that left the DPB, which nothing this AU + // does can undo. + if let Some(state) = self.state.as_mut() { + for &id in &vk_plan.release_after_decode { + if !state.slots.release(id) { + trace!(id, "deferred release of an id the slot map no longer holds"); + } + } } - - // Post-submit bookkeeping. - let dst_sem = state.pool.pictures[dst].semaphore; - state.pool.pictures[dst].value = signal_value; - state.pool.pictures[dst].pending = true; - if state.dpb.is_none() { - state.pool.pictures[dst].bound = true; - state.slot_image[setup] = Some(dst); - } - state.cmd_marks[cmd_index] = Some((dst_sem, signal_value)); - state.query_marks[query_index as usize] = submission; - state.submitted += 1; - state.last_submit = Some((dst_sem, signal_value)); - state - .ring - .pending - .set_pending(upload.slot, (dst_sem, signal_value)); - - // Refresh the per-slot reference cache from this AU's facts. - state.slot_refs[setup] = Some(vk_plan.setup_ref); - for r in &vk_plan.refs { - state.slot_refs[usize::from(r.slot)] = Some(r.std); - } - - self.pending.insert( - vk_plan.setup_id, - PendingPic { - image: dst, - submission, - query_slot: query_index, - timeline_value: signal_value, - crop: plan.picture.display_crop, - colour: plan.picture.colour, - poc: plan.picture.pic_order_cnt, - is_idr: plan.picture.is_idr, - recovery, - decode_order, - }, - ); + submitted?; // The plan's DPB verdicts over the pending map: outputs become ready // frames (their images move pending → held until released); diff --git a/crates/pf-vkdecode/src/pic.rs b/crates/pf-vkdecode/src/pic.rs index 2e7c0edad..df8aa7724 100644 --- a/crates/pf-vkdecode/src/pic.rs +++ b/crates/pf-vkdecode/src/pic.rs @@ -12,7 +12,6 @@ use ash::vk::native as hh; use pf_bitstream::h264::AuPlan; use pf_bitstream::h264::PicId; use pf_bitstream::h264::RefPic; -use tracing::trace; use crate::slots::SlotError; use crate::slots::SlotMap; @@ -56,6 +55,35 @@ pub struct DecodePlanVk { pub setup_is_reference: bool, /// The unique referenced pictures across all slices, in first-appearance order. pub refs: Vec, + /// Slots this access unit's own end-of-picture bookkeeping retires while the + /// decode op still BINDS them. Release them once that op is recorded — never + /// inside the conversion, and never dropped. + /// + /// The H.264 twin of [`crate::pic_av1::DecodePlanVkAv1::release_after_decode`], + /// and it exists for exactly the same reason: [`SlotMap::assign`] takes the lowest + /// free slot, so a release here hands the setup assignment two lines later the + /// slot a reference of this very AU still occupies. `pf_bitstream`'s `H264Planner` + /// snapshots `dpb_refs` in `begin_picture`, BEFORE `finish_picture` runs 8.2.5's + /// marking and C.4.5.3's bump, so a picture the sliding window unmarks and the + /// bump evicts is in both `dpb_refs` and `dpb.removed` for one AU — which needs + /// the eviction to be of an already-OUTPUT picture, i.e. low-delay H.264. + /// + /// **Measured 2026-08-07 on this rung as well as the DXVA one:** 297 of 300 AUs of + /// every stream a punktfunk host emits, at 720p, 1080p and 2160p alike. See + /// [`pf_dxvadec::pic::DecodePlanDxva::release_after_decode`]'s docs for the full + /// measurement and for why `test-25fps.h264` measures zero. + /// + /// Both of this rung's DPB modes break on it, differently and neither loudly: + /// in DISTINCT mode `slot_view` hands the aliased reference the same DPB array + /// layer the setup writes, a read-write alias of one subresource; in COINCIDE mode + /// the binding sync clears `slot_image[setup]` (it is the setup slot now) and the + /// reference resolves to no bound image at all, dropping out of `pReferenceSlots` + /// with a `trace!` and nothing else. + /// + /// The caller must apply these on its FAILURE paths too. They are slot-ledger + /// bookkeeping the planner already committed, not something this AU's fate can + /// undo — dropping them leaks a slot per AU and reaches `SlotError::Full`. + pub release_after_decode: Vec, } /// Conversion failures. Stream damage never lands here — pf-bitstream degrades it to @@ -162,11 +190,14 @@ fn ref_info(rp: &RefPic) -> hh::StdVideoDecodeH264ReferenceInfo { /// reference, e.g. the sliding window dropping the oldest short-term reference, /// so `removed` must not be applied before the lists are mapped; /// 3. slice offsets are validated (read-only); -/// 4. `removed` is applied — removals were real regardless of this AU's fate — and -/// the setup slot is assigned last (its failures are caller bugs; nothing is ever -/// half-applied). Released slots become assignable to later pictures; keeping the -/// underlying images alive until in-flight decodes complete is WP-B's -/// synchronization, not this map's. +/// 4. the setup slot is assigned last (its failures are caller bugs; nothing is ever +/// half-applied). `removed` is NOT applied here — it leaves as +/// [`DecodePlanVk::release_after_decode`] for the caller to apply once the decode +/// op is recorded, because applying it now would give the assignment back a slot +/// this AU's own references occupy (that field's docs carry the measurement). +/// Released slots become assignable to later pictures; keeping the underlying +/// images alive until in-flight decodes complete is WP-B's synchronization, not +/// this map's. pub fn plan_to_vk( plan: &AuPlan, slots: &mut SlotMap, @@ -276,24 +307,26 @@ pub fn plan_to_vk( ); } - // Mutations LAST, after every fallible step above (fn docs). Removals first — - // they were real regardless of this AU's fate — then the setup assignment. + // Mutations LAST, after every fallible step above (fn docs). + // + // The removals are handed back rather than applied: releasing one here returns + // its slot to the setup assignment below, and this AU's own references sit in + // those slots (`DecodePlanVk::release_after_decode`). // // The AU's own picture can itself appear in `removed`: a non-reference picture // with no free frame buffer bypasses the DPB and is stored-and-evicted within // one plan. Its slot must still exist for the decode itself, so it is assigned - // here and released right after — see `DecodePlanVk::setup_is_reference`. + // here and released right after — see `DecodePlanVk::setup_is_reference`. That + // is the one removal that is NOT deferred: handing the caller the slot being + // decoded into is the very aliasing the deferral exists to prevent. let setup_evicted = plan.dpb.removed.contains(&setup_id); - for &id in &plan.dpb.removed { - if id == setup_id { - continue; - } - if !slots.release(id) { - // Tolerated but never silent: reachable only when the caller skipped - // feeding an AU's plan through this map. - trace!(id, "DpbUpdate removed an id this SlotMap never assigned"); - } - } + let release_after_decode: Vec = plan + .dpb + .removed + .iter() + .copied() + .filter(|id| *id != setup_id) + .collect(); let setup_slot = slots.assign(setup_id)?; if setup_evicted { slots.release(setup_id); @@ -307,6 +340,7 @@ pub fn plan_to_vk( setup_id, setup_is_reference: pic.is_reference, refs, + release_after_decode, }) } @@ -398,7 +432,10 @@ mod tests { }); let vk = plan_to_vk(&plan, slots, 0).expect("the clean vector converts"); - // Binding sync: released slots unbind; the setup slot rebinds fresh. + // Binding sync: released slots unbind; the setup slot rebinds fresh. The + // deferred releases have deliberately NOT run yet — that is the whole + // point of `DecodePlanVk::release_after_decode`, and doing it in the wrong + // order here would unbind the images this AU's own references read. let setup = usize::from(vk.setup_slot); let mut held_slots = vec![false; slot_image.len()]; for (slot, _id) in slots.held() { @@ -425,6 +462,12 @@ mod tests { slot_image[setup] = Some(dst); pending.insert(vk.setup_id, dst); + // Post-submit: the slots the conversion held back, exactly where + // `Decoder::decode` applies them. + for id in &vk.release_after_decode { + assert!(slots.release(*id), "a deferred id held no slot"); + } + // Settle: outputs deliver to the consumer; removed-never-output free. for id in &plan.dpb.outputs { if let Some(picture) = pending.remove(id) { @@ -522,10 +565,28 @@ mod tests { ); // Mirror the map's bookkeeping: record the new picture, drop the removed. + // + // The removals are dropped only after the deferred releases run, because + // that is when the MAP drops them — before that the conversion is still + // holding them so this AU's submission can name their slots + // (`DecodePlanVk::release_after_decode`). let stored = plan.dpb.stored.unwrap(); assert_eq!(vk.setup_id, stored); assert_eq!(vk.setup_is_reference, plan.picture.is_reference); held.insert(stored, vk.setup_slot); + assert_eq!( + vk.release_after_decode, + plan.dpb + .removed + .iter() + .copied() + .filter(|id| *id != stored) + .collect::>(), + "the deferral is the plan's whole `removed` list less the stored id" + ); + for id in &vk.release_after_decode { + assert!(slots.release(*id), "a deferred id held no slot"); + } for id in &plan.dpb.removed { held.remove(id); } @@ -799,13 +860,22 @@ mod tests { fn a_full_dpb_bump_reuses_the_slot_but_the_pool_model_binds_a_fresh_image() { // Depth-1 DPB (Level 1 at 320x240 ⇒ max_dpb_frames 1, capacity 2): every // stored P evicts the previous picture, and that picture's id lands in - // BOTH `outputs` and `removed` of the SAME plan — so `plan_to_vk` frees - // the evicted slot and immediately re-assigns it as this AU's setup. - // That SLOT reuse is fine and expected; the picture-pool model's whole - // point is that the re-activated slot binds a DIFFERENT free image, so - // the delivered picture's image is never the new decode target while the - // consumer holds it (the HIGH overwrite bug of the adversarial round, - // and the .25 field failure's class). + // BOTH `outputs` and `removed` of the SAME plan. + // + // ⚠ This test used to assert that `plan_to_vk` freed the evicted slot and + // re-assigned it as THIS AU's setup, calling that "the planner's normal + // behaviour". It was not: AU1 is a P picture that REFERENCES the picture it + // was evicting, so the submission named one slot as both `pSetupReferenceSlot` + // and a reference — a decode into the surface being predicted from. The same + // defect the AV1 rung was fixed for on 2026-08-07, authored here in miniature + // and asserted as correct. `DecodePlanVk::release_after_decode` is the fix, and + // this depth-1 stream is its tightest possible case: capacity 2, so the setup + // has exactly one slot to go to and it is the spare. + // + // What the test still proves, and what it was really written for, is the pool + // decoupling: the delivered picture's IMAGE is never the new decode target + // while the consumer holds it (the HIGH overwrite bug of the adversarial + // round, and the .25 field failure's class). let sps = SpsBuilder::new() .seq_parameter_set_id(0) .profile_idc(Profile::Main) @@ -851,16 +921,39 @@ mod tests { .unwrap(); assert!(p1.dpb.outputs.contains(&vk0.setup_id) && p1.dpb.removed.contains(&vk0.setup_id)); let vk1 = plan_to_vk(&p1, &mut slots, 0).unwrap(); - assert_eq!( + + // AU1 REFERENCES the very picture it evicts — so the eviction is deferred and + // the setup goes to the spare slot instead. Without the deferral these two + // assertions are what fails, and they are the whole defect in two lines. + assert!( + vk1.refs.iter().any(|r| r.id == vk0.setup_id), + "AU1 must reference the picture it evicts, or this proves nothing" + ); + assert_ne!( vk1.setup_slot, vk0.setup_slot, - "slot reuse across the bump is the planner's normal behaviour" + "the setup must not take the slot of a picture this AU still references" + ); + for r in &vk1.refs { + assert_ne!(r.slot, vk1.setup_slot, "a reference aliases the setup slot"); + } + assert_eq!( + vk1.release_after_decode, + vec![vk0.setup_id], + "the eviction is handed back, not applied" ); - // Binding sync: the re-activated slot drops its old binding; picture 0's - // image is now delivered to the consumer (held), NOT freed. + // Binding sync: the setup slot is fresh, so nothing is unbound for it; + // picture 0's image is delivered to the consumer (held), NOT freed. Its slot + // is still HELD at this point — which is exactly what keeps its image bound + // while the decode op reads it as a reference. + assert!( + slots + .held() + .any(|(slot, id)| slot == vk0.setup_slot && id == vk0.setup_id), + "the referenced picture must still hold its slot through the submission" + ); bound[img0] = false; held[img0] += 1; // outputs → delivered, consumer holds it - slot_image[usize::from(vk1.setup_slot)] = None; // The pool hands the re-activated slot a FRESH image — never image 0. let img1 = free(&bound, &held).expect("headroom guarantees a free image"); @@ -872,6 +965,17 @@ mod tests { bound[img1] = true; slot_image[usize::from(vk1.setup_slot)] = Some(img1); + // Post-submit: the deferred release lands, and NOW the evicted slot is free — + // a picture later than this submission may have it, which is the only thing + // the deferral ever postponed. + for id in &vk1.release_after_decode { + assert!(slots.release(*id)); + } + assert!( + !slots.held().any(|(slot, _)| slot == vk0.setup_slot), + "the deferred release must actually free the slot" + ); + // Once the consumer releases frame 0, image 0 returns to the free list. held[img0] -= 1; assert_eq!(free(&bound, &held), Some(img0)); diff --git a/crates/pf-vkdecode/tests/data/lowdelay-640x480.h264 b/crates/pf-vkdecode/tests/data/lowdelay-640x480.h264 new file mode 100644 index 0000000000000000000000000000000000000000..5b56588c9ffad9d9c65e25443e6d66d850a8dc09 GIT binary patch literal 282407 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 z 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¨)slz?A^AE*S^Q} z>hrX27NO)gTPSDan?0uj;~|9}!wExEPQyrTAKd@9XI_EJiH7@|=NqnHhuxBzi7Xe$AuJZoYyGF8aoA3rmM<*_di@D%0(QJkpL>3z|~w zecc~CbFTMTv(ZmWV=&j^#h~8wyo~Cauvw_KDr-1$b`v02nPx2qe>()x`Q0EyUFS}; z1emeFD`EJ2KHst|*Xxo2yOnEGAUjt=ZeXG64-)4thMI53ROK;_28L+;!YCH!#vktJ z^^`on`7CxRJ0!JTh==@JEV<7`{RPvwkEuQike{lO>puj=w9gGM11ORB*hc#s!=r^iVfmyBP2%zX@`xa z_Lv)D98)p4U|-)@+~KW0@`BxZ;hcn66D)EJ&^1&22S^f4I+}D>2)o(NS1U?R4r5c? zY6Sk3b5I;|C(Q4L@p^­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

%^mq*3xwH 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Ị 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 literal 0 HcmV?d00001 diff --git a/crates/pf-vkdecode/tests/gpu_parity.rs b/crates/pf-vkdecode/tests/gpu_parity.rs index e40a69a7a..aba27889f 100644 --- a/crates/pf-vkdecode/tests/gpu_parity.rs +++ b/crates/pf-vkdecode/tests/gpu_parity.rs @@ -46,6 +46,18 @@ //! prefix for a driver to mis-skip and no second framing to test (see //! `common::split_av1_aus`). Its absence is deliberate. //! +//! # The two legs that decode OUR OWN streams +//! +//! [`LOWDELAY_H264`] and [`LOWDELAY_H265`] are not conformance vectors — they are +//! `punktfunk-host spike` output, vendored because a conformance vector proves +//! conformance to itself and the encoder we ship behind is a different stream. The +//! H.264 one is here because it caught a defect the vector is structurally blind to +//! (117 of its 120 access units named one surface as both the decode target and a +//! reference); the H.265 one is here because HEVC is EXEMPT from that defect for a +//! structural reason, and an exemption with no stream behind it is how the H.264 +//! defect survived two milestones. Both are backed by a non-ignored CPU guard that +//! asserts the stream still has the property it was vendored for. +//! //! # Why the AV1 leg exists at all //! //! Because until it did, the AV1 rung had no pixel evidence whatsoever. An @@ -139,6 +151,51 @@ const GOLDENS_LOWDELAY: &str = include_str!("data/lowdelay-640x480.nv12.sha256") const LOWDELAY_FRAME_COUNT: usize = 120; const DISPLAY_LOWDELAY: (u32, u32) = (640, 480); +/// **Our own host's HEVC**, the twin of [`LOWDELAY_H264`] — and the one vendored to +/// keep an exemption honest rather than to catch a defect. +/// +/// H.264 and AV1 both had to defer their slot releases past the decode op because +/// their planners snapshot the marked DPB BEFORE the marking that retires a picture. +/// `H265Planner` snapshots AFTER `decode_rps`, so an RPS-dropped picture is never in +/// the set `RefPicList`/`pReferenceSlots` is built from, and the HEVC conversions +/// still release inline. That argument is correct — and it was, until this stream, +/// backed by `test-25fps.h265` (which REORDERS, so it cannot reach the shape at all) +/// plus one throwaway measurement. +/// +/// This stream reaches the shape. `sps_max_dec_pic_buffering_minus1 = 4` against four +/// pictures marked in steady state, `sps_max_num_reorder_pics = 0`: 115 of its 120 +/// access units retire exactly one picture, and **all 115 of them would alias** if +/// the snapshot moved above `decode_rps`. Measured `removed ∩ dpb_refs` is 0 of 120, +/// so the exemption is a measurement on our own encoder's output rather than a +/// re-derivable argument. +/// +/// ⚠ On THIS rung that counterfactual is about the planner, not about +/// [`pf_vkdecode::plan_to_vk_h265`]: Vulkan's `pReferenceSlots` is spec-defined as the +/// slots the decode operation uses, so the conversion binds `plan.rps` — the three +/// current sets, which `decode_rps` itself derives — and never reads `dpb_refs` at +/// all. The DXVA rung is the one that binds the whole marked DPB (`RefPicList` is +/// spec-defined that way, and an RFI long-term anchor must survive in it), so it is +/// the rung a moved snapshot would actually alias on; +/// `pf_dxvadec::pic_h265`'s tests drive that counterfactual through the conversion. +/// What the leg below adds on this rung is the thing no HEVC leg here had: PIXELS +/// from our own encoder, under a DPB that evicts and reuses a slot on 115 of 120 +/// access units instead of a vector whose reordering keeps eviction slack. +/// +/// Provenance, the `punktfunk-host spike` command and the two-build ffmpeg +/// cross-check are in the golden file's header, as for the H.264 sibling. +const LOWDELAY_H265: &[u8] = include_bytes!("data/lowdelay-640x480.h265"); +const GOLDENS_LOWDELAY_H265: &str = include_str!("data/lowdelay-640x480-h265.nv12.sha256"); + +/// The HEVC low-delay stream's own frame count and display region. +/// +/// Deliberately NOT shared with [`LOWDELAY_FRAME_COUNT`]/[`DISPLAY_LOWDELAY`] even +/// though the two fixtures agree today: they are separate files from separate +/// encoder configurations, and one regenerated at another size must fail on its own +/// leg rather than silently redefine the other's geometry. Same reason +/// [`DISPLAY_H264`] and [`DISPLAY_H265`] are two constants holding 320x240. +const LOWDELAY_H265_FRAME_COUNT: usize = 120; +const DISPLAY_LOWDELAY_H265: (u32, u32) = (640, 480); + /// The Main 10 vector is 50 display frames. const MAIN10_FRAME_COUNT: usize = 50; @@ -932,6 +989,39 @@ fn main10_every_frame_hashes_bit_identical_to_libavcodec() { ); } +/// The HEVC twin of [`low_delay_host_h264_every_frame_hashes_bit_identical_to_libavcodec`]: +/// **our own host's HEVC**, in the shape the vendored vector cannot produce. +/// +/// Its job is the opposite of the H.264 leg's. That one exists because the rung was +/// broken and only this stream shape could show it. This one exists because until now +/// no HEVC leg anywhere had decoded a single frame our own encoder produced: both +/// existing legs run vendored vectors, and the H.264 sibling is the standing proof +/// that a vector's silence about a stream shape is not evidence. +/// +/// What it exercises that `h265_every_frame_hashes_bit_identical_to_libavcodec` does +/// not: a five-picture DPB with four references marked and no reordering, so the +/// `SlotMap` retires and reissues a slot on 115 of the 120 access units, back to back, +/// with the decode target taking the slot freed in the same access unit. The vector +/// reorders, which keeps that eviction slack and never puts the two together. +/// +/// It is NOT the leg that would catch a moved `dpb_snapshot()` — see [`LOWDELAY_H265`] +/// for why that lands on the DXVA rung instead, and +/// [`the_low_delay_h265_stream_agrees_with_its_goldens_and_keeps_the_exemption_falsifiable`] +/// for the guard that keeps the planner property itself pinned, on CPU, in ordinary CI. +#[test] +#[ignore = "needs a Vulkan Video H.265 decode device (fleet boxes; see module docs)"] +fn low_delay_host_h265_every_frame_hashes_bit_identical_to_libavcodec() { + h265_parity_run( + &common::split_h265_aus(LOWDELAY_H265), + GOLDENS_LOWDELAY_H265, + LOWDELAY_H265_FRAME_COUNT, + 0, + EXPECTED_FORMAT, + DISPLAY_LOWDELAY_H265, + "H.265 (low-delay host stream)", + ); +} + /// The HEVC leg of the production prefix form — the one that would have caught /// the shipped defect. See [`h264_four_byte_start_codes_decode_bit_identically`]. #[test] @@ -1725,6 +1815,160 @@ fn the_low_delay_stream_agrees_with_its_goldens_and_still_exercises_the_aliasing ); } +/// The HEVC low-delay stream's CPU guard — the twin of the H.264 one above, with the +/// extra assertion HEVC needs and H.264 does not. +/// +/// H.264's guard pins that the stream still ALIASES (117 of 120), because its GPU leg +/// exists to catch a defect. HEVC's leg exists to keep an exemption from rotting, so +/// pinning `both == 0` alone would be exactly the vacuous check `fd6241a2` called out: +/// zero is also what a stream that never removes anything reports, and what a stream +/// that reorders reports. So this pins three numbers instead: +/// +/// - **115 access units remove a picture** — the stream reaches the DPB pressure at all; +/// - **0 of them intersect `dpb_refs`** — the exemption, measured; +/// - **115 of them WOULD intersect** a snapshot taken before `decode_rps`. +/// +/// The third is what makes the second worth having. `pre_rps_marked(N)` is exact rather +/// than approximate: `begin_picture` runs `decode_rps` → `update_dpb_before_decoding` → +/// `dpb_snapshot`, and the only thing that happens between AU N-1's snapshot and AU N's +/// `decode_rps` is `finish_picture(N-1)` storing its picture marked short-term. So the +/// marked set AU N's RPS sees is exactly `dpb_refs(N-1) ∪ {stored(N-1)}`, which is what +/// `dpb_snapshot()` would have returned from the other side of that call. +#[test] +fn the_low_delay_h265_stream_agrees_with_its_goldens_and_keeps_the_exemption_falsifiable() { + use pf_bitstream::h265::H265Planner; + + let goldens = golden_hashes(GOLDENS_LOWDELAY_H265); + assert_goldens_are_a_real_set( + &goldens, + LOWDELAY_H265_FRAME_COUNT, + "data/lowdelay-640x480-h265.nv12.sha256", + ); + + let aus = common::split_h265_aus(LOWDELAY_H265); + assert_eq!(aus.len(), LOWDELAY_H265_FRAME_COUNT); + + let mut planner = H265Planner::new(); + let mut outputs = 0usize; + let mut iraps = 0usize; + let mut with_removals = 0usize; + let mut both = 0usize; + let mut would_alias = 0usize; + let mut first_sps = None; + // The marked DPB as AU N's `decode_rps` finds it: AU N-1's snapshot plus the + // picture AU N-1 stored. See the doc comment for why this is exact. + let mut pre_rps_marked: Vec = Vec::new(); + for (index, au) in aus.iter().enumerate() { + let plan = planner.plan_au(au).unwrap_or_else(|e| { + panic!("AU {index}: the low-delay HEVC stream must plan, got {e:?}") + }); + outputs += plan.dpb.outputs.len(); + iraps += usize::from(plan.picture.is_irap); + if !plan.dpb.removed.is_empty() { + with_removals += 1; + } + both += plan + .dpb + .removed + .iter() + .filter(|id| plan.dpb_refs.iter().any(|r| r.id == **id)) + .count(); + would_alias += plan + .dpb + .removed + .iter() + .filter(|id| pre_rps_marked.contains(id)) + .count(); + + // The picture shape both HEVC legs hard-code: `probe_stream_support(1, 0)` + // and an NV12 pool. Fail here, on CPU, rather than as a confusing + // hardware-only refusal. + assert_eq!( + ( + plan.picture.chroma_format_idc, + plan.picture.bit_depth_luma_minus8 + ), + (1, 0), + "AU {index}: the low-delay HEVC stream must stay Main 4:2:0 8-bit" + ); + if index == 0 { + assert!(plan.picture.is_idr, "the stream opens with an IDR"); + assert_eq!( + (plan.picture.coded_width, plan.picture.coded_height), + DISPLAY_LOWDELAY_H265, + "the stream is 640x480" + ); + assert_eq!( + ( + plan.picture.display_crop.x, + plan.picture.display_crop.y, + plan.picture.display_crop.width, + plan.picture.display_crop.height, + ), + (0, 0, DISPLAY_LOWDELAY_H265.0, DISPLAY_LOWDELAY_H265.1), + "640 and 480 are both multiples of MinCbSizeY, so there is no \ + conformance window and the coded size IS what the goldens hashed" + ); + } + first_sps.get_or_insert(( + plan.picture.max_dpb_frames, + plan.sps.max_num_reorder_pics[usize::from(plan.sps.max_sub_layers_minus1)], + )); + + pre_rps_marked = plan.dpb_refs.iter().map(|r| r.id).collect(); + if let Some(id) = plan.dpb.stored { + assert!( + plan.picture.is_reference, + "AU {index}: every picture of this stream is a reference — a \ + sub-layer non-reference picture would break the pre-RPS \ + reconstruction below" + ); + pre_rps_marked.push(id); + } + } + outputs += planner.flush().outputs.len(); + + assert_eq!( + outputs, + goldens.len(), + "the planner outputs {outputs} pictures but the goldens carry {} hashes", + goldens.len() + ); + assert_eq!( + iraps, 1, + "the stream holds exactly one IRAP (the opening IDR); a CRA/BLA would make \ + RASL skips reachable and the expected frame count needs rederiving" + ); + assert_eq!( + first_sps, + Some((5, 0)), + "DPB depth and sps_max_num_reorder_pics — a five-picture DPB against the four \ + pictures 8.3.2 keeps marked, with no reordering, is what puts an RPS drop and \ + the eviction it causes in one access unit" + ); + + assert_eq!( + with_removals, 115, + "the stream must still retire a picture on nearly every access unit; without \ + that the two numbers below are both trivially zero" + ); + assert_eq!( + both, 0, + "{both} picture(s) are in an access unit's own marked DPB AND removed by it. \ + That is the H.264/AV1 aliasing precondition, and HEVC is supposed to be \ + structurally incapable of it — `H265Planner`'s snapshot has moved ahead of \ + `decode_rps`. Restore the ordering, or give the HEVC conversions the \ + `release_after_decode` deferral the other two carry; do NOT relax this number" + ); + assert_eq!( + would_alias, 115, + "the fixture must stay CAPABLE of exposing the defect it is here to rule out. \ + A regenerated stream that reordered, or that carried a DPB deeper than its \ + reference count, would report 0 here — and the zero above would then prove \ + nothing at all, exactly as `test-25fps.h264` proved nothing for two milestones" + ); +} + #[test] fn the_main10_vector_is_ten_bit_and_agrees_with_its_goldens() { use pf_bitstream::h265::H265Planner; From e8a7a1e6afac0f615f4fab5fb2078d0be516d0c3 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 7 Aug 2026 22:55:25 +0200 Subject: [PATCH 16/21] =?UTF-8?q?fix(client/vaapi):=20the=20third=20rung?= =?UTF-8?q?=20does=20NOT=20alias=20=E2=80=94=20and=20now=20it=20cannot=20s?= =?UTF-8?q?tart=20to?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The D3D11VA and Vulkan rungs both decoded into a surface they were predicting from, on 117 of 120 access units of our own host's low-delay H.264 (`1c54d099` for AV1, `834b2443` for H.264). `pf-vaadec` feeds `reference_frames` from the same `plan.dpb_refs` snapshot, releases its whole `removed` list inline exactly as the two broken conversions did, and neither fix commit touched it. It is still exempt — this is the evidence, and the thing that keeps it true. **Measured on the CPU, no GPU needed.** `walk_for_aliasing` drives the planner and `plan_to_va` over both streams and counts four shapes. On `lowdelay-640x480.h264` the aliasing PRECONDITION is fully present: 117 of 120 access units remove a picture their own `dpb_refs` still names, and on the same 117 the setup picture is handed the slot of a picture that access unit READS — the D3D11VA/Vulkan defect verbatim, in this conversion, today. On the vendored conformance vector both counts are 0, which is why that vector proved nothing on two other backends for two milestones. Aliased submissions: **0 on both**. **Why.** A slot is not a surface here. `plan_to_va` never invents one — every reference it can name is read out of the `surfaces` table it is handed — and the decode target is a separate parameter the caller takes from OUTSIDE that table. `setup_surface` reaches the submission at exactly one field per codec (H.264/H.265 `curr_pic.picture_id`, AV1 `current_frame` and `current_display_picture`); HEVC is doubly safe, because its per-slice `RefPicList` stores an INDEX into `reference_frames` rather than a surface. AV1's documented substitution fallback is the one place the target can be named as a reference, and only where the store resolved nothing at all to prefer. **The exemption was incidental; it is structural now.** It needs the reference table and the decode target to come from ONE snapshot of the bindings, and the rung had that only by writing `free_surface()` and `surface_table()` adjacently at three call sites. Split them and this rung acquires the defect exactly: the table must be the PRE-removal one (that is where the references are), while a free list consulted after the removals offers precisely the displaced picture's surface. `Session::acquire_target` now returns the index, the surface and the table together from `&self`, so a later edit cannot move one call and not the other. No behaviour change: same order, same values, same refusal message. Tests. `no_submission_names_its_decode_target_as_one_of_its_own_references` (both streams, 0) with `taking_the_decode_target_from_the_slot_table_aliases_on_the_low_delay_stream` as the counterfactual that reproduces the defect on 117 of 120 — so the walk demonstrably CAN see it when it is there. `the_low_delay_stream_reassigns_slots_whose_pictures_it_still_reads` pins 0/250 and 117/120 so neither can drift silently. `the_decode_target_can_never_be_a_surface_the_reference_table_names` sweeps every binding state a 4-surface/3-slot pool can hold, and `taking_the_free_surface_after_the_removals_would_hand_out_a_referenced_surface` is the ordering counterfactual. ⚠ One existing test lost a VACUOUS half. `the_setup_picture_routinely_inherits_a_just_freed_slot` asserted the decode target was never also a reference while handing every picture its own never-reused surface id — distinct integers cannot collide, so that assertion could not fail whatever the conversion did. Its real measurement (225 of 250 access units reuse a just-freed slot, which is why the target is a parameter) is kept; the collision half is gone, and the doc says where the question is actually answered and why a recycling pool is what it takes to answer it. Gates, run on `.25` (Radeon 780M, radeonsi, Mesa 26.0.3, VA-API 1.23), this rung being Linux-only: `cargo fmt --all -- --check`; `cargo clippy -p pf-client-core -p pf-vaadec --all-targets --features sdl3/build-from-source -- -D warnings`; `cargo test -p pf-client-core --lib --features sdl3/build-from-source` (171 passed); the same filtered to `video_vaapi_native` with `--include-ignored` (18 passed); `cargo test -p pf-vaadec` (48 passed). Plus the pf-lxcheck2 container for the cross-platform half — fmt, clippy and `cargo test -p pf-vaadec`, all clean. All four VAAPI legs still decode with the refactor in place, not one access unit refused: H.264 225 of 250 access units delivering a frame, H.265 204 of 250, HEVC Main 10 45 of 50 (P010), AV1 250 of 250 — the same counts and the same tiled modifier 0x200000010401b04 those legs recorded before it. ⚠ The H.26x legs live on `fix/vaapi-h264-h265-hardware-proof`, not on this branch, so they were run by overlaying that commit's test module onto the scratch tree; only the AV1 leg and the libva probe are reachable from here. This is a decode measurement, not frame-hash parity — the rung exports a driver-tiled DRM-PRIME dmabuf, so there is no CPU-readable image to hash. The alias assertions above are the real evidence and they need no device. ⚠ NOT taken: `finish`'s `outputs.last()`, which ships one frame per access unit and drops the rest of what a bump displaces (225/204/45 against 250/250/50), with no end-of-stream flush. It cannot bite punktfunk — hosts emit zero-reorder output, so `outputs` never holds more than one picture — and fixing it changes `decode()`'s one-frame-per-access-unit contract with the pump (it wants a deliverable queue, which `video_vk_native` already keeps) plus an end-of-stream flush and the `keyframe`-labels-the-access-unit defect in the same function. It is recorded and asserted on that other branch, whose three delivered-count assertions any fix has to move in the same commit; doing that from here, blind to them, would be worse than leaving it. --- .../pf-client-core/src/video_vaapi_native.rs | 223 ++++++++++- crates/pf-vaadec/src/pic.rs | 352 +++++++++++++++--- 2 files changed, 508 insertions(+), 67 deletions(-) diff --git a/crates/pf-client-core/src/video_vaapi_native.rs b/crates/pf-client-core/src/video_vaapi_native.rs index cd0a725a3..5e0bde005 100644 --- a/crates/pf-client-core/src/video_vaapi_native.rs +++ b/crates/pf-client-core/src/video_vaapi_native.rs @@ -47,6 +47,56 @@ //! presenter holds simply stays off the free list until its release token comes //! back. A surface is free when no live picture is bound to it AND no consumer holds //! it — two conditions, tracked separately, because they end at different times. +//! +//! # Why this rung is exempt from the decode-into-a-reference defect +//! +//! The D3D11VA and Vulkan rungs both had to grow a `release_after_decode` deferral: +//! their conversions released the pictures an access unit displaces INSIDE the +//! conversion, then assigned the decode target a slot, and [`pf_vaadec::SlotMap::assign`] +//! handed back the slot just vacated — so one surface was named as both the decode +//! target and one of that submission's own references. On H.264 that fired on **117 of +//! 120** access units of a punktfunk host's low-delay output. +//! +//! `pf-vaadec`'s conversions still release inline and this rung is still exempt, for a +//! reason that is a property of the interface rather than of any stream: **a slot is +//! not a surface here.** `plan_to_va` never invents a surface — every reference it can +//! name is read out of the `surfaces` table it is handed — and the decode target is a +//! separate parameter the caller takes from OUTSIDE that table. Two things carry that, +//! and both are load-bearing: +//! +//! * [`Session::acquire_target`] returns the target and the table **together, from one +//! snapshot**, because they are only safe together. A free surface is by construction +//! a surface no slot binds, and the table is exactly what the slots bind, so the +//! target cannot be in it. Taking the two at different moments — the table before +//! this access unit's removals, where references must resolve, and the free surface +//! after them, where the displaced picture's surface has become free — is precisely +//! the defect, and `taking_the_free_surface_after_the_removals_would_hand_out_a_ +//! referenced_surface` shows it happening. +//! * The conversion's half is pinned across every platform by `pf-vaadec`'s +//! `no_submission_names_its_decode_target_as_one_of_its_own_references`, driven over +//! the same low-delay stream, with +//! `taking_the_decode_target_from_the_slot_table_aliases_on_the_low_delay_stream` as +//! the counterfactual that shows the walk can see the defect when it is there. +//! +//! It holds for all three codecs and for the same one-line reason: `setup_surface` +//! reaches the submission at exactly ONE field in each conversion — H.264 and H.265' +//! `curr_pic.picture_id`, AV1's `current_frame`/`current_display_picture` — and every +//! reference field is resolved through the `surfaces` table. HEVC is doubly covered: +//! its per-slice `RefPicList` stores an INDEX into `reference_frames`, so it cannot +//! name a surface that array does not already hold. +//! +//! ⚠ One documented exception, and it is not this defect: `plan_to_va_av1` substitutes +//! a live surface for a reference slot the planner reports empty, and where the store +//! resolved NOTHING at all the fallback is the decode target itself (that conversion's +//! module docs say why, and prefer a resolved reference wherever one exists). It names +//! the target only when there is no other live surface to name, on a frame that is +//! already concealed and will not be shown. +//! +//! ⚠ And one assumption, stated because it is the only way the argument fails: the pool +//! holds DISTINCT `VASurfaceID`s. Two pool entries with one id would let a free index +//! resolve to a bound surface. `vaCreateSurfaces` cannot return duplicates — this rung +//! also destroys each exactly once, which the same duplication would double-free — so +//! it is an assumption about libva rather than about this file. use std::os::fd::AsRawFd as _; use std::os::fd::FromRawFd as _; @@ -686,6 +736,29 @@ impl Session { .collect() } + /// The decode target — pool index and `VASurfaceID` — together with the reference + /// table the conversion resolves against. `None` when the pool is exhausted. + /// + /// **The three are returned together because they are only safe together**, and + /// that is this rung's whole exemption from the aliasing defect the other two + /// backends had to defer their way out of (module docs). A free surface is by + /// definition a surface no slot binds; [`Self::surface_table`] is exactly what the + /// slots bind; so a target drawn from the same snapshot cannot appear in the table, + /// and no reference the conversion resolves through that table can be the surface + /// it is about to write. + /// + /// ⚠ Taking the two at DIFFERENT moments is the defect. References must resolve + /// against the store as it stood BEFORE this access unit's removals, so the table + /// has to be the pre-removal one; and a free list consulted AFTER those removals + /// offers the displaced picture's surface, which the pre-removal table still names. + /// `taking_the_free_surface_after_the_removals_would_hand_out_a_referenced_surface` + /// is that mismatch, made to happen. Returning a tuple is what stops a future edit + /// from reintroducing it by moving one call and not the other. + fn acquire_target(&self) -> Option<(usize, VaSurfaceId, Vec)> { + let index = self.free_surface()?; + Some((index, self.surfaces[index], self.surface_table())) + } + /// Release every libva object this session owns, in creation-reverse order. /// Called explicitly (a `Drop` here could not reach the display). fn destroy(mut self, d: &Display) { @@ -1011,11 +1084,9 @@ impl NativeVaapiDecoder { shape, &mut self.generation, )?; - let free = s - .free_surface() + let (free, target, table) = s + .acquire_target() .ok_or_else(|| anyhow!("surface pool exhausted ({} surfaces)", s.surfaces.len()))?; - let target = s.surfaces[free]; - let table = s.surface_table(); let converted = pf_vaadec::plan_to_va(&plan, au, &mut s.slots, &table, target) .map_err(|e| anyhow!("{e}"))?; @@ -1087,11 +1158,9 @@ impl NativeVaapiDecoder { shape, &mut self.generation, )?; - let free = s - .free_surface() + let (free, target, table) = s + .acquire_target() .ok_or_else(|| anyhow!("surface pool exhausted ({} surfaces)", s.surfaces.len()))?; - let target = s.surfaces[free]; - let table = s.surface_table(); let converted = pf_vaadec::plan_to_va_h265(&plan, au, &mut s.slots, &table, target) .map_err(|e| anyhow!("{e}"))?; @@ -1237,11 +1306,9 @@ impl NativeVaapiDecoder { shape, &mut self.generation, )?; - let free = s - .free_surface() + let (free, target, table) = s + .acquire_target() .ok_or_else(|| anyhow!("surface pool exhausted ({} surfaces)", s.surfaces.len()))?; - let target = s.surfaces[free]; - let table = s.surface_table(); let converted = match pf_vaadec::plan_to_va_av1(plan, au, &mut s.slots, &table, target) { Ok(converted) => converted, Err(e) => { @@ -2129,6 +2196,138 @@ mod tests { ); } + /// The decode target is never a surface the reference table names — swept over + /// every binding state a small pool can be in. + /// + /// This rung's exemption from the aliasing defect the D3D11VA and Vulkan rungs had + /// to defer their way out of (module docs), stated as the one thing it actually + /// rests on. `pf-vaadec` proves the conversion can only name surfaces out of the + /// table it is handed; this proves the table and the target cannot overlap. + /// + /// Swept rather than exemplified because the claim is structural — a free surface + /// is by definition one no slot binds, and the table is exactly what the slots bind + /// — so it should hold in states an ordinary run never reaches, and a sweep is what + /// says so. The `held` and `pending` masks are varied too even though they can only + /// ever REMOVE candidates from the free list: a future claim that could add one + /// back is exactly what this would catch. + #[test] + fn the_decode_target_can_never_be_a_surface_the_reference_table_names() { + const SURFACES: usize = 4; + const SLOTS: usize = 3; + let choices: Vec> = std::iter::once(None) + .chain((0..SURFACES).map(Some)) + .collect(); + + let (mut states, mut with_a_target, mut exhausted) = (0usize, 0usize, 0usize); + for a in &choices { + for b in &choices { + for c in &choices { + let bound = [*a, *b, *c]; + // Two slots binding ONE surface is not a state the pool can reach — + // `bind_setup` only ever binds a surface nothing else claims — and + // asserting about it would be asserting about a defect elsewhere. + let mut distinct: Vec = bound.iter().flatten().copied().collect(); + let claimed = distinct.len(); + distinct.sort_unstable(); + distinct.dedup(); + if distinct.len() != claimed { + continue; + } + for held_mask in 0..(1u32 << SURFACES) { + for pending_mask in 0..(1u32 << SURFACES) { + let mut s = session(SURFACES, SLOTS); + s.slot_surface = bound.to_vec(); + s.held = (0..SURFACES).map(|i| held_mask >> i & 1 == 1).collect(); + s.pending = (0..SURFACES) + .filter(|i| pending_mask >> i & 1 == 1) + .map(|i| (100 + i as u64, i)) + .collect(); + states += 1; + + let Some((index, target, table)) = s.acquire_target() else { + exhausted += 1; + continue; + }; + with_a_target += 1; + assert_eq!( + target, s.surfaces[index], + "the target must be the pool's surface at the index it \ + returned, or the caller binds one and submits another" + ); + assert_eq!(table.len(), SLOTS, "one table entry per slot"); + assert!( + !table.contains(&target), + "bindings {bound:?}, held {held_mask:#06b}, pending \ + {pending_mask:#06b}: the decode target {target:#x} is \ + in the reference table {table:x?} — every submission \ + built from that pair decodes into a surface it may be \ + predicting from" + ); + } + } + } + } + } + // The sweep has to reach both answers, or it is asserting about one branch. + assert!(states > 1000, "only {states} states swept"); + assert!(with_a_target > 0 && exhausted > 0); + } + + /// The order the rung must NOT be written in, and the reason + /// [`Session::acquire_target`] hands the target and the table back together. + /// + /// The exemption above is not a property of the pool alone: it needs the target and + /// the table to come from ONE snapshot. Split them and this rung acquires the + /// D3D11VA/Vulkan defect exactly — because the table must be the PRE-removal one + /// (a reference an access unit names can be a picture the same access unit evicts, + /// which on a punktfunk host's own low-delay H.264 is 117 access units in 120), + /// while a free list consulted after those removals offers precisely the displaced + /// picture's surface. + #[test] + fn taking_the_free_surface_after_the_removals_would_hand_out_a_referenced_surface() { + let mut s = session(4, 3); + + // Two decoded reference pictures, each in its own surface, both already + // displayed and returned by the presenter — so only the SLOT binding keeps + // their surfaces off the free list. That is the steady state of a low-delay + // stream, where a picture is output by its own access unit and evicted by the + // sliding window several units later. + s.slots.assign(11).expect("a free slot"); + bind_setup(&mut s, Some(11), Some(0)); + s.slots.assign(12).expect("a free slot"); + bind_setup(&mut s, Some(12), Some(1)); + s.pending.clear(); + + // What the conversion resolves its references through, taken BEFORE this access + // unit's removals — which is not a choice, it is where the references are. + let table = s.surface_table(); + assert!( + table.contains(&s.surfaces[0]), + "picture 11's surface must still be a resolvable reference" + ); + + // The order the rung is written in: one snapshot, and the target cannot be in + // the table it came with. + let (_, target, same_table) = s.acquire_target().expect("the pool has spares"); + assert_eq!( + same_table, table, + "acquire_target must not re-derive the table" + ); + assert!(!table.contains(&target)); + + // The defect: the conversion applies the removal, the bindings follow it, and + // only THEN is the free list consulted. + s.slots.release(11); + s.sync_slot_bindings(); + let late = s.free_surface().expect("the pool has spares"); + assert_eq!( + s.surfaces[late], table[0], + "the late free list offers the surface of the picture this access unit just \ + displaced, and the pre-removal table still names it as a reference — \ + decode into that and the driver predicts from the picture it is writing" + ); + } + /// A picture the conversion REFUSED binds no surface — so nothing can show it and /// nothing can predict from it. /// diff --git a/crates/pf-vaadec/src/pic.rs b/crates/pf-vaadec/src/pic.rs index a66d3825f..5da7957ea 100644 --- a/crates/pf-vaadec/src/pic.rs +++ b/crates/pf-vaadec/src/pic.rs @@ -549,6 +549,20 @@ mod tests { "../../pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264" ); + /// A punktfunk HOST's own output: 120 pictures of 640x480, `max_num_ref_frames = 3` + /// alongside `max_dec_frame_buffering = 3` and `max_num_reorder_frames = 0`. + /// + /// Vendored beside `pf-vkdecode`'s per-frame goldens, and the only stream in the + /// tree that produces the shape this module's exemption is about. The conformance + /// vector above cannot: its level gives it a 7-frame DPB against 2 reference + /// frames, so 8.2.5's sliding window unmarks a picture two access units before + /// C.4.5.3's bump can evict it, and it reorders, which keeps an unmarked picture + /// alive past the unit that unmarked it. Both are properties of that vector rather + /// than of H.264, and between them they hid a defect that fired on 297 of 300 + /// access units of every stream we ship, on two other backends, for two milestones. + const LOWDELAY_640X480: &[u8] = + include_bytes!("../../pf-vkdecode/tests/data/lowdelay-640x480.h264"); + /// Minimal H.264 access-unit splitter. The production wire delivers whole access /// units, so pf-bitstream keeps its splitter test-only; this is the same rule — /// a new AU begins at a non-VCL NALU following slices, or at a slice declaring @@ -693,83 +707,311 @@ mod tests { ); } - /// The decode target must never be a surface this same access unit READS. + /// What one walk of a stream through [`plan_to_va`] measured. /// - /// This is the question a slot ledger cannot answer, and it is why the caller - /// binds the setup surface instead of the conversion reading one out of a - /// slot-indexed table. + /// Every field is a count of ACCESS UNITS, so the four are directly comparable and + /// each is bounded by [`Self::converted`]. + #[derive(Debug, Default)] + struct AliasWalk { + /// Access units planned and converted. + converted: usize, + /// The setup picture was assigned a slot this access unit's OWN removals had + /// just freed. `SlotMap::assign` takes the lowest free slot, so this is the + /// ordinary case rather than an edge one — and it is why a decode target read + /// out of a slot-indexed table would be the surface of the picture just + /// displayed. + inherited_a_just_freed_slot: usize, + /// This access unit's own `removed` list names a picture its `dpb_refs` + /// snapshot also names: 8.2.5's sliding window unmarked a reference in the very + /// unit whose C.4.5.3 bump evicted it. The aliasing PRECONDITION, and the shape + /// the vendored conformance vector never produces. + removed_and_referenced: usize, + /// The setup picture took the slot of a picture this same access unit READS. + /// This is the D3D11VA/Vulkan defect verbatim — `CurrPic` and a reference entry + /// resolving through one slot — and on those two backends the surface followed + /// the slot, so the submission aliased. Here the surface does not follow the + /// slot, which is what [`Self::aliased`] measures. + setup_took_a_read_pictures_slot: usize, + /// The submission names the decode target as one of its own references, in + /// `reference_frames` or in any slice's `RefPicList0`/`1`. Must be zero. + aliased: usize, + } + + /// Drive `stream` through the planner and [`plan_to_va`], modelling the caller the + /// way the Linux rung is written, and count the four shapes above. /// - /// `SlotMap::assign` takes the LOWEST free slot, and a slot freed by this - /// access unit's own removals is free by the time the setup picture is - /// assigned. Measured on the vendored vector, that is not an edge case: the - /// setup picture inherits a just-freed slot on **225 of 250** access units. - /// A surface bound BY SLOT would therefore decode, on nine frames in ten, - /// into the surface still holding the picture that was just displayed — which - /// under zero-copy the consumer may still be sampling. Hence the pool model - /// this crate's callers use, and hence `setup_surface`. - /// - /// The second half of the test is the reassurance that comes with it: given - /// the caller's contract (a surface bound to no live picture), the decode - /// target is never a surface the same access unit READS. That is checked - /// against both readable sets, which are not the same snapshot — `dpb_refs` is - /// taken after this AU's marking process, the per-slice lists before it. - #[test] - fn the_setup_picture_routinely_inherits_a_just_freed_slot() { + /// The model is one line and it is the whole contract: the decode target is a + /// surface that **is not in the table the conversion is handed**, and it enters + /// that table only after the conversion returns. `video_vaapi_native`'s + /// `the_low_delay_stream_never_hands_the_decoder_a_surface_it_is_predicting_from` + /// is the same walk driven through the REAL `Session` pool, which is what says the + /// rung honours the contract; this one says what the contract buys. + fn walk_for_aliasing(stream: &[u8]) -> AliasWalk { use pf_bitstream::h264::H264Planner; - let aus = split_aus(TEST_25FPS_H264); let mut planner = H264Planner::new(); - let mut surfaces: Vec = Vec::new(); let mut slots: Option = None; - let mut collisions = 0usize; - let mut first: Option = None; - let mut inherited = 0usize; + // Slot to surface — precisely `Session::surface_table()` on the Linux rung. + let mut table: Vec = Vec::new(); + let mut out = AliasWalk::default(); - for (index, au) in aus.iter().enumerate() { - let plan = planner.plan_au(au).expect("the clean vector plans"); + for (index, au) in split_aus(stream).into_iter().enumerate() { + let plan = planner + .plan_au(au) + .unwrap_or_else(|e| panic!("AU {index}: this stream must plan, got {e:?}")); let map = slots.get_or_insert_with(|| SlotMap::new(plan.picture.max_dpb_frames)); - surfaces.resize(map.capacity(), VA_INVALID_SURFACE); - // Which slots this AU's own removals will free — read BEFORE the - // conversion applies them, because afterwards the ledger has forgotten. + assert_eq!( + map.capacity(), + plan.picture.max_dpb_frames + 1, + "AU {index}: neither stream renegotiates its DPB depth mid-walk" + ); + table.resize(map.capacity(), VA_INVALID_SURFACE); + + if plan + .dpb + .removed + .iter() + .any(|id| plan.dpb_refs.iter().any(|r| r.id == *id)) + { + out.removed_and_referenced += 1; + } + // Which slots this AU's removals will free, read BEFORE the conversion + // applies them — afterwards the ledger has forgotten. let freed: Vec = plan .dpb .removed .iter() .filter_map(|id| map.slot_of(*id)) .collect(); + + // Ids start well away from slot indices and are never reused, so a stale or + // aliased reference shows up as a value rather than as a plausible-looking + // off-by-one and cannot hide behind a surface that happens to be right + // again. The assertion is the model's own precondition: a target the table + // already names would beg the question this walk exists to answer. let setup_surface = SURFACE_BASE + index as u32; - let out = plan_to_va(&plan, au, map, &surfaces, setup_surface) - .expect("the clean vector converts"); - surfaces[usize::from(out.setup_slot)] = setup_surface; - if freed.contains(&out.setup_slot) { - inherited += 1; + assert!( + !table.contains(&setup_surface), + "AU {index}: the model handed out a surface the table already names" + ); + let displaced = table.clone(); + let converted = plan_to_va(&plan, au, map, &table, setup_surface) + .unwrap_or_else(|e| panic!("AU {index}: conversion failed: {e}")); + table[usize::from(converted.setup_slot)] = setup_surface; + + // Both readable sets, and they are not the same snapshot: `dpb_refs` is + // taken after this AU's marking process, the per-slice lists before it. + let named: Vec = converted + .pic_params + .reference_frames + .iter() + .chain( + converted + .slices + .iter() + .flat_map(|s| s.ref_pic_list0.iter().chain(s.ref_pic_list1.iter())), + ) + .filter(|e| e.flags & VA_PICTURE_H264_INVALID == 0) + .map(|e| e.picture_id) + .collect(); + + if freed.contains(&converted.setup_slot) { + out.inherited_a_just_freed_slot += 1; } - let curr = out.pic_params.curr_pic.picture_id; - let names = - |e: &VaPictureH264| e.flags & VA_PICTURE_H264_INVALID == 0 && e.picture_id == curr; - let read_by_this_au = out.pic_params.reference_frames.iter().any(names) - || out.slices.iter().any(|s| { - s.ref_pic_list0.iter().any(names) || s.ref_pic_list1.iter().any(names) - }); - if read_by_this_au { - collisions += 1; - first.get_or_insert(index); + let evicted_surface = displaced[usize::from(converted.setup_slot)]; + if evicted_surface != VA_INVALID_SURFACE && named.contains(&evicted_surface) { + out.setup_took_a_read_pictures_slot += 1; } + assert_eq!( + converted.pic_params.curr_pic.picture_id, setup_surface, + "AU {index}: the current picture must be the surface the caller bound" + ); + if named.contains(&setup_surface) { + out.aliased += 1; + } + out.converted += 1; } - // The measurement this design rests on. A floor rather than the exact - // count, so a planner change that shifts it by a frame does not fail — - // but one that made slot reuse RARE would, and would mean the doc above - // has stopped being true. + out + } + + /// The setup picture routinely inherits a slot its own access unit just freed — + /// which is why the decode target is a PARAMETER and not `surfaces[setup_slot]`. + /// + /// `SlotMap::assign` takes the LOWEST free slot, and a slot freed by this access + /// unit's own removals is free by the time the setup picture is assigned. Measured + /// on the vendored vector that is not an edge case: **225 of 250** access units. A + /// surface bound BY SLOT would therefore decode, on nine frames in ten, into the + /// surface still holding the picture that was just displayed — which under + /// zero-copy the consumer may still be sampling. Hence the pool model this crate's + /// callers use, and hence `setup_surface`. + /// + /// ⚠ This test used to carry a second half asserting the decode target was never + /// also a reference. It was VACUOUS: the walk hands every picture its own + /// never-reused surface id, so distinct ids cannot collide and the assertion could + /// not fail whatever the conversion did. The real question needs a surface pool + /// that RECYCLES, and it is answered by the two tests below and by + /// `video_vaapi_native`'s walk through the real one. + #[test] + fn the_setup_picture_routinely_inherits_a_just_freed_slot() { + let walk = walk_for_aliasing(TEST_25FPS_H264); + assert_eq!(walk.converted, 250); + // A floor rather than the exact count, so a planner change that shifts it by a + // frame does not fail — but one that made slot reuse RARE would, and would mean + // the documentation citing this number has stopped being true. assert!( - inherited > 200, - "the setup picture inherited a just-freed slot on only {inherited} of 250 access \ + walk.inherited_a_just_freed_slot > 200, + "the setup picture inherited a just-freed slot on only {} of 250 access \ units — the reason `setup_surface` is a parameter no longer holds, and the \ - documentation that cites it needs re-measuring" + documentation that cites it needs re-measuring", + walk.inherited_a_just_freed_slot + ); + } + + /// The aliasing PRECONDITION, on both streams — the number that says the exemption + /// below is being tested by something rather than merely passing. + /// + /// Two conditions have to coincide inside ONE access unit for a conversion that + /// releases eagerly to hand the decode target a picture it is predicting from: the + /// access unit must remove a picture, and that picture must still be in the + /// `dpb_refs` snapshot the reference lists are built from. Low-delay H.264 is + /// exactly what makes them coincide, and NVENC seals it by writing + /// `max_num_ref_frames = 3` ALONGSIDE `max_dec_frame_buffering = 3` — a DPB exactly + /// as deep as its reference count — while `max_num_reorder_frames = 0` means the + /// evicted picture has already been output and is therefore evictable at all. + /// + /// The vendored conformance vector produces the shape ZERO times, which is why it + /// proved nothing on two other backends for two milestones. If that zero ever moves + /// the reasoning above is wrong and the 117 needs re-deriving before it means + /// anything. + #[test] + fn the_low_delay_stream_reassigns_slots_whose_pictures_it_still_reads() { + let vector = walk_for_aliasing(TEST_25FPS_H264); + assert_eq!(vector.converted, 250); + assert!( + vector.inherited_a_just_freed_slot > 0, + "no access unit of the vendored vector reused a freed slot, so the zeroes \ + below would be empty for a reason that has nothing to do with the hazard" ); assert_eq!( - collisions, 0, - "the decode target collided with a picture this access unit reads, on \ - {collisions} of 250 (first at AU {first:?})" + vector.removed_and_referenced, 0, + "the vendored vector is supposed to be BLIND to this shape" + ); + assert_eq!( + vector.setup_took_a_read_pictures_slot, 0, + "and therefore never to hand the setup picture a slot it still reads" + ); + + let lowdelay = walk_for_aliasing(LOWDELAY_640X480); + assert_eq!(lowdelay.converted, 120); + assert_eq!( + lowdelay.removed_and_referenced, 117, + "the low-delay stream must still exercise the aliasing precondition on \ + nearly every access unit — if this drops to zero the exemption below is no \ + longer being TESTED by anything, whatever else still passes" + ); + assert_eq!( + lowdelay.setup_took_a_read_pictures_slot, 117, + "and the slot really is handed straight back to the decode target: this is \ + the D3D11VA/Vulkan defect, present here, and harmless only because the \ + SURFACE does not follow the slot" + ); + } + + /// The exemption itself: no submission names its decode target as one of its own + /// references, on either stream. + /// + /// This conversion still releases its whole `removed` list inline, exactly as the + /// two backends that had to grow a `release_after_decode` deferral once did. It is + /// safe doing so for one reason, and it is a property of the INTERFACE rather than + /// of any stream: `plan_to_va` never invents a surface. Every reference it can name + /// is read out of the `surfaces` table it was handed, so a decode target that is + /// not in that table cannot be named, whatever the ledger does with slots. A slot + /// is not a surface here; on DXVA it was. + /// + /// ⚠ That makes this a statement about the CALLER's contract, so it is only half + /// the proof. The other half — that the Linux rung really does pick its decode + /// target from outside the table — cannot be made here, because the pool lives in + /// `pf-client-core`. It is + /// `video_vaapi_native`'s + /// `the_low_delay_stream_never_hands_the_decoder_a_surface_it_is_predicting_from`, + /// which drives this same stream through the real `Session`. + #[test] + fn no_submission_names_its_decode_target_as_one_of_its_own_references() { + for (name, walk) in [ + ("the vendored vector", walk_for_aliasing(TEST_25FPS_H264)), + ("the low-delay stream", walk_for_aliasing(LOWDELAY_640X480)), + ] { + assert_eq!( + walk.aliased, 0, + "{name}: {} of {} access units decode into a surface they predict from", + walk.aliased, walk.converted + ); + } + } + + /// A decode target the caller took from INSIDE the slot table is named as its own + /// reference — the counterfactual that gives the test above its teeth. + /// + /// Without this, `aliased == 0` would be consistent with a conversion that could + /// never alias for reasons of its own, and a reader could not tell which. This + /// picks the target the way the two broken backends effectively did — the surface + /// sitting in the slot the setup picture is about to take — and shows the same walk + /// then aliases on 117 of 120 access units of the low-delay stream. So the walk can + /// see the defect; it does not see it because the contract holds. + #[test] + fn taking_the_decode_target_from_the_slot_table_aliases_on_the_low_delay_stream() { + use pf_bitstream::h264::H264Planner; + + let mut planner = H264Planner::new(); + let mut slots: Option = None; + let mut table: Vec = Vec::new(); + let (mut converted, mut aliased) = (0usize, 0usize); + + for (index, au) in split_aus(LOWDELAY_640X480).into_iter().enumerate() { + let plan = planner.plan_au(au).expect("the low-delay stream plans"); + let map = slots.get_or_insert_with(|| SlotMap::new(plan.picture.max_dpb_frames)); + table.resize(map.capacity(), VA_INVALID_SURFACE); + // The bug, modelled: convert first to learn the slot, then re-run the same + // access unit against the real ledger with the target read OUT of the + // table. Two passes only because the slot is not known until the conversion + // returns; the submission compared below is the second one. + // + // The probe's own `setup_surface` is arbitrary and deliberately so — the + // slot is chosen by `SlotMap::assign` from the ledger alone and no + // conversion consults the target to pick it, which is why one pass can + // stand in for the other. + let mut probe = map.clone(); + let peek = plan_to_va(&plan, au, &mut probe, &table, SURFACE_BASE) + .expect("the low-delay stream converts"); + let target = table[usize::from(peek.setup_slot)]; + let target = if target == VA_INVALID_SURFACE { + SURFACE_BASE + index as u32 + } else { + target + }; + let out = plan_to_va(&plan, au, map, &table, target).expect("the same conversion"); + table[usize::from(out.setup_slot)] = target; + + let names = |e: &VaPictureH264| { + e.flags & VA_PICTURE_H264_INVALID == 0 && e.picture_id == target + }; + if out.pic_params.reference_frames.iter().any(names) + || out + .slices + .iter() + .any(|s| s.ref_pic_list0.iter().any(names) || s.ref_pic_list1.iter().any(names)) + { + aliased += 1; + } + converted += 1; + } + + assert_eq!(converted, 120); + assert_eq!( + aliased, 117, + "binding the decode target BY SLOT is supposed to reproduce the defect on \ + this stream; if it no longer does, the exemption test above is passing for \ + a reason nobody has checked" ); } From d25a20a2339c854c406e7f05ad8c5d451cb34c0b Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 7 Aug 2026 23:07:21 +0200 Subject: [PATCH 17/21] feat(vkdecode): the AV1 rungs meet a second tile for the first time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every AV1 frame either decode rung has ever been measured against is `tile_cols = tile_rows = 1`. The vendored vector is single-tile on all 274 of its frames, so every tile array the conversions fill — `tiles.widths`, `tiles.heights`, the per-tile records — had only ever been written at index 0, and a conversion that wrote tile 0 and left the rest zero would pass the whole suite. Our encoder splits 4K into TWO TILE ROWS. **The fixture.** `lowdelay-3840x2160.ivf.av1`, 261 KB, 60 frames — `punktfunk-host spike --source synthetic --codec av1 --width 3840 --height 2160 --fps 60 --seconds 1 --bitrate 1` on .21 (NVENC, RTX 5070 Ti), wrapped to IVF with `ffmpeg -f obu … -c copy` so `common::split_av1_aus` (the vendored parser's own `IvfIterator`) frames it exactly as it frames the vector, with no second splitter that could disagree. **4K is not a size choice, it is the only shape with the property.** Measured on the same box with the same command: 1280x720, 1920x1080 and 2560x1440 all give `tile_cols = tile_rows = 1`; 3840x2160 gives `tile_cols = 1, tile_rows = 2` with `width_in_sbs_minus_1 = [59]`, `height_in_sbs_minus_1 = [16, 16]`, and both tiles in ONE Tile Group OBU. 60 frames instead of 120 pays for the resolution: 261 KB, under both the 282 KB H.264 and 270 KB H.265 low-delay fixtures. Goldens are libavcodec's software decode, cross-checked between ffmpeg n8.1.2 (Arch x86_64, libdav1d) and 8.1.1 (Homebrew, macOS arm64, libdav1d) whose 746,496,000-byte raw outputs are BYTE-IDENTICAL, not merely equal per frame. 60 of 60 digests distinct. **AV1's frame accounting is asserted, never derived.** The vendored vector is 250 temporal units carrying 274 coded frames of which 24 are hidden; this stream is 60 units, 60 coded, 60 shown, 0 hidden, 0 `show_existing_frame`, 1 key frame. Neither is the general case, so both parity harnesses now take units / decoded / shown as three independent parameters instead of computing one from another, and the CPU guard states all six numbers. **A CPU gate that needed no hardware at all.** `pic_av1`'s new `a_two_tile_frame_fills_both_row_entries_and_leaves_the_rest_zero` pins the second row entry against its OWN `height_in_sbs_minus_1`, requires the two rows to tile the frame exactly, and requires TWO tile RECORDS out of ONE tile group with rows (0,0) and (1,0) — the transposition a square grid could never reveal — each spanning real bytes. The existing one-tile test asserts index 0 is right and `1..` are zero, which a broken multi-tile conversion also satisfies. ⚠⚠ **This is a file, and on AV1 that distinction has already cost a release.** "250/250 delivered frames bit-identical to libavcodec" was true for the entire period the host was shipping only the FIRST TILE of every 4K frame: the verification ran against a vendored file while the truncation lived in packetisation, and the suite stayed green throughout. This fixture closes the multi-tile gap on the DECODE rungs and closes nothing about fragmentation, reassembly, loss or AU boundaries — the golden header, both module docs and the leg docs all say so, at length, so the next reader does not inherit the same false confidence. Legs: `low_delay_host_av1_every_frame_hashes_bit_identical_to_libavcodec` on the Vulkan rung (11 ignored legs now) and on the D3D11VA rung, plus two non-ignored CPU tests. Verified: 11/11 Vulkan parity legs on .21 (RTX 5070 Ti, 610.57.04), the new one 60/60 bit-identical; workspace clippy `-D warnings` and `cargo fmt --all --check` clean on .21. --- .../pf-client-core/src/video_d3d11_native.rs | 156 +++++++-- crates/pf-dxvadec/src/pic_av1.rs | 130 ++++++++ .../data/lowdelay-3840x2160-av1.nv12.sha256 | 126 +++++++ .../tests/data/lowdelay-3840x2160.ivf.av1 | Bin 0 -> 266862 bytes crates/pf-vkdecode/tests/gpu_parity.rs | 314 +++++++++++++++++- 5 files changed, 685 insertions(+), 41 deletions(-) create mode 100644 crates/pf-vkdecode/tests/data/lowdelay-3840x2160-av1.nv12.sha256 create mode 100644 crates/pf-vkdecode/tests/data/lowdelay-3840x2160.ivf.av1 diff --git a/crates/pf-client-core/src/video_d3d11_native.rs b/crates/pf-client-core/src/video_d3d11_native.rs index 26fcd45bd..408d7990b 100644 --- a/crates/pf-client-core/src/video_d3d11_native.rs +++ b/crates/pf-client-core/src/video_d3d11_native.rs @@ -55,8 +55,21 @@ //! [`NativeD3d11Decoder::frame_av1`] now applies the conversion's //! `release_after_decode` once the decode op is issued. //! -//! ⚠ Still no SOAK on the goldens, so this leg's evidence is one 250-frame vector on two -//! vendors — narrower than the H.264/H.265 legs above. +//! Since 2026-08-07 a SECOND AV1 stream runs beside the vector: our own host's 4K +//! output, `low_delay_host_av1_every_frame_hashes_bit_identical_to_libavcodec`. Not for +//! the aliasing — the vector covers that better than any host stream could — but +//! because every frame of the vector is `tile_cols = tile_rows = 1`, so every tile +//! array `plan_to_dxva_av1` fills had only ever been written at index 0. Our encoder +//! splits 4K into two tile rows carried in one Tile Group OBU, which is two tile +//! RECORDS from one group; 1440p and below measured single-tile, so 4K is the only +//! shape that has it. +//! +//! ⚠ Still no SOAK on the goldens, so this leg's evidence is two vendored streams on two +//! vendors — narrower than the H.264/H.265 legs above. ⚠⚠ And both are FILES. "250/250 +//! delivered frames bit-identical" was true for the entire period the host was shipping +//! only the FIRST TILE of every 4K frame: that verification ran against a vendored file +//! while the truncation lived in packetisation, and this suite stayed green throughout. +//! Nothing here covers fragmentation, reassembly or loss. //! //! 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. @@ -1835,6 +1848,42 @@ mod parity { const AV1_DECODED_COUNT: usize = 274; const AV1_SHOWN_COUNT: usize = 250; + /// The vendored AV1 vector's render region, and what its goldens hash. + const DISPLAY_AV1: (u32, u32) = (320, 240); + + /// **Our own host's AV1**, and the only stream this rung decodes with more than + /// ONE TILE. + /// + /// Unlike the H.264 and H.265 low-delay siblings this is not about the + /// release-ordering defect — the vendored vector already aliases on 268 of its 274 + /// frames, which is exactly why parity caught that one here. It closes a different + /// gap: no host-generated AV1 stream had pixel coverage anywhere, and our encoder's + /// AV1 is structurally unlike the vector. At 4K the split encode emits + /// `tile_cols = 1, tile_rows = 2` — `height_in_sbs_minus_1 = [16, 16]` — with both + /// tiles in a SINGLE Tile Group OBU. 1440p and below measured single-tile, so 4K is + /// the only shape that has the property; 60 frames rather than 120 pays for it, at + /// 261 KB. + /// + /// ⚠ A file fixture is not the wire path, and on AV1 that distinction has already + /// cost a release. "250/250 delivered frames bit-identical" was true for the whole + /// period the host was shipping only the first tile of every 4K frame — the + /// verification ran against a vendored file and the truncation lived in + /// packetisation. This leg gives the multi-tile shape pixel coverage on the DECODE + /// rung and proves nothing about fragmentation, reassembly or loss. + const LOWDELAY_AV1: &[u8] = + include_bytes!("../../pf-vkdecode/tests/data/lowdelay-3840x2160.ivf.av1"); + const GOLDENS_LOWDELAY_AV1: &str = + include_str!("../../pf-vkdecode/tests/data/lowdelay-3840x2160-av1.nv12.sha256"); + + /// 60 units, 60 decoded, 60 shown — three constants, never derived from each + /// other. Our host emits one shown frame per temporal unit with no hidden frames + /// and no `show_existing_frame`, which is the OPPOSITE shape to the vendored + /// vector's 250 / 274 / 250 and the reason the harness takes all three. + const LOWDELAY_AV1_UNIT_COUNT: usize = 60; + const LOWDELAY_AV1_DECODED_COUNT: usize = 60; + const LOWDELAY_AV1_SHOWN_COUNT: usize = 60; + const DISPLAY_LOWDELAY_AV1: (u32, u32) = (3840, 2160); + /// The golden file's hash lines (comments and blanks skipped). fn golden_hashes(file: &'static str) -> Vec<&'static str> { file.lines() @@ -2042,7 +2091,7 @@ mod parity { /// the whole difference. `display` is still the planner's own output list; /// AV1 has no bumping process, so a picture is output by the unit that shows /// it and there is no flush to drain at the end. - fn order_av1(units: &[&[u8]]) -> Order { + fn order_av1(units: &[&[u8]], render: (u32, u32)) -> Order { let mut planner = pf_dxvadec::Av1Planner::new(); let mut order = Order { decode: Vec::new(), @@ -2062,8 +2111,8 @@ mod parity { ); assert_eq!( (plan.picture.render_width, plan.picture.render_height), - (320, 240), - "unit {index}: the goldens are the 320x240 render region" + render, + "unit {index}: the goldens are the {render:?} render region" ); if let Some(id) = plan.dpb.stored { order.decode.push(id); @@ -2350,18 +2399,45 @@ mod parity { /// ⚠ Still unexercised, because the vendored vector has none: /// `show_existing_frame`. fn av1_parity_run(units: &[&[u8]], order: &Order, goldens: &[&str]) { + av1_parity_run_against( + units, + order, + goldens, + AV1_UNIT_COUNT, + AV1_DECODED_COUNT, + AV1_SHOWN_COUNT, + "AV1", + ); + } + + /// [`av1_parity_run`] with its stream's own counts, for the leg that does not + /// decode the vendored vector. + /// + /// The three counts are three parameters, never derived from one another: the + /// vendored vector is 250 units / 274 decoded / 250 shown, and our host's stream is + /// 60 / 60 / 60. A harness that computed "hidden = 0" or "decoded = units" from + /// either would silently stop checking the other. + fn av1_parity_run_against( + units: &[&[u8]], + order: &Order, + goldens: &[&str], + unit_count: usize, + decoded_count: usize, + shown_count: usize, + label: &str, + ) { assert_eq!( units.len(), - AV1_UNIT_COUNT, - "the IVF reader disagrees with the vector's temporal-unit count" + unit_count, + "{label}: the IVF reader disagrees with the stream's temporal-unit count" ); - assert_eq!(order.decode.len(), AV1_DECODED_COUNT); + assert_eq!(order.decode.len(), decoded_count); assert_eq!(order.per_unit.len(), units.len()); assert_eq!(order.display.len(), goldens.len()); let luid = pinned_adapter(); let mut decoder = NativeD3d11Decoder::new(Codec::Av1, StreamFormat::SDR_420_8, luid, false) - .unwrap_or_else(|e| panic!("AV1: the box must host AV1 Profile 0 — {e:#}")); + .unwrap_or_else(|e| panic!("{label}: the box must host AV1 Profile 0 — {e:#}")); let mut readback = Readback { ctx: decoder.context.clone(), staging: None, @@ -2409,20 +2485,23 @@ mod parity { decoded += 1; } } - assert_eq!(decoded, AV1_DECODED_COUNT); + assert_eq!(decoded, decoded_count); assert_eq!( - presented, AV1_SHOWN_COUNT, - "every unit of this vector shows exactly one frame, so the production \ - path must have handed back {AV1_SHOWN_COUNT} pictures" + presented, shown_count, + "{label}: every unit of this stream shows exactly one frame, so the \ + production path must have handed back {shown_count} pictures" ); - let hidden = AV1_DECODED_COUNT - presented; + let hidden = decoded_count - presented; assert_eq!( hidden, - AV1_DECODED_COUNT - AV1_SHOWN_COUNT, - "the rung must have decoded 24 frames it never handed back — this counts \ - what `decode_av1` RETURNED against what it decoded, so at zero the \ - `!sub.show` suppression is not working (or this vector stopped hiding \ - frames, which `the_av1_vector_hides_frames…` would catch first)" + decoded_count - shown_count, + "{label}: the rung must have decoded {} frames it never handed back — this \ + counts what `decode_av1` RETURNED against what it decoded, so a mismatch \ + on the vendored vector means the `!sub.show` suppression is not working \ + (or it stopped hiding frames, which `the_av1_vector_hides_frames…` would \ + catch first). On a stream with no hidden frames both sides are zero and \ + this is a tautology — deliberately, so one harness serves both shapes", + decoded_count - shown_count ); let mut mismatches = 0usize; @@ -2432,7 +2511,7 @@ mod parity { .unwrap_or_else(|| panic!("display frame {n} names PicId {id}, never decoded")); if got != golden { if mismatches < 10 { - eprintln!("AV1: display frame {n} (PicId {id}): {got} != {golden}"); + eprintln!("{label}: display frame {n} (PicId {id}): {got} != {golden}"); } mismatches += 1; } @@ -2440,15 +2519,15 @@ mod parity { assert_eq!( mismatches, 0, - "AV1: {mismatches}/{} frames diverge from libavcodec (first 10 above; frame \ + "{label}: {mismatches}/{} frames diverge from libavcodec (first 10 above; frame \ 0 is a key frame — if IT mismatches suspect the readback geometry \ (pitch/crop/plane offset) or the tile records rather than the reference \ handling)", goldens.len() ); eprintln!( - "AV1: {} delivered frames bit-identical to libavcodec, {hidden} hidden frames \ - decoded and withheld", + "{label}: {} delivered frames bit-identical to libavcodec, {hidden} hidden \ + frames decoded and withheld", goldens.len() ); } @@ -2500,7 +2579,7 @@ mod parity { #[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 order = order_av1(&units, DISPLAY_AV1); let goldens = golden_hashes(GOLDENS_AV1); // Plan facts per PicId, from a planner run alongside the decoder's own. @@ -2628,10 +2707,35 @@ mod parity { #[ignore = "needs a Windows D3D11 video device (see module docs)"] fn av1_every_delivered_frame_hashes_bit_identical_to_libavcodec() { let units = split_ivf(TEST_25FPS_AV1); - let order = order_av1(&units); + let order = order_av1(&units, DISPLAY_AV1); av1_parity_run(&units, &order, &golden_hashes(GOLDENS_AV1)); } + /// **Our own host's AV1, at the only resolution where it emits more than one tile.** + /// + /// The leg above runs a vector whose every frame is `tile_cols = tile_rows = 1`, so + /// every tile field `plan_to_dxva_av1` fills is the degenerate case. This stream is + /// `tile_rows = 2` on all 60 frames with both tiles in one Tile Group OBU, which is + /// the 4K split-encode shape the host actually ships — and it is 4K, so the + /// readback moves 12.4 MB per frame rather than 115 KB. See [`LOWDELAY_AV1`] for + /// what it does and does not cover; the short version is that it is a file, and the + /// last AV1 truncation lived somewhere a file cannot reach. + #[test] + #[ignore = "needs a Windows D3D11 video device (see module docs)"] + fn low_delay_host_av1_every_frame_hashes_bit_identical_to_libavcodec() { + let units = split_ivf(LOWDELAY_AV1); + let order = order_av1(&units, DISPLAY_LOWDELAY_AV1); + av1_parity_run_against( + &units, + &order, + &golden_hashes(GOLDENS_LOWDELAY_AV1), + LOWDELAY_AV1_UNIT_COUNT, + LOWDELAY_AV1_DECODED_COUNT, + LOWDELAY_AV1_SHOWN_COUNT, + "AV1 (low-delay host stream, 4K two-tile)", + ); + } + #[test] #[ignore = "needs a Windows D3D11 video device (see module docs)"] fn h264_every_frame_hashes_bit_identical_to_libavcodec() { @@ -2817,7 +2921,7 @@ mod parity { fn the_ivf_reader_agrees_with_the_planner_and_the_av1_goldens() { let units = split_ivf(TEST_25FPS_AV1); assert_eq!(units.len(), AV1_UNIT_COUNT, "AV1 temporal units"); - let order = order_av1(&units); + let order = order_av1(&units, DISPLAY_AV1); assert_eq!( order.decode.len(), AV1_DECODED_COUNT, diff --git a/crates/pf-dxvadec/src/pic_av1.rs b/crates/pf-dxvadec/src/pic_av1.rs index d8ce47c88..4446ecc83 100644 --- a/crates/pf-dxvadec/src/pic_av1.rs +++ b/crates/pf-dxvadec/src/pic_av1.rs @@ -857,6 +857,19 @@ mod tests { "../../pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1" ); + /// **Our own host's AV1 at 4K**, vendored beside the goldens the GPU legs decode it + /// against (`lowdelay-3840x2160-av1.nv12.sha256` carries the `punktfunk-host spike` + /// command and the ffmpeg cross-check). + /// + /// It is here for ONE property the vendored vector cannot supply: **two tiles**. + /// Every frame of `test-25fps.ivf.av1` is `tile_cols = tile_rows = 1`, so + /// [`the_tile_sizes_are_superblock_counts_not_the_coded_minus_one`] can only ever + /// read index 0 of the tile arrays and assert the rest are zero. This stream is + /// `tile_cols = 1, tile_rows = 2` on all 60 frames, with both tiles in a single + /// Tile Group OBU — the 4K split-encode shape the host ships. + const LOWDELAY_3840X2160_AV1: &[u8] = + include_bytes!("../../pf-vkdecode/tests/data/lowdelay-3840x2160.ivf.av1"); + /// Convert one plan the way the RUNG must: the conversion, then the releases it /// defers past the decode op ([`DecodePlanDxvaAv1::release_after_decode`]). /// @@ -1588,6 +1601,123 @@ mod tests { assert_eq!(frames, 274); } + /// The same tile arrays with a SECOND tile in them — the case the vendored vector + /// cannot reach and this conversion had therefore never been run against. + /// + /// [`the_tile_sizes_are_superblock_counts_not_the_coded_minus_one`] asserts index 0 + /// is right and `1..` are zero, which is everything a one-tile vector can say. Both + /// halves of that are shapes a multi-tile bug would satisfy: a conversion that + /// wrote only tile 0 and left the rest zero would pass it on every frame of the + /// vector and hand the driver a frame with half its height missing here. + /// + /// So this pins the row arrays with both entries live, that the second entry is the + /// SECOND tile's size rather than a repeat of the first, and that everything past + /// the grid is still zero. `tile_rows = 2` with `height_in_sbs_minus_1 = [16, 16]` + /// against a 2160-line frame is 17 + 17 = 34 superblocks of 64, i.e. 2176 lines — + /// the padded height, which is the arithmetic the one-tile test's `div_ceil` also + /// checks but cannot check twice. + #[test] + fn a_two_tile_frame_fills_both_row_entries_and_leaves_the_rest_zero() { + let mut planner = Av1Planner::new(); + let mut slots = SlotMap::new(NUM_REF_SLOTS); + let mut frames = 0u32; + + for packet in IvfIterator::new(LOWDELAY_3840X2160_AV1) { + for plan in planner + .plan_au(packet) + .expect("the low-delay 4K stream plans") + { + if plan.dpb.stored.is_none() { + continue; + } + let dx = convert(packet, &plan, &mut slots); + frames += 1; + let t = &plan.header.tile_info; + // `#[repr(packed)]` — copy the block out before reading its arrays. + let tiles = dx.pic_params.tiles; + let (widths, heights) = (tiles.widths, tiles.heights); + assert_eq!( + (tiles.cols, tiles.rows), + (1, 2), + "frame {frames}: this stream is vendored FOR its second tile row. \ + One tile here means it was regenerated below 4K (1440p and down \ + measured single-tile) and this test has quietly become a duplicate \ + of the vendored vector's" + ); + let sb = if plan.sequence.use_128x128_superblock { + 128 + } else { + 64 + }; + assert_eq!( + (widths[0], 0u16), + (plan.header.frame_width.div_ceil(sb) as u16, 0u16), + "frame {frames}: the single tile COLUMN spans the whole width" + ); + // Both row entries, each the coded value plus one — and read + // independently, so a conversion that broadcast entry 0 across the + // array would still have to get entry 1's own coded value right. + assert_eq!( + (heights[0], heights[1]), + ( + t.height_in_sbs_minus_1[0] as u16 + 1, + t.height_in_sbs_minus_1[1] as u16 + 1 + ), + "frame {frames}: each tile row's height is its OWN \ + `height_in_sbs_minus_1 + 1`" + ); + assert_eq!( + u32::from(heights[0]) + u32::from(heights[1]), + plan.header.frame_height.div_ceil(sb), + "frame {frames}: the two tile rows must tile the frame exactly — a \ + short second row is a frame with missing lines, which is precisely \ + the shape the host once shipped over the wire" + ); + // Past the grid the arrays stay zero: a driver reading `rows` entries + // never sees them, and a phantom entry is a tile the frame has not. + assert!(widths[1..].iter().all(|w| *w == 0)); + assert!(heights[2..].iter().all(|h| *h == 0)); + + // TWO tile RECORDS from ONE tile group, with distinct rows and + // non-empty spans. `tile_cols * tile_rows` records is libavcodec's own + // count, and this stream is the only one here where it exceeds the + // number of tile GROUPS — so a conversion that emitted one record per + // group (the coarser shape the module docs warn against) is + // indistinguishable from a correct one on the vendored vector and + // fails here. + assert_eq!( + plan.tiles.len(), + 1, + "frame {frames}: both tiles arrive in one Tile Group OBU" + ); + assert_eq!( + dx.tiles.len(), + 2, + "frame {frames}: one record per TILE, not per tile group" + ); + assert_eq!( + (dx.tiles[0].row, dx.tiles[0].column), + (0, 0), + "frame {frames}: tile 0 is row 0" + ); + assert_eq!( + (dx.tiles[1].row, dx.tiles[1].column), + (1, 0), + "frame {frames}: tile 1 is the SECOND ROW of a single column — a \ + (0, 1) here means rows and columns are transposed, which one \ + square tile grid could never show" + ); + assert!( + dx.tiles.iter().all(|r| r.data_size > 0), + "frame {frames}: every tile record must span real bytes; a \ + zero-length second record is the whole bottom half of the frame \ + missing" + ); + } + } + assert_eq!(frames, 60, "the low-delay 4K stream is 60 coded frames"); + } + /// Three fields whose correct value is a SENTINEL or a constant, on every frame /// of the vector — none of which any other assertion here would notice. /// diff --git a/crates/pf-vkdecode/tests/data/lowdelay-3840x2160-av1.nv12.sha256 b/crates/pf-vkdecode/tests/data/lowdelay-3840x2160-av1.nv12.sha256 new file mode 100644 index 000000000..68969458c --- /dev/null +++ b/crates/pf-vkdecode/tests/data/lowdelay-3840x2160-av1.nv12.sha256 @@ -0,0 +1,126 @@ +# SHA-256 per DELIVERED frame of lowdelay-3840x2160.ivf.av1, DISPLAY order — 60 frames. +# +# Each frame is the 3840x2160 render region as tightly packed NV12: +# Y plane 3840*2160 bytes, then interleaved UV 3840*1080 bytes = 12441600 bytes/frame. +# (`render_width`/`render_height` equal the frame size, so there is no crop.) +# +# THE STREAM IS OURS, not a conformance vector, and that is the point of it. +# `punktfunk-host spike` on .21 (NVENC, RTX 5070 Ti, driver 610.57.04, +# punktfunk-host 0.25.0-0.00011708), 2026-08-07: +# +# punktfunk-host spike --source synthetic --codec av1 --width 3840 --height 2160 \ +# --fps 60 --seconds 1 --bitrate 1 --no-loopback --out lowdelay-3840x2160.av1 +# ffmpeg -f obu -i lowdelay-3840x2160.av1 -c copy -f ivf lowdelay-3840x2160.ivf.av1 +# +# The spike writes the low-overhead OBU stream; the IVF wrapper is added so this file +# is framed exactly like the vendored vector and `common::split_av1_aus` — the +# vendored parser's own `IvfIterator` — splits it with no second implementation that +# could disagree. `-c copy` re-frames, it does not re-encode. +# +# ⭐ WHY 4K, when every other fixture here is chosen to be small. It is the ONLY +# resolution at which our encoder emits more than one tile. Measured on the same box, +# same command, 2026-08-07: 1280x720, 1920x1080 and 2560x1440 all give +# `tile_cols = tile_rows = 1`; 3840x2160 gives `tile_cols = 1, tile_rows = 2` — +# `width_in_sbs_minus_1 = [59]`, `height_in_sbs_minus_1 = [16, 16]` — and BOTH tiles +# ride in ONE Tile Group OBU (`tg_start = 0, tg_end = 1`). That is the exact shape +# behind the defect where the host shipped only the first tile of every 4K frame, and +# a single-tile fixture cannot express it at all. +# +# 60 frames rather than 120 to pay for it: one second at 60 fps is 261 KB, which is +# SMALLER than the 282 KB H.264 and 270 KB H.265 low-delay fixtures, and still leaves +# 55 of the 60 access units exercising the reference-slot pressure below. +# +# 60 = 60 = 60, and that is itself worth pinning. Unlike the vendored vector (250 +# temporal units carrying 274 coded frames, 24 of them hidden), THIS stream is one +# coded frame per temporal unit, all shown: 60 units, 60 coded frames, 60 displayed, +# one KEY frame, zero hidden, zero `show_existing_frame`. The parity legs' frame +# accounting must not silently assume either shape, so the CPU guard asserts all of +# these numbers rather than deriving one from another. +# +# Main 4:2:0 8-bit (`seq_profile = 0`, `high_bitdepth = 0`, `mono_chrome = 0`) and NO +# FILM GRAIN, so the Vulkan decode profile is the grain-DISABLED one, exactly as for +# the vendored vector — see that file's header for why grain is a profile property +# rather than a per-frame toggle. +# +# ⚠ WHAT THIS FIXTURE DOES NOT COVER. It is a FILE, and a file is not the wire path. +# The headline "250/250 delivered frames bit-identical to libavcodec" was true for AV1 +# the entire time the host was shipping only the first tile of every 4K frame: that +# verification ran against a vendored file, the packetisation and reassembly it never +# touched were where the frames were being truncated, and the suite stayed green. This +# fixture closes a different gap — it is the first pixel evidence for AV1 from our own +# encoder, in a multi-tile shape — and it closes NOTHING about fragmentation, +# reassembly, loss or the session's AU boundaries. Those need an end-to-end test. +# +# Goldens from libavcodec's SOFTWARE decoder (AV1 decoding is exactly specified, so +# every conformant decoder is bit-identical): +# +# ffmpeg -i lowdelay-3840x2160.ivf.av1 -f rawvideo -pix_fmt nv12 \ +# -fps_mode passthrough ref.yuv +# # then split ref.yuv into 12441600-byte frames and sha256 each +# +# CROSS-CHECKED between two independent builds on two architectures whose +# 746,496,000-byte raw outputs are BYTE-IDENTICAL (not merely equal per frame): +# sha256 90c5be20342cba4d80bd0ceb1568cc3e2037f427d34c658895c7742968c93600 from both +# ffmpeg n8.1.2 (Arch/CachyOS, gcc 16, x86_64, libdav1d) +# ffmpeg 8.1.1 (Homebrew, clang, macOS arm64, libdav1d) +# 60 of 60 digests distinct. +5c587f77a16733533c178c115cf4fad5866b13ebc4bf7673a72be062b4aab9d9 +b07527d4a8d7fb8b4ce53d97818e86493237e98c492e5718d42ea0974cd00da2 +59156f4f4d3ef030fe884e7085a2f1b3887ba00e0c8e58c5e3f425a31f5aade6 +693bbc7da0f499d2955e6c13ed6ee3c13603faed938bc357264c534bed9faa0f +14d1722b39ef61ede697a87f5d11eafcf7d9a812fb966fd3142c483036a08b87 +94e6cd41d300298677b014650e5f19b420c38e82741452da573d189d947f1a41 +f832d8126cdf3195be4a25aa5e80eefdd2c1089dce5bb3d3531f9bdb58e9c3b0 +be9248bec91e4bd5ab06e3a32d1ac02b759ae94735ecc88b14b845c5e4f24120 +581fdce69ff5188c949612fd00bd801c717ab9fc1f2de635d734879aae0a7bdd +9256582fc2d242e57a2fbd414a481d4cfca494fa56254bdb15004b4d65c866c6 +7955081dd59795399a1cb7a3eeade261ba01ef89c8c27f1e133c958be054ddec +5c8e963f60385add18001a3992fe5c5abb697391213f0f992144e5bba8f00745 +13062b6a6e2c557f4b8956a2f05a9ec941b3bd15b6ff4c337662b3eeabc75c41 +a80151a16345232be1db60fe2c1e7e61df38a1f676868fcc1b367fa42cdfaf9e +3bb8d3934ce8b772d922c3f9004a2ce4db0bb1b9dcc63c5ac2ac90900b5693ae +0e2b7d0148bcb1873256fba13058607b6088dc0478e9ed4773b657cd5d2d736f +a620a352fd7f71675ea408a53c65e193f986c4e03da8008df996858410804323 +d89b4fdf771dbae3422dc193b8425af78a8a27520a09f2c8039fc6bb1501fd9a +8bbdfbcb580c3c2694b8666869abd4cc39083d3dcf68d9b9ddfcc0f87ba8579e +f779dcc8cc901b9fd878ab7f7e0b74ffe6123711fca9a9cde5c7f79f8edfc2d7 +172994574f60daea3dc17097a9ef001528cdd469388ee1c168021e622fb9616b +4ff8294a6e9866cab539ec30a434484370b74b529d4f1b8a83186b6cf3862abb +4a23e983efa8d890f89f654813032996af9ec74967e0bfe3bde9c1157e7c31af +3f35ff00a47818184adcc25d86546503d62dd15ee1992971fe8c8d378fb1112d +dd39f5cb55e8ec815dc8608f59612823cbfb96bdf4a470c9255ce745fbe43a7d +62208c265976740965ae43a75c63e18eb1f6dce9781c98db105c4b4e6bcaee05 +a4227c20e5b717c8d6f3bc02290bf5c6f14ffb5655ee0b5d56ea906de39471a8 +99e146b61d3591d32f297b87eb02957bbfe7a323bf6b09da0f25840018ff3168 +7abfa53dc8d8902eee943cad6bd3d9893954c04f52ef999f64822ebab935f474 +69a8db91bc8767047ee167362d235c494be63957efeef400e12d447c65136438 +8786d36d405ab6b2493e98415784e33fe09b49fe9730ad899ec1db418ab71122 +fe5bb80bdf4a100305b157fb1f60732bebb9c7b70ab22726004bb4fc9925705f +264595ab85a2f8e30d316e48b02d8863c341d8ddc9d34e42affbad9520958027 +cc96871daa10607f13227d6dbabf56f9ffea7eb2acc4d71272cffae286da1970 +911bf0ef3c152bf45f527de8b4deb5a60d80f5793bf7488dd57009b1cbdf51be +543851960eb05d8b2a0a3b7ee50ce6fcc975b4ac6404247464e535364e610804 +e650439e22ebc94c24479a3d28472afb776389f4eb9aff62c8eaa7b96223e014 +3ca9d4ba265c19540c73eb51c9d4003ca3b762cf4d89f6d771d6c9edb07df65f +f106640e15ae6b53056c9fb64e94b2a3ccce80a32f8146a5cb170363c1a5d019 +b7004a617d142bb7238063c35e1173888aa028282fdbdad8ffaca3519fb0228d +98f03d7add2fbd046b6805514d0844abbf7c2ee43de2509d3344843ad2d7220d +15c7a6c87d1ded36db5b5aa9e8394b184d3a6c7ccee4e1041b2473db5a1d0d2c +8d98f9655751c2c2afc7d4a87dac58c0d185e7bf2d897f93b0e999cef7897920 +91cc279dacbdae06ca5cde05c395e73cb675802e1a4f1ac3ae95a0344e2ef6ec +d042152a54fb3a0c129c2beba6c73609c4e9e4abb77d5c0bd731832bd70c6ab8 +5a9a9ccfce845caefcbfca7ac553674f84ff9a7f2846c55541512290a89a9295 +6ed1c17c84a6c454600b079ab709782cfd494adc5433613738158c5163e60211 +116d24252b578e6e4519611b9542372175b3fbed7ff76276daae6e0c8d2cfa77 +edaf4b5827b58b5712266d84914706b76b4aaefffa2e70a703f5261e9c774c00 +85b63f4a86b1e5e19372dfa98040382c94114bdff710b301430ec1894c0d06ab +bf19eb95a3fdeed167d2a2a65fbbcbd426125451de649d018f7c1e2bdfeced50 +e0dc2d2422909b997cd39d2d66842f2f9da9f9002f6b46b93ed2666a324e04ab +68ae1aad5ed221897100680c932f5c5ce71ba4957cc6b784a9ebc0775342d714 +784acdc2a7cbb0cd135c01dac00d4d63385fce60f4d053d30e9b972d97bc6db6 +5a08dac838c5f012e79e60cdf17ab25db8b58ad83df9b2d1b94a7c1f40e04943 +0c53d382480cc0ef5402dcd71d80c23662a9c3078b3989371eca11a4000cc4dc +a50fafe9ad90c4cc77dc6ada3184e8a7fcd744fd57855ee05d40cc96e6cc10af +eb1b41ec865ab2b6090cba6603c687bd059aac1d183ed76846cad1b9a579c7e0 +92d9d93b9cccb596ac7022aaca97e32b4ec7417dc8f0047a9575957464d6b5d5 +210d44dfefe088b47caac2861171abdcef07c828ab673fa98a6e64cda8d42107 diff --git a/crates/pf-vkdecode/tests/data/lowdelay-3840x2160.ivf.av1 b/crates/pf-vkdecode/tests/data/lowdelay-3840x2160.ivf.av1 new file mode 100644 index 0000000000000000000000000000000000000000..78ac2c5f76a127bd8a04deb2fd9363100cb6fd6f GIT binary patch literal 266862 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

+&9Hrg0e^CnY15`W)2bF&Wb=LdDL0+F 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�a8HZ;^ehgBmM&kJ8GN_p| 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

>p~g7gZD7`=l#p>9%a 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 literal 0 HcmV?d00001 diff --git a/crates/pf-vkdecode/tests/gpu_parity.rs b/crates/pf-vkdecode/tests/gpu_parity.rs index aba27889f..b1a03d3fe 100644 --- a/crates/pf-vkdecode/tests/gpu_parity.rs +++ b/crates/pf-vkdecode/tests/gpu_parity.rs @@ -46,17 +46,27 @@ //! prefix for a driver to mis-skip and no second framing to test (see //! `common::split_av1_aus`). Its absence is deliberate. //! -//! # The two legs that decode OUR OWN streams +//! # The three legs that decode OUR OWN streams //! -//! [`LOWDELAY_H264`] and [`LOWDELAY_H265`] are not conformance vectors — they are -//! `punktfunk-host spike` output, vendored because a conformance vector proves -//! conformance to itself and the encoder we ship behind is a different stream. The -//! H.264 one is here because it caught a defect the vector is structurally blind to -//! (117 of its 120 access units named one surface as both the decode target and a -//! reference); the H.265 one is here because HEVC is EXEMPT from that defect for a -//! structural reason, and an exemption with no stream behind it is how the H.264 -//! defect survived two milestones. Both are backed by a non-ignored CPU guard that -//! asserts the stream still has the property it was vendored for. +//! [`LOWDELAY_H264`], [`LOWDELAY_H265`] and [`LOWDELAY_AV1`] are not conformance +//! vectors — they are `punktfunk-host spike` output, vendored because a conformance +//! vector proves conformance to itself and the encoder we ship behind is a different +//! stream. Each is here for its own reason: +//! +//! * **H.264** caught a defect the vector is structurally blind to — 117 of its 120 +//! access units named one surface as both the decode target and a reference. +//! * **H.265** is EXEMPT from that defect for a structural reason, and an exemption +//! with no stream behind it is how the H.264 defect survived two milestones. +//! * **AV1** is neither: the vendored AV1 vector already aliases on 268 of its 274 +//! frames, so that class was covered. It is here because the vector is ONE TILE on +//! every frame while our encoder splits 4K into two tile rows, so every tile array +//! the conversions fill had only ever been exercised at index 0. +//! +//! All three are backed by a non-ignored CPU guard asserting the stream still has the +//! property it was vendored for. ⚠ And all three are FILES. A file fixture says +//! nothing about packetisation, reassembly or loss — which for AV1 is not a +//! hypothetical caveat but a recorded failure: this suite reported 250/250 throughout +//! the period the host was shipping only the first tile of every 4K frame. //! //! # Why the AV1 leg exists at all //! @@ -186,6 +196,40 @@ const DISPLAY_LOWDELAY: (u32, u32) = (640, 480); const LOWDELAY_H265: &[u8] = include_bytes!("data/lowdelay-640x480.h265"); const GOLDENS_LOWDELAY_H265: &str = include_str!("data/lowdelay-640x480-h265.nv12.sha256"); +/// **Our own host's AV1**, and the only stream here with more than ONE TILE. +/// +/// The vendored AV1 vector already exercises the reference-slot aliasing shape (268 of +/// its 274 frames), so unlike the H.264 and H.265 siblings this is not vendored to +/// close that. It closes a different gap: no host-generated AV1 stream was tested at +/// pixel level anywhere, and our encoder's AV1 is structurally unlike the vector — +/// `RFI_DPB = 5` references, reference-frame invalidation, and at 4K a split encode +/// that puts **two tile rows in one frame**. +/// +/// 4K is not a size choice, it is the only shape that has the property. Measured on +/// .21, same command at four resolutions: 1280x720, 1920x1080 and 2560x1440 all give +/// `tile_cols = tile_rows = 1`; 3840x2160 gives `tile_cols = 1, tile_rows = 2` with +/// both tiles in ONE Tile Group OBU. It is paid for with 60 frames instead of 120, +/// which lands at 261 KB — under both other low-delay fixtures. +/// +/// ⚠ It is a FILE, and a file is not the wire path. "250/250 bit-identical to +/// libavcodec" was true for AV1 throughout the period the host was shipping only the +/// first tile of every 4K frame: that number came from a vendored file while the +/// truncation lived in packetisation. This fixture gives the multi-tile shape pixel +/// coverage on the DECODE rungs and says nothing whatever about fragmentation, +/// reassembly or loss. The golden file's header says the same, at length. +const LOWDELAY_AV1: &[u8] = include_bytes!("data/lowdelay-3840x2160.ivf.av1"); +const GOLDENS_LOWDELAY_AV1: &str = include_str!("data/lowdelay-3840x2160-av1.nv12.sha256"); + +/// The low-delay AV1 stream's temporal units, DISPLAYED frames and render region. +/// +/// Units and frames are two constants holding 60 rather than one, and that is +/// deliberate: for the vendored vector they are 250 and 250 while the CODED count is +/// 274, and a leg that derived one from the other would be asserting AV1's frame +/// accounting instead of measuring it. +const LOWDELAY_AV1_UNIT_COUNT: usize = 60; +const LOWDELAY_AV1_FRAME_COUNT: usize = 60; +const DISPLAY_LOWDELAY_AV1: (u32, u32) = (3840, 2160); + /// The HEVC low-delay stream's own frame count and display region. /// /// Deliberately NOT shared with [`LOWDELAY_FRAME_COUNT`]/[`DISPLAY_LOWDELAY`] even @@ -1050,24 +1094,52 @@ fn h265_four_byte_start_codes_decode_bit_identically() { /// `show_existing_frame`) is the point at which this should grow the same parameters /// the H.265 body carries — not before. fn av1_parity_run(aus: &[&[u8]], label: &str) { + av1_parity_run_against( + aus, + label, + GOLDENS_AV1, + "data/test-25fps-av1.nv12.sha256", + FRAME_COUNT, + FRAME_COUNT, + DISPLAY_AV1, + ); +} + +/// [`av1_parity_run`] with its stream's own goldens and geometry, for the leg that +/// does not decode the vendored vector. +/// +/// `units` and `frames` are SEPARATE parameters and must stay so. They are equal for +/// the low-delay host stream (one shown frame per temporal unit) and unequal for the +/// vendored vector only in the sense that its 250 units carry 274 coded frames of +/// which 250 are shown — deriving either from the other is exactly the assumption +/// AV1 punishes. +fn av1_parity_run_against( + aus: &[&[u8]], + label: &str, + goldens_file: &'static str, + goldens_path: &str, + units: usize, + frames: usize, + display: (u32, u32), +) { // As the other legs: one codec at a time on the device, and the `set_var` below // happens only under this lock (see `common::gpu_lock`). let _gpu = common::gpu_lock(); std::env::set_var("PF_VKD_TEST_READBACK", "1"); - let goldens = golden_hashes(GOLDENS_AV1); + let goldens = golden_hashes(goldens_file); // Non-vacuity, before any hardware is touched: the right number of entries, all // real digests, all distinct (see the helper's docs — a frozen-frame decoder // must not be able to pass this leg). - assert_goldens_are_a_real_set(&goldens, FRAME_COUNT, "data/test-25fps-av1.nv12.sha256"); + assert_goldens_are_a_real_set(&goldens, frames, goldens_path); // …and the leg must actually be fed something. An IVF whose packets failed to // parse would hand `collect_hashes` an empty AU list, which delivers no frames // and would then fail as a frame-count mismatch that reads like a decoder defect. assert_eq!( aus.len(), - FRAME_COUNT, - "{label}: the vector must split into {FRAME_COUNT} temporal units" + units, + "{label}: the stream must split into {units} temporal units" ); let setup = common::bring_up(&common::Request { @@ -1104,7 +1176,7 @@ fn av1_parity_run(aus: &[&[u8]], label: &str) { setup.pd, &setup.device, setup.graphics_qf, - DISPLAY_AV1, + display, EXPECTED_FORMAT, ) }; @@ -1148,6 +1220,33 @@ fn av1_every_frame_hashes_bit_identical_to_libavcodec() { av1_parity_run(&common::split_av1_aus(common::TEST_25FPS_AV1), "AV1"); } +/// **Our own host's AV1, at the only resolution where it emits more than one tile.** +/// +/// The leg above proves the conversion against a vector with `tile_cols = tile_rows +/// = 1` on every one of its 274 frames, so every tile-info field it exercises is the +/// degenerate case: one `width_in_sbs_minus_1`, one `height_in_sbs_minus_1`, one +/// `context_update_tile_id`, `TileCols = TileRows = 1`. This stream carries +/// `tile_rows = 2` with `height_in_sbs_minus_1 = [16, 16]` on all 60 frames, and both +/// tiles arrive in a single Tile Group OBU — so a conversion that got the tile arrays, +/// the per-tile sizing or the tile-group range wrong would decode the vector perfectly +/// and this stream visibly (see [`LOWDELAY_AV1`]). +/// +/// It is also 4K, which no other parity leg in this program is: the readback moves +/// 12,441,600 bytes per frame instead of 115,200. +#[test] +#[ignore = "needs a Vulkan Video AV1 decode device (fleet boxes; see module docs)"] +fn low_delay_host_av1_every_frame_hashes_bit_identical_to_libavcodec() { + av1_parity_run_against( + &common::split_av1_aus(LOWDELAY_AV1), + "AV1 (low-delay host stream, 4K two-tile)", + GOLDENS_LOWDELAY_AV1, + "data/lowdelay-3840x2160-av1.nv12.sha256", + LOWDELAY_AV1_UNIT_COUNT, + LOWDELAY_AV1_FRAME_COUNT, + DISPLAY_LOWDELAY_AV1, + ); +} + /// Frame 0's pixels against libavcodec's, byte for byte — the diagnostic leg. /// /// [`av1_every_frame_hashes_bit_identical_to_libavcodec`] is the verdict; this is @@ -1969,6 +2068,191 @@ fn the_low_delay_h265_stream_agrees_with_its_goldens_and_keeps_the_exemption_fal ); } +/// The AV1 low-delay stream's CPU guard, and the property it was vendored for: **more +/// than one tile**. +/// +/// A regenerated fixture could lose that in two silent ways — a re-run at a lower +/// resolution (1440p and below are single-tile on this encoder) or a driver/encoder +/// change that stopped splitting — and in both cases the GPU leg would go on passing +/// 60/60 while duplicating what the vendored vector already covers. So the tile shape +/// is asserted per frame, not sampled. +/// +/// It also pins AV1's frame accounting explicitly rather than by derivation. The +/// vendored vector is 250 units / 274 coded / 24 hidden / 250 shown; this stream is +/// 60 / 60 / 0 / 60. Neither is the general case, and a leg that assumed either would +/// break on the other for reasons that look like a decoder defect. +#[test] +fn the_low_delay_av1_stream_agrees_with_its_goldens_and_still_carries_two_tiles() { + use pf_bitstream::av1::Av1Planner; + + let goldens = golden_hashes(GOLDENS_LOWDELAY_AV1); + assert_goldens_are_a_real_set( + &goldens, + LOWDELAY_AV1_FRAME_COUNT, + "data/lowdelay-3840x2160-av1.nv12.sha256", + ); + + let aus = common::split_av1_aus(LOWDELAY_AV1); + assert_eq!( + aus.len(), + LOWDELAY_AV1_UNIT_COUNT, + "the low-delay AV1 stream is {LOWDELAY_AV1_UNIT_COUNT} temporal units" + ); + assert!( + aus.iter().all(|au| !au.is_empty()), + "no temporal unit is empty — an IVF reader returning empty packets would make \ + the parity leg decode nothing and blame the decoder" + ); + + let mut planner = Av1Planner::new(); + let mut outputs = 0usize; + let mut coded_frames = 0usize; + let mut multi_frame_units = 0usize; + let mut hidden = 0usize; + let mut show_existing = 0usize; + let mut keys = 0usize; + let mut with_removals = 0usize; + let mut aliasing_shape = 0usize; + for (index, au) in aus.iter().enumerate() { + let plans = planner.plan_au(au).unwrap_or_else(|e| { + panic!("temporal unit {index}: the low-delay stream must plan, got {e:?}") + }); + if plans.len() > 1 { + multi_frame_units += 1; + } + for plan in &plans { + coded_frames += 1; + outputs += plan.dpb.outputs.len(); + keys += usize::from(plan.picture.is_key); + hidden += usize::from(!plan.picture.show_frame); + if plan.dpb.stored.is_none() { + show_existing += 1; + } + assert!( + plan.warnings.is_empty(), + "temporal unit {index}: a clean stream plans without warnings, got {:?}", + plan.warnings + ); + + // THE PROPERTY. Two tile ROWS, one tile COLUMN, both tiles in a single + // Tile Group OBU — the 4K split-encode shape, on every frame including + // the key frame. + let tile = &plan.header.tile_info; + assert_eq!( + (tile.tile_cols, tile.tile_rows), + (1, 2), + "frame {coded_frames} (unit {index}): this fixture exists because our \ + encoder emits TWO TILE ROWS at 4K. A single-tile stream here means it \ + was regenerated at a lower resolution (1440p and below measured \ + single-tile) or the encoder stopped splitting — either way the GPU leg \ + below is now a duplicate of the vendored vector's and this fixture's \ + 260 KB buys nothing. Regenerate at 3840x2160; do NOT relax this" + ); + assert_eq!( + ( + tile.width_in_sbs_minus_1[0], + tile.height_in_sbs_minus_1[0], + tile.height_in_sbs_minus_1[1], + ), + (59, 16, 16), + "frame {coded_frames}: the per-tile superblock sizing the conversions \ + copy into their tile arrays" + ); + assert_eq!( + plan.tiles.len(), + 1, + "frame {coded_frames}: both tiles ride in ONE Tile Group OBU" + ); + assert_eq!( + (plan.tiles[0].tg_start, plan.tiles[0].tg_end), + (0, 1), + "frame {coded_frames}: the single tile group covers tiles 0..=1 — a \ + range of 0..=0 is the truncation shape the host once shipped" + ); + + // The picture shape both AV1 legs hard-code (`probe_stream_support(1, 8, + // false)` plus an NV12 pool). Film grain especially: it is part of the + // Vulkan decode PROFILE, so a grain-bearing stream is a different device + // requirement, not merely different pixels. + assert_eq!( + ( + plan.picture.chroma_format_idc, + plan.picture.bit_depth, + plan.sequence.film_grain_params_present, + ), + (1, 8, false), + "frame {coded_frames}: Main 4:2:0 8-bit, no film grain" + ); + if coded_frames == 1 { + assert!(plan.picture.is_key, "the stream opens on a key frame"); + assert_eq!( + (plan.picture.render_width, plan.picture.render_height), + DISPLAY_LOWDELAY_AV1, + "the render region the readback crops to and the goldens hash" + ); + assert_eq!( + (plan.picture.upscaled_width, plan.picture.frame_height), + DISPLAY_LOWDELAY_AV1, + "no superres and no AV1 conformance-window equivalent — the coded \ + picture IS the render region" + ); + } + + if !plan.dpb.removed.is_empty() { + with_removals += 1; + } + aliasing_shape += plan + .dpb + .removed + .iter() + .filter(|id| plan.dpb_refs.iter().any(|r| r.id == **id)) + .count(); + } + } + + // AV1's frame accounting, pinned rather than derived. This stream is the SIMPLE + // shape — one shown frame per temporal unit — which is exactly why it must be + // stated: the vendored vector is not, and a leg that learned its habits from one + // of them silently mis-counts the other. + assert_eq!( + ( + coded_frames, + outputs, + multi_frame_units, + hidden, + show_existing, + keys + ), + ( + LOWDELAY_AV1_FRAME_COUNT, + LOWDELAY_AV1_FRAME_COUNT, + 0, + 0, + 0, + 1 + ), + "coded / displayed / multi-frame units / hidden / show_existing / key frames — \ + our host emits one shown frame per temporal unit and one key frame at the \ + head, against the vendored vector's 274 / 250 / 24 / 24 / 0 / 1" + ); + assert_eq!( + outputs, + goldens.len(), + "the planner outputs {outputs} pictures but the goldens carry {}", + goldens.len() + ); + + // Not the reason this fixture exists — the vendored vector already aliases on 268 + // of its 274 frames — but recorded so a regeneration cannot quietly drop below the + // vector's coverage while claiming to be the host-shaped stream. + assert_eq!( + (with_removals, aliasing_shape), + (55, 55), + "55 of the 60 frames displace a reference they still name, which is the \ + precondition `release_after_decode` exists for" + ); +} + #[test] fn the_main10_vector_is_ten_bit_and_agrees_with_its_goldens() { use pf_bitstream::h265::H265Planner; From 1482e6b3733d59be0b52bac923af9fd479d096e4 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 7 Aug 2026 23:13:17 +0200 Subject: [PATCH 18/21] =?UTF-8?q?docs(client):=20all=20four=20VAAPI=20legs?= =?UTF-8?q?=20have=20decoded=20=E2=80=94=20the=20evidence=20table=20said?= =?UTF-8?q?=20two=20never=20had?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The H.264 and H.265 rows still read "NEVER decoded a frame on any hardware". That stopped being true on 2026-08-07, in the same session that proved AV1: every access unit of the vendored H.264 (250), H.265 (250) and HEVC Main 10 (50) vectors was accepted on .25 (Radeon 780M, RDNA3, Mesa 26.0.3) with no decode error — NV12 for the 8-bit legs, P010 for Main 10, all on the same tiled AMD modifier — and probe_this_machines_libva reports VLD decode for all three profiles. The row records the delivered counts honestly rather than rounding them up: 225/204/45 against 250/250/50 access units, because `finish` shows `outputs.last()` and drops the other pictures an AU bumps, and nothing flushes the DPB at end of stream. That is this rung's own behaviour — D3D11VA delivers all 250 — and it is invisible on punktfunk's zero-reorder host output. It is recorded and asserted rather than fixed: changing the one-frame-per-AU contract touches the pump's deliverable queue, an end-of-stream flush, and the `keyframe`-labels-the-access-unit defect in the same function, so it belongs in a commit that moves all three. Still `verified = false` for all four, and the note says why in the words the unproven-rung test requires: never frame-hash parity-checked. That is not pedantry — the D3D11VA AV1 row two lines above is a rung that decoded 250 frames and produced wrong pixels for every one of them. Parity is what distinguishes them, and this rung exports a tiled dmabuf with no CPU-readable image, so it needs a readback path nothing has written yet. --- crates/pf-client-core/src/video.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/crates/pf-client-core/src/video.rs b/crates/pf-client-core/src/video.rs index f936eed8e..e6b295afc 100644 --- a/crates/pf-client-core/src/video.rs +++ b/crates/pf-client-core/src/video.rs @@ -52,7 +52,7 @@ //! | 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 | **yes** — 250/250 delivered frames bit-identical to libavcodec on an RTX 3500 Ada AND an Intel Arc (2026-08-07). It got there from 186/250 and 245/250 DIVERGING frames on those same two GPUs: `plan_to_dxva_av1` released the picture this frame's own refresh displaces before assigning the decode target its slot, and `SlotMap::assign` hands back the slot just vacated — so 268 of the vector's 274 frames named one surface as both `CurrPicTextureIndex` and a `RefFrameMapTextureIndex` entry. Intel followed the aliased surface (structurally wrong from display frame 4); NVIDIA tolerated it until the `order_hint` wrap at 64 made one 16x24 luma block depend on it. ONE defect, two driver tolerances — the two unlike signatures were not two bugs. TWO vendors, still NO soak on the goldens: the 5-minute 4K60 soak this row used to cite measured throughput, and "streams cleanly" was true throughout the failure | //! | 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) | +//! | native VAAPI | | H.264, H.265 (Main / Main 10) | **not proven** — but all three have now DECODED, on `.25` (Radeon 780M, RDNA3, radeonsi, Mesa 26.0.3, VA-API 1.23) on 2026-08-07: every access unit of the vendored H.264 (250), H.265 (250) and HEVC Main 10 (50) vectors was accepted with no decode error, NV12 for the 8-bit legs and **P010** for Main 10, all on the same tiled AMD modifier, and `probe_this_machines_libva` reports VLD decode for H.264 High, HEVC Main and HEVC Main 10. Delivered frame counts are 225/204/45 rather than 250/250/50, and that is the RUNG, not the driver: `finish` shows `outputs.last()` and never more, so an access unit that bumps several pictures out of the DPB shows the last and drops the rest, and nothing flushes the DPB at end of stream — a conformance vector reorders, punktfunk's zero-reorder host output never does. Never frame-hash parity-checked, for the same reason as AV1: the rung exports a tiled dmabuf with no CPU-readable image | //! | 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 @@ -1146,11 +1146,13 @@ pub fn native_evidence(rung: NativeRung, wire: u8) -> RungEvidence { Intel Arc (2026-08-07), after fixing a decode target that aliased a reference \ surface on 268 of 274 frames - two vendors, no soak (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. + // 2026-08-07: the VAAPI rung decoded its first frames ever, and by the end of that + // day ALL FOUR legs had — 250/250 of the vendored AV1 vector, then every access unit + // of the H.264 (250), H.265 (250) and HEVC Main 10 (50) vectors, on `.25` (Radeon + // 780M, RDNA3, Mesa 26.0.3): NV12 for the 8-bit legs, P010 for Main 10, all on a + // tiled AMD modifier. So "never decoded a frame anywhere" is no longer true of ANY of + // them and must not be printed. The arm stays split only because AV1's note carries + // its own frame count; both halves say the same thing about parity. // // 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 @@ -1164,8 +1166,10 @@ pub fn native_evidence(rung: NativeRung, wire: u8) -> RungEvidence { ), (NativeRung::Vaapi, _) => ( false, - "NEVER decoded a frame on any hardware - the AV1 leg has now run on RDNA3, these \ - two have not (M6/M7)", + "decoded every access unit of the vendored H.264, H.265 and HEVC Main 10 \ + vectors on RDNA3 (Mesa 26.0.3, 2026-08-07) - their first frames on any \ + hardware - but has never been frame-hash parity-checked: the rung exports \ + a tiled dmabuf with no CPU-readable image (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 From f926bab9f790d7ca91060644cff34a103a454b36 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Sat, 8 Aug 2026 00:10:35 +0200 Subject: [PATCH 19/21] fix(client): the native VAAPI rung stopped dropping decoded frames on the floor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `finish` showed `outputs.last()` and retired every other picture an access unit bumped out of the DPB without ever displaying it, and nothing flushed the DPB at end of stream. Measured on .25 against the vendored vectors: 225 of 250 frames for H.264, 204 of 250 for H.265, 45 of 50 for HEVC Main 10. D3D11VA and Vulkan deliver every frame, so this was the rung's alone. All four legs now deliver 250 / 250 / 50 / 250. The same function carried a second defect. `DmabufFrame::keyframe` was stamped with the CURRENT access unit's `is_idr`, not the flag of the picture it was about to display, and on a reordering stream those are different pictures: the IDR is bumped out several units after it decodes and arrived flagged `false` on all three legs' first frame, while a later AU draining the DPB flagged some old trailing picture as a keyframe. That field is `DecodedImage::is_keyframe`, the pump's post-loss re-anchor signal, so a mislabel re-anchors on the wrong frame. Three changes, all inside this rung: * **A deliverable queue**, the same shape as `video_vk_native`'s — extend, ship the front, trim the oldest past the bound, count and rate-limit the drops into `DecodeHealth::dropped`. Its DEPTH is derived differently and the divergence is documented: the Vulkan rung's bound is `HOLD_HEADROOM - PIPELINE_HOLD` = 1 because a queued frame there counts against the pool ON TOP of the DPB's own residency. Here the three claims are disjoint and a bumped picture MOVES from `pending`/slot to `held`, so the queue inherits the claim rather than adding one. The bound is the DPB's depth — the deepest carry-over a bump can leave — and the measured cost is at most one surface (zero on H.264, whose three seven-picture IDR drains are the deepest bursts these vectors have). A bound of 1 would have left 235 of 250 on H.264, most of the defect still in place. * **An end-of-stream flush.** This rung has no EOS signal and cannot have one: the pump feeds access units until the session ends and then drops the decoder. So `flush` has the two honest callers — `Drop`, where nothing can be presented and the job is to release the queue's surfaces and the DPB's before the pool goes, and a caller that KNOWS the stream ended, which today is the conformance harness. One walk, not a production path and an untested teardown path. AV1 needs none: it shows at most one frame per temporal unit and buffers nothing, which its 250/250 says out loud. * **`PictureFacts` recorded when a picture decodes**, and read back when it is displayed. `keyframe` was the defect; `color` and `display` are the same mistake one field along — an in-band HDR switch changes the VUI mid-stream and AV1's render region is per-frame, so a queued frame shown two units later would have been drawn with the newest picture's signalling. Concealment answers `Ok(None)` and deliberately does NOT drain the queue, which is the Vulkan rung's order and is load-bearing: `clears_demotion_streak` is `delivered || !concealed`, so shipping a queued frame on a concealed AU would zero the streak and take away the escape hatch that stops a rung concealing forever from holding a frozen picture. The three delivered-count assertions moved with the fix, and so did the CPU derivation that reproduces them without a GPU — it now simulates the whole delivery model (ledger, queue, one-per-AU hand-off, flush) in the order `decode` does it, and carries the old behaviour beside the new one as a counterfactual: a queue bound of 0 with no flush still reproduces 225/204/45 exactly, and the test fails if it ever stops being SHORT. `settle` was split out as the pure half of `finish` so the claim walk, the display ordering and the picture facts are all assertable with no device; `the_queue_never_needs_a_surface_the_pool_does_not_have` runs the surface-lifetime arithmetic over the real vectors and pins the peak claims (9 of a 16-surface pool on H.264, 8 of 14 on both HEVC vectors), with an unbounded queue as the counterfactual that shows the bound doing its job. Gates run: `cargo fmt --all -- --check`, `cargo clippy -p pf-client-core -p pf-vaadec --all-targets --features sdl3/build-from-source -- -D warnings`, `cargo test -p pf-client-core --lib --features sdl3/build-from-source` (176 pass), the same filtered to `video_vaapi_native -- --include-ignored` (23 pass, 0 ignored) and `cargo test -p pf-vaadec` (48 pass) — all on .25 (Radeon 780M, RDNA3, radeonsi, Mesa 26.0.3, VA-API 1.23); plus `cargo fmt --all -- --check` and `cargo clippy --workspace --all-targets -- -D warnings` in pf-lxcheck2. --- crates/pf-client-core/src/video.rs | 17 +- .../pf-client-core/src/video_vaapi_native.rs | 1487 ++++++++++++++--- 2 files changed, 1231 insertions(+), 273 deletions(-) diff --git a/crates/pf-client-core/src/video.rs b/crates/pf-client-core/src/video.rs index e6b295afc..1afe5e2e1 100644 --- a/crates/pf-client-core/src/video.rs +++ b/crates/pf-client-core/src/video.rs @@ -52,7 +52,7 @@ //! | 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 | **yes** — 250/250 delivered frames bit-identical to libavcodec on an RTX 3500 Ada AND an Intel Arc (2026-08-07). It got there from 186/250 and 245/250 DIVERGING frames on those same two GPUs: `plan_to_dxva_av1` released the picture this frame's own refresh displaces before assigning the decode target its slot, and `SlotMap::assign` hands back the slot just vacated — so 268 of the vector's 274 frames named one surface as both `CurrPicTextureIndex` and a `RefFrameMapTextureIndex` entry. Intel followed the aliased surface (structurally wrong from display frame 4); NVIDIA tolerated it until the `order_hint` wrap at 64 made one 16x24 luma block depend on it. ONE defect, two driver tolerances — the two unlike signatures were not two bugs. TWO vendors, still NO soak on the goldens: the 5-minute 4K60 soak this row used to cite measured throughput, and "streams cleanly" was true throughout the failure | //! | 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 (Main / Main 10) | **not proven** — but all three have now DECODED, on `.25` (Radeon 780M, RDNA3, radeonsi, Mesa 26.0.3, VA-API 1.23) on 2026-08-07: every access unit of the vendored H.264 (250), H.265 (250) and HEVC Main 10 (50) vectors was accepted with no decode error, NV12 for the 8-bit legs and **P010** for Main 10, all on the same tiled AMD modifier, and `probe_this_machines_libva` reports VLD decode for H.264 High, HEVC Main and HEVC Main 10. Delivered frame counts are 225/204/45 rather than 250/250/50, and that is the RUNG, not the driver: `finish` shows `outputs.last()` and never more, so an access unit that bumps several pictures out of the DPB shows the last and drops the rest, and nothing flushes the DPB at end of stream — a conformance vector reorders, punktfunk's zero-reorder host output never does. Never frame-hash parity-checked, for the same reason as AV1: the rung exports a tiled dmabuf with no CPU-readable image | +//! | native VAAPI | | H.264, H.265 (Main / Main 10) | **not proven** — but all three have now DECODED, on `.25` (Radeon 780M, RDNA3, radeonsi, Mesa 26.0.3, VA-API 1.23) on 2026-08-07: every access unit of the vendored H.264 (250), H.265 (250) and HEVC Main 10 (50) vectors was accepted with no decode error, **250/250/50 frames delivered**, NV12 for the 8-bit legs and **P010** for Main 10, all on the same tiled AMD modifier, and `probe_this_machines_libva` reports VLD decode for H.264 High, HEVC Main and HEVC Main 10. The counts were 225/204/45 when these legs were first written, and that was the RUNG rather than the driver: `finish` showed `outputs.last()` and retired the rest unshown, and nothing flushed the DPB at end of stream. Both are fixed — `video_vaapi_native` grew a bounded deliverable queue and a flush, and the keyframe flag now travels with the PICTURE rather than with the access unit that displays it (the pump's post-loss re-anchor signal, mislabelled on every one of these legs' first frames). Never frame-hash parity-checked, for the same reason as AV1: the rung exports a tiled dmabuf with no CPU-readable image | //! | 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 @@ -242,8 +242,9 @@ pub struct DecodeHealth { pub worst_run: u32, /// Frames that decoded CORRECTLY and were then discarded without ever being /// shown, because the backend's deliverable queue overflowed - /// (`video_vk_native::MAX_DELIVERABLE` — a decoder making more pictures - /// display-ready per access unit than the pump can take one at a time). + /// (`video_vk_native::MAX_DELIVERABLE` and `video_vaapi_native::max_deliverable` + /// — a decoder making more pictures display-ready per access unit than the pump + /// can take one at a time). /// /// Deliberately its own number and not folded into any of the three above: /// nothing was damaged, nothing was refused and no driver failed, so counting @@ -253,8 +254,8 @@ pub struct DecodeHealth { /// discarding a frame per AU is one running at half the frame rate it thinks /// it is, and before this counter existed it read as perfectly clean. /// - /// Structurally 0 on every rung but native Vulkan — it is the only one with a - /// deliverable queue — and not on the session stats line today; the + /// Structurally 0 on every rung but native Vulkan and native VAAPI — the two with + /// a deliverable queue — and not on the session stats line today; the /// rate-limited `warn` at the drop site is the field signal, and this is the /// number a stats field would read. pub dropped: u64, @@ -773,8 +774,10 @@ 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. ⚠ 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` + /// order. ⚠ All four legs have now decoded on RDNA3 — 250/250/50/250 for + /// H.264/H.265/HEVC Main 10/AV1 — but every one of them is unverified for want of + /// frame-hash parity, which the tiled dmabuf makes impossible without a readback path + /// this rung does not have ([`native_evidence`]) — so `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 diff --git a/crates/pf-client-core/src/video_vaapi_native.rs b/crates/pf-client-core/src/video_vaapi_native.rs index c1a361d09..5bd498bb8 100644 --- a/crates/pf-client-core/src/video_vaapi_native.rs +++ b/crates/pf-client-core/src/video_vaapi_native.rs @@ -48,6 +48,30 @@ //! back. A surface is free when no live picture is bound to it AND no consumer holds //! it — two conditions, tracked separately, because they end at different times. //! +//! # One access unit in, one frame out — and the queue that makes that honest +//! +//! [`NativeVaapiDecoder::decode`] hands the pump at most one frame, because that is the +//! pump's contract. An access unit can make SEVERAL pictures displayable at once: an +//! IDR with `no_output_of_prior_pics_flag` clear drains the whole DPB, and ordinary +//! reordering bumps a burst whenever the buffer empties. Until 2026-08-07 this rung +//! showed the last of them and retired the rest unshown, and nothing flushed the DPB at +//! the end of a stream — measured at 225 of 250 frames on the vendored H.264 vector, +//! 204 of 250 on H.265 and 45 of 50 on HEVC Main 10, while the D3D11VA and Vulkan rungs +//! delivered every one. +//! +//! The surplus now waits in [`NativeVaapiDecoder::deliverable`] for the access units +//! that output nothing — on a reordering stream, exactly where the reorder buffer +//! refills — bounded by [`max_deliverable`], and [`NativeVaapiDecoder::flush`] drains +//! the tail. It costs the surface pool nothing, because a queued frame INHERITS the +//! claim the picture had as a DPB reference rather than adding a new one; that +//! arithmetic is [`max_deliverable`]'s docs and +//! `the_queue_never_needs_a_surface_the_pool_does_not_have`. +//! +//! ⚠ None of it engages on the wire. punktfunk hosts emit zero-reorder low-delay +//! output, so `outputs` never holds more than one picture, the queue is empty on every +//! access unit, and the flush finds an empty DPB. That is why the defect survived to be +//! found by a conformance vector rather than by a session. +//! //! # Why this rung is exempt from the decode-into-a-reference defect //! //! The D3D11VA and Vulkan rungs both had to grow a `release_after_decode` deferral: @@ -664,6 +688,46 @@ impl Drop for VaFrameGuard { // The session // --------------------------------------------------------------------------- +/// The facts that belong to a PICTURE rather than to the access unit that happens to +/// bump it out of the DPB. +/// +/// Recorded when the picture decodes, because that is the only moment they are known +/// to be its own. On a reordering stream the access unit that displays a picture can +/// be several units later and says something different about all three: +/// +/// * **`keyframe`** was the whole defect. [`finish`] used to be handed the CURRENT +/// access unit's `is_idr` and stamp it on whichever picture bumping displaced — so +/// an IDR bumped out three units after it decoded arrived flagged `false`, and the +/// later AU that drained the DPB flagged some ordinary trailing picture as a +/// keyframe. That flag is [`crate::video::DecodedImage::is_keyframe`], the pump's +/// post-loss re-anchor signal: mislabelled, the pump re-anchors on the wrong frame +/// and keeps asking for a keyframe it has already been sent. +/// * **`color`** is read per picture off the ACTIVE SPS/VUI and never latched, +/// because the Windows host switches an HDR desktop to PQ/BT.2020 in-band with a +/// new SPS. Stamping the displaying AU's description onto a picture decoded under +/// the previous one is the same mistake one field along. +/// * **`display`** is a per-FRAME value on AV1 (5.9.6's render size, which may change +/// without a key frame), so a queued frame shown two units later would be cropped +/// to whatever the newest frame asked for. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct PictureFacts { + /// Intra keyframe (IDR / AV1 key frame) — THIS picture's, not its display AU's. + keyframe: bool, + color: ColorDesc, + /// The DISPLAY region. A recorded fact rather than a read of `s.shape` because + /// AV1's is per-frame. + display: (u32, u32), +} + +/// A decoded picture that still owes an output, and where it lives. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct PendingPicture { + id: u64, + /// Pool index. + surface: usize, + facts: PictureFacts, +} + /// The live config, context and surface pool for one [`StreamShape`]. struct Session { shape: StreamShape, @@ -676,10 +740,10 @@ struct Session { /// DPB slot → pool index, rebound at ACTIVATION (module docs). `None` for a slot /// holding no picture. slot_surface: Vec>, - /// Decoded pictures the planner has not output yet, `(PicId, pool index)`. - /// Separate from the slot binding because the two end at different times: a - /// non-reference picture leaves the DPB immediately but still owes an output. - pending: Vec<(u64, usize)>, + /// Decoded pictures the planner has not output yet. Separate from the slot + /// binding because the two end at different times: a non-reference picture + /// leaves the DPB immediately but still owes an output. + pending: Vec, slots: pf_vaadec::SlotMap, /// The surface fourcc the pool was created with (NV12 or P010). fourcc: u32, @@ -694,11 +758,18 @@ impl Session { /// the DPB when the planner removes it, stops being pending when it is output, /// and stops being held when the presenter's fence has been waited — and the /// display is usually the LAST of the three. + /// + /// ⚠ `held` covers TWO claims since the deliverable queue existed: a frame the + /// consumer has, and a frame waiting in [`NativeVaapiDecoder::deliverable`] for a + /// later access unit. They are deliberately one flag, because a queued frame has + /// already been exported and its guard is what returns the surface either way — + /// so a frame dropped by [`trim_deliverable`] frees its surface by exactly the + /// same path a presented one does. fn free_surface(&self) -> Option { (0..self.surfaces.len()).find(|i| { !self.held[*i] && !self.slot_surface.contains(&Some(*i)) - && !self.pending.iter().any(|(_, p)| p == i) + && !self.pending.iter().any(|p| p.surface == *i) }) } @@ -935,11 +1006,128 @@ const _: () = { // The decoder // --------------------------------------------------------------------------- +/// How many display-ready frames this rung holds back for LATER access units before it +/// starts dropping the oldest (see [`trim_deliverable`]). +/// +/// **The DPB's own depth, and derived rather than chosen.** The deepest burst one +/// access unit can bump is the whole DPB plus the picture that caused the bump — an +/// IDR with `no_output_of_prior_pics_flag` clear draining a full buffer, which is +/// exactly what the vendored H.264 vector does three times (measured: seven pictures +/// output by one AU, on a stream whose `max_dpb_frames` is seven). One of them ships +/// immediately, so the CARRY-OVER a bump can leave is the DPB's depth and no more. +/// +/// It costs the pool **at most one surface**, and that is a property of THIS rung +/// rather than a hope. A surface is claimed three separate ways here — a live slot, a +/// pending output, a `held` frame — and a bumped picture MOVES from the first two to +/// the third: [`settle`] takes it out of `pending` in the same breath the bump +/// released its slot, and [`ship`] then marks it `held`. So `|slots ∪ pending| + +/// |queued|` is conserved across a bump, and between bumps it is flat — every access +/// unit decodes one picture into the pool and ships one frame out of the queue. +/// Measured by [`the_queue_never_needs_a_surface_the_pool_does_not_have`] over the +/// real vectors with no device: the deepest simultaneous claim is **9 of a 16-surface +/// pool** on H.264 — where the queue's marginal cost is exactly zero, on the vector +/// with the deepest bursts there are — and 8 of 14 on both HEVC vectors, one more +/// than the same walk with no queue at all. Six or seven of the eight surfaces +/// [`pf_vaadec::config::PRESENTER_HEADROOM`] exists for are still there for the +/// consumer. +/// +/// ⚠ This is where it diverges from the Vulkan rung's [`crate::video_vk_native`] +/// `MAX_DELIVERABLE`, which is `HOLD_HEADROOM - PIPELINE_HOLD` = **1** — and the +/// difference is real, not a disagreement. There a delivered-but-unreleased frame is +/// counted against `picture_count = required_slots + HOLD_HEADROOM` ON TOP of the +/// DPB's own residency (`build_frame` marks the picture held the moment the decoder +/// declares it ready), so its queue and the pipeline share one budget of eight. Here +/// they do not share: the queue inherits the claim the DPB just gave up. A bound of +/// one would have left this rung dropping five of every seven-picture drain — 235 of +/// 250 on the H.264 vector instead of 250, which is most of the defect this fix +/// exists to end still in place. +/// +/// It is a bound and not a plain queue for the reason the Vulkan rung gives at +/// length: "transient" is an assumption about the HOST, and a stream that reliably +/// made two frames displayable per access unit would grow this by one per AU until +/// the pool ran out — after which every AU refuses with "surface pool exhausted", +/// three in a second demote the rung, and nothing in the log would say the cause was +/// a queue that could never drain. +/// +/// ⚠ On H.264 and H.265 that shape cannot arise and the bound is pure defence: an +/// access unit decodes at most ONE picture, so it can only ever output what earlier +/// units decoded, and the queue sheds one per unit — which is why the vendored +/// vectors drop nothing at any depth. An AV1 temporal unit may decode several, and +/// that is where the bound is load-bearing rather than decorative. On the wire none +/// of it engages: punktfunk hosts emit zero-reorder low-delay output, so `outputs` +/// never holds more than one picture and the queue is empty on every single access +/// unit. +fn max_deliverable(s: &Session) -> usize { + s.shape.max_dpb_frames +} + +/// One `warn` per this many dropped deliverable frames, after the first. +/// +/// The same rate limit and the same reasoning as the Vulkan rung's: the shape that +/// drops at all drops on EVERY access unit, and a warn per frame at frame rate buries +/// the log it exists to explain — while a single line at the start of a session that +/// then goes quiet reads as a one-off. So: the first drop in full, then a heartbeat +/// with the running total (~every 5 s at 60 fps). +const DROP_WARN_EVERY: u64 = 300; + +/// Trim the deliverable queue to `cap` by dropping from the FRONT, returning the +/// dropped frames so the caller can count them. +/// +/// Oldest-first, for the Vulkan rung's reason: by the time a queue this deep exists +/// the front frame is several access units stale and the consumer one stage on is +/// itself newest-wins (the pump's `force_send` overwrites an unconsumed frame). +/// Dropping the NEWEST would keep the stalest picture and present the stream in +/// ever-lagging order; dropping the oldest keeps display order for everything that +/// survives and costs the frames that were already too late to matter. +/// +/// ⚠ Called AFTER this access unit's own frame has been taken off the front, so `cap` +/// bounds the CARRY-OVER — what is held back for later access units — exactly as +/// [`max_deliverable`] says. Trimming before the take would make a unit that produced +/// two outputs drop the FIRST of them and ship the second, which is display order +/// inverted inside a single access unit. +/// +/// A dropped frame needs no explicit release: its [`VaFrameGuard`] closes the exported +/// fds and returns the surface to the free list on drop, which is the same path a +/// presented frame takes. Returning them rather than dropping them here is what lets +/// the caller count and log before they go. +/// +/// Pure over the queue, so the bound is CPU-testable without a device. +fn trim_deliverable( + queue: &mut std::collections::VecDeque, + cap: usize, +) -> Vec { + let mut dropped = Vec::new(); + while queue.len() > cap { + match queue.pop_front() { + Some(frame) => dropped.push(frame), + // Unreachable: `len() > cap >= 0` means the queue is non-empty. Written as + // a break rather than an `expect` so a bound of 0 on an empty queue could + // never be a panic in the decode path. + None => break, + } + } + dropped +} + /// The native VAAPI rung. pub(crate) struct NativeVaapiDecoder { display: Display, planner: Planner, session: Option, + /// Display-ready frames not yet handed to the pump, oldest first. + /// + /// [`Self::decode`] is one access unit in, at most one frame out — the pump's + /// contract — while one access unit can bump SEVERAL pictures out of the DPB. The + /// surplus waits here for the access units that output nothing, which on a + /// reordering stream is exactly where the reorder buffer refills. Bounded by + /// [`max_deliverable`]; empty on every access unit of a punktfunk stream. + /// + /// Every frame in here holds a pool surface through its own guard, and survives a + /// session renegotiation intact for the same reason a consumer-held frame does: + /// the exported PRIME fds hold their own reference on the underlying buffer + /// object, so the pixels outlive the `VASurface` (see [`ensure_session`]) and the + /// stale-generation release token is counted rather than applied. + deliverable: std::collections::VecDeque, health: DecodeHealth, /// A concealed AU asks the pump for a re-anchor, through the same one throttle /// every other ask uses. Drained by [`Self::take_recovery_request`]. @@ -979,6 +1167,7 @@ impl NativeVaapiDecoder { display, planner, session: None, + deliverable: std::collections::VecDeque::new(), health: DecodeHealth { // VAAPI has no per-picture decode-status query — there is no // counterpart to Vulkan's `RESULT_STATUS_ONLY`, exactly as on @@ -1021,10 +1210,26 @@ impl NativeVaapiDecoder { /// Decode one access unit. /// - /// `Ok(None)` means "no picture from this AU", and covers three different + /// One access unit in, **at most one displayable frame out** — the pump's + /// contract. An access unit that bumps SEVERAL pictures out of the DPB delivers + /// the first of them and holds the rest in [`Self::deliverable`] for the access + /// units that output nothing, which on a reordering stream is exactly where the + /// reorder buffer refills. Nothing is discarded for want of a return slot; the + /// only frames that go unshown are the ones a queue past [`max_deliverable`] + /// drops, and that bound never engages on a punktfunk stream. + /// + /// ⚠ Until 2026-08-07 this shipped `outputs.last()` and RETIRED the rest without + /// ever displaying them, which cost the vendored vectors 25 frames of 250 on + /// H.264 and 46 of 250 on H.265. It could not bite punktfunk's own streams — + /// zero-reorder low-delay output never bumps two pictures at once — but it is + /// exactly the class of defect this program exists to find, and the three + /// hardware legs measured it. + /// + /// `Ok(None)` means "no picture from this AU", and covers four different /// things, deliberately none of them errors: /// - /// * the planner output nothing yet (reordering, or the very first AUs); + /// * the planner output nothing yet and the queue is empty (reordering, or the + /// very first AUs); /// * the picture was CONCEALED — an integrity warning says a reference was /// substituted, so the output is released unshown, [`DecodeHealth::damaged`] /// records it and a re-anchor is requested through the pump's one throttle. @@ -1034,7 +1239,8 @@ impl NativeVaapiDecoder { /// * an HEVC RASL picture skipped after an open-GOP join. `PlanError::RaslSkipped` /// is the spec's own answer (8.1.3 NOTE) and must NEVER reach the reanchor /// path — mapping it to an error would make every open-GOP join beg the host - /// for a keyframe it has no reason to send. + /// for a keyframe it has no reason to send; + /// * the whole session was refused before a pool existed. pub(crate) fn decode(&mut self, au: &[u8]) -> Result> { self.drain_releases(); let result = match self.planner { @@ -1053,10 +1259,142 @@ impl NativeVaapiDecoder { Ok((_, damaged)) => self.health.note(*damaged, false, 0), Err(_) => self.health.note(false, true, 0), } - result.map(|(frame, _)| frame) + // A REFUSED access unit puts nothing on screen, and that has to hold for the + // frames it had already exported before it failed: the codec arm returns them + // in its `Err`-free half only, so an error path drops them here by never + // reaching the queue at all. + let (fresh, damaged) = result?; + if damaged { + // Concealment answers `Ok(None)` and does NOT drain the queue — the same + // order the Vulkan rung keeps, and it is load-bearing rather than tidy. + // `clears_demotion_streak(delivered, concealed)` is `delivered || !concealed`: + // shipping a queued frame here would report `delivered` on a concealed AU + // and zero the streak, which is exactly the escape hatch that keeps a rung + // concealing FOREVER from holding a frozen picture with no way down. The + // queued frames are clean pictures from earlier units and lose nothing by + // waiting — the pump has just armed a freeze that withholds a non-keyframe + // anyway — so they ship on the next clean access unit. + debug_assert!( + fresh.is_empty(), + "finish ships nothing from a damaged access unit" + ); + return Ok(None); + } + self.deliverable.extend(fresh); + Ok(self.take_deliverable()) } - fn decode_h264(&mut self, au: &[u8]) -> Result<(Option, bool)> { + /// Hand the pump the oldest display-ready frame and bound what stays behind. + /// + /// This access unit's own frame comes off the FRONT first, because the bound is on + /// the CARRY-OVER (see [`trim_deliverable`]): a unit that produced two outputs + /// ships the first and holds the second, rather than dropping the first to ship + /// the second and inverting display order inside one access unit. + fn take_deliverable(&mut self) -> Option { + let shipped = self.deliverable.pop_front(); + // No session means no pool, so there is nothing the queue could legitimately + // still be holding; a cap of 0 is the honest reading of "no surfaces exist" + // rather than a magic number. + let cap = self.session.as_ref().map_or(0, max_deliverable); + // The PRE-trim depth: how far past the bound the queue actually got. Read + // after the trim it would be the constant `cap` every single time. + let queued = self.deliverable.len(); + for frame in trim_deliverable(&mut self.deliverable, cap) { + self.health.note_dropped(); + if self.health.dropped == 1 || self.health.dropped % DROP_WARN_EVERY == 0 { + tracing::warn!( + queued, + cap, + dropped_total = self.health.dropped, + "native VAAPI: more display-ready frames than the pump can take — \ + dropping the oldest so its surface is not held forever" + ); + } + // The guard closes the exported fds and returns the surface; nothing else + // is owed. + drop(frame); + } + shipped + } + + /// Drain the DPB: every picture the planner is still buffering becomes + /// display-ready, in display order, and every id it held is released. + /// + /// # What "end of stream" means for this rung + /// + /// It has no end-of-stream signal and cannot have one: the pump feeds access units + /// until the session ends and then drops the decoder, and there is no call after + /// the last access unit through which a frame could still reach the screen. So the + /// two callers are the two honest ones, and they are the SAME walk rather than a + /// production path and an untested teardown path: + /// + /// * **Teardown** ([`Drop`]), where nothing can be presented and the job is to + /// release — the queue's surfaces AND the DPB's — before the session destroys + /// the pool underneath them. + /// * **A caller that KNOWS the stream ended**, which today is the conformance + /// harness. Without this the vendored vectors lose their tail outright — seven + /// pictures of 250 on H.264, one on H.265, two of 50 on Main 10, decoded and + /// buffered for reorder and never asked for — plus whatever the deliverable + /// queue is still carrying, which is why the counts the legs print (7 / 2 / 2) + /// are not the DPB's tail alone. + /// + /// AV1 has no flush and needs none: it shows at most one frame per temporal unit + /// and buffers no output between them (`Av1Planner` has no counterpart to the + /// H.26x planners' `flush`), so its tail is empty by construction — which the + /// hardware leg's 250 of 250 says out loud. + /// + /// Best-effort by design. An export that fails at teardown must not panic and has + /// nothing to return an error to; it is logged and the picture is dropped, and the + /// harness sees it as a frame count that does not add up, which is loud enough. + pub(crate) fn flush(&mut self) -> Vec { + self.drain_releases(); + let mut out: Vec = std::mem::take(&mut self.deliverable).into(); + let Self { + display, + planner, + session, + release_tx, + .. + } = self; + let Some(s) = session.as_mut() else { + return out; + }; + // The two H.26x planners' `flush` return the same SHAPE under two different + // types (`h264::DpbUpdate` and `h265::DpbUpdate`), which is why this is two + // arms and not one generic call. + let (outputs, removed) = match planner { + Planner::H264(p) => { + let update = p.flush(); + (update.outputs, update.removed) + } + Planner::H265(p) => { + let update = p.flush(); + (update.outputs, update.removed) + } + Planner::Av1(_) => (Vec::new(), Vec::new()), + }; + let claimed = settle(s, &outputs, &removed); + for picture in claimed { + match ship(display, s, picture, release_tx) { + Ok(frame) => out.push(frame), + Err(e) => tracing::warn!( + error = %e, + id = picture.id, + "native VAAPI: a flushed picture could not be exported" + ), + } + } + // No conversion runs on this path, so this is the only place the planner's + // releases can reach the ledger — the same reason `show_existing_av1` applies + // them by hand. Without it a resumed stream finds every slot taken. + for id in &removed { + s.slots.release(*id); + } + s.sync_slot_bindings(); + out + } + + fn decode_h264(&mut self, au: &[u8]) -> Result<(Vec, bool)> { let plan = match &mut self.planner { Planner::H264(p) => p.plan_au(au).map_err(|e| anyhow!("{e:?}"))?, _ => unreachable!("dispatched on the planner's own arm"), @@ -1090,7 +1428,15 @@ impl NativeVaapiDecoder { let converted = pf_vaadec::plan_to_va(&plan, au, &mut s.slots, &table, target) .map_err(|e| anyhow!("{e}"))?; - bind_setup(s, plan.dpb.stored, Some(free)); + // Recorded HERE, with the picture that is decoding, rather than read again at + // display time: on a reordering stream the access unit that displays this + // picture is a later one and says something different (see [`PictureFacts`]). + let facts = PictureFacts { + keyframe: plan.picture.is_idr, + color: colour_of(&plan.picture.colour), + display: (s.shape.display_width, s.shape.display_height), + }; + bind_setup(s, plan.dpb.stored, Some(free), facts); let iq = Some(as_ptr(&converted.iq_matrix)); let slices = one_record_each(&converted.slices, &converted.slice_data)?; @@ -1104,30 +1450,28 @@ impl NativeVaapiDecoder { au, )?; - let display_size = (s.shape.display_width, s.shape.display_height); - let frame = finish( + let frames = finish( display, s, &plan.dpb.outputs, &plan.dpb.removed, damaged, - plan.picture.is_idr, - colour_of(&plan.picture.colour), - display_size, &mut self.recovery_request, &self.release_tx, )?; - Ok((frame, damaged)) + Ok((frames, damaged)) } - fn decode_h265(&mut self, au: &[u8]) -> Result<(Option, bool)> { + fn decode_h265(&mut self, au: &[u8]) -> Result<(Vec, bool)> { let plan = match &mut self.planner { Planner::H265(p) => match p.plan_au(au) { Ok(plan) => plan, // The contract pf-bitstream's h265 module docs record for this // wiring: a skipped RASL picture is an Ok-skip, never an error and // never a re-anchor. See [`Self::decode`]. - Err(pf_vaadec::PlanErrorH265::RaslSkipped { .. }) => return Ok((None, false)), + Err(pf_vaadec::PlanErrorH265::RaslSkipped { .. }) => { + return Ok((Vec::new(), false)) + } Err(e) => return Err(anyhow!("{e:?}")), }, _ => unreachable!("dispatched on the planner's own arm"), @@ -1164,7 +1508,14 @@ impl NativeVaapiDecoder { let converted = pf_vaadec::plan_to_va_h265(&plan, au, &mut s.slots, &table, target) .map_err(|e| anyhow!("{e}"))?; - bind_setup(s, plan.dpb.stored, Some(free)); + // This picture's own facts, not the facts of whichever later access unit + // bumps it out (see [`PictureFacts`]). + let facts = PictureFacts { + keyframe: plan.picture.is_idr, + color: colour_of(&plan.picture.colour), + display: (s.shape.display_width, s.shape.display_height), + }; + bind_setup(s, plan.dpb.stored, Some(free), facts); // The IQ matrix is submitted ONLY where the sequence codes scaling lists. // Handing the driver an all-zero matrix on a "use the defaults" stream is @@ -1184,20 +1535,16 @@ impl NativeVaapiDecoder { au, )?; - let display_size = (s.shape.display_width, s.shape.display_height); - let frame = finish( + let frames = finish( display, s, &plan.dpb.outputs, &plan.dpb.removed, damaged, - plan.picture.is_idr, - colour_of(&plan.picture.colour), - display_size, &mut self.recovery_request, &self.release_tx, )?; - Ok((frame, damaged)) + Ok((frames, damaged)) } /// One AV1 **temporal unit**: decode every frame in it, present at most one. @@ -1244,12 +1591,12 @@ impl NativeVaapiDecoder { /// access unit whose tile groups were lost. That refusal is handled in /// [`Self::frame_av1`] and binds no surface at all, so its picture can be neither /// exported nor predicted from. - fn decode_av1(&mut self, au: &[u8]) -> Result<(Option, bool)> { + fn decode_av1(&mut self, au: &[u8]) -> Result<(Vec, bool)> { let plans = match &mut self.planner { Planner::Av1(p) => p.plan_au(au).map_err(|e| anyhow!("{e}"))?, _ => unreachable!("dispatched on the planner's own arm"), }; - let mut shown = None; + let mut shown: Vec = Vec::new(); let mut damaged_unit = false; for plan in &plans { let damaged = plan @@ -1260,9 +1607,7 @@ impl NativeVaapiDecoder { if !plan.warnings.is_empty() { tracing::debug!(warnings = ?plan.warnings, damaged, "native VAAPI AV1 plan warnings"); } - if let Some(frame) = self.frame_av1(au, plan, damaged)? { - shown = Some(frame); - } + shown.extend(self.frame_av1(au, plan, damaged)?); } if damaged_unit { // A frame may already have been exported before a LATER frame of the @@ -1271,7 +1616,7 @@ impl NativeVaapiDecoder { // the surface to the free list, which is exactly what an unshown picture // should do. drop(shown); - return Ok((None, true)); + return Ok((Vec::new(), true)); } Ok((shown, false)) } @@ -1289,7 +1634,7 @@ impl NativeVaapiDecoder { au: &[u8], plan: &pf_vaadec::AuPlanAv1, damaged: bool, - ) -> Result> { + ) -> Result> { // `show_existing_frame` decodes nothing at all: it re-displays a picture some // earlier hidden frame put in a reference slot. if plan.dpb.stored.is_none() { @@ -1306,6 +1651,31 @@ impl NativeVaapiDecoder { shape, &mut self.generation, )?; + // AV1's display region is the RENDER size, not the coded size — and it is a + // per-FRAME value, so it cannot live in the session shape the way a + // conformance window does. Which is also why it belongs to the PICTURE: a + // frame held back in the deliverable queue must still be shown at the region + // ITS header asked for, not the newest one's. + // + // ⚠ CLAMPED to the decoded picture. AV1 5.9.6 puts no upper bound on the + // render size — a stream may legally ask to be shown at more than it coded — + // and an unclamped crop would hand the presenter a region larger than the + // surface. The same clamp is in the Vulkan and D3D11 rungs. + // + // ⚠ Treated as a CROP, which is what both other native rungs do. libavcodec + // instead keeps the frame at `upscaled_width` x `frame_height` and expresses + // the render size as a sample aspect RATIO, so on a stream where the two + // differ this rung shows less picture than libavcodec would. No + // punktfunk host emits such a stream; the choice is here so the three native + // rungs answer alike, not because it is settled. + let facts = PictureFacts { + keyframe: plan.picture.is_key, + color: colour_of(&plan.picture.colour), + display: ( + plan.picture.render_width.min(plan.picture.upscaled_width), + plan.picture.render_height.min(plan.picture.frame_height), + ), + }; let (free, target, table) = s .acquire_target() .ok_or_else(|| anyhow!("surface pool exhausted ({} surfaces)", s.surfaces.len()))?; @@ -1319,7 +1689,7 @@ impl NativeVaapiDecoder { // because it is also correct for the refusals that fire before any // mutation: there is no slot to clear and no surface to bind either // way. - bind_setup(s, plan.dpb.stored, None); + bind_setup(s, plan.dpb.stored, None, facts); // A lost tile group on a plan the planner ALREADY called damaged is // concealment, not a defect: the access unit simply did not carry the // tiles its frame header announced, which is what one dropped packet @@ -1339,19 +1709,10 @@ impl NativeVaapiDecoder { &plan.dpb.outputs, &plan.dpb.removed, true, - plan.picture.is_key, - colour_of(&plan.picture.colour), - // Unread — `finish` returns before it looks at the display - // region when `damaged` — but written the same way as the - // submitting path below, so the two cannot drift apart. - ( - plan.picture.render_width.min(plan.picture.upscaled_width), - plan.picture.render_height.min(plan.picture.frame_height), - ), &mut self.recovery_request, &self.release_tx, )?; - return Ok(None); + return Ok(Vec::new()); } return Err(anyhow!("{e}")); } @@ -1363,7 +1724,7 @@ impl NativeVaapiDecoder { // the surface and only the pending-output claim keeps it out of the free // list. (`DecodePlanVaAv1::setup_slot` is `None` there; it is not consulted // here for exactly that reason.) - bind_setup(s, plan.dpb.stored, Some(free)); + bind_setup(s, plan.dpb.stored, Some(free), facts); if converted.substituted_refs != 0 { tracing::debug!( @@ -1394,34 +1755,12 @@ impl NativeVaapiDecoder { au, )?; - // AV1's display region is the RENDER size, not the coded size — and it is a - // per-FRAME value, so it cannot live in the session shape the way a - // conformance window does. - // - // ⚠ CLAMPED to the decoded picture. AV1 5.9.6 puts no upper bound on the - // render size — a stream may legally ask to be shown at more than it coded — - // and an unclamped crop would hand the presenter a region larger than the - // surface. The same clamp is in the Vulkan and D3D11 rungs. - // - // ⚠ Treated as a CROP, which is what both other native rungs do. libavcodec - // instead keeps the frame at `upscaled_width` x `frame_height` and expresses - // the render size as a sample aspect RATIO, so on a stream where the two - // differ this rung shows less picture than libavcodec would. No - // punktfunk host emits such a stream; the choice is here so the three native - // rungs answer alike, not because it is settled. - let display_size = ( - plan.picture.render_width.min(plan.picture.upscaled_width), - plan.picture.render_height.min(plan.picture.frame_height), - ); - let frame = finish( + let frames = finish( display, s, &plan.dpb.outputs, &plan.dpb.removed, damaged, - plan.picture.is_key, - colour_of(&plan.picture.colour), - display_size, &mut self.recovery_request, &self.release_tx, )?; @@ -1437,9 +1776,9 @@ impl NativeVaapiDecoder { // be a session that dies of "pool exhausted" some minutes later with nothing // pointing back here. if converted.setup_slot.is_none() && !plan.dpb.outputs.contains(&converted.setup_id) { - s.pending.retain(|(id, _)| *id != converted.setup_id); + s.pending.retain(|p| p.id != converted.setup_id); } - Ok(frame) + Ok(frames) } /// A `show_existing_frame` access unit: export a surface the pool already holds. @@ -1460,14 +1799,14 @@ impl NativeVaapiDecoder { &mut self, plan: &pf_vaadec::AuPlanAv1, damaged: bool, - ) -> Result> { + ) -> Result> { let Self { display, session, .. } = self; // Nothing has decoded yet: the unit is already concealed (the planner // reported `MissingShowExisting`) and there is no session to look in. let Some(s) = session.as_mut() else { - return Ok(None); + return Ok(Vec::new()); }; // Showing a KEY frame this way resets the whole reference store (AV1 7.20), // so the plan's removals are real and this rung's ledger has to follow them — @@ -1477,19 +1816,18 @@ impl NativeVaapiDecoder { s.slots.release(id); } s.sync_slot_bindings(); - let display_size = ( - plan.picture.render_width.min(plan.picture.upscaled_width), - plan.picture.render_height.min(plan.picture.frame_height), - ); + // ⚠ No `PictureFacts` are built here, and that is the point of recording them + // at decode time: the picture this unit displays was decoded by an EARLIER + // hidden frame and already carries its own keyframe flag, colour description + // and render region. The display-only header the vendored parser restores + // from the reference says the same thing, but taking it from the pending + // entry means `show_existing_frame` needs no per-surface facts table at all. finish( display, s, &plan.dpb.outputs, &plan.dpb.removed, damaged, - plan.picture.is_key, - colour_of(&plan.picture.colour), - display_size, &mut self.recovery_request, &self.release_tx, ) @@ -1498,6 +1836,19 @@ impl NativeVaapiDecoder { impl Drop for NativeVaapiDecoder { fn drop(&mut self) { + // Teardown is the only end of stream this rung can observe (see + // [`NativeVaapiDecoder::flush`]). Nothing here can be presented, so the point + // is to RELEASE: the deliverable queue's surfaces and the DPB's, before + // `Session::destroy` pulls the pool out from under them. Same walk the + // conformance harness drives, deliberately — a teardown path nothing exercises + // is a teardown path nothing checks. + let tail = self.flush().len(); + if tail > 0 { + tracing::debug!( + count = tail, + "native VAAPI: released frames never shown at teardown" + ); + } if self.stale_releases > 0 { // Not an error — a renegotiated session's frames come home to a pool // that no longer exists — but a count worth seeing, because the only @@ -1690,14 +2041,18 @@ fn ensure_session<'a>( /// read back as `VA_INVALID_ID`, which the conversion then substitutes with a live /// surface. Nothing is pushed to `pending` either: an undecoded surface must never be /// exportable. -fn bind_setup(s: &mut Session, stored: Option, surface: Option) { +/// +/// `facts` travels with the picture from here (see [`PictureFacts`]) and is read back +/// by [`settle`] when the picture is finally displayed — which on a reordering stream +/// is a different access unit saying different things. +fn bind_setup(s: &mut Session, stored: Option, surface: Option, facts: PictureFacts) { s.sync_slot_bindings(); let Some(id) = stored else { return }; if let Some(slot) = s.slots.slot_of(id) { s.slot_surface[usize::from(slot)] = surface; } if let Some(surface) = surface { - s.pending.push((id, surface)); + s.pending.push(PendingPicture { id, surface, facts }); } } @@ -1872,7 +2227,14 @@ fn submit( result } -/// Turn this AU's OUTPUT list into at most one shipped frame. +/// Claim every picture this access unit displays and retire everything it displaced — +/// the PURE half of [`finish`], and the half a test can drive without a device. +/// +/// Returns the claimed pictures **in display order**, each carrying the facts recorded +/// when it decoded ([`PictureFacts`]). A claimed picture is no longer in `pending`, so +/// until the caller either ships it (which marks its surface `held`) or drops it, the +/// only thing keeping its surface off the free list is a DPB slot it may no longer +/// have — which is why claiming and shipping happen in one breath. /// /// Display order, not decode order. `plan.dpb.outputs` is what the planner says is /// ready to be shown and in what order, and the surface for each is looked up by @@ -1881,9 +2243,15 @@ fn submit( /// order; that is a known finding on a rung that blits its output away, and there /// was no reason to inherit it here where the display-order queue costs a lookup.) /// -/// Newest wins, which is the same rule the FFmpeg VAAPI rung applies inside its -/// receive loop: on a live stream a picture already superseded is not worth a frame -/// interval. Superseded outputs are released rather than exported. +/// ⚠ **Every output, not the last one.** Until 2026-08-07 this took `outputs.last()` +/// and RETIRED the rest unshown — "newest wins", borrowed from the FFmpeg VAAPI rung's +/// receive loop, where it is a statement about a live stream that has already fallen +/// behind rather than about a decoder's own reorder buffer. Applied here it discarded +/// pictures nobody had yet had the chance to fall behind on: a bump is how a reordering +/// stream delivers, and an IDR draining a full DPB bumps the whole buffer at once. It +/// cost the vendored H.264 vector 18 frames at three access units. The caller queues +/// what it cannot hand over at once ([`NativeVaapiDecoder::deliverable`]); dropping is +/// that queue's decision to make, at its bound, with a counter and a log line. /// /// The retirement rule is `pf_vkdecode`'s `settle_dpb`, reimplemented here over this /// rung's flat pending list rather than reasoned out again, because both halves of it @@ -1896,59 +2264,40 @@ fn submit( /// * **An output naming no pending picture is a TRACE, not an error.** Ids planned /// before this decoder existed, or dropped across a session rebuild, are /// display-order gaps. -#[allow(clippy::too_many_arguments)] -fn finish( - d: &Display, - s: &mut Session, - outputs: &[u64], - removed: &[u64], - damaged: bool, - keyframe: bool, - color: ColorDesc, - // The DISPLAY region for this picture. A parameter rather than a read of - // `s.shape` because AV1's is per-FRAME: its render size may change without a key - // frame, so it cannot live in the shape that rebuilds the session. - display: (u32, u32), - recovery_request: &mut bool, - tx: &mpsc::Sender, -) -> Result> { - // A concealed picture is not shown: it was decoded from a substitute reference, - // so shipping it paints the substitution on screen. Nothing this AU output is - // shown, the pump is asked to re-anchor, and the caller records the damage. - let shown = if damaged { - None - } else { - outputs.last().copied() - }; - // OUTPUTS FIRST, and the shown one is taken out before anything else runs. - // A picture is normally output and removed by the SAME access unit — that is - // what bumping is — so retiring `removed` before claiming the frame would - // discard the very picture about to be displayed, on essentially every AU. - let claimed = shown.and_then(|id| { - let found = s.pending.iter().position(|(pid, _)| *pid == id); - if found.is_none() { - tracing::trace!(id, "output id without a pending picture"); - } - found.map(|index| s.pending.remove(index).1) - }); +/// +/// OUTPUTS FIRST, and they are taken out before anything else runs: a picture is +/// normally output and removed by the SAME access unit — that is what bumping is — so +/// retiring `removed` before claiming would discard the very pictures about to be +/// displayed, on essentially every access unit. +fn settle(s: &mut Session, outputs: &[u64], removed: &[u64]) -> Vec { + let mut claimed = Vec::with_capacity(outputs.len()); for id in outputs { - if Some(*id) != shown { - s.pending.retain(|(pid, _)| pid != id); + match s.pending.iter().position(|p| p.id == *id) { + Some(index) => claimed.push(s.pending.remove(index)), + None => tracing::trace!(id, "output id without a pending picture"), } } // Whatever left the DPB is retired from the pending list whether or not it was // ever output. Its SURFACE only becomes free if nothing else holds it — a // reference still bound to a slot, or a frame the consumer has, stays put. for id in removed { - s.pending.retain(|(pid, _)| pid != id); + s.pending.retain(|p| p.id != *id); } - if damaged { - *recovery_request = true; - return Ok(None); - } - let Some(surface_index) = claimed else { - return Ok(None); - }; + claimed +} + +/// Export one claimed picture as the dmabuf frame the presenter imports, and take the +/// consumer's hold on its surface. +/// +/// Split out of [`finish`] so the flush path ships by exactly the same walk rather than +/// by a second one written to match. +fn ship( + d: &Display, + s: &mut Session, + picture: PendingPicture, + tx: &mpsc::Sender, +) -> Result { + let surface_index = picture.surface; let surface = s.surfaces[surface_index]; // OWNED from here. `export` wraps the descriptor's fds the moment the call @@ -1982,17 +2331,17 @@ fn finish( stride: p.stride, }) .collect(); - Ok(Some(DmabufFrame { - // The DISPLAY region. The surface is allocated at the coded size and is - // taller/wider than the picture; handing over the coded size would show the - // codec's granule padding. - width: display.0, - height: display.1, + Ok(DmabufFrame { + // The DISPLAY region THIS picture asked for. The surface is allocated at the + // coded size and is taller/wider than the picture; handing over the coded size + // would show the codec's granule padding. + width: picture.facts.display.0, + height: picture.facts.display.1, fourcc: exported.fourcc, modifier: exported.modifier, planes, - color, - keyframe, + color: picture.facts.color, + keyframe: picture.facts.keyframe, guard: DrmFrameGuard(VaFrameGuard { _fds: fds, tx: tx.clone(), @@ -2001,7 +2350,44 @@ fn finish( generation: s.generation, }, }), - })) + }) +} + +/// Turn this access unit's OUTPUT list into shipped frames, in display order. +/// +/// [`settle`] does the ledger, [`ship`] does the export; this is the two together plus +/// the concealment rule that decides whether anything is shown at all. +/// +/// A refusal part-way through ships nothing: the frames already exported are dropped by +/// the `?`, and their guards close the fds and hand the surfaces straight back. The +/// pictures not yet reached are dropped too — [`settle`] already took them out of +/// `pending`, so nothing claims their surfaces and they return to the free list on the +/// spot. Which is the rule every rung in this program keeps: nothing from a refused +/// access unit reaches the screen. +fn finish( + d: &Display, + s: &mut Session, + outputs: &[u64], + removed: &[u64], + damaged: bool, + recovery_request: &mut bool, + tx: &mpsc::Sender, +) -> Result> { + let claimed = settle(s, outputs, removed); + // A concealed picture is not shown: it was decoded from a substitute reference, + // so shipping it paints the substitution on screen. Nothing this AU output is + // shown, the pump is asked to re-anchor, and the caller records the damage. The + // claimed pictures simply drop here — never exported, never held, so their + // surfaces are free the moment this returns. + if damaged { + *recovery_request = true; + return Ok(Vec::new()); + } + let mut frames = Vec::with_capacity(claimed.len()); + for picture in claimed { + frames.push(ship(d, s, picture, tx)?); + } + Ok(frames) } /// Wait for the decode and export the surface as DRM-PRIME dmabufs. @@ -2098,6 +2484,27 @@ mod tests { } } + /// Facts a test does not care about. Everything that DOES care about them builds + /// its own, so a shared default can never be what makes an assertion pass. + const PLAIN: PictureFacts = PictureFacts { + keyframe: false, + color: ColorDesc { + primaries: 1, + transfer: 1, + matrix: 1, + full_range: false, + }, + display: (64, 64), + }; + + fn pending(id: u64, surface: usize) -> PendingPicture { + PendingPicture { + id, + surface, + facts: PLAIN, + } + } + /// The whole rule, in one test: a surface is free only when NOTHING claims it, /// and the three claims end at different moments. #[test] @@ -2109,9 +2516,11 @@ mod tests { "a fresh pool starts at the front" ); - // 0: a live DPB reference. 1: decoded, still owing an output. 2: on screen. + // 0: a live DPB reference. 1: decoded, still owing an output. 2: on screen — + // or waiting in the deliverable queue, which is the same claim (see + // [`Session::free_surface`]). s.slot_surface[0] = Some(0); - s.pending.push((7, 1)); + s.pending.push(pending(7, 1)); s.held[2] = true; assert_eq!( s.free_surface(), @@ -2240,7 +2649,7 @@ mod tests { s.held = (0..SURFACES).map(|i| held_mask >> i & 1 == 1).collect(); s.pending = (0..SURFACES) .filter(|i| pending_mask >> i & 1 == 1) - .map(|i| (100 + i as u64, i)) + .map(|i| pending(100 + i as u64, i)) .collect(); states += 1; @@ -2293,9 +2702,9 @@ mod tests { // stream, where a picture is output by its own access unit and evicted by the // sliding window several units later. s.slots.assign(11).expect("a free slot"); - bind_setup(&mut s, Some(11), Some(0)); + bind_setup(&mut s, Some(11), Some(0), PLAIN); s.slots.assign(12).expect("a free slot"); - bind_setup(&mut s, Some(12), Some(1)); + bind_setup(&mut s, Some(12), Some(1), PLAIN); s.pending.clear(); // What the conversion resolves its references through, taken BEFORE this access @@ -2344,17 +2753,17 @@ mod tests { // Picture 11 decoded into surface 0 and took slot 0. s.slots.assign(11).expect("a free slot"); - bind_setup(&mut s, Some(11), Some(0)); + bind_setup(&mut s, Some(11), Some(0), PLAIN); assert_eq!(s.slot_surface[0], Some(0)); assert_eq!(s.surface_table()[0], s.surfaces[0]); - assert_eq!(s.pending, vec![(11, 0)]); + assert_eq!(s.pending, vec![pending(11, 0)]); // Picture 12's access unit lost its tile groups. The conversion released 11, // handed 12 the slot it just gave back — the routine case, not a contrived one // — and then refused. s.slots.release(11); assert_eq!(s.slots.assign(12).expect("the slot 11 gave back"), 0); - bind_setup(&mut s, Some(12), None); + bind_setup(&mut s, Some(12), None, PLAIN); assert_eq!( s.slot_surface[0], None, @@ -2367,7 +2776,7 @@ mod tests { "and the table the conversion reads must say so, so it can substitute" ); assert!( - !s.pending.iter().any(|(id, _)| *id == 12), + !s.pending.iter().any(|p| p.id == 12), "an undecoded picture owes no output — a pending entry is what would let \ a later show_existing_frame export a surface the driver never wrote" ); @@ -2378,6 +2787,230 @@ mod tests { assert_eq!(s.slots.slot_of(12), Some(0)); } + /// **Every** picture an access unit displays is claimed, in the planner's display + /// order — the defect this rung carried until 2026-08-07, with the behaviour it + /// replaced written out beside it. + /// + /// [`settle`] is the pure half of [`finish`] precisely so this can be asserted with + /// no libva, no device and no surfaces: it is a walk over a list. + #[test] + fn settle_claims_every_output_in_display_order_not_only_the_last() { + let mut s = session(8, 5); + // Four pictures decoded and buffered for reorder, each in its own surface — + // the state a reordering stream is in when an IDR drains the buffer. + for (index, id) in [11u64, 12, 13, 14].iter().enumerate() { + s.pending.push(pending(*id, index)); + } + // Display order 13, 11, 14, 12: deliberately neither decode order nor sorted, + // because the planner's list IS the display order and this rung must present + // in it rather than re-derive one. + let outputs = [13u64, 11, 14, 12]; + let claimed = settle(&mut s, &outputs, &outputs); + + assert_eq!( + claimed.iter().map(|p| p.id).collect::>(), + outputs, + "every bumped picture must come back, in the order the planner listed them" + ); + assert_eq!( + claimed.iter().map(|p| p.surface).collect::>(), + vec![2, 0, 3, 1], + "and each must resolve to ITS OWN surface, not to its position in the list" + ); + assert!( + s.pending.is_empty(), + "a claimed picture no longer owes an output" + ); + + // ⚠ The counterfactual. What this used to do, in one line, run against the same + // access unit: ship `outputs.last()` and retire the other three unshown. + let old_rule: Vec = outputs.last().copied().into_iter().collect(); + assert_eq!(old_rule, vec![12]); + assert_eq!( + claimed.len() - old_rule.len(), + 3, + "the old rule dropped three of these four pictures — on the vendored H.264 \ + vector that is 18 frames at three access units, and nothing counted them" + ); + } + + /// The two retirement rules, which are the reason a pool sized for a stream does + /// not walk into exhaustion anyway. + #[test] + fn settle_retires_what_left_the_dpb_unshown_and_traces_an_output_it_cannot_place() { + let mut s = session(4, 3); + s.pending.push(pending(11, 0)); + s.pending.push(pending(12, 1)); + + // Picture 12 leaves the DPB without ever being output — `no_output_of_prior_pics` + // at an IDR, the everyday case. A pending list that only shrank on OUTPUT would + // hold its surface for the rest of the session. + let claimed = settle(&mut s, &[11], &[11, 12]); + assert_eq!(claimed.iter().map(|p| p.id).collect::>(), vec![11]); + assert!(s.pending.is_empty(), "12 was retired unshown"); + + // An output naming no pending picture is a display-order gap, not an error: + // ids planned before this decoder existed, or dropped across a rebuild. + assert!(settle(&mut s, &[99], &[]).is_empty()); + } + + /// A displayed picture carries **its own** facts, not those of whichever access + /// unit happens to bump it out. + /// + /// All three fields fail differently and all three were wrong: `keyframe` is the + /// pump's post-loss re-anchor signal, `color` decides whether PQ content is drawn + /// as BT.709, and `display` is AV1's per-frame render region. + #[test] + fn a_displayed_picture_carries_its_own_facts_not_its_display_units() { + /// What the rung stamped until 2026-08-07: the ACCESS UNIT's flag, whatever + /// picture the bump happened to display. + fn old_label(bumping_au_is_idr: bool) -> bool { + bumping_au_is_idr + } + + let mut s = session(4, 3); + let idr = PictureFacts { + keyframe: true, + color: ColorDesc { + primaries: 9, + transfer: 16, + matrix: 9, + full_range: false, + }, + display: (1920, 1080), + }; + let trail = PictureFacts { + keyframe: false, + color: ColorDesc { + primaries: 1, + transfer: 1, + matrix: 1, + full_range: false, + }, + display: (1280, 720), + }; + s.pending.push(PendingPicture { + id: 11, + surface: 0, + facts: idr, + }); + s.pending.push(PendingPicture { + id: 12, + surface: 1, + facts: trail, + }); + + let claimed = settle(&mut s, &[11, 12], &[11, 12]); + assert_eq!(claimed[0].facts, idr); + assert_eq!(claimed[1].facts, trail); + + // ⚠ The counterfactual, and it fails BOTH ways round. + assert_ne!( + old_label(false), + claimed[0].facts.keyframe, + "the IDR is bumped out by an ordinary TRAILING access unit several units \ + later, so the old rule flagged the pump's one re-anchor frame as not a \ + keyframe — measured on all three hardware legs' first delivered frame" + ); + assert_ne!( + old_label(true), + claimed[1].facts.keyframe, + "and the access unit that DRAINS the DPB at a later IDR flagged every old \ + trailing picture draining with it as a keyframe — a re-anchor on a frame \ + that is not one" + ); + assert_ne!( + claimed[0].facts.color, claimed[1].facts.color, + "the same access unit displays pictures decoded under different SPS/VUIs \ + when the host switches an HDR desktop to PQ in-band" + ); + assert_ne!( + claimed[0].facts.display, claimed[1].facts.display, + "and AV1's render region is a per-FRAME value, so a queued frame shown two \ + units later would be cropped to whatever the newest frame asked for" + ); + } + + /// A frame with no fds — every field the queue's bound cares about, and nothing + /// that needs a device. Its guard is real, so dropping it really does release. + fn queued_frame(tx: &mpsc::Sender, surface: usize) -> DmabufFrame { + DmabufFrame { + width: 64, + height: 64, + fourcc: pf_vaadec::VA_FOURCC_NV12, + modifier: 0, + planes: Vec::new(), + color: PLAIN.color, + keyframe: false, + guard: DrmFrameGuard(VaFrameGuard { + _fds: Vec::new(), + tx: tx.clone(), + release: VaRelease { + surface, + generation: 1, + }, + }), + } + } + + /// The queue drops its OLDEST rather than pinning surfaces forever — and a dropped + /// frame's surface really does come back. + /// + /// The second half is what makes this a surface-lifetime test rather than a + /// bookkeeping one: every frame the queue holds is a `held` surface, so a bound + /// that dropped frames without releasing them would trade one leak for another. + #[test] + fn the_deliverable_queue_drops_its_oldest_and_frees_the_surface_it_held() { + let (tx, rx) = mpsc::channel(); + let mut s = session(8, 5); + let mut queue: std::collections::VecDeque = + (0..5).map(|i| queued_frame(&tx, i)).collect(); + for i in 0..5 { + s.held[i] = true; + } + + let dropped = trim_deliverable(&mut queue, 3); + assert_eq!( + dropped + .iter() + .map(|f| f.guard.0.release.surface) + .collect::>(), + vec![0, 1], + "the OLDEST two go: dropping the newest would keep the stalest picture and \ + present the stream in ever-lagging order" + ); + assert_eq!( + queue + .iter() + .map(|f| f.guard.0.release.surface) + .collect::>(), + vec![2, 3, 4], + "and what survives keeps display order" + ); + + // The surfaces come back only when the dropped frames actually drop. + let mut stale = 0u64; + drain_releases_into(&rx, Some(&mut s), &mut stale); + assert!( + s.held[0] && s.held[1], + "a frame still owned holds its surface — the trim returns them so the \ + caller can count them, and the release is the drop" + ); + drop(dropped); + drain_releases_into(&rx, Some(&mut s), &mut stale); + assert!( + !s.held[0] && !s.held[1], + "dropping a trimmed frame returns its surface by exactly the path a \ + presented frame takes" + ); + assert_eq!(stale, 0, "and none of it is a stale-generation token"); + + // Idempotent, and a bound of 0 drains rather than loops. + assert!(trim_deliverable(&mut queue, 3).is_empty()); + assert_eq!(trim_deliverable(&mut queue, 0).len(), 3); + assert!(trim_deliverable(&mut queue, 0).is_empty()); + } + /// A conformance window with a non-zero ORIGIN is refused, not cropped from the /// wrong corner: nothing downstream carries an origin. #[test] @@ -2734,6 +3367,15 @@ mod tests { Err(e) => panic!("unit {index}: VAAPI AV1 decode failed: {e:#}"), } } + // AV1 buffers no output between temporal units — it shows at most one frame per + // unit and `Av1Planner` has no `flush` to call — so its tail is empty by + // construction. Asserted rather than assumed, because the same call on the + // H.26x legs hands back seven frames. + assert!( + decoder.flush().is_empty(), + "AV1 strands nothing in the DPB: every temporal unit's shown frame is \ + delivered by the unit itself" + ); let (w, h, fourcc, modifier) = first.expect("not one frame came back"); eprintln!( @@ -2783,38 +3425,63 @@ mod tests { /// The Main 10 vector is 50. const MAIN10_AU_COUNT: usize = 50; - /// How many frames each vector can yield THROUGH THIS RUNG — which is not how many - /// frames it contains, and the gap is a property of the rung worth stating once - /// here rather than three times below. + /// How many frames each vector yields THROUGH THIS RUNG: **every picture it + /// displays**, which is what the vectors contain and what the Vulkan and D3D11VA + /// legs have always delivered. /// - /// [`finish`] shows `outputs.last()` and never more: one frame per access unit, at - /// most. So an access unit whose plan bumps SEVERAL pictures out of the DPB — which - /// is what an IDR with `no_output_of_prior_pics_flag` clear does, and what ordinary - /// B-pyramid reordering does at every other picture — displays the last of them and - /// drops the rest, and an access unit whose plan outputs nothing yet displays - /// nothing. There is no end-of-stream flush either, so whatever is still in the DPB - /// when the vector ends never comes out. + /// Three things have to hold together for these to be the frame counts rather than + /// something smaller, and until 2026-08-07 none of them did: /// - /// Measured on `.25` and reproduced exactly by - /// [`the_planner_already_says_how_many_frames_these_legs_can_deliver`], which is - /// what keeps these three numbers explanations rather than recordings: + /// * [`settle`] claims **every** output, not `outputs.last()`. An access unit that + /// bumps several pictures out of the DPB — what an IDR with + /// `no_output_of_prior_pics_flag` clear does, and what ordinary reordering does + /// whenever the buffer drains — used to display the last and retire the rest. + /// * [`NativeVaapiDecoder::deliverable`] carries the surplus to the access units + /// that output nothing, since the pump takes one frame per call. On a reordering + /// stream those units are exactly where the reorder buffer refills, which is why + /// the queue drains: the vendored H.264 vector's three seven-picture drains are + /// each followed by precisely six output-less access units. + /// * [`NativeVaapiDecoder::flush`] drains the DPB at the end, or the tail the + /// planner is still buffering never comes out at all. /// - /// | vector | pictures the planner outputs | this rung delivers | dropped | - /// |---|---|---|---| - /// | H.264 | 243 (7 stranded in the DPB) | **225** | 18, at the 3 IDRs that drain the DPB | - /// | H.265 | 249 (1 stranded) | **204** | 45, one on each of the 45 AUs that bump two | - /// | Main 10 | 48 (2 stranded) | **45** | 3, likewise | + /// Derived, not recorded, by [`the_planner_already_says_how_many_frames_these_legs_can_deliver`], + /// which simulates all three over the real vectors on any CPU with no libva: /// - /// ⚠ This does NOT bite punktfunk's own streams and is not what these legs exist to - /// find: hosts emit zero-reorder low-delay output with no B pictures, so `outputs` - /// never holds more than one picture and the rung is exact. It is the same - /// divergence `video_d3d11_native`'s parity module records for the D3D11VA rung, - /// and it is written down here for the same reason — a conformance vector reorders, - /// punktfunk does not, and a reader comparing 225 against "250 frames" needs to - /// know which of the two they are looking at. - const H264_DELIVERED: usize = 225; - const H265_DELIVERED: usize = 204; - const MAIN10_DELIVERED: usize = 45; + /// | vector | AUs | pictures the planner outputs | stranded in the DPB | delivered | + /// |---|---|---|---|---| + /// | H.264 | 250 | 243 (25 units output none, 222 one, 3 seven) | 7 | **250** | + /// | H.265 | 250 | 249 (46 none, 159 one, 45 two) | 1 | **250** | + /// | Main 10 | 50 | 48 (5 none, 42 one, 3 two) | 2 | **50** | + /// + /// ⚠ The "flushed" count each leg prints is NOT the stranded column: a flush hands + /// back the deliverable queue's leftovers as well as the DPB's tail. Measured on + /// `.25`, H.264 flushes 7 (nothing left queued), H.265 flushes 2 (one still queued + /// plus its one stranded picture) and Main 10 flushes 2. The totals are what these + /// constants pin, because the split between the two is a property of where the + /// output-less access units happen to fall. + /// + /// ⚠ What this REPLACED, kept as [`H264_LAST_ONLY`] and asserted as a + /// counterfactual rather than described: one frame per access unit and no flush + /// delivered 225 / 204 / 45. That defect could not bite punktfunk's own streams — + /// hosts emit zero-reorder low-delay output with no B pictures, so `outputs` never + /// holds more than one picture and the queue is empty on every access unit — which + /// is exactly why it survived until a conformance vector was pointed at the rung. + const H264_DELIVERED: usize = 250; + const H265_DELIVERED: usize = 250; + const MAIN10_DELIVERED: usize = 50; + + /// What the rung delivered until 2026-08-07: `outputs.last()` per access unit and + /// no end-of-stream flush. + /// + /// Kept as constants because a counterfactual with no expected value is a + /// counterfactual that cannot fail. These are what + /// [`the_planner_already_says_how_many_frames_these_legs_can_deliver`] reproduces + /// when it runs the simulation with a queue bound of zero and no flush — the two + /// halves of the old behaviour — and they are the numbers the three hardware legs + /// asserted when they were written. + const H264_LAST_ONLY: usize = 225; + const H265_LAST_ONLY: usize = 204; + const MAIN10_LAST_ONLY: usize = 45; /// Byte offsets of every Annex-B NAL header in `stream`, in order. /// @@ -2935,65 +3602,352 @@ mod tests { ); } - /// Access units whose plan outputs at least one picture — one delivered frame each, - /// and the ONLY thing that separates [`H264_DELIVERED`] and friends from the - /// vectors' frame counts. + /// One access unit's whole effect on the DPB, as the planner reports it — enough + /// to simulate both the DELIVERY and the SURFACE CLAIMS with no device. /// - /// Two small walks rather than one generic one because the two planners share no - /// trait: `AuPlan` and `AuPlanH265` are different types with the same `dpb.outputs` - /// field, which is exactly the shape a macro would obscure for six saved lines. - fn output_bearing_aus_h264(aus: &[&[u8]]) -> usize { - let mut planner = pf_vaadec::H264Planner::new(); - aus.iter() - .filter(|au| { - !planner - .plan_au(au) - .expect("the vendored H.264 vector plans") - .dpb - .outputs - .is_empty() - }) - .count() + /// `stored` is a LIST rather than an `Option` because an AV1 access unit is a + /// temporal unit and may decode several pictures; H.264 and H.265 fill it with the + /// nought or one their planners report. + #[derive(Debug, Default, Clone)] + struct AuEffect { + stored: Vec, + outputs: Vec, + removed: Vec, } - /// [`output_bearing_aus_h264`] for HEVC. A skipped RASL picture counts as no - /// output, which is what the rung does with it too ([`NativeVaapiDecoder::decode`]). - fn output_bearing_aus_h265(aus: &[&[u8]]) -> usize { + /// A whole vector, as this rung's model of it: every access unit's effect, the + /// end-of-stream flush's, and the DPB depth the session would be built for — which + /// is also the queue bound [`max_deliverable`] derives. + struct VectorEffects { + aus: Vec, + flush: AuEffect, + /// `plan.picture.max_dpb_frames`, read from the stream rather than assumed. + max_dpb_frames: usize, + } + + /// [`VectorEffects`] for an H.264 vector. + /// + /// Two small walks rather than one generic one because the two planners share no + /// trait: `AuPlan` and `AuPlanH265` are different types with the same `dpb` fields, + /// which is exactly the shape a macro would obscure for six saved lines. + fn effects_h264(aus: &[&[u8]]) -> VectorEffects { + let mut planner = pf_vaadec::H264Planner::new(); + let mut max_dpb_frames = 0usize; + let walked = aus + .iter() + .map(|au| { + let plan = planner + .plan_au(au) + .expect("the vendored H.264 vector plans"); + max_dpb_frames = max_dpb_frames.max(plan.picture.max_dpb_frames); + AuEffect { + stored: plan.dpb.stored.into_iter().collect(), + outputs: plan.dpb.outputs.clone(), + removed: plan.dpb.removed.clone(), + } + }) + .collect(); + let update = planner.flush(); + VectorEffects { + aus: walked, + flush: AuEffect { + stored: Vec::new(), + outputs: update.outputs, + removed: update.removed, + }, + max_dpb_frames, + } + } + + /// [`effects_h264`] for HEVC. A skipped RASL picture is an access unit with no + /// effect at all, which is what the rung does with it too + /// ([`NativeVaapiDecoder::decode_h265`]). + fn effects_h265(aus: &[&[u8]]) -> VectorEffects { let mut planner = pf_vaadec::H265Planner::new(); - aus.iter() - .filter(|au| match planner.plan_au(au) { - Ok(plan) => !plan.dpb.outputs.is_empty(), - Err(pf_vaadec::PlanErrorH265::RaslSkipped { .. }) => false, + let mut max_dpb_frames = 0usize; + let walked = aus + .iter() + .map(|au| match planner.plan_au(au) { + Ok(plan) => { + max_dpb_frames = max_dpb_frames.max(plan.picture.max_dpb_frames); + AuEffect { + stored: plan.dpb.stored.into_iter().collect(), + outputs: plan.dpb.outputs.clone(), + removed: plan.dpb.removed.clone(), + } + } + Err(pf_vaadec::PlanErrorH265::RaslSkipped { .. }) => AuEffect::default(), Err(e) => panic!("the vendored HEVC vector must plan: {e:?}"), }) - .count() + .collect(); + let update = planner.flush(); + VectorEffects { + aus: walked, + flush: AuEffect { + stored: Vec::new(), + outputs: update.outputs, + removed: update.removed, + }, + max_dpb_frames, + } + } + + /// What a whole vector does to this rung. + #[derive(Debug, PartialEq, Eq)] + struct Delivery { + /// Frames the pump receives, flush included. + delivered: usize, + /// Frames decoded correctly and discarded because the queue hit its bound. + dropped: usize, + /// The most surfaces claimed at once, at the moment [`Session::acquire_target`] + /// looks for a free one — a live slot, a pending output, a queued frame or the + /// frame the consumer was just handed. + peak_claim: usize, + } + + /// Drive one vector through this rung's DELIVERY MODEL — the ledger, the deliverable + /// queue, the one-frame-per-access-unit hand-off and the end-of-stream flush — with + /// no libva, no device and no surfaces. + /// + /// A faithful re-walk of [`NativeVaapiDecoder::decode`] in the order it does things, + /// because the order is where the defects were: the decode target is taken BEFORE + /// this unit's removals settle, outputs are claimed BEFORE `removed` retires them, + /// and the trim runs AFTER this unit's own frame has been shipped. + /// + /// `flush` is `None` for the counterfactual that reproduces the old behaviour. + fn simulate(effects: &[AuEffect], flush: Option<&AuEffect>, cap: usize) -> Delivery { + let mut live: std::collections::BTreeSet = Default::default(); + let mut pending: Vec = Vec::new(); + let mut queue: std::collections::VecDeque = Default::default(); + let (mut delivered, mut dropped, mut peak_claim) = (0usize, 0usize, 0usize); + let mut consumer = 0usize; + + /// [`settle`] itself, over ids: outputs claimed into the queue in display + /// order, THEN whatever left the DPB retired whether it was output or not. + fn settle_ids( + effect: &AuEffect, + live: &mut std::collections::BTreeSet, + pending: &mut Vec, + queue: &mut std::collections::VecDeque, + ) { + for id in &effect.outputs { + if let Some(index) = pending.iter().position(|p| p == id) { + queue.push_back(pending.remove(index)); + } + } + for id in &effect.removed { + live.remove(id); + pending.retain(|p| p != id); + } + } + + for effect in effects { + // `acquire_target` runs first and needs one free surface on top of + // everything already claimed — plus the frame the consumer was handed last + // access unit, which it has not necessarily let go of. + let claimed = + live.len() + pending.iter().filter(|p| !live.contains(p)).count() + queue.len(); + peak_claim = peak_claim.max(claimed + consumer + 1); + + // The conversion applies this unit's removals to the slot ledger, then + // `bind_setup` binds the picture it decoded. + for id in &effect.removed { + live.remove(id); + } + for id in &effect.stored { + live.insert(*id); + pending.push(*id); + } + settle_ids(effect, &mut live, &mut pending, &mut queue); + + // `take_deliverable`: this unit's own frame off the front, then the bound. + consumer = usize::from(queue.pop_front().is_some()); + delivered += consumer; + while queue.len() > cap { + queue.pop_front(); + dropped += 1; + } + } + + if let Some(effect) = flush { + // `flush` hands back the queue AND everything the planner was still + // buffering, all at once — it is not bounded by the pump's one-per-call + // contract, because there is no next call. + settle_ids(effect, &mut live, &mut pending, &mut queue); + delivered += queue.len(); + queue.clear(); + assert!( + pending.is_empty(), + "a flush leaves nothing owing an output: {pending:?}" + ); + } + Delivery { + delivered, + dropped, + peak_claim, + } } /// The three delivered-frame counts the hardware legs assert are what the PLANNER - /// implies, not what a hardware run happened to print. + /// implies, not what a hardware run happened to print — **and** the old behaviour + /// beside them, so the fix is a difference rather than an assertion. /// /// This is the difference between a number that explains itself and a number /// somebody wrote down: it runs on any Linux box, with no GPU and no libva, and it /// fails the moment a vector is regenerated or the planner's bumping changes — /// which would otherwise show up as three mysterious hardware failures on a machine - /// somebody had to walk to. See [`H264_DELIVERED`] for why the counts are below the - /// vectors' frame counts at all. + /// somebody had to walk to. #[test] fn the_planner_already_says_how_many_frames_these_legs_can_deliver() { - assert_eq!( - output_bearing_aus_h264(&split_h264_aus(H264_25FPS)), - H264_DELIVERED, - "H.264: access units whose plan outputs a picture" + let vectors = [ + ( + "H.264", + effects_h264(&split_h264_aus(H264_25FPS)), + H264_DELIVERED, + H264_LAST_ONLY, + ), + ( + "H.265", + effects_h265(&split_h265_aus(H265_25FPS)), + H265_DELIVERED, + H265_LAST_ONLY, + ), + ( + "Main 10", + effects_h265(&split_h265_aus(MAIN10_H265)), + MAIN10_DELIVERED, + MAIN10_LAST_ONLY, + ), + ]; + + for (label, vector, expected, last_only) in &vectors { + // The bound the rung would actually run with, read off the stream rather + // than chosen here — `max_deliverable` is `max_dpb_frames`. + let cap = vector.max_dpb_frames; + let run = simulate(&vector.aus, Some(&vector.flush), cap); + assert_eq!( + run.delivered, *expected, + "{label}: every displayed picture must reach the pump (cap {cap}, \ + {run:?})" + ); + assert_eq!( + run.dropped, 0, + "{label}: and none of them may be dropped for want of queue depth" + ); + + // ⚠ The counterfactual: the two halves of the old behaviour, together. A + // queue bound of 0 is "one picture per access unit, the rest retired + // unshown"; no flush is "the tail never comes out". That is what the rung + // did until 2026-08-07, and it is what these three legs asserted. + let old = simulate(&vector.aus, None, 0); + assert_eq!( + old.delivered, *last_only, + "{label}: the pre-fix model must still reproduce the number the \ + hardware legs measured, or this is not the defect that was fixed" + ); + assert!( + old.delivered < run.delivered, + "{label}: and it must be SHORT — a counterfactual that delivers \ + everything is not a counterfactual" + ); + } + } + + /// The deliverable queue never asks the pool for a surface it does not have. + /// + /// This is [`max_deliverable`]'s surface-lifetime argument run over the real + /// vectors rather than asserted. A queued frame INHERITS the claim its picture had + /// as a DPB reference — [`settle`] takes it out of `pending` in the same breath the + /// bump released its slot — so the queue's marginal cost is **at most one surface**, + /// measured at exactly zero on the H.264 vector, whose three seven-picture drains + /// are the deepest bursts any of these vectors produce. + /// + /// The counterfactual is the bound itself: an UNBOUNDED queue on a stream that + /// bumps two pictures on every access unit grows by one per unit until the pool + /// runs out, which is the failure `max_deliverable` exists to prevent and the one + /// no log would explain. + #[test] + fn the_queue_never_needs_a_surface_the_pool_does_not_have() { + // The peaks are pinned, not merely bounded: a change that quietly claimed two + // more surfaces would still fit the pool and would still be a change worth + // seeing. `without` is the same walk with no carry-over at all — the rung as it + // stood before the queue existed. + for (label, vector, peak, without) in [ + ("H.264", effects_h264(&split_h264_aus(H264_25FPS)), 9, 9), + ("H.265", effects_h265(&split_h265_aus(H265_25FPS)), 8, 7), + ("Main 10", effects_h265(&split_h265_aus(MAIN10_H265)), 8, 7), + ] { + let dpb = vector.max_dpb_frames; + let pool = pf_vaadec::surface_count(dpb); + let run = simulate(&vector.aus, Some(&vector.flush), dpb); + let queueless = simulate(&vector.aus, Some(&vector.flush), 0); + + // The claim counted here is the DECODER's: live slots, pictures owing an + // output, queued frames, the frame just handed over and the target about to + // be taken. Everything the pool holds beyond it is the presenter's. + assert_eq!( + (run.peak_claim, queueless.peak_claim), + (peak, without), + "{label}: peak surfaces claimed at once, with the queue and without it \ + (dpb {dpb}, pool {pool})" + ); + assert!( + run.peak_claim <= queueless.peak_claim + 1, + "{label}: the queue must INHERIT the DPB's claim, not add to it — \ + {} against {}", + run.peak_claim, + queueless.peak_claim + ); + assert!( + pool - run.peak_claim >= pf_vaadec::config::PRESENTER_HEADROOM - 2, + "{label}: {} of a {pool}-surface pool claimed, leaving {} of the \ + {}-surface presenter headroom — a session that cannot find a free \ + surface refuses the access unit and demotes the rung", + run.peak_claim, + pool - run.peak_claim, + pf_vaadec::config::PRESENTER_HEADROOM, + ); + } + + // ⚠ On H.264 and H.265 the queue is SELF-limiting and the bound never has to + // engage: an access unit decodes at most one picture, so it can only ever + // output what earlier units decoded, and the queue sheds one per unit. That is + // why the three vectors above drop nothing at any depth — and it is exactly + // why a bound is still needed, because the property is a fact about those + // codecs rather than about this rung. An AV1 temporal unit may decode SEVERAL + // pictures, and a non-conformant one that showed two per unit would grow the + // queue by one per unit until the pool ran out: every access unit after that + // refuses with "surface pool exhausted", three in a second demote the rung, + // and nothing in the log would name a queue that could never drain. + let relentless: Vec = (0..64u64) + .map(|i| AuEffect { + stored: vec![i * 2, i * 2 + 1], + outputs: vec![i * 2, i * 2 + 1], + removed: vec![i * 2, i * 2 + 1], + }) + .collect(); + let bounded = simulate(&relentless, None, 4); + assert!( + bounded.dropped > 0, + "the bound must engage on a temporal unit shape that never lets the queue \ + drain" ); - assert_eq!( - output_bearing_aus_h265(&split_h265_aus(H265_25FPS)), - H265_DELIVERED, - "H.265: access units whose plan outputs a picture" + assert!( + bounded.peak_claim <= 4 + 4, + "and hold the claim flat: peak {}", + bounded.peak_claim ); + let unbounded = simulate(&relentless, None, usize::MAX); assert_eq!( - output_bearing_aus_h265(&split_h265_aus(MAIN10_H265)), - MAIN10_DELIVERED, - "Main 10: access units whose plan outputs a picture" + unbounded.dropped, 0, + "unbounded drops nothing — it just grows" + ); + assert!( + unbounded.peak_claim > bounded.peak_claim * 2, + "without the bound the same stream grows a queue nothing can drain — peak \ + {} against {}", + unbounded.peak_claim, + bounded.peak_claim ); } @@ -3050,28 +4004,40 @@ mod tests { let mut delivered = 0usize; let mut first: Option = None; + let mut record = |frame: &DmabufFrame, where_: &str| { + assert!( + !frame.planes.is_empty(), + "{label} {where_}: a delivered frame exported no dmabuf planes" + ); + if first.is_none() { + first = Some(FirstFrame { + width: frame.width, + height: frame.height, + fourcc: frame.fourcc, + modifier: frame.modifier, + keyframe: frame.keyframe, + }); + } + }; for (index, au) in aus.iter().enumerate() { match decoder.decode(au) { Ok(Some(frame)) => { - assert!( - !frame.planes.is_empty(), - "{label} AU {index}: a delivered frame exported no dmabuf planes" - ); - if first.is_none() { - first = Some(FirstFrame { - width: frame.width, - height: frame.height, - fourcc: frame.fourcc, - modifier: frame.modifier, - keyframe: frame.keyframe, - }); - } + record(&frame, &format!("AU {index}")); delivered += 1; } Ok(None) => {} Err(e) => panic!("{label} AU {index}: VAAPI decode failed: {e:#}"), } } + // The tail: pictures the planner was still buffering for reorder when the + // vector ran out. Seven of 250 on H.264, one on H.265, two of 50 on Main 10 — + // decoded, never asked for, and lost outright until this rung had a flush. + let flushed = decoder.flush(); + for (index, frame) in flushed.iter().enumerate() { + record(frame, &format!("flushed frame {index}")); + } + let tail = flushed.len(); + delivered += tail; let first = first.unwrap_or_else(|| panic!("{label}: not one frame came back")); let FirstFrame { @@ -3082,8 +4048,9 @@ mod tests { keyframe, } = first; eprintln!( - "VAAPI {label}: {delivered} of {} access units delivered a frame, first \ - {width}x{height} fourcc={:?} modifier={modifier:#x} keyframe={keyframe}", + "VAAPI {label}: {delivered} frames from {} access units ({tail} of them \ + flushed at end of stream), first {width}x{height} fourcc={:?} \ + modifier={modifier:#x} keyframe={keyframe}", aus.len(), std::str::from_utf8(&fourcc.to_le_bytes()).unwrap_or("?"), ); @@ -3113,32 +4080,23 @@ mod tests { pf_vaadec::VA_FOURCC_NV12, "an 8-bit pool exports NV12" ); - assert_eq!( - delivered, - output_bearing_aus_h264(&aus), - "every access unit whose plan outputs a picture must deliver one" - ); assert_eq!( delivered, H264_DELIVERED, - "see H264_DELIVERED for why this is 225 and not 250" + "every picture the vector displays must reach the pump — see \ + H264_DELIVERED, and the_planner_already_says_how_many_frames_these_legs_\ + can_deliver for the same number derived without a device" ); - // ⚠ A DEFECT this leg found, asserted so that fixing it is noticed rather than - // so that it is preserved. `finish` is handed the CURRENT access unit's - // `is_idr`, not the flag of the picture it is about to display — and on a - // reordering stream those are different pictures. The first frame delivered - // here IS the IDR, bumped out several access units after it decoded, and it - // arrives flagged `keyframe: false`; conversely the AU that drains the DPB at a - // later IDR flags whichever OLD picture it displays as a keyframe. The flag is - // `DecodedImage::is_keyframe`, the pump's post-loss re-anchor signal, so a rung - // that mislabels it would keep asking for a keyframe it has already been sent. - // It cannot bite punktfunk today for the same reason the frame count cannot: - // hosts emit zero-reorder output, so the decoded picture and the displayed one - // are always the same picture. Fix it and this line is the one to delete. + // The first frame delivered IS the IDR, bumped out several access units after + // it decoded — so this is the label travelling with the PICTURE rather than + // with whatever access unit displaced it. It is `DecodedImage::is_keyframe`, + // the pump's post-loss re-anchor signal: mislabelled (as it was until + // 2026-08-07) the pump re-anchors on the wrong frame and keeps asking for a + // keyframe it has already been sent. assert!( - !first.keyframe, - "the rung labels the ACCESS UNIT, not the picture it delivers — if this \ - now passes the label was fixed, which is good; delete this assertion" + first.keyframe, + "the vector opens on an IDR, so the first delivered frame must be flagged \ + as a keyframe" ); } @@ -3166,16 +4124,15 @@ mod tests { pf_vaadec::VA_FOURCC_NV12, "an 8-bit pool exports NV12" ); - assert_eq!( - delivered, - output_bearing_aus_h265(&aus), - "every access unit whose plan outputs a picture must deliver one" - ); assert_eq!( delivered, H265_DELIVERED, - "see H264_DELIVERED for why this is 204 and not 250" + "every picture the vector displays must reach the pump" + ); + assert!( + first.keyframe, + "the vector opens on an IDR_N_LP — the same picture-not-access-unit label \ + the H.264 leg documents" ); - assert!(!first.keyframe, "the same mislabel the H.264 leg documents"); } /// And the ten-bit leg, which is the one every HDR session lands on. @@ -3204,15 +4161,13 @@ mod tests { pf_vaadec::VA_FOURCC_P010, "a ten-bit stream must build a P010 pool, not an 8-bit one" ); - assert_eq!( - delivered, - output_bearing_aus_h265(&aus), - "every access unit whose plan outputs a picture must deliver one" - ); assert_eq!( delivered, MAIN10_DELIVERED, - "see H264_DELIVERED for why this is 45 and not 50" + "every picture the vector displays must reach the pump" + ); + assert!( + first.keyframe, + "the same picture-not-access-unit label the H.264 leg documents" ); - assert!(!first.keyframe, "the same mislabel the H.264 leg documents"); } } From a9e7c033c3b972ebcb48b85da354e45ccac3dfd6 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Sat, 8 Aug 2026 00:55:03 +0200 Subject: [PATCH 20/21] =?UTF-8?q?test(client/vaapi):=20the=20last=20rung?= =?UTF-8?q?=20of=20the=20ladder,=20finally=20checked=20in=20pixels=20?= =?UTF-8?q?=E2=80=94=207=20legs,=20all=20bit-identical?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every other decode rung earns `verified` with frame-hash parity against libavcodec. VAAPI could not: it hands out a DRM-PRIME dmabuf whose memory the driver tiles, so nothing could read its decoded pixels back, and all four of its legs sat at "never frame-hash parity-checked". That was never bookkeeping. The D3D11VA AV1 rung decoded 250 frames, streamed 4K60 through a clean five-minute soak, and produced WRONG PIXELS for 186 of 250 frames on NVIDIA and 245 of 250 on Intel. It looked perfect on glass; only the goldens caught it, and the same defect turned out to be in H.264 on two other rungs. VAAPI was the one rung where that class of bug could still be sitting with nothing able to see it. It is not. Measured on .25 (Radeon 780M, RDNA3, radeonsi, Mesa 26.0.3, VA-API 1.23) on 2026-08-08, against the SAME golden files the Vulkan and D3D11VA rungs are held to, read across the crate boundary rather than copied: H.264 vendored vector 250/250 bit-identical (7 from the flush) H.264 our host, low-delay 640x480 120/120 bit-identical (3 from the flush) H.265 vendored vector 250/250 bit-identical (2 from the flush) H.265 our host, low-delay 640x480 120/120 bit-identical (0 from the flush) HEVC Main 10, P010 50/50 bit-identical (2 from the flush) AV1 vendored vector 250/250 delivered of 274 decoded, and display frame 0 byte-identical to libavcodec's own PIXELS AV1 our host, 4K two-tile 60/60 bit-identical ⚠ ONE vendor. AMD/radeonsi only; no Intel iHD box has run these legs. The readback that made it possible: * `pf-vaadec`'s `va` module gains `VAImage` and `VAImageFormat`, hand-declared with every size and offset measured off libva 2.23.0's real headers by `layout-probe.c` and pinned as compile-time assertions — the same discipline the decode buffers already keep. The trap: `VAImage::width`/`height` are 16-bit, so `data_size` sits at 60 and not at the 64 counting 32-bit fields gives, and every field after them is two bytes earlier than it looks. * `pack_two_plane` is the pure geometry — the crop to the picture, the padding columns dropped per row, and the chroma plane taken from the driver's OWN `offsets[1]` rather than from `pitch * display_height`, which is the 1088-row smear this program has already paid for once. It needs no device, so ten CPU tests cover it on macOS and in the container. * `video_vaapi_native::parity` drives the seven streams above through the production entry point and hashes what the rung DELIVERS, in delivery order, tail included — so the delivery path is under test as well as the decode, and a frame's surface comes from its own release token rather than from an inference about which pool entry holds which picture. THE READBACK CANNOT REACH THE PRODUCTION PATH, and that is structural rather than a promise. `vaDeriveImage`, `vaCreateImage`, `vaGetImage`, `vaMapBuffer` and the rest are resolved by a `#[cfg(test)]` type that dlopens libva itself; the production `Libva` gains no field; `sha2` is a dev dependency. A CPU test scans this file's own source and fails if any of those symbols is dlsym'd outside the harness, so a refactor cannot quietly undo it. Derive is not guaranteed, so both routes are implemented and neither is optional: `vaDeriveImage` first, `vaCreateImage` + `vaGetImage` as the fallback (which also detiles), and if neither yields the pool's own fourcc the leg FAILS naming what the driver gave it. There is no skip path — a parity test that passes because it could not read anything is the failure mode this program has been bitten by three times. Both answer on radeonsi, the first frame of every leg is read through BOTH and they must agree, and `PF_VAAPI_READBACK=getimage` reproduces the H.264 leg's 250/250 through the copying route alone, so the fallback is exercised rather than merely written. And it can fail — proven, not asserted. Planting the real geometry defect this driver's layout makes visible (rows read contiguously, ignoring the 512-byte pitch behind a 320-wide picture) fails at display frame 0 with the full localisation: 68312 luma and 14998 chroma samples differing, max |delta| 255, luma bounding box (0,1)..(319,239) — and with the goldens forced through one route, 250/250 diverging with "suspect the readback geometry". `compare` and `localise` also have CPU counterfactuals, and a hardware leg proves the readback reads real and DISTINCT pixels and localises a one-byte flip to the exact pixel. ⚠ One thing the hardware legs do NOT cover, found by planting the other defect and watching it do nothing: radeonsi's decode surfaces for every fixture here have no VERTICAL padding — `offsets[1]` is exactly `pitch * height` — so the chroma-plane trap is untested on this driver, and `pf-vaadec`'s `reading_chroma_at_the_display_height_would_have_been_caught` is the only place it is checked at all. `probe_this_machines_readback_routes` now prints the derived layout and says which of the two it is, so the next driver answers for itself instead of being assumed. --- crates/pf-client-core/Cargo.toml | 15 +- .../pf-client-core/src/video_vaapi_native.rs | 2054 ++++++++++++++++- crates/pf-vaadec/layout-probe.c | 48 + crates/pf-vaadec/src/lib.rs | 32 +- crates/pf-vaadec/src/va.rs | 598 ++++- 5 files changed, 2706 insertions(+), 41 deletions(-) diff --git a/crates/pf-client-core/Cargo.toml b/crates/pf-client-core/Cargo.toml index d44fe6b3a..1c772a38e 100644 --- a/crates/pf-client-core/Cargo.toml +++ b/crates/pf-client-core/Cargo.toml @@ -165,11 +165,16 @@ windows = { git = "https://github.com/microsoft/windows-rs", rev = "acb5a1a74410 "winuser", ] } -[target.'cfg(windows)'.dev-dependencies] -# The native D3D11VA rung's frame-hash parity test compares decoded surfaces against the -# libavcodec goldens M5 captured — the same SHA-256 list, and the same crate, pf-vkdecode's -# Vulkan parity legs use (already in the workspace lock). The goldens are checked-in -# hashes; nothing links FFmpeg to read them. +[target.'cfg(any(target_os = "linux", windows))'.dev-dependencies] +# The two platform native rungs' frame-hash parity tests compare decoded surfaces against +# the libavcodec goldens M5 captured — the same SHA-256 list, and the same crate, +# pf-vkdecode's Vulkan parity legs use (already in the workspace lock). The goldens are +# checked-in hashes; nothing links FFmpeg to read them. +# +# Windows was the only platform here until the VAAPI rung grew a readback: `cfg(windows)` +# for `video_d3d11_native::parity`, now `cfg(linux)` as well for +# `video_vaapi_native::parity`. A DEV dependency, so no shipped binary gains anything — +# which is also part of why the VAAPI readback cannot reach the production video path. sha2 = "0.10" [features] diff --git a/crates/pf-client-core/src/video_vaapi_native.rs b/crates/pf-client-core/src/video_vaapi_native.rs index 5bd498bb8..511306185 100644 --- a/crates/pf-client-core/src/video_vaapi_native.rs +++ b/crates/pf-client-core/src/video_vaapi_native.rs @@ -3283,7 +3283,7 @@ mod tests { /// file `pf-vkdecode`'s Vulkan parity leg and `video_d3d11_native`'s D3D11VA leg /// walk, so a count that disagrees with 250 is this rung's problem, not the /// vector's. - const AV1_25FPS: &[u8] = include_bytes!( + pub(super) const AV1_25FPS: &[u8] = include_bytes!( "../../pf-bitstream/vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1" ); @@ -3292,7 +3292,7 @@ mod tests { /// not depend on the vendored parser crate — the same reason and the same walk as /// `video_d3d11_native`'s `split_ivf`, and kept honest by the unit count asserted /// at the top of the test below. - fn split_ivf(stream: &[u8]) -> Vec<&[u8]> { + pub(super) fn split_ivf(stream: &[u8]) -> Vec<&[u8]> { assert_eq!(&stream[0..4], b"DKIF", "the AV1 vector must be an IVF file"); let header = usize::from(u16::from_le_bytes([stream[6], stream[7]])); let mut out = Vec::new(); @@ -3314,17 +3314,17 @@ mod tests { /// Does this machine's VAAPI actually DECODE AV1 — the question the evidence table /// has answered "no hardware has ever tried" since M6. /// - /// This is deliberately weaker than the Vulkan and D3D11VA AV1 legs, and the - /// difference is worth stating rather than hiding: those two hash every decoded - /// frame against libavcodec's goldens, because both can read their decoded surface - /// back. This rung hands out a **DRM-PRIME dmabuf** whose memory is tiled by the - /// driver, so there is no CPU-readable image to hash without adding a - /// `vaDeriveImage`/`vaGetImage` path that production does not use and does not - /// want. So this asserts what CAN be asserted honestly — that every temporal unit - /// is accepted, that the expected number of frames comes back, and that each one - /// is a real exported surface of the right shape — and it is NOT frame-hash parity. - /// It is what turns "never decoded a frame anywhere" into a measurement; promoting - /// the rung to `verified` still wants parity, and that wants a readback path first. + /// A DECODE measurement, not frame-hash parity: it asserts that every temporal unit + /// is accepted, that the expected number of frames comes back, and that each one is + /// a real exported surface of the right shape. It says nothing about the PIXELS. + /// + /// That used to be all this rung could claim — it hands out a **DRM-PRIME dmabuf** + /// whose memory the driver tiles, so there was no CPU-readable image to hash. The + /// `parity` module below adds one, test-only, and + /// `parity::av1_every_delivered_frame_hashes_bit_identical_to_libavcodec` is the leg + /// that checks the pixels. This one is kept because it is the cheaper question and + /// it fails FIRST: a rung that stopped decoding at all should say so without + /// waiting for 250 hashes. /// /// Fails loudly rather than skipping when the device has no AV1 entry point: it is /// `#[ignore]`d, so it only runs when someone deliberately points it at a box that @@ -3399,13 +3399,13 @@ mod tests { /// file, at the same relative path, that `pf-vkdecode`'s Vulkan legs and /// `video_d3d11_native`'s D3D11VA leg decode — so a count that disagrees with /// theirs is this rung's problem rather than the vector's. - const H264_25FPS: &[u8] = include_bytes!( + pub(super) const H264_25FPS: &[u8] = include_bytes!( "../../pf-bitstream/vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264" ); /// The vendored H.265 twin: 250 access units, 320x240 Main 8-bit 4:2:0, ONE slice /// per picture, one `IDR_N_LP` then 249 TRAIL pictures. - const H265_25FPS: &[u8] = include_bytes!( + pub(super) const H265_25FPS: &[u8] = include_bytes!( "../../pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265" ); @@ -3418,7 +3418,8 @@ mod tests { /// path every HDR session takes. `finish` refuses a surface whose exported fourcc /// is not the one the pool was created with, so this leg is also the only thing /// that would catch a driver quietly handing back NV12 for a ten-bit stream. - const MAIN10_H265: &[u8] = include_bytes!("../../pf-vkdecode/tests/data/test-main10.h265"); + pub(super) const MAIN10_H265: &[u8] = + include_bytes!("../../pf-vkdecode/tests/data/test-main10.h265"); /// Both 8-bit vectors are 250 access units. const H26X_AU_COUNT: usize = 250; @@ -3538,7 +3539,7 @@ mod tests { /// slice, 5 = IDR slice), and `first_mb_in_slice == 0` is the top bit of the byte /// after it. Load-bearing rather than decorative on this vector: it codes two /// slices per picture, so without the flag every picture would be split in two. - fn split_h264_aus(stream: &[u8]) -> Vec<&[u8]> { + pub(super) fn split_h264_aus(stream: &[u8]) -> Vec<&[u8]> { split_aus(stream, |s, h| { let is_slice = matches!(s[h] & 0x1f, 1 | 5); let first = is_slice && s.get(h + 1).is_some_and(|b| b & 0x80 != 0); @@ -3551,7 +3552,7 @@ mod tests { /// the top bit of the byte at `+2` where H.264 reads `+1`. Getting either wrong /// silently merges or splits AUs, which surfaces as a frame-count mismatch a long /// way from its cause. - fn split_h265_aus(stream: &[u8]) -> Vec<&[u8]> { + pub(super) fn split_h265_aus(stream: &[u8]) -> Vec<&[u8]> { split_aus(stream, |s, h| { let is_slice = (s[h] >> 1) & 0x3f < 32; let first = is_slice && s.get(h + 2).is_some_and(|b| b & 0x80 != 0); @@ -3972,18 +3973,17 @@ mod tests { /// /// # What these legs prove, and what they do not /// - /// Deliberately weaker than the Vulkan and D3D11VA H.26x legs, and the difference - /// is worth stating rather than hiding behind a test name: those hash every decoded - /// frame against libavcodec's goldens, because both can read their decoded surface - /// back. This rung hands out a **DRM-PRIME dmabuf** whose memory the driver tiles, - /// so there is no CPU-readable image to hash without adding a - /// `vaDeriveImage`/`vaGetImage` path that production does not use and does not - /// want. So this asserts what CAN be asserted honestly — that every access unit is - /// accepted, that the expected number of frames comes back, and that each one is a - /// real exported surface of the right shape and fourcc — and it is **NOT frame-hash - /// parity**. It is what turns "never decoded a frame anywhere" into a measurement; - /// promoting these legs to `verified` still wants parity, and parity wants a - /// readback path that does not exist yet. + /// A DECODE measurement, not frame-hash parity: every access unit is accepted, the + /// expected number of frames comes back, and each one is a real exported surface of + /// the right shape and fourcc. Nothing here looks at a PIXEL. + /// + /// That used to be all this rung could claim, because it hands out a **DRM-PRIME + /// dmabuf** whose memory the driver tiles and there was no CPU-readable image to + /// hash. The `parity` module below adds one, test-only, and its legs are what check + /// the pixels against libavcodec's goldens — the same goldens the Vulkan and + /// D3D11VA rungs are held to. These legs stay because they are the cheaper question + /// and they fail FIRST: a rung that stopped decoding at all should say so without + /// waiting for 250 hashes. /// /// Fails loudly rather than skipping when the device has no entry point for the /// profile: these legs are `#[ignore]`d, so they only run when someone deliberately @@ -4061,7 +4061,8 @@ mod tests { /// table has answered "no hardware has ever tried" since M6. /// /// See [`run_annex_b`] for what this proves and, more importantly, what it does - /// not: it is a decode measurement, not frame-hash parity. + /// not: it is a decode measurement. The pixels are + /// `parity::h264_every_frame_hashes_bit_identical_to_libavcodec`'s business. #[test] #[ignore = "needs a machine with a libva runtime and an H.264 VLD entry point"] fn h264_decodes_the_vendored_vector_on_this_machines_vaapi() { @@ -4171,3 +4172,1994 @@ mod tests { ); } } + +#[cfg(test)] +mod parity { + //! Frame-hash parity for this rung — the evidence M6 and M7 shipped without, and + //! the last rung of the ladder that had none. + //! + //! `#[ignore]`d: every leg needs a real VAAPI device. Run them on a box with + //! + //! ```text + //! cargo test -p pf-client-core --lib video_vaapi_native -- --include-ignored --nocapture + //! ``` + //! + //! and pin a GPU on a multi-GPU box with `PUNKTFUNK_VAAPI_DEVICE=/dev/dri/renderD…` + //! (the same pin the rung itself honours). + //! + //! # What it proves, and against what + //! + //! Exactly what `pf-vkdecode`'s `gpu_parity` proves for the Vulkan rung and + //! `video_d3d11_native`'s `parity` for the D3D11VA one, against the same reference + //! and — deliberately — the SAME GOLDEN FILES, read across the crate boundary + //! rather than copied: H.264, H.265 and AV1 decoding are exactly specified, so a + //! conformant decoder must reproduce libavcodec's SOFTWARE output bit for bit. One + //! golden set for three rungs is what makes their verdicts directly comparable; + //! three copies would be three measurements. + //! + //! Until this module existed the VAAPI rung's four legs could only claim that every + //! access unit was ACCEPTED and that a surface of the right shape came back. That + //! is a much weaker claim than it reads as, and this program has now been shown + //! exactly how much weaker: the D3D11VA AV1 rung streamed 4K60 for five clean + //! minutes while producing wrong pixels for 186 of 250 frames on one GPU and 245 of + //! 250 on another. Nothing but a golden caught it. + //! + //! # Measured + //! + //! **All seven legs, 2026-08-08, on `.25`** — Radeon 780M (RDNA3), radeonsi, Mesa + //! 26.0.3, VA-API 1.23, `/dev/dri/renderD128`: + //! + //! | leg | frames | flush tail | verdict | + //! |---|---|---|---| + //! | H.264, vendored vector | 250 | 7 | bit-identical | + //! | H.264, our host's low-delay 640x480 | 120 | 3 | bit-identical | + //! | H.265, vendored vector | 250 | 2 | bit-identical | + //! | H.265, our host's low-delay 640x480 | 120 | 0 | bit-identical | + //! | HEVC Main 10, P010 | 50 | 2 | bit-identical | + //! | AV1, vendored vector | 250 delivered of 274 decoded | 0 | bit-identical, and display frame 0 byte-identical to libavcodec's own PIXELS | + //! | AV1, our host's 4K two-tile | 60 | 0 | bit-identical | + //! + //! `vaDeriveImage` answers on radeonsi and is the route every leg took. `vaGetImage` + //! also answers; the two agreed byte for byte on every leg's first frame, and + //! `PF_VAAPI_READBACK=getimage` reproduces the H.264 leg's 250/250 through the + //! copying route alone — so the fallback is exercised rather than merely written. + //! + //! ⚠ ONE vendor. This is AMD/radeonsi only; Intel's iHD driver has a different + //! surface layout and a different `vaDeriveImage` answer, and no Intel box has run + //! these legs. The D3D11VA AV1 defect was invisible on NVIDIA for 64 frames and + //! structural on Intel from frame 4 — one driver passing is evidence about that + //! driver. + //! + //! # ⚠ The readback is TEST-ONLY, and that is structural rather than a promise + //! + //! The production path exports a DRM-PRIME dmabuf and the presenter samples it. + //! Nothing on it maps a surface, and nothing may: a per-frame CPU readback on the + //! live path would cost exactly the copy zero-copy exists to avoid. Four things + //! keep this module off it, and the first is the one that matters: + //! + //! 1. **The entry points are resolved HERE, in `#[cfg(test)]` code.** [`ImageApi`] + //! dlopens `libva.so.2` itself and stores the image function pointers in a type + //! that does not exist outside `cargo test`. In a shipped build there is no + //! `vaMapBuffer` pointer to call, so no production path can reach one however + //! wrong it becomes. + //! 2. **The production [`Libva`] gains no field.** Its list of entry points is + //! unchanged by this module, which is the one-screen check a reviewer can do. + //! 3. [`the_readback_entry_points_are_resolved_only_inside_this_module`] asserts + //! (1) and (2) mechanically, by scanning this file's own source: every `dlsym` + //! of an image entry point must sit after this module's header. It is a CPU + //! test, so ordinary `cargo test` enforces it on every platform. + //! 4. `sha2` is a DEV dependency, so nothing shipped links the hashing either. + //! + //! # Two routes, because derive is not guaranteed + //! + //! libva offers two ways to read a surface, and a driver need only implement one: + //! + //! * **`vaDeriveImage`** maps the surface's own memory. Cheap, and refused outright + //! by drivers whose decode surfaces are tiled or otherwise not linearly + //! addressable. + //! * **`vaCreateImage` + `vaGetImage`** asks the driver to copy — and detile — the + //! region into an image of a format it declares it can produce. + //! + //! [`Readback`] tries derive first, falls back to create+get, and **fails loudly + //! naming what the driver gave it** if neither yields the pool's own fourcc. A + //! parity test that quietly passed because it could not read anything is the + //! failure mode this program has been bitten by three times; there is no skip path + //! here. `PF_VAAPI_READBACK=derive|getimage` forces one route, and the first frame + //! of every leg is read through BOTH when both work and the two must agree — which + //! is the only check that can catch a derive that "succeeds" onto tiled bytes. + //! + //! # It hashes what the rung DELIVERS, in the order it delivers it + //! + //! The goldens are one hash per DISPLAY frame. Since the delivery fix this rung + //! hands back every displayed picture in display order — `settle` claims every + //! output rather than only the last, and [`NativeVaapiDecoder::flush`] drains the + //! tail the DPB is still holding — so delivery order IS golden order and the + //! comparison is a straight zip. Three things follow, and all three are why this + //! shape was chosen over hashing decoded pictures by `PicId`: + //! + //! * a frame's surface comes from its OWN release token, so the harness never has + //! to infer which surface holds which picture — an inference that was subtly + //! wrong in an earlier draft of this file, because a surface freed at the top of + //! an access unit can be taken as that same unit's decode target and so never + //! looks newly held; + //! * the DELIVERY path is under test too. A rung that decoded perfectly and + //! presented in the wrong order, or dropped a picture, fails here — and dropping + //! pictures is precisely what this rung did until 2026-08-08; + //! * the frame carries its own display region and keyframe flag + //! ([`PictureFacts`]), so the harness reads geometry from the same place the + //! presenter does rather than from a second guess. + //! + //! ⚠ What this shape does NOT cover: AV1's **hidden frames**. 24 of the vendored + //! vector's 274 decoded pictures are never displayed, so they are never delivered + //! and never hashed here. They are not unverified — every shown frame after one + //! predicts from it, so a hidden picture decoded wrong shows up as a wrong hash on + //! the frames that reference it — but a defect confined to a hidden frame's own + //! pixels would be seen one frame late rather than at once. + //! + //! # The crop, and the ten-bit trap + //! + //! Surfaces are allocated at the CODED size and are taller than the picture, so the + //! chroma plane starts at the driver's own `offsets[1]` and never at + //! `pitch * display_height` — the 1088-row smear this project has already paid for. + //! That walk is [`pf_vaadec::pack_two_plane`], and it is unit-tested with no device + //! at all. Main 10's goldens are **P010**, two bytes per sample with the ten bits in + //! the HIGH end of each little-endian word; a driver handing back LSB-aligned + //! samples produces a buffer of exactly the right length and the wrong content, + //! which [`Divergence::low_bits_set`] is here to name. + + use sha2::Digest; + + use super::tests::split_h264_aus; + use super::tests::split_h265_aus; + use super::tests::split_ivf; + use super::tests::AV1_25FPS; + use super::tests::H264_25FPS; + use super::tests::H265_25FPS; + use super::tests::MAIN10_H265; + use super::*; + + // ----------------------------------------------------------------------- + // The vectors and their goldens — the same files the other two rungs use + // ----------------------------------------------------------------------- + + /// libavcodec's per-display-frame NV12 hashes for the vendored H.264 vector. + /// Read across the crate boundary rather than copied — see the module docs. + const GOLDENS_H264: &str = include_str!("../../pf-vkdecode/tests/data/test-25fps.nv12.sha256"); + const GOLDENS_H265: &str = + include_str!("../../pf-vkdecode/tests/data/test-25fps-h265.nv12.sha256"); + const GOLDENS_MAIN10: &str = + include_str!("../../pf-vkdecode/tests/data/test-main10.p010.sha256"); + const GOLDENS_AV1: &str = + include_str!("../../pf-vkdecode/tests/data/test-25fps-av1.nv12.sha256"); + + /// **Our own host's low-delay H.264** and its goldens — the stream shape a + /// conformance vector cannot be, and the one that caught the D3D11VA rung's + /// release-ordering defect. 120 pictures of 640x480 IPPP with + /// `max_num_reorder_frames = 0` and a DPB exactly as deep as its three references. + /// + /// This rung is argued EXEMPT from that defect for a reason that is a property of + /// the interface rather than of any stream (this file's module docs): a slot is not + /// a surface here, and [`Session::acquire_target`] takes the target and the + /// reference table from one snapshot. That argument is good; it had never been + /// checked in PIXELS on the stream it is about, and "we reasoned it cannot happen" + /// is what the other two rungs also believed. + const LOWDELAY_H264: &[u8] = + include_bytes!("../../pf-vkdecode/tests/data/lowdelay-640x480.h264"); + const GOLDENS_LOWDELAY_H264: &str = + include_str!("../../pf-vkdecode/tests/data/lowdelay-640x480.nv12.sha256"); + + /// The HEVC twin of [`LOWDELAY_H264`]: 120 pictures of 640x480 IPPP, + /// `sps_max_num_reorder_pics = 0`, 115 of the 120 access units retiring a picture. + const LOWDELAY_H265: &[u8] = + include_bytes!("../../pf-vkdecode/tests/data/lowdelay-640x480.h265"); + const GOLDENS_LOWDELAY_H265: &str = + include_str!("../../pf-vkdecode/tests/data/lowdelay-640x480-h265.nv12.sha256"); + + /// **Our own host's AV1**, and the only stream this rung decodes with more than ONE + /// TILE: at 4K the split encode emits `tile_cols = 1, tile_rows = 2` with both tiles + /// in a single Tile Group OBU. 1440p and below measured single-tile, so 4K is the + /// only shape that has the property. + /// + /// ⚠ A file fixture is not the wire path, and on AV1 that distinction has already + /// cost a release: "250/250 delivered frames bit-identical" was true for the whole + /// period the host was shipping only the first tile of every 4K frame, because the + /// truncation lived in packetisation. This leg gives the multi-tile shape pixel + /// coverage on the DECODE rung and proves nothing about fragmentation or loss. + const LOWDELAY_AV1: &[u8] = + include_bytes!("../../pf-vkdecode/tests/data/lowdelay-3840x2160.ivf.av1"); + const GOLDENS_LOWDELAY_AV1: &str = + include_str!("../../pf-vkdecode/tests/data/lowdelay-3840x2160-av1.nv12.sha256"); + + /// libavcodec's decode of the AV1 vector's FIRST display frame, as raw NV12 — + /// 115200 bytes, and the only golden in this program that is pixels rather than a + /// hash. + /// + /// It buys the one thing a hash cannot: when display frame 0 diverges, this says + /// WHERE. Frame 0 of that vector is a key frame with no references at all, so a + /// divergence there is readback geometry (pitch, crop, plane offset) or the tile + /// records — never reference handling — and [`localise`] separates those by naming + /// the plane, the bounding box and the magnitude. + const AV1_FRAME0_NV12: &[u8] = + include_bytes!("../../pf-vkdecode/tests/data/test-25fps-av1.frame0.nv12"); + + /// The vendored vectors' access-unit / temporal-unit counts. + const H26X_AU_COUNT: usize = 250; + const MAIN10_AU_COUNT: usize = 50; + const AV1_UNIT_COUNT: usize = 250; + /// 250 temporal units carrying **274 frames**, of which 250 are shown. + const AV1_DECODED_COUNT: usize = 274; + const AV1_SHOWN_COUNT: usize = 250; + const DISPLAY_AV1: (u32, u32) = (320, 240); + + /// Our host's streams. Three separate constants per AV1 stream, never derived from + /// one another: the vendored vector is 250 / 274 / 250 and this one is 60 / 60 / 60, + /// and a harness that computed "hidden = 0" from either would stop checking the + /// other. + const LOWDELAY_H264_AU_COUNT: usize = 120; + const LOWDELAY_H265_AU_COUNT: usize = 120; + const LOWDELAY_AV1_UNIT_COUNT: usize = 60; + const LOWDELAY_AV1_DECODED_COUNT: usize = 60; + const LOWDELAY_AV1_SHOWN_COUNT: usize = 60; + const DISPLAY_LOWDELAY_AV1: (u32, u32) = (3840, 2160); + + /// The golden file's hash lines (comments and blanks skipped). + fn golden_hashes(file: &'static str) -> Vec<&'static str> { + file.lines() + .map(str::trim) + .filter(|line| !line.is_empty() && !line.starts_with('#')) + .collect() + } + + fn sha256_hex(data: &[u8]) -> String { + use std::fmt::Write as _; + sha2::Sha256::digest(data) + .iter() + .fold(String::with_capacity(64), |mut out, byte| { + let _ = write!(out, "{byte:02x}"); + out + }) + } + + // ----------------------------------------------------------------------- + // The readback — libva's image entry points, resolved ONLY here + // ----------------------------------------------------------------------- + + /// The image half of libva, dlopen'd by the harness itself. + /// + /// Deliberately NOT fields on the production [`Libva`]: keeping them in a + /// `#[cfg(test)]` type is what makes "the readback cannot reach the production + /// path" a fact about what is COMPILED rather than a claim about what is called + /// (module docs). `dlopen` is reference-counted, so resolving these out of a second + /// handle on `libva.so.2` reaches the same mapped library and the same + /// per-`VADisplay` driver state as the rung's own handle — the display pointer they + /// are handed is the rung's. + struct ImageApi { + _va: libloading::Library, + derive_image: + unsafe extern "C" fn(VaDisplay, VaSurfaceId, *mut pf_vaadec::VaImage) -> VaStatus, + create_image: unsafe extern "C" fn( + VaDisplay, + *mut pf_vaadec::VaImageFormat, + c_int, + c_int, + *mut pf_vaadec::VaImage, + ) -> VaStatus, + get_image: unsafe extern "C" fn( + VaDisplay, + VaSurfaceId, + c_int, + c_int, + c_uint, + c_uint, + c_uint, + ) -> VaStatus, + destroy_image: unsafe extern "C" fn(VaDisplay, c_uint) -> VaStatus, + map_buffer: unsafe extern "C" fn(VaDisplay, VaBufferId, *mut *mut c_void) -> VaStatus, + unmap_buffer: unsafe extern "C" fn(VaDisplay, VaBufferId) -> VaStatus, + max_image_formats: unsafe extern "C" fn(VaDisplay) -> c_int, + query_image_formats: + unsafe extern "C" fn(VaDisplay, *mut pf_vaadec::VaImageFormat, *mut c_int) -> VaStatus, + } + + impl ImageApi { + fn load() -> Result { + // SAFETY: the same contract `Libva::load` documents — `Library::new` runs + // the trusted system libva's initialisers (already loaded by the rung, so + // this is a refcount bump), and each `lib.get` resolves a documented libva + // symbol AT the field's own type, transcribed from `va.h`. The `Library` + // handle is stored beside the pointers, so every one outlives its uses. + unsafe { + let va = libloading::Library::new("libva.so.2") + .map_err(|e| anyhow!("libva.so.2 (no VAAPI runtime on this system): {e}"))?; + macro_rules! get { + ($lib:expr, $name:literal) => { + *$lib + .get(concat!($name, "\0").as_bytes()) + .map_err(|e| anyhow!(concat!("dlsym ", $name, ": {}"), e))? + }; + } + let derive_image = get!(va, "vaDeriveImage"); + let create_image = get!(va, "vaCreateImage"); + let get_image = get!(va, "vaGetImage"); + let destroy_image = get!(va, "vaDestroyImage"); + let map_buffer = get!(va, "vaMapBuffer"); + let unmap_buffer = get!(va, "vaUnmapBuffer"); + let max_image_formats = get!(va, "vaMaxNumImageFormats"); + let query_image_formats = get!(va, "vaQueryImageFormats"); + Ok(ImageApi { + derive_image, + create_image, + get_image, + destroy_image, + map_buffer, + unmap_buffer, + max_image_formats, + query_image_formats, + _va: va, + }) + } + } + } + + /// Which libva call read the surface. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + enum Route { + Derive, + GetImage, + } + + /// A `vaCreateImage`d image, reused for every frame of a leg. + struct Staging { + image: pf_vaadec::VaImage, + size: (u32, u32), + fourcc: u32, + } + + /// GPU→CPU readback of one decoded surface, cropped to the picture and packed + /// tightly as NV12/P010 — byte for byte the layout the goldens hash. + struct Readback { + api: ImageApi, + /// Every `VAImageFormat` this driver offers, from `vaQueryImageFormats`. The + /// descriptor `vaCreateImage` is handed is the driver's OWN rather than one + /// this file guessed a `bits_per_pixel` for. + formats: Vec, + staging: Option, + /// `PF_VAAPI_READBACK`, if it pinned a route. + forced: Option, + /// The route that worked, once one has. Latched so a driver that refuses derive + /// pays for that refusal once rather than once per frame. + route: Option, + /// The image size `vaGetImage` accepted — the picture, or the whole surface on + /// a driver that refuses a sub-region. + get_size: Option<(u32, u32)>, + derived: u64, + fetched: u64, + } + + impl Readback { + fn new(d: &Display) -> Readback { + let api = ImageApi::load().expect("libva's image entry points must resolve"); + // SAFETY: a live display; the vector is allocated to the size libva itself + // reports and `count` is a local written through by the call. + let formats = unsafe { + let max = (api.max_image_formats)(d.display); + if max <= 0 { + Vec::new() + } else { + let mut formats = + vec![pf_vaadec::VaImageFormat::default(); max.unsigned_abs() as usize]; + let mut count: c_int = 0; + let status = + (api.query_image_formats)(d.display, formats.as_mut_ptr(), &mut count); + if status == VA_STATUS_SUCCESS { + formats.truncate(count.clamp(0, max) as usize); + formats + } else { + Vec::new() + } + } + }; + let forced = match std::env::var("PF_VAAPI_READBACK").ok().as_deref() { + Some("derive") => Some(Route::Derive), + Some("getimage") => Some(Route::GetImage), + Some(other) => panic!("PF_VAAPI_READBACK={other} — expected derive or getimage"), + None => None, + }; + Readback { + api, + formats, + staging: None, + forced, + route: None, + get_size: None, + derived: 0, + fetched: 0, + } + } + + /// The fourccs this driver says it can produce, for a refusal that names them. + fn offered(&self) -> String { + self.formats + .iter() + .map(|f| { + let b = f.fourcc.to_le_bytes(); + std::str::from_utf8(&b) + .map(str::to_string) + .unwrap_or_else(|_| format!("{:#010x}", f.fourcc)) + }) + .collect::>() + .join(" ") + } + + /// Map an image, pack the picture out of it, unmap. The only place a raw + /// pointer becomes a slice. + fn read_mapped( + &self, + d: &Display, + image: &pf_vaadec::VaImage, + display: (u32, u32), + fourcc: u32, + ) -> std::result::Result, String> { + let mut base: *mut c_void = std::ptr::null_mut(); + // SAFETY: a live display and an image id this call site owns; `base` is a + // local written through. + let status = unsafe { (self.api.map_buffer)(d.display, image.buf, &mut base) }; + if status != VA_STATUS_SUCCESS { + return Err(format!("{:#}", d.va.err("vaMapBuffer", status))); + } + if base.is_null() { + // SAFETY: pairing the successful map above. + unsafe { (self.api.unmap_buffer)(d.display, image.buf) }; + return Err("vaMapBuffer succeeded and returned a null pointer".to_string()); + } + // SAFETY: `vaMapBuffer` returned a pointer to `data_size` readable bytes — + // that is what the field means — and the mapping stays valid until the + // `vaUnmapBuffer` below, which is after the last read. `pack_two_plane` + // bounds-checks every row it takes against this length, so a descriptor + // that disagrees with its own buffer is a refusal rather than a read past + // the end. + let mapped = + unsafe { std::slice::from_raw_parts(base.cast::(), image.data_size as usize) }; + let packed = pf_vaadec::pack_two_plane(image, mapped, display, fourcc).map_err(|e| { + format!( + "{e} — the driver's image is {}x{}, {} plane(s), pitches {:?}, \ + offsets {:?}, data_size {}", + image.width, + image.height, + image.num_planes, + image.pitches, + image.offsets, + image.data_size + ) + }); + // SAFETY: pairing the successful map above; nothing reads `mapped` after. + unsafe { (self.api.unmap_buffer)(d.display, image.buf) }; + packed + } + + /// What a derived image says about the surface's real layout, for the probe. + /// + /// Worth printing rather than assuming, and this is not idle: on `.25`'s + /// radeonsi the decode surfaces for every fixture here turned out to have NO + /// vertical padding — `offsets[1]` is exactly `pitch * height` — so the + /// chroma-plane trap that walk exists to avoid is not exercised by ANY hardware + /// leg on this driver. That is why `pf-vaadec`'s + /// `reading_chroma_at_the_display_height_would_have_been_caught` drives a + /// deliberately padded surface on the CPU: it is the only place that geometry is + /// checked at all, and a reader who assumed the hardware legs covered it would + /// be wrong. + fn describe(&self, d: &Display, surface: VaSurfaceId) -> String { + let mut image = pf_vaadec::VaImage::zeroed(); + // SAFETY: a live display and a surface from its own pool; `image` is a + // zeroed local of the measured layout that outlives the call. + let status = unsafe { (self.api.derive_image)(d.display, surface, &mut image) }; + if status != VA_STATUS_SUCCESS { + return format!("vaDeriveImage: {:#}", d.va.err("vaDeriveImage", status)); + } + let text = format!( + "{}x{}, {} plane(s), pitches {:?}, offsets {:?}, data_size {} — chroma \ + at pitch*height would be {}, so this surface is {}", + image.width, + image.height, + image.num_planes, + image.pitches, + image.offsets, + image.data_size, + image.pitches[0] * u32::from(image.height), + if image.offsets[1] == image.pitches[0] * u32::from(image.height) { + "NOT vertically padded (the crop trap is untested here)" + } else { + "vertically PADDED (the crop trap is live here)" + } + ); + // SAFETY: destroying the image this call derived, exactly once. + unsafe { (self.api.destroy_image)(d.display, image.image_id) }; + text + } + + /// `vaDeriveImage` — the surface's own memory, when the driver can address it + /// linearly. + fn read_via_derive( + &self, + d: &Display, + surface: VaSurfaceId, + display: (u32, u32), + fourcc: u32, + ) -> std::result::Result, String> { + let mut image = pf_vaadec::VaImage::zeroed(); + // SAFETY: a live display and a surface from its own pool; `image` is a + // zeroed local of the measured layout that outlives the call. + let status = unsafe { (self.api.derive_image)(d.display, surface, &mut image) }; + if status != VA_STATUS_SUCCESS { + return Err(format!("{:#}", d.va.err("vaDeriveImage", status))); + } + let out = self.read_mapped(d, &image, display, fourcc); + // SAFETY: destroying the image this call derived, exactly once. Required + // even on the failure path — the derive succeeded, so the image exists. + unsafe { (self.api.destroy_image)(d.display, image.image_id) }; + out + } + + /// Ensure the staging image is `size` in `fourcc`, creating or recreating it. + fn ensure_staging( + &mut self, + d: &Display, + size: (u32, u32), + fourcc: u32, + ) -> std::result::Result<(), String> { + if self + .staging + .as_ref() + .is_some_and(|s| s.size == size && s.fourcc == fourcc) + { + return Ok(()); + } + self.destroy_staging(d); + let mut format = *self + .formats + .iter() + .find(|f| f.fourcc == fourcc) + .ok_or_else(|| { + format!( + "this driver offers no VAImageFormat for the surface pool's own \ + fourcc; it offers [{}]", + self.offered() + ) + })?; + let mut image = pf_vaadec::VaImage::zeroed(); + // SAFETY: a live display; `format` and `image` are locals of the measured + // layouts that outlive the call, and libva copies the format it is handed. + let status = unsafe { + (self.api.create_image)( + d.display, + &mut format, + size.0 as c_int, + size.1 as c_int, + &mut image, + ) + }; + if status != VA_STATUS_SUCCESS { + return Err(format!("{:#}", d.va.err("vaCreateImage", status))); + } + self.staging = Some(Staging { + image, + size, + fourcc, + }); + Ok(()) + } + + fn destroy_staging(&mut self, d: &Display) { + if let Some(s) = self.staging.take() { + // SAFETY: an image this type created on this display, destroyed once. + unsafe { (self.api.destroy_image)(d.display, s.image.image_id) }; + } + } + + /// `vaCreateImage` + `vaGetImage` at one image size. + fn get_into( + &mut self, + d: &Display, + surface: VaSurfaceId, + size: (u32, u32), + display: (u32, u32), + fourcc: u32, + ) -> std::result::Result, String> { + self.ensure_staging(d, size, fourcc)?; + let image = self.staging.as_ref().expect("just ensured").image; + // SAFETY: a live display, a surface from its own pool and an image this + // type created on it. The region is inside the surface: `size` is either + // the picture (which the surface contains) or the surface itself. + let status = unsafe { + (self.api.get_image)( + d.display, + surface, + 0, + 0, + size.0 as c_uint, + size.1 as c_uint, + image.image_id, + ) + }; + if status != VA_STATUS_SUCCESS { + return Err(format!( + "{:#} (image {}x{})", + d.va.err("vaGetImage", status), + size.0, + size.1 + )); + } + self.read_mapped(d, &image, display, fourcc) + } + + /// `vaGetImage`, trying the picture-sized region first and the whole surface + /// second — a driver that refuses a sub-region still answers, and the crop then + /// happens in [`pf_vaadec::pack_two_plane`] instead. + fn read_via_get_image( + &mut self, + d: &Display, + surface: VaSurfaceId, + display: (u32, u32), + coded: (u32, u32), + fourcc: u32, + ) -> std::result::Result, String> { + if let Some(size) = self.get_size { + return self.get_into(d, surface, size, display, fourcc); + } + let mut sizes = vec![display]; + if coded != display { + sizes.push(coded); + } + let mut why = Vec::new(); + for size in sizes { + match self.get_into(d, surface, size, display, fourcc) { + Ok(bytes) => { + self.get_size = Some(size); + return Ok(bytes); + } + Err(e) => why.push(e), + } + } + Err(why.join("; ")) + } + + fn read_route( + &mut self, + route: Route, + d: &Display, + surface: VaSurfaceId, + display: (u32, u32), + coded: (u32, u32), + fourcc: u32, + ) -> std::result::Result, String> { + match route { + Route::Derive => { + let out = self.read_via_derive(d, surface, display, fourcc); + if out.is_ok() { + self.derived += 1; + } + out + } + Route::GetImage => { + let out = self.read_via_get_image(d, surface, display, coded, fourcc); + if out.is_ok() { + self.fetched += 1; + } + out + } + } + } + + /// The picture in `surface`, by whichever route this driver supports. + /// + /// Panics — loudly, with what every route said — when none of them can read it. + /// There is deliberately no skip: a leg that could not read a surface must + /// fail, not pass quietly (module docs). + fn read( + &mut self, + d: &Display, + surface: VaSurfaceId, + display: (u32, u32), + coded: (u32, u32), + fourcc: u32, + what: &str, + ) -> Vec { + // The surface must be finished before it is read. The production export + // does exactly this before the fds leave, and for the same reason: VAAPI + // exposes no fence to the consumer. + // + // SAFETY: a live display and a surface from its own pool. + let status = unsafe { (d.va.sync_surface)(d.display, surface) }; + if status != VA_STATUS_SUCCESS { + panic!("{what}: {:#}", d.va.err("vaSyncSurface", status)); + } + if let Some(route) = self.route { + return match self.read_route(route, d, surface, display, coded, fourcc) { + Ok(bytes) => bytes, + Err(e) => panic!( + "{what}: the {route:?} readback stopped working part-way through \ + a run — {e}" + ), + }; + } + let order = match self.forced { + Some(r) => vec![r], + None => vec![Route::Derive, Route::GetImage], + }; + let mut why = Vec::new(); + for route in order { + match self.read_route(route, d, surface, display, coded, fourcc) { + Ok(bytes) => { + eprintln!("readback route: {route:?}"); + self.route = Some(route); + return bytes; + } + Err(e) => why.push(format!("{route:?}: {e}")), + } + } + panic!( + "{what}: NO readback route could read the decoded surface, so this leg \ + can prove nothing and refuses to pass — {}. The driver offers image \ + formats [{}]", + why.join(" | "), + self.offered() + ); + } + + /// Read one surface through BOTH routes and require them to agree. + /// + /// The only check that can catch a `vaDeriveImage` which "succeeds" onto tiled + /// bytes: the descriptor looks ordinary, the walk reads it happily, and the + /// pixels are a permutation of the picture. `vaGetImage` asks the driver to + /// detile, so where both answer, agreement is evidence that derive's mapping + /// really is linear. + /// + /// A route that refuses is reported, not failed: that is exactly the case + /// [`Self::read`] is written to survive. + fn cross_check( + &mut self, + d: &Display, + surface: VaSurfaceId, + display: (u32, u32), + coded: (u32, u32), + fourcc: u32, + what: &str, + ) { + let derived = self.read_via_derive(d, surface, display, fourcc); + let fetched = self.read_via_get_image(d, surface, display, coded, fourcc); + match (&derived, &fetched) { + (Ok(a), Ok(b)) => { + assert_eq!( + a.len(), + b.len(), + "{what}: the two readback routes disagree on the picture's size" + ); + if a != b { + let diff = localise(a, b, display, fourcc); + panic!( + "{what}: vaDeriveImage and vaGetImage read DIFFERENT pixels \ + out of one surface — {diff}. Derive is handing back memory \ + this walk cannot address linearly (tiled or swizzled), so \ + every hash taken through it is meaningless. Re-run with \ + PF_VAAPI_READBACK=getimage" + ); + } + eprintln!("{what}: both readback routes agree ({} bytes)", a.len()); + } + (Ok(a), Err(e)) => eprintln!( + "{what}: vaDeriveImage answers ({} bytes); vaGetImage does not — {e}", + a.len() + ), + (Err(e), Ok(b)) => eprintln!( + "{what}: vaGetImage answers ({} bytes); vaDeriveImage does not — {e}", + b.len() + ), + (Err(a), Err(b)) => panic!( + "{what}: NEITHER readback route can read this surface — derive: {a} \ + | getimage: {b}. The driver offers image formats [{}]", + self.offered() + ), + } + } + + /// One line naming which route answered and how often, so a run says it rather + /// than leaving it to be inferred from a passing test. + fn summary(&self) -> String { + format!( + "readback via {:?} ({} derived, {} vaGetImage)", + self.route, self.derived, self.fetched + ) + } + } + + // ----------------------------------------------------------------------- + // Divergence: what a hash mismatch will not tell you + // ----------------------------------------------------------------------- + + /// Where two same-shaped pictures differ. + /// + /// "N frames differ" is not a lead; "first divergence at frame 3, one 16x24 luma + /// block, chroma clean" is what solved the last two defects in this program. This + /// is what turns the former into the latter wherever a reference picture exists — + /// [`AV1_FRAME0_NV12`] for AV1 display frame 0, the two readback routes against + /// each other, and the harness's own bytes in the counterfactual that proves the + /// comparison can fail. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + struct Divergence { + luma_samples: usize, + chroma_samples: usize, + /// Inclusive bounding box of the differing LUMA samples, in picture + /// coordinates. + luma_box: Option<(u32, u32, u32, u32)>, + max_delta: u32, + /// Ten-bit only: samples whose low six bits are set. P010 puts the ten + /// meaningful bits in the HIGH end of each 16-bit word, so a non-zero count + /// here means the driver handed back LSB-aligned samples and the divergence is + /// a format misunderstanding rather than a decode fault. + low_bits_set: usize, + } + + impl std::fmt::Display for Divergence { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.luma_samples == 0 && self.chroma_samples == 0 { + return write!(f, "identical"); + } + write!( + f, + "{} luma sample(s), {} chroma sample(s), max |delta| {}", + self.luma_samples, self.chroma_samples, self.max_delta + )?; + if let Some((x0, y0, x1, y1)) = self.luma_box { + write!( + f, + ", luma bounding box ({x0},{y0})..({x1},{y1}) = {}x{}", + x1 - x0 + 1, + y1 - y0 + 1 + )?; + } + if self.chroma_samples == 0 { + write!(f, ", chroma CLEAN")?; + } + if self.low_bits_set > 0 { + write!( + f, + ", and {} sample(s) have their low six bits set — P010's ten bits \ + belong in the HIGH end of each word, so suspect the FORMAT before \ + the decode", + self.low_bits_set + )?; + } + Ok(()) + } + } + + /// Compare two tightly packed pictures of the same shape. + fn localise(got: &[u8], want: &[u8], display: (u32, u32), fourcc: u32) -> Divergence { + let stride = if fourcc == pf_vaadec::VA_FOURCC_P010 { + 2usize + } else { + 1 + }; + let (width, height) = (display.0 as usize, display.1 as usize); + let luma_bytes = width * height * stride; + let sample = |buf: &[u8], at: usize| -> u32 { + if stride == 2 { + u32::from(u16::from_le_bytes([buf[at], buf[at + 1]])) + } else { + u32::from(buf[at]) + } + }; + let mut d = Divergence { + luma_samples: 0, + chroma_samples: 0, + luma_box: None, + max_delta: 0, + low_bits_set: 0, + }; + let end = got.len().min(want.len()); + let mut at = 0usize; + while at + stride <= end { + let (a, b) = (sample(got, at), sample(want, at)); + if stride == 2 && a & 0x3f != 0 { + d.low_bits_set += 1; + } + if a != b { + d.max_delta = d.max_delta.max(a.abs_diff(b)); + if at < luma_bytes { + d.luma_samples += 1; + let index = at / stride; + let (x, y) = ((index % width) as u32, (index / width) as u32); + d.luma_box = Some(match d.luma_box { + None => (x, y, x, y), + Some((x0, y0, x1, y1)) => (x0.min(x), y0.min(y), x1.max(x), y1.max(y)), + }); + } else { + d.chroma_samples += 1; + } + } + at += stride; + } + d + } + + // ----------------------------------------------------------------------- + // Decode and display order, from a planner run alongside the decoder's own + // ----------------------------------------------------------------------- + + /// The decode order and the display order of a stream's pictures, as `PicId`s. + /// + /// Both come from a planner run ALONGSIDE the rung's own, over the same access + /// units: the planner is deterministic, so the ids it hands this walk are the ids + /// it hands the rung, and no production code has to grow a test accessor. + /// + /// The hardware legs do not USE this to find surfaces — they hash what the rung + /// delivers, in delivery order (module docs). It is what the CPU guards check the + /// golden files and the frame counts against, so a regenerated vector fails on a + /// laptop rather than on the one box with a VAAPI driver. + struct Order { + /// Every DECODED picture in submission order — one per access unit on + /// H.264/H.265, one per FRAME on AV1 where a unit may carry several. + decode: Vec, + /// The same ids in the planner's output (bumping) order, flush included. + display: Vec, + /// The ids each ACCESS UNIT decodes, in submission order. An access unit that + /// decodes nothing — an HEVC RASL skipped after an open-GOP join — contributes + /// an EMPTY entry rather than none, so the index stays the unit's own. + per_unit: Vec>, + } + + impl Order { + fn empty() -> Order { + Order { + decode: Vec::new(), + display: Vec::new(), + per_unit: Vec::new(), + } + } + } + + fn order_h264(aus: &[&[u8]]) -> Order { + let mut planner = pf_vaadec::H264Planner::new(); + let mut order = Order::empty(); + for (index, au) in aus.iter().enumerate() { + let plan = planner + .plan_au(au) + .unwrap_or_else(|e| panic!("AU {index}: the clean vector must plan, got {e:?}")); + assert_eq!( + (plan.picture.display_crop.x, plan.picture.display_crop.y), + (0, 0), + "AU {index}: this rung REFUSES a non-zero conformance-window origin \ + (`shape_of`), so a vector that had one could not be decoded here at all" + ); + let id = plan + .dpb + .stored + .unwrap_or_else(|| panic!("AU {index}: every picture of this vector is stored")); + order.decode.push(id); + order.per_unit.push(vec![id]); + order.display.extend(plan.dpb.outputs.iter().copied()); + } + order.display.extend(planner.flush().outputs); + order + } + + /// [`order_h264`] for HEVC, with the one thing H.264 has no counterpart to: a + /// **RASL picture skipped after an open-GOP join** decodes nothing. + /// + /// `PlanErrorH265::RaslSkipped` is the spec's own answer (8.1.3 NOTE) and the rung + /// treats it as an Ok-skip, so such an access unit contributes no picture and no + /// output. + fn order_h265(aus: &[&[u8]]) -> Order { + let mut planner = pf_vaadec::H265Planner::new(); + let mut order = Order::empty(); + for (index, au) in aus.iter().enumerate() { + let plan = match planner.plan_au(au) { + Ok(plan) => plan, + Err(pf_vaadec::PlanErrorH265::RaslSkipped { .. }) => { + order.per_unit.push(Vec::new()); + continue; + } + Err(e) => panic!("AU {index}: the clean vector must plan, got {e:?}"), + }; + assert_eq!( + (plan.picture.display_crop.x, plan.picture.display_crop.y), + (0, 0), + "AU {index}: this rung refuses a non-zero conformance-window origin" + ); + let id = plan + .dpb + .stored + .unwrap_or_else(|| panic!("AU {index}: every picture of this vector is stored")); + order.decode.push(id); + order.per_unit.push(vec![id]); + order.display.extend(plan.dpb.outputs.iter().copied()); + } + order.display.extend(planner.flush().outputs); + order + } + + /// The AV1 stream's decode and display orders. + /// + /// Where the H.264/H.265 walks push one decoded picture per access unit, this one + /// pushes one per FRAME and a unit may carry several — which is the whole + /// difference. `display` is still the planner's own output list; AV1 has no bumping + /// process, so a picture is output by the unit that shows it and there is no flush + /// to drain at the end. + fn order_av1(units: &[&[u8]], render: (u32, u32)) -> Order { + let mut planner = pf_vaadec::Av1Planner::new(); + let mut order = Order::empty(); + for (index, unit) in units.iter().enumerate() { + let plans = planner + .plan_au(unit) + .unwrap_or_else(|e| panic!("unit {index}: the clean vector must plan, got {e}")); + let mut this_unit = Vec::new(); + for plan in &plans { + assert!( + plan.warnings.is_empty(), + "unit {index}: a clean vector must plan without warnings, got {:?}", + plan.warnings + ); + assert_eq!( + (plan.picture.render_width, plan.picture.render_height), + render, + "unit {index}: the goldens are the {render:?} render region" + ); + if let Some(id) = plan.dpb.stored { + order.decode.push(id); + this_unit.push(id); + } + order.display.extend(plan.dpb.outputs.iter().copied()); + } + order.per_unit.push(this_unit); + } + order + } + + // ----------------------------------------------------------------------- + // The runs + // ----------------------------------------------------------------------- + + /// Read back the picture a delivered frame carries, packed as the goldens hash it. + /// + /// The frame names its own surface — [`VaRelease::surface`], stamped when `finish` + /// exported it — so nothing here has to work out which pool entry holds which + /// picture. It also carries its own display region and fourcc, recorded when the + /// picture DECODED (`PictureFacts`), which is the same pair the presenter is handed; + /// reading them from anywhere else would be a second guess that could differ. + fn read_frame( + decoder: &NativeVaapiDecoder, + readback: &mut Readback, + frame: &DmabufFrame, + what: &str, + ) -> Vec { + let release = frame.guard.0.release; + let (surface, coded, fourcc) = { + let s = decoder + .session + .as_ref() + .unwrap_or_else(|| panic!("{what}: a frame came back with no session behind it")); + assert_eq!( + release.generation, s.generation, + "{what}: this frame names a RETIRED surface pool, so its pixels are not \ + this session's — nothing in these vectors renegotiates, so a mismatch \ + here is a bookkeeping defect rather than a stream that resized" + ); + ( + s.surfaces[release.surface], + (s.shape.coded_width, s.shape.coded_height), + s.fourcc, + ) + }; + assert_eq!( + frame.fourcc, fourcc, + "{what}: the frame's fourcc is not the pool's — `finish` is supposed to \ + refuse that before it ships" + ); + let display = (frame.width, frame.height); + // The first frame of a leg is read through BOTH routes, and they must agree. + // Skipped when a route was PINNED: the pin exists precisely for a box where one + // of them is wrong, and failing the run because it is wrong would defeat it. + if readback.route.is_none() && readback.forced.is_none() { + readback.cross_check(&decoder.display, surface, display, coded, fourcc, what); + } + let bytes = readback.read(&decoder.display, surface, display, coded, fourcc, what); + assert_eq!( + bytes.len(), + pf_vaadec::packed_len(display, fourcc).expect("the pool's fourcc is one of ours"), + "{what}: the readback is not the golden's own layout" + ); + bytes + } + + /// Compare the hash of every DELIVERED frame against the goldens, in order, + /// printing the first ten divergences and returning how many there were and where + /// the first one is. + /// + /// A separate function from the run that produces the hashes so it can be driven + /// from a CPU test with a deliberately corrupted list — + /// [`the_comparison_catches_a_corrupted_frame`] is that counterfactual, and it is + /// the answer to "prove this harness can fail". + fn compare(hashes: &[String], goldens: &[&str], label: &str) -> (usize, Option) { + let mut mismatches = 0usize; + let mut first = None; + for (n, (got, golden)) in hashes.iter().zip(goldens.iter()).enumerate() { + if got != golden { + if mismatches < 10 { + eprintln!("{label}: display frame {n}: {got} != {golden}"); + } + if first.is_none() { + first = Some(n); + } + mismatches += 1; + } + } + (mismatches, first) + } + + /// The verdict, spelled the way the last two defects were localised from. + fn verdict( + mismatches: usize, + first: Option, + total: usize, + label: &str, + readback: &str, + opening: &str, + ) { + assert_eq!( + mismatches, 0, + "{label}: {mismatches}/{total} frames diverge from libavcodec's software \ + decode (first ten above; first divergence at display frame {first:?}). \ + {opening} Read the signature as evidence about WHERE, not about WHAT — the \ + D3D11VA AV1 defect had two unlike signatures on two vendors and was ONE \ + bug. Readback was {readback}; PF_VAAPI_READBACK=getimage forces the copying \ + route, and PF_VAAPI_DUMP= writes the raw planes" + ); + eprintln!( + "{label}: {total} frames bit-identical to libavcodec software decode ({readback})" + ); + } + + /// Write one frame's raw planes to the temp directory when `PF_VAAPI_DUMP` is set — + /// the lever that turned "186 frames differ" into a located defect on the D3D11VA + /// rung, by giving `ffmpeg -f rawvideo` something to compare against. + fn dump(tag: &Option, label: &str, what: &str, bytes: &[u8]) { + let Some(tag) = tag else { return }; + let path = std::env::temp_dir().join(format!( + "pf-vaapi-{tag}-{}-{what}.bin", + label.replace([' ', '(', ')', ',', '.'], "") + )); + std::fs::write(&path, bytes).expect("write the dump"); + eprintln!("dumped {what} -> {}", path.display()); + } + + /// Everything a run collects, so the two drivers below can share the assertions + /// that matter rather than two hand-copied sets. + struct Delivered { + hashes: Vec, + /// The first delivered frame's keyframe flag — a fact about the DELIVERY path + /// that used to be wrong on every reordering stream. + first_keyframe: bool, + /// The first delivered frame's pixels, for the one leg that has libavcodec's. + first_bytes: Vec, + /// How many frames the deliverable queue had to DROP. Anything but zero is a + /// golden that can never be checked. + dropped: u64, + } + + /// Drive one stream's access units through a real rung, hashing every frame it + /// hands back, then drain the tail. + /// + /// The tail is not optional and not bookkeeping: `flush` is where the pictures the + /// DPB is still buffering for reorder come from — seven of the H.264 vector's 250, + /// two of the HEVC vector's, two of Main 10's — and a run that stopped at the last + /// access unit would be exactly that many frames short of the goldens, which reads + /// like missing pictures rather than like a harness that never asked. + fn drive( + decoder: &mut NativeVaapiDecoder, + readback: &mut Readback, + units: &[&[u8]], + label: &str, + ) -> Delivered { + let mut hashes = Vec::new(); + let mut first_keyframe = false; + let mut first_bytes = Vec::new(); + for (index, unit) in units.iter().enumerate() { + let frame = decoder + .decode(unit) + .unwrap_or_else(|e| panic!("{label} AU {index}: decode failed — {e:#}")); + if let Some(frame) = frame { + let what = format!("{label} AU {index} -> display frame {}", hashes.len()); + let bytes = read_frame(decoder, readback, &frame, &what); + if hashes.is_empty() { + first_keyframe = frame.keyframe; + first_bytes = bytes.clone(); + } + hashes.push(sha256_hex(&bytes)); + } + } + let tail = decoder.flush(); + eprintln!("{label}: {} frame(s) came out of the flush", tail.len()); + for frame in &tail { + let what = format!("{label} flush -> display frame {}", hashes.len()); + let bytes = read_frame(decoder, readback, frame, &what); + if hashes.is_empty() { + first_keyframe = frame.keyframe; + first_bytes = bytes.clone(); + } + hashes.push(sha256_hex(&bytes)); + } + Delivered { + hashes, + first_keyframe, + first_bytes, + dropped: decoder.health().dropped, + } + } + + /// The assertions every leg makes about what came back, before a single hash is + /// compared. + fn check_delivery(d: &Delivered, goldens: &[&str], label: &str) { + assert_eq!( + d.dropped, 0, + "{label}: the rung DROPPED {} display-ready frame(s) because its deliverable \ + queue overflowed. Every one of them is a golden that can never be checked, \ + so the comparison below would be measuring a shorter stream than the \ + goldens describe", + d.dropped + ); + assert_eq!( + d.hashes.len(), + goldens.len(), + "{label}: the rung delivered {} frames and the goldens carry {}. This is the \ + delivery path, not the decode: the rung must hand back every picture the \ + planner outputs, `flush` included", + d.hashes.len(), + goldens.len() + ); + assert!( + d.first_keyframe, + "{label}: the FIRST delivered frame is not flagged as a keyframe. Every one \ + of these streams opens on an IDR or an AV1 key frame, and that frame is the \ + first thing displayed — a rung that labels the access unit rather than the \ + picture it displays gets this wrong on any stream that reorders, and \ + `DecodedImage::is_keyframe` is the pump's post-loss re-anchor signal" + ); + } + + /// Decode `aus` through a real [`NativeVaapiDecoder`] and compare every delivered + /// frame against libavcodec's goldens. + fn parity_run( + codec: pf_vaadec::Codec, + stream: StreamFormat, + aus: &[&[u8]], + order: &Order, + goldens: &[&str], + expected_aus: usize, + label: &str, + ) { + assert_eq!( + aus.len(), + expected_aus, + "{label}: the vector must split into {expected_aus} access units — a \ + different count means this file's splitter disagrees with pf-bitstream's, \ + and nothing below it is meaningful" + ); + assert_eq!( + order.display.len(), + goldens.len(), + "{label}: the planner outputs {} pictures, the goldens carry {}", + order.display.len(), + goldens.len() + ); + + let mut decoder = NativeVaapiDecoder::new(codec, stream) + .unwrap_or_else(|e| panic!("{label}: this box must host this profile — {e:#}")); + let mut readback = Readback::new(&decoder.display); + let dump_tag = std::env::var("PF_VAAPI_DUMP").ok(); + + let delivered = drive(&mut decoder, &mut readback, aus, label); + dump(&dump_tag, label, "display0", &delivered.first_bytes); + check_delivery(&delivered, goldens, label); + + let (mismatches, first) = compare(&delivered.hashes, goldens, label); + let readback_note = readback.summary(); + readback.destroy_staging(&decoder.display); + verdict( + mismatches, + first, + goldens.len(), + label, + &readback_note, + "Display frame 0 is intra-only — if IT diverges suspect the readback \ + geometry (pitch, crop, plane offset) or the surface format rather than the \ + decode.", + ); + } + + /// The AV1 leg of [`parity_run`]. + /// + /// It drives the same production entry point — [`NativeVaapiDecoder::decode`] takes + /// a whole temporal unit, exactly as the stream does — so the unit loop, + /// `frame_av1`'s slot bookkeeping and the `show` suppression are all under test. Two + /// things make it a separate function rather than a parameter: + /// + /// * a temporal unit is not a picture. 24 of the vendored vector's 250 units carry a + /// HIDDEN frame as well as the shown one, so 274 pictures decode and 250 display, + /// and this leg asserts that gap from the planner rather than assuming it; + /// * it has libavcodec's actual PIXELS for display frame 0 ([`AV1_FRAME0_NV12`]), + /// which is the only place in this program a divergence can be localised without + /// a second GPU to compare against. + #[allow(clippy::too_many_arguments)] + fn av1_parity_run( + units: &[&[u8]], + order: &Order, + goldens: &[&str], + unit_count: usize, + decoded_count: usize, + shown_count: usize, + frame0_golden: Option<&[u8]>, + label: &str, + ) { + assert_eq!( + units.len(), + unit_count, + "{label}: the IVF reader disagrees with the stream's temporal-unit count" + ); + assert_eq!(order.decode.len(), decoded_count); + assert_eq!(order.per_unit.len(), units.len()); + assert_eq!(order.display.len(), goldens.len()); + assert_eq!(order.display.len(), shown_count); + + let mut decoder = NativeVaapiDecoder::new(pf_vaadec::Codec::Av1, StreamFormat::SDR_420_8) + .unwrap_or_else(|e| panic!("{label}: this box must host AV1 Profile 0 — {e:#}")); + let mut readback = Readback::new(&decoder.display); + let dump_tag = std::env::var("PF_VAAPI_DUMP").ok(); + + let delivered = drive(&mut decoder, &mut readback, units, label); + dump(&dump_tag, label, "display0", &delivered.first_bytes); + check_delivery(&delivered, goldens, label); + + let hidden = decoded_count - shown_count; + assert_eq!( + delivered.hashes.len(), + shown_count, + "{label}: {decoded_count} pictures decode and {shown_count} display, so \ + {hidden} must have been decoded and WITHHELD. On a stream with no hidden \ + frames both sides are equal and this is a tautology — deliberately, so one \ + harness serves both shapes" + ); + + // The one place in this program where a divergence can be localised without a + // second GPU to compare against: libavcodec's own first frame, as pixels. + if let Some(golden) = frame0_golden { + if delivered.first_bytes.as_slice() != golden { + let diff = localise( + &delivered.first_bytes, + golden, + DISPLAY_AV1, + pf_vaadec::VA_FOURCC_NV12, + ); + panic!( + "{label}: display frame 0 does not match libavcodec's own pixels — \ + {diff}. It is a KEY frame with no references, so this is readback \ + geometry, the surface format, or the tile records — never reference \ + handling" + ); + } + eprintln!("{label}: display frame 0 is byte-identical to libavcodec's pixels"); + } + + let (mismatches, first) = compare(&delivered.hashes, goldens, label); + let readback_note = readback.summary(); + readback.destroy_staging(&decoder.display); + verdict( + mismatches, + first, + goldens.len(), + label, + &readback_note, + &format!( + "{hidden} hidden frame(s) were decoded and withheld. Display frame 0 is a \ + key frame — if IT diverges suspect the readback geometry or the tile \ + records rather than the reference handling." + ), + ); + } + + // ----------------------------------------------------------------------- + // The legs + // ----------------------------------------------------------------------- + + #[test] + #[ignore = "needs a machine with a libva runtime and an H.264 VLD entry point"] + fn h264_every_frame_hashes_bit_identical_to_libavcodec() { + let aus = split_h264_aus(H264_25FPS); + let order = order_h264(&aus); + parity_run( + pf_vaadec::Codec::H264, + StreamFormat::SDR_420_8, + &aus, + &order, + &golden_hashes(GOLDENS_H264), + H26X_AU_COUNT, + "H.264", + ); + } + + /// **Our own host's output** rather than a conformance vector — the shape that + /// caught the D3D11VA rung's release-ordering defect after the vector had passed + /// 250/250 on four GPUs across two milestones. + /// + /// See [`LOWDELAY_H264`] for why this rung is argued exempt from that defect, and + /// why the argument being good is not the same as its having been checked. + #[test] + #[ignore = "needs a machine with a libva runtime and an H.264 VLD entry point"] + fn low_delay_host_h264_every_frame_hashes_bit_identical_to_libavcodec() { + let aus = split_h264_aus(LOWDELAY_H264); + let order = order_h264(&aus); + parity_run( + pf_vaadec::Codec::H264, + StreamFormat::SDR_420_8, + &aus, + &order, + &golden_hashes(GOLDENS_LOWDELAY_H264), + LOWDELAY_H264_AU_COUNT, + "H.264 (low-delay host stream)", + ); + } + + #[test] + #[ignore = "needs a machine with a libva runtime and an HEVC Main VLD entry point"] + fn h265_every_frame_hashes_bit_identical_to_libavcodec() { + let aus = split_h265_aus(H265_25FPS); + let order = order_h265(&aus); + parity_run( + pf_vaadec::Codec::H265, + StreamFormat::SDR_420_8, + &aus, + &order, + &golden_hashes(GOLDENS_H265), + H26X_AU_COUNT, + "H.265", + ); + } + + #[test] + #[ignore = "needs a machine with a libva runtime and an HEVC Main VLD entry point"] + fn low_delay_host_h265_every_frame_hashes_bit_identical_to_libavcodec() { + let aus = split_h265_aus(LOWDELAY_H265); + let order = order_h265(&aus); + parity_run( + pf_vaadec::Codec::H265, + StreamFormat::SDR_420_8, + &aus, + &order, + &golden_hashes(GOLDENS_LOWDELAY_H265), + LOWDELAY_H265_AU_COUNT, + "H.265 (low-delay host stream)", + ); + } + + /// The ten-bit path, which every HDR session lands on. + /// + /// It exercises geometry the 8-bit legs cannot: **P010 samples are two bytes**, so a + /// row is `width * 2`, and HEVC's granule pads a 240-line picture to a 256-line + /// surface — the chroma plane therefore starts a long way from where the display + /// height would put it. And it is the only leg that can tell a Main 10 session that + /// BUILDS from one that decodes correctly: VAAPI has no per-picture decode status + /// query, so a stream decoding to garbage logs exactly as cleanly. + #[test] + #[ignore = "needs a machine with a libva runtime and an HEVC Main 10 VLD entry point"] + fn main10_every_frame_hashes_bit_identical_to_libavcodec() { + let aus = split_h265_aus(MAIN10_H265); + let order = order_h265(&aus); + parity_run( + pf_vaadec::Codec::H265, + StreamFormat { + bit_depth: 10, + ..StreamFormat::SDR_420_8 + }, + &aus, + &order, + &golden_hashes(GOLDENS_MAIN10), + MAIN10_AU_COUNT, + "HEVC Main 10", + ); + } + + #[test] + #[ignore = "needs a machine with a libva runtime and an AV1 VLD entry point"] + fn av1_every_delivered_frame_hashes_bit_identical_to_libavcodec() { + let units = split_ivf(AV1_25FPS); + let order = order_av1(&units, DISPLAY_AV1); + av1_parity_run( + &units, + &order, + &golden_hashes(GOLDENS_AV1), + AV1_UNIT_COUNT, + AV1_DECODED_COUNT, + AV1_SHOWN_COUNT, + Some(AV1_FRAME0_NV12), + "AV1", + ); + } + + /// **Our own host's AV1, at the only resolution where it emits more than one tile.** + /// The leg above runs a vector whose every frame is `tile_cols = tile_rows = 1`, so + /// every tile field `plan_to_va_av1` fills is the degenerate case. This stream is + /// `tile_rows = 2` on all 60 frames with both tiles in one Tile Group OBU — and it + /// is 4K, so the readback moves 12.4 MB per frame. + #[test] + #[ignore = "needs a machine with a libva runtime and an AV1 VLD entry point"] + fn low_delay_host_av1_every_frame_hashes_bit_identical_to_libavcodec() { + let units = split_ivf(LOWDELAY_AV1); + let order = order_av1(&units, DISPLAY_LOWDELAY_AV1); + av1_parity_run( + &units, + &order, + &golden_hashes(GOLDENS_LOWDELAY_AV1), + LOWDELAY_AV1_UNIT_COUNT, + LOWDELAY_AV1_DECODED_COUNT, + LOWDELAY_AV1_SHOWN_COUNT, + // The raw golden is the vendored vector's frame 0, at 320x240. This stream + // is 4K, so there is nothing to compare pixels against. + None, + "AV1 (low-delay host stream, 4K two-tile)", + ); + } + + // ----------------------------------------------------------------------- + // The harness's own evidence: that it reads real pixels and CAN fail + // ----------------------------------------------------------------------- + + /// Which readback routes this machine's driver supports, on a real decoded surface, + /// and what they hand back. + /// + /// A diagnostic, not a gate — but it FAILS rather than skips if neither route works, + /// because a box someone deliberately pointed this at is a box that is supposed to + /// be able to answer. + #[test] + #[ignore = "needs a machine with a libva runtime and an H.264 VLD entry point"] + fn probe_this_machines_readback_routes() { + let aus = split_h264_aus(H264_25FPS); + let mut decoder = NativeVaapiDecoder::new(pf_vaadec::Codec::H264, StreamFormat::SDR_420_8) + .expect("this box is supposed to have a VAAPI H.264 decode entry point"); + let mut frame = None; + for (index, au) in aus.iter().enumerate() { + frame = decoder + .decode(au) + .unwrap_or_else(|e| panic!("AU {index}: decode failed — {e:#}")); + if frame.is_some() { + break; + } + } + let frame = frame.expect("some access unit of the vendored vector must deliver a frame"); + let release = frame.guard.0.release; + let (surface, display, coded, fourcc) = { + let s = decoder.session.as_ref().expect("a session"); + ( + s.surfaces[release.surface], + (frame.width, frame.height), + (s.shape.coded_width, s.shape.coded_height), + s.fourcc, + ) + }; + let mut readback = Readback::new(&decoder.display); + eprintln!("driver image formats: [{}]", readback.offered()); + eprintln!("surface {surface:#x}: picture {display:?} in a {coded:?} pool"); + eprintln!( + "derived layout: {}", + readback.describe(&decoder.display, surface) + ); + // SAFETY: a live display and a surface from its own pool. + let status = unsafe { (decoder.display.va.sync_surface)(decoder.display.display, surface) }; + assert_eq!(status, VA_STATUS_SUCCESS, "vaSyncSurface"); + for route in [Route::Derive, Route::GetImage] { + match readback.read_route(route, &decoder.display, surface, display, coded, fourcc) { + Ok(bytes) => eprintln!(" {route:?}: {} bytes", bytes.len()), + Err(e) => eprintln!(" {route:?}: NO — {e}"), + } + } + readback.cross_check( + &decoder.display, + surface, + display, + coded, + fourcc, + "readback probe", + ); + assert!( + readback.derived > 0 || readback.fetched > 0, + "neither readback route works on this device — parity is impossible here, \ + and saying so is the point of this probe" + ); + readback.destroy_staging(&decoder.display); + } + + /// **The counterfactual on hardware**: the readback reads real, distinct pixels, and + /// the comparison the legs use catches a frame that is wrong by one byte. + /// + /// Three tests in this program have been found asserting nothing, so the parity legs + /// owe a proof that they can fail. This is it, driven through the same [`Readback`], + /// the same [`sha256_hex`], the same [`localise`] and the same [`compare`] the legs + /// use: + /// + /// * a readback that returned zeros, or the same surface every time, would make + /// every hash equal — so two different pictures must hash differently; + /// * a readback that returned a constant would still have the right LENGTH — so the + /// picture must not be one repeated byte; + /// * and one flipped byte must be caught, and LOCALISED to the pixel. + #[test] + #[ignore = "needs a machine with a libva runtime and an H.264 VLD entry point"] + fn the_readback_reads_real_pixels_and_the_comparison_can_fail() { + let aus = split_h264_aus(H264_25FPS); + let goldens = golden_hashes(GOLDENS_H264); + let mut decoder = NativeVaapiDecoder::new(pf_vaadec::Codec::H264, StreamFormat::SDR_420_8) + .expect("this box is supposed to have a VAAPI H.264 decode entry point"); + let mut readback = Readback::new(&decoder.display); + + let mut frames: Vec> = Vec::new(); + for (index, au) in aus.iter().take(20).enumerate() { + let frame = decoder.decode(au).expect("the clean vector decodes"); + let Some(frame) = frame else { continue }; + let what = format!("counterfactual AU {index}"); + let bytes = read_frame(&decoder, &mut readback, &frame, &what); + assert!( + bytes.iter().any(|b| *b != bytes[0]), + "{what}: the readback handed back {} identical bytes — that is an \ + unwritten or unmapped surface, not a picture", + bytes.len() + ); + frames.push(bytes); + } + readback.destroy_staging(&decoder.display); + assert!( + frames.len() >= 2, + "the first twenty access units must deliver at least two pictures" + ); + + let hashes: Vec = frames.iter().map(|b| sha256_hex(b)).collect(); + let distinct: std::collections::HashSet<&String> = hashes.iter().collect(); + assert!( + distinct.len() > 1, + "{} decoded pictures produced ONE hash — the readback is reading the same \ + surface, or the same bytes, every time", + hashes.len() + ); + let here: Vec<&str> = goldens[..hashes.len()].to_vec(); + assert_eq!( + compare(&hashes, &here, "counterfactual"), + (0, None), + "the first {} display frames must already agree with libavcodec, or this \ + test is measuring a defect rather than its own falsifiability", + hashes.len() + ); + + // Now the corruption. One luma byte in the LAST frame checked, and the + // comparison must name that frame and only that frame. + let victim = hashes.len() - 1; + let mut corrupted = frames[victim].clone(); + // The luma sample at the dead centre of this vector's 320x240 picture, so the + // bounding box is a statement about a PIXEL rather than about the first byte of + // the buffer or the edge of a plane. + let at = 120 * 320 + 160; + corrupted[at] ^= 0x01; + let diff = localise( + &corrupted, + &frames[victim], + (320, 240), + pf_vaadec::VA_FOURCC_NV12, + ); + assert_eq!( + diff.luma_samples, 1, + "one flipped luma byte, one differing sample" + ); + assert_eq!(diff.chroma_samples, 0, "chroma must read CLEAN"); + assert_eq!(diff.max_delta, 1); + assert_eq!( + diff.luma_box, + Some((160, 120, 160, 120)), + "the divergence must be localised to the pixel that was flipped" + ); + + let mut dirty = hashes.clone(); + dirty[victim] = sha256_hex(&corrupted); + assert_eq!( + compare(&dirty, &here, "counterfactual"), + (1, Some(victim)), + "the comparison every leg uses must catch a one-byte corruption, and name \ + which display frame carries it" + ); + } + + // ----------------------------------------------------------------------- + // CPU guards — NOT `#[ignore]`d, so ordinary CI notices drift + // ----------------------------------------------------------------------- + + /// The invariant the module docs rest on, asserted mechanically: **the surface + /// readback's libva entry points are resolved only inside this test module.** + /// + /// Not a stylistic preference. A per-frame `vaMapBuffer` on the production video + /// path would be exactly the copy zero-copy exists to avoid, and this project has a + /// standing rule against it. Reading this file's own source is what turns "we were + /// careful" into something a refactor cannot quietly undo: the production [`Libva`] + /// must resolve none of these, and this module must resolve all of them. + /// + /// Doc comments elsewhere in the file name the same calls in prose; the scan looks + /// for the QUOTED symbol strings a `dlsym` needs, which prose never contains. + #[test] + fn the_readback_entry_points_are_resolved_only_inside_this_module() { + const SOURCE: &str = include_str!("video_vaapi_native.rs"); + const MARKER: &str = "mod parity {"; + let at = SOURCE + .find(MARKER) + .expect("this module's own header is in this module's own file"); + let (production, harness) = SOURCE.split_at(at); + for symbol in [ + "\"vaDeriveImage\"", + "\"vaCreateImage\"", + "\"vaGetImage\"", + "\"vaDestroyImage\"", + "\"vaMapBuffer\"", + "\"vaUnmapBuffer\"", + "\"vaQueryImageFormats\"", + "\"vaMaxNumImageFormats\"", + ] { + assert!( + !production.contains(symbol), + "{symbol} is resolved OUTSIDE the `#[cfg(test)] mod parity` block. The \ + surface readback is a test-only facility: a shipped build must not be \ + able to map a decode surface at all, which is what keeps the zero-copy \ + guarantee structural rather than a promise" + ); + assert!( + harness.contains(symbol), + "{symbol} is no longer resolved by the parity harness — if the readback \ + moved, this guard has to move with it or it protects nothing" + ); + } + } + + /// The planners' display orders match the golden sets, on every one of the seven + /// streams these legs decode — checked on CPU so a regenerated vector or a change in + /// the bumping process fails here rather than as a mysterious hardware failure on a + /// machine somebody had to walk to. + #[test] + fn every_golden_set_matches_its_planners_display_order() { + for (label, order, goldens) in [ + ( + "H.264", + order_h264(&split_h264_aus(H264_25FPS)), + golden_hashes(GOLDENS_H264), + ), + ( + "H.264 low-delay", + order_h264(&split_h264_aus(LOWDELAY_H264)), + golden_hashes(GOLDENS_LOWDELAY_H264), + ), + ( + "H.265", + order_h265(&split_h265_aus(H265_25FPS)), + golden_hashes(GOLDENS_H265), + ), + ( + "H.265 low-delay", + order_h265(&split_h265_aus(LOWDELAY_H265)), + golden_hashes(GOLDENS_LOWDELAY_H265), + ), + ( + "HEVC Main 10", + order_h265(&split_h265_aus(MAIN10_H265)), + golden_hashes(GOLDENS_MAIN10), + ), + ( + "AV1", + order_av1(&split_ivf(AV1_25FPS), DISPLAY_AV1), + golden_hashes(GOLDENS_AV1), + ), + ( + "AV1 low-delay 4K", + order_av1(&split_ivf(LOWDELAY_AV1), DISPLAY_LOWDELAY_AV1), + golden_hashes(GOLDENS_LOWDELAY_AV1), + ), + ] { + assert_eq!( + order.display.len(), + goldens.len(), + "{label}: the planner outputs {} pictures and the golden file carries {}", + order.display.len(), + goldens.len() + ); + assert!( + order.decode.len() >= order.display.len(), + "{label}: a picture cannot be displayed without being decoded" + ); + for id in &order.display { + assert!( + order.decode.contains(id), + "{label}: display order names PicId {id}, which nothing decodes — the \ + hardware legs would fail on this with a message about the rung" + ); + } + assert_eq!( + goldens.len(), + goldens + .iter() + .collect::>() + .len(), + "{label}: two display frames carry the SAME golden hash. That is not \ + impossible in principle, but on these vectors it would mean the golden \ + file was generated from a stream that repeated a frame — and a parity \ + leg cannot tell a correctly repeated frame from a rung that delivered \ + one picture twice" + ); + } + } + + /// The counts the AV1 legs assert are what the PLANNER implies, and the two AV1 + /// streams really are the opposite shapes their constants claim. + #[test] + fn the_two_av1_streams_are_the_opposite_shapes_the_legs_claim() { + let vendored = order_av1(&split_ivf(AV1_25FPS), DISPLAY_AV1); + assert_eq!(vendored.per_unit.len(), AV1_UNIT_COUNT); + assert_eq!(vendored.decode.len(), AV1_DECODED_COUNT); + assert_eq!(vendored.display.len(), AV1_SHOWN_COUNT); + assert_eq!( + vendored.per_unit.iter().filter(|u| u.len() > 1).count(), + AV1_DECODED_COUNT - AV1_SHOWN_COUNT, + "24 units must carry a hidden frame as well as the shown one — without them \ + the AV1 leg proves nothing the H.264 leg does not already prove" + ); + + let ours = order_av1(&split_ivf(LOWDELAY_AV1), DISPLAY_LOWDELAY_AV1); + assert_eq!(ours.per_unit.len(), LOWDELAY_AV1_UNIT_COUNT); + assert_eq!(ours.decode.len(), LOWDELAY_AV1_DECODED_COUNT); + assert_eq!(ours.display.len(), LOWDELAY_AV1_SHOWN_COUNT); + assert!( + ours.per_unit.iter().all(|u| u.len() == 1), + "our host emits one frame per temporal unit and no hidden frames — the \ + OPPOSITE shape to the vendored vector, which is why both legs exist" + ); + } + + /// Both vendored H.26x vectors REORDER, and our own streams do not. + /// + /// The first half is why the legs need `flush` and why delivery order is a claim + /// worth checking at all; the second is why our fixtures represent what punktfunk + /// actually streams. Asserted so neither claim can go stale. + #[test] + fn the_vendored_vectors_reorder_and_our_own_streams_do_not() { + for (label, order) in [ + ("H.264", order_h264(&split_h264_aus(H264_25FPS))), + ("H.265", order_h265(&split_h265_aus(H265_25FPS))), + ] { + assert_ne!( + order.decode, order.display, + "{label}: this vector no longer reorders — the tail `flush` drains would \ + then be empty and these legs would stop covering the reordering path" + ); + } + for (label, order) in [ + ( + "H.264 low-delay", + order_h264(&split_h264_aus(LOWDELAY_H264)), + ), + ( + "H.265 low-delay", + order_h265(&split_h265_aus(LOWDELAY_H265)), + ), + ] { + assert_eq!( + order.decode, order.display, + "{label}: our host emits zero-reorder output, so decode order IS display \ + order — if that stops being true these fixtures no longer represent \ + what punktfunk streams" + ); + } + } + + /// **The counterfactual, on CPU**: the comparison every leg's verdict rests on + /// catches a wrong frame and names it. + /// + /// Runs on macOS and in the container with no device, so the falsifiability of the + /// parity legs is checked by ordinary CI rather than only on the one box that has a + /// VAAPI driver. + #[test] + fn the_comparison_catches_a_corrupted_frame() { + let goldens = ["aa", "bb", "cc"]; + let clean: Vec = goldens.iter().map(|g| (*g).to_string()).collect(); + assert_eq!( + compare(&clean, &goldens, "cpu"), + (0, None), + "an agreeing set must report no divergence" + ); + + let mut one = clean.clone(); + one[1] = "beef".to_string(); + assert_eq!( + compare(&one, &goldens, "cpu"), + (1, Some(1)), + "one wrong frame must be reported once, at its DISPLAY index" + ); + + let mut two = one.clone(); + two[0] = "dead".to_string(); + assert_eq!( + compare(&two, &goldens, "cpu"), + (2, Some(0)), + "the first divergence must be the FIRST one, not the last seen" + ); + } + + /// [`localise`] separates the plane, the region and the magnitude — the three things + /// a hash cannot say and the three that located the last two defects. + #[test] + fn a_divergence_names_the_plane_the_box_and_the_magnitude() { + let (w, h) = (320u32, 240u32); + let clean = vec![0x40u8; (w * h + w * h / 2) as usize]; + + // One luma block, chroma clean — the NVIDIA signature. + let mut one_block = clean.clone(); + for y in 24..48u32 { + for x in 16..32u32 { + one_block[(y * w + x) as usize] = 0x48; + } + } + let d = localise(&one_block, &clean, (w, h), pf_vaadec::VA_FOURCC_NV12); + assert_eq!(d.luma_samples, 16 * 24); + assert_eq!(d.chroma_samples, 0); + assert_eq!(d.luma_box, Some((16, 24, 31, 47))); + assert_eq!(d.max_delta, 8); + assert!(format!("{d}").contains("chroma CLEAN")); + assert!(format!("{d}").contains("16x24")); + + // Chroma too, and badly — the Intel signature. + let structural = vec![0xffu8; clean.len()]; + let d = localise(&structural, &clean, (w, h), pf_vaadec::VA_FOURCC_NV12); + assert_eq!(d.luma_samples, (w * h) as usize); + assert_eq!(d.chroma_samples, (w * h / 2) as usize); + assert_eq!(d.max_delta, 0xff - 0x40); + assert!(!format!("{d}").contains("chroma CLEAN")); + + assert_eq!( + localise(&clean, &clean, (w, h), pf_vaadec::VA_FOURCC_NV12).luma_samples, + 0 + ); + assert_eq!( + format!( + "{}", + localise(&clean, &clean, (w, h), pf_vaadec::VA_FOURCC_NV12) + ), + "identical" + ); + } + + /// Ten-bit samples read as LSB-aligned are NAMED as a format problem rather than + /// reported as a decode divergence. + /// + /// The trap the Main 10 golden's header warns about, and the one thing about that + /// leg a reader would otherwise have to re-derive from 50 wrong hashes: P010 puts + /// the ten bits in the HIGH end of each 16-bit word, so a driver handing back + /// `yuv420p10le` produces a buffer of exactly the right LENGTH and entirely the + /// wrong content. + #[test] + fn lsb_aligned_ten_bit_samples_are_called_out_as_a_format_problem() { + let (w, h) = (16u32, 16u32); + let samples = (w * h + w * h / 2) as usize; + // MSB-aligned: 0x0200 is 8 << 6, and its low six bits are clear. + let msb: Vec = (0..samples).flat_map(|_| 0x0200u16.to_le_bytes()).collect(); + // LSB-aligned: the same ten-bit value, 8, unshifted. + let lsb: Vec = (0..samples).flat_map(|_| 0x0008u16.to_le_bytes()).collect(); + + let d = localise(&lsb, &msb, (w, h), pf_vaadec::VA_FOURCC_P010); + assert_eq!(d.low_bits_set, samples, "every sample carries low bits"); + assert!( + format!("{d}").contains("low six bits"), + "the report must point at the FORMAT: {d}" + ); + + let d = localise(&msb, &msb, (w, h), pf_vaadec::VA_FOURCC_P010); + assert_eq!(d.low_bits_set, 0); + assert_eq!(format!("{d}"), "identical"); + } +} diff --git a/crates/pf-vaadec/layout-probe.c b/crates/pf-vaadec/layout-probe.c index c09b975ad..a46dea789 100644 --- a/crates/pf-vaadec/layout-probe.c +++ b/crates/pf-vaadec/layout-probe.c @@ -505,5 +505,53 @@ int main(void) { S(VAConfigAttrib); O(VAConfigAttrib, type); O(VAConfigAttrib, value); + + /* + * The IMAGE pair — `vaDeriveImage` / `vaCreateImage` + `vaGetImage` write these, + * and they are the only way anything reads a decoded VAAPI surface back on the + * CPU. `VAImage` is the awkward one of the whole file: `width` and `height` are + * `unsigned short`, so the four-byte fields around them are NOT where counting + * 32-bit words would put them, and `component_order` is four `char` rather than a + * padded word. Both are measured here rather than reasoned about. + * + * ⚠ The readback these describe is TEST-ONLY (see `video_vaapi_native`'s `parity` + * module). The production path exports a DRM-PRIME dmabuf and never maps a + * surface; the structures are declared for the same reason every other structure + * in this file is, so a parity harness can be written without a libva build + * dependency. + */ + S(VAImageFormat); + O(VAImageFormat, fourcc); + O(VAImageFormat, byte_order); + O(VAImageFormat, bits_per_pixel); + O(VAImageFormat, depth); + O(VAImageFormat, red_mask); + O(VAImageFormat, green_mask); + O(VAImageFormat, blue_mask); + O(VAImageFormat, alpha_mask); + O(VAImageFormat, va_reserved); + + S(VAImage); + O(VAImage, image_id); + O(VAImage, format); + O(VAImage, buf); + O(VAImage, width); + O(VAImage, height); + O(VAImage, data_size); + O(VAImage, num_planes); + O(VAImage, pitches); + O(VAImage, offsets); + O(VAImage, num_palette_entries); + O(VAImage, entry_bytes); + O(VAImage, component_order); + O(VAImage, va_reserved); + printf("count VAImage pitches %zu\n", + sizeof(((VAImage *)0)->pitches) / sizeof(((VAImage *)0)->pitches[0])); + printf("count VAImage offsets %zu\n", + sizeof(((VAImage *)0)->offsets) / sizeof(((VAImage *)0)->offsets[0])); + printf("count VAImage component_order %zu\n", + sizeof(((VAImage *)0)->component_order)); + printf("enum VA_LSB_FIRST %d\n", VA_LSB_FIRST); + printf("enum VA_MSB_FIRST %d\n", VA_MSB_FIRST); return 0; } diff --git a/crates/pf-vaadec/src/lib.rs b/crates/pf-vaadec/src/lib.rs index 10e424b51..539797d07 100644 --- a/crates/pf-vaadec/src/lib.rs +++ b/crates/pf-vaadec/src/lib.rs @@ -23,10 +23,18 @@ //! everything decidable without a device — including [`drm`], the export //! descriptor the driver writes back and the plane walk that reads it. //! -//! ⚠ **Nothing here has decoded a frame.** The rung is pin-only -//! (`PUNKTFUNK_DECODER=native-vaapi`) and no VAAPI hardware has been reachable -//! during M7, so everything below is a CPU-side conversion checked against -//! libavcodec and against measured layouts, not against a picture. +//! **Every conversion in this crate has now been checked in PIXELS.** On 2026-08-08, +//! on `.25` (Radeon 780M, RDNA3, radeonsi, Mesa 26.0.3, VA-API 1.23), +//! `pf-client-core`'s `video_vaapi_native::parity` decoded seven streams through the +//! rung and hashed every delivered frame against libavcodec's software decode — the +//! same golden files the Vulkan and D3D11VA rungs are held to — and all seven came +//! back bit-identical: 250 + 120 H.264, 250 + 120 H.265, 50 HEVC Main 10 (P010), and +//! 250 + 60 AV1. That was possible at all because [`va::pack_two_plane`] and the +//! `VAImage` pair below give a TEST-ONLY readback of a decoded surface; nothing on the +//! production path maps one, and that module's docs say how it is kept that way. +//! +//! ⚠ ONE vendor. AMD/radeonsi only — Intel's iHD driver has neither run these legs nor +//! been asked to. //! //! Five things this crate settled that a reader would otherwise have to re-derive: //! @@ -150,3 +158,19 @@ pub use va::VaIqMatrixBufferH264; pub use va::VaPictureH264; pub use va::VaPictureParameterBufferH264; pub use va::VaSliceParameterBufferH264; + +// The CPU-readable view of a decoded surface, and the pure walk that packs one into +// the layout this program's goldens hash. +// +// ⚠ TEST-ONLY. Nothing on the production video path maps a surface — the rung exports +// a DRM-PRIME dmabuf and the presenter samples it, which is the zero-copy contract — +// so the only caller is `pf-client-core`'s `video_vaapi_native::parity`, which exists +// solely under `#[cfg(test)]`. These are declared here so that harness needs no +// `libva-dev` and so its geometry can be checked with no device at all (`va`'s module +// docs say why at length). +pub use va::pack_two_plane; +pub use va::packed_len; +pub use va::ImageReadError; +pub use va::VaImage; +pub use va::VaImageFormat; +pub use va::VA_LSB_FIRST; diff --git a/crates/pf-vaadec/src/va.rs b/crates/pf-vaadec/src/va.rs index 6de946cae..cdb250bd3 100644 --- a/crates/pf-vaadec/src/va.rs +++ b/crates/pf-vaadec/src/va.rs @@ -1,4 +1,5 @@ -//! The libva decode buffer layouts for H.264, **hand-declared**. +//! The libva decode buffer layouts for H.264, **hand-declared** — plus the +//! codec-independent `VAImage` pair the test-only surface readback needs. //! //! There is no libva binding in this workspace and this crate deliberately does not //! introduce one: it must compile and be tested on macOS and in the Linux container, @@ -41,6 +42,24 @@ //! This crate never invents one: the conversion (`plan_to_va`) takes the caller's //! slot → `VASurfaceID` table and indexes it, so the Linux layer owns surface //! allocation and this half stays pure. +//! +//! # The image half, and why it is here at all +//! +//! [`VaImage`] and [`VaImageFormat`] are not decode buffers: they are what +//! `vaDeriveImage` (or `vaCreateImage` + `vaGetImage`) writes back when something +//! wants to READ a decoded surface on the CPU. Nothing on the production video path +//! does — the rung exports a DRM-PRIME dmabuf and the presenter samples it, which is +//! the zero-copy contract this project refuses to spend — so the only caller is the +//! frame-hash parity harness in `pf-client-core`'s `video_vaapi_native::parity`, which +//! exists solely under `#[cfg(test)]`. +//! +//! They live here for the same reason every other structure in this file does: the +//! harness must not force a `libva-dev` build dependency on a crate that compiles on +//! macOS and in the container. Declaring them costs nothing at runtime (nothing +//! constructs one outside a test) and lets the readback's geometry — the part that has +//! already cost this program a release, in the shape of a chroma plane read at the +//! DISPLAY height instead of the driver's reported offset — be unit-tested with no +//! device at all. That walk is [`pack_two_plane`]. /// `VA_INVALID_SURFACE` — what an unused `ReferenceFrames` / `RefPicList` entry /// carries. Paired with [`VA_PICTURE_H264_INVALID`]; drivers key on the flag, but a @@ -389,6 +408,346 @@ const _: () = { assert!(offset_of!(VaSliceParameterBufferH264, va_reserved) == 3112); }; +// --------------------------------------------------------------------------- +// The image pair — a CPU-readable view of a decoded surface (module docs). +// +// ⚠ TEST-ONLY BY CONSTRUCTION. Nothing on the production video path maps a surface; +// these types exist so a parity harness can, without this crate growing a libva +// build dependency. `pack_two_plane` below is pure and is the only logic here. +// --------------------------------------------------------------------------- + +/// `VA_LSB_FIRST` — the byte order every YUV format libva describes uses. Named +/// because [`VaImageFormat`] carries the field and a zero there is not a "left unset", +/// it is an invalid enumerator. +pub const VA_LSB_FIRST: u32 = 1; +/// `VA_MSB_FIRST` — measured beside it so the pair reads as an enumeration rather +/// than as one magic number. +pub const VA_MSB_FIRST: u32 = 2; + +/// `VAImageFormat` — what a `VAImage` is in, and what `vaCreateImage` is asked for. +/// +/// The RGB fields are dead weight for this crate's two formats (NV12 and P010) and +/// are declared anyway: they occupy bytes 12..32 and dropping them would shift +/// `va_reserved`, which is exactly the class of mistake the assertions below exist +/// to make a compile error. +#[repr(C)] +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +pub struct VaImageFormat { + pub fourcc: u32, + /// [`VA_LSB_FIRST`] or [`VA_MSB_FIRST`]. + pub byte_order: u32, + pub bits_per_pixel: u32, + /// RGB only. + pub depth: u32, + pub red_mask: u32, + pub green_mask: u32, + pub blue_mask: u32, + pub alpha_mask: u32, + /// `va_reserved[VA_PADDING_LOW]` — "must be zero". + pub va_reserved: [u32; 4], +} + +/// `VAImage` — the descriptor `vaDeriveImage` / `vaCreateImage` fills in. +/// +/// ⚠ `width` and `height` are **`unsigned short`**, not `unsigned int`. That is the +/// one thing about this structure a reader would get wrong by counting 32-bit words: +/// every field after them sits two bytes earlier than the obvious arithmetic puts it, +/// which is why `data_size` is at 60 and not 64. Measured, not reasoned about. +/// +/// `pitches` and `offsets` are per PLANE and are the driver's own — the chroma plane +/// begins at `offsets[1]`, which on a decode surface is nowhere near +/// `pitches[0] * display_height` because the surface is padded to the codec's granule. +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct VaImage { + /// `VAImageID`, and what `vaGetImage` / `vaDestroyImage` are handed. + pub image_id: u32, + pub format: VaImageFormat, + /// `VABufferID` — the buffer `vaMapBuffer` returns the pixels of. + pub buf: u32, + pub width: u16, + pub height: u16, + /// The whole mapped extent, in bytes. Everything [`pack_two_plane`] reads is + /// bounds-checked against it. + pub data_size: u32, + pub num_planes: u32, + pub pitches: [u32; 3], + pub offsets: [u32; 3], + /// Palette fields, meaningless for YUV and declared for their bytes. + pub num_palette_entries: i32, + pub entry_bytes: i32, + pub component_order: [i8; 4], + /// `va_reserved[VA_PADDING_LOW]`. + pub va_reserved: [u32; 4], +} + +impl VaImage { + /// An all-zero descriptor — what a caller hands `vaDeriveImage` to fill. + /// + /// Zero rather than uninitialised on purpose: a failed derive leaves a descriptor + /// the caller still reads — to decide whether there is an image to destroy, and to + /// report what the driver DID hand back — and reading uninitialised bytes to do + /// that is undefined behaviour rather than a diagnostic. + pub const fn zeroed() -> VaImage { + VaImage { + image_id: 0, + format: VaImageFormat { + fourcc: 0, + byte_order: 0, + bits_per_pixel: 0, + depth: 0, + red_mask: 0, + green_mask: 0, + blue_mask: 0, + alpha_mask: 0, + va_reserved: [0; 4], + }, + buf: 0, + width: 0, + height: 0, + data_size: 0, + num_planes: 0, + pitches: [0; 3], + offsets: [0; 3], + num_palette_entries: 0, + entry_bytes: 0, + component_order: [0; 4], + va_reserved: [0; 4], + } + } +} + +/// Why a mapped image could not be read as the picture it was supposed to hold. +/// +/// Every arm carries what the DRIVER said rather than a verdict, because the whole +/// point of this walk refusing instead of guessing is that the refusal names the +/// thing that has to be looked at next. A harness that quietly produced a short or +/// mis-strided buffer would compare hashes of garbage against libavcodec's and report +/// a decode defect that is not there. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ImageReadError { + /// The caller asked for a format this walk does not describe. Only the two + /// two-plane YUV formats the surface pool is ever created with are supported. + UnsupportedFourcc { fourcc: u32 }, + /// The image came back in a different format from the surface pool's — a driver + /// that substituted, which is precisely the "derive handed you something you + /// cannot interpret" case. + Fourcc { got: u32, want: u32 }, + /// Fewer than two planes: a packed or opaque layout, not NV12/P010. + NotTwoPlane { planes: u32 }, + /// The image is smaller than the region asked for. + TooSmall { + image: (u32, u32), + display: (u32, u32), + }, + /// A row of the picture does not fit the plane's own pitch. + Pitch { + plane: usize, + pitch: u32, + need: usize, + }, + /// A row would be read past the end of the mapped buffer. + OutOfBounds { + plane: usize, + row: u32, + at: usize, + end: usize, + mapped: usize, + }, +} + +impl std::fmt::Display for ImageReadError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ImageReadError::UnsupportedFourcc { fourcc } => { + write!(f, "no two-plane layout for fourcc {}", fourcc_name(*fourcc)) + } + ImageReadError::Fourcc { got, want } => write!( + f, + "the image is {} but the surface pool is {}", + fourcc_name(*got), + fourcc_name(*want) + ), + ImageReadError::NotTwoPlane { planes } => { + write!(f, "the image has {planes} plane(s), not two") + } + ImageReadError::TooSmall { image, display } => write!( + f, + "the image is {}x{} but the picture is {}x{}", + image.0, image.1, display.0, display.1 + ), + ImageReadError::Pitch { plane, pitch, need } => write!( + f, + "plane {plane}'s pitch is {pitch} bytes, a row needs {need}" + ), + ImageReadError::OutOfBounds { + plane, + row, + at, + end, + mapped, + } => write!( + f, + "plane {plane} row {row} spans {at}..{end} of a {mapped}-byte mapping" + ), + } + } +} + +impl std::error::Error for ImageReadError {} + +/// A fourcc as its four characters, for a message a human can act on. +fn fourcc_name(fourcc: u32) -> String { + let bytes = fourcc.to_le_bytes(); + match std::str::from_utf8(&bytes) { + Ok(s) if bytes.iter().all(|b| b.is_ascii_graphic()) => s.to_string(), + _ => format!("{fourcc:#010x}"), + } +} + +/// How many bytes one tightly packed `display`-sized picture of `fourcc` occupies — +/// the layout every golden set in this program hashes. +/// +/// `None` for a fourcc with no two-plane 4:2:0 layout here. +pub fn packed_len(display: (u32, u32), fourcc: u32) -> Option { + let bytes_per_sample = bytes_per_sample(fourcc)?; + let (w, h) = (display.0 as usize, display.1 as usize); + Some(w * bytes_per_sample * (h + h.div_ceil(2))) +} + +/// One luma sample's size in bytes for the two formats the pool is ever built with. +fn bytes_per_sample(fourcc: u32) -> Option { + match fourcc { + crate::drm::VA_FOURCC_NV12 => Some(1), + // ⚠ P010 is 16 bits per sample with the ten meaningful bits in the HIGH end + // of each little-endian word. This walk moves bytes and never touches the + // alignment; a driver that handed back LSB-aligned samples would produce a + // buffer of exactly the right SIZE and the wrong content, which is a + // divergence the goldens catch and this function cannot. + crate::drm::VA_FOURCC_P010 => Some(2), + _ => None, + } +} + +/// Read the `display`-sized picture out of a mapped `VAImage`, packed tightly — byte +/// for byte the layout `pf-vkdecode`'s golden files hash. +/// +/// This is the whole of the readback that can be wrong without a device, so it is the +/// whole of what is worth testing without one. Three things it does deliberately: +/// +/// * **The chroma plane starts at `offsets[1]`**, the driver's own number, never at +/// `pitches[0] * height`. A decode surface is padded to the codec's granule — 240 +/// lines of HEVC live in a 256-line surface — so computing the offset from the +/// display height reads the tail of the luma padding as chroma and smears every +/// row. This project has already paid for that once on another rung. +/// * **Padding columns are dropped per row.** `pitches[0]` is the surface's stride, +/// which is wider than the picture; only `width * bytes_per_sample` bytes of each +/// row belong to the golden. +/// * **Every read is bounds-checked against the mapping the driver declared**, and a +/// failure is returned rather than clamped. A short mapping means the descriptor +/// and the buffer disagree, and no hash taken from it means anything. +/// +/// `mapped` must be the buffer `vaMapBuffer` returned, of length +/// [`VaImage::data_size`]; the caller passes it as a slice so this function needs no +/// `unsafe` and can be driven from a plain array in a test. +pub fn pack_two_plane( + image: &VaImage, + mapped: &[u8], + display: (u32, u32), + fourcc: u32, +) -> Result, ImageReadError> { + let bytes_per_sample = + bytes_per_sample(fourcc).ok_or(ImageReadError::UnsupportedFourcc { fourcc })?; + if image.format.fourcc != fourcc { + return Err(ImageReadError::Fourcc { + got: image.format.fourcc, + want: fourcc, + }); + } + if image.num_planes < 2 { + return Err(ImageReadError::NotTwoPlane { + planes: image.num_planes, + }); + } + let (width, height) = display; + if u32::from(image.width) < width || u32::from(image.height) < height { + return Err(ImageReadError::TooSmall { + image: (u32::from(image.width), u32::from(image.height)), + display, + }); + } + // One row of the picture, in both planes: 4:2:0 chroma is half the rows but + // interleaved (U,V) pairs, so a chroma row carries exactly as many BYTES as a + // luma row. + let row_bytes = width as usize * bytes_per_sample; + let rows = [height, height.div_ceil(2)]; + let mut out = Vec::with_capacity(row_bytes * (rows[0] + rows[1]) as usize); + for (plane, plane_rows) in rows.iter().enumerate() { + let pitch = image.pitches[plane] as usize; + if pitch < row_bytes { + return Err(ImageReadError::Pitch { + plane, + pitch: image.pitches[plane], + need: row_bytes, + }); + } + let base = image.offsets[plane] as usize; + for row in 0..*plane_rows { + let at = base + row as usize * pitch; + let end = at + row_bytes; + if end > mapped.len() { + return Err(ImageReadError::OutOfBounds { + plane, + row, + at, + end, + mapped: mapped.len(), + }); + } + out.extend_from_slice(&mapped[at..end]); + } + } + Ok(out) +} + +// --------------------------------------------------------------------------- +// Image layout proofs — the probe's output, pinned (libva 2.23.0-1ubuntu1, +// x86_64-linux-gnu, measured 2026-08-07 by `layout-probe.c`). +// --------------------------------------------------------------------------- + +const _: () = { + use std::mem::offset_of; + use std::mem::size_of; + + assert!(size_of::() == 48); + assert!(offset_of!(VaImageFormat, fourcc) == 0); + assert!(offset_of!(VaImageFormat, byte_order) == 4); + assert!(offset_of!(VaImageFormat, bits_per_pixel) == 8); + assert!(offset_of!(VaImageFormat, depth) == 12); + assert!(offset_of!(VaImageFormat, red_mask) == 16); + assert!(offset_of!(VaImageFormat, green_mask) == 20); + assert!(offset_of!(VaImageFormat, blue_mask) == 24); + assert!(offset_of!(VaImageFormat, alpha_mask) == 28); + assert!(offset_of!(VaImageFormat, va_reserved) == 32); + + // ⚠ `width`/`height` are 16-bit, which is why `data_size` is at 60 rather than + // at the 64 that counting 32-bit fields would give. + assert!(size_of::() == 120); + assert!(offset_of!(VaImage, image_id) == 0); + assert!(offset_of!(VaImage, format) == 4); + assert!(offset_of!(VaImage, buf) == 52); + assert!(offset_of!(VaImage, width) == 56); + assert!(offset_of!(VaImage, height) == 58); + assert!(offset_of!(VaImage, data_size) == 60); + assert!(offset_of!(VaImage, num_planes) == 64); + assert!(offset_of!(VaImage, pitches) == 68); + assert!(offset_of!(VaImage, offsets) == 80); + assert!(offset_of!(VaImage, num_palette_entries) == 92); + assert!(offset_of!(VaImage, entry_bytes) == 96); + assert!(offset_of!(VaImage, component_order) == 100); + assert!(offset_of!(VaImage, va_reserved) == 104); +}; + #[cfg(test)] mod tests { use super::*; @@ -585,4 +944,241 @@ mod tests { .all(|e| e.picture_id == VA_INVALID_SURFACE)); assert_eq!(s.slice_data_flag, VA_SLICE_DATA_FLAG_ALL); } + + // ----------------------------------------------------------------------- + // The image walk. Every one of these runs on macOS and in the container: the + // geometry is the half of a surface readback that can be wrong without a + // device, and it is the half that has been wrong before. + // ----------------------------------------------------------------------- + + /// A driver-shaped `VAImage`: a surface PADDED past the picture in both axes, + /// with the chroma plane where the driver puts it rather than where the display + /// height would. + // `_picture` is named at every call site so each test reads as the shape it is + // about, and is deliberately not consulted: the walk takes the picture size from + // its own argument, which is the whole point of the crop. + fn padded_image( + _picture: (u16, u16), + surface: (u16, u16), + pitch: u32, + fourcc: u32, + ) -> (VaImage, Vec) { + let mut image = VaImage::zeroed(); + image.format.fourcc = fourcc; + image.format.byte_order = VA_LSB_FIRST; + image.width = surface.0; + image.height = surface.1; + image.num_planes = 2; + image.pitches = [pitch, pitch, 0]; + // The trap, expressed: chroma starts after the WHOLE padded luma plane. + image.offsets = [0, pitch * u32::from(surface.1), 0]; + let total = pitch as usize * (surface.1 as usize + surface.1.div_ceil(2) as usize); + image.data_size = total as u32; + // Fill the mapping so every byte says where it came from: luma rows count + // 0.., chroma rows 128.., and the padding columns are 0xff so a walk that + // read them would produce something unmistakable. + let mut mapped = vec![0xffu8; total]; + for y in 0..surface.1 as usize { + for x in 0..pitch as usize { + mapped[y * pitch as usize + x] = if x < surface.0 as usize { + (y % 100) as u8 + } else { + 0xff + }; + } + } + let chroma = image.offsets[1] as usize; + for y in 0..surface.1.div_ceil(2) as usize { + for x in 0..pitch as usize { + mapped[chroma + y * pitch as usize + x] = if x < surface.0 as usize { + 128 + (y % 100) as u8 + } else { + 0xff + }; + } + } + (image, mapped) + } + + #[test] + fn the_walk_crops_to_the_picture_and_takes_chroma_from_the_drivers_offset() { + // 320x240 picture in a 320x256 surface at a 384-byte pitch — HEVC's 128-line + // granule and a stride that is not the width, which is the everyday shape. + let (image, mapped) = padded_image((320, 240), (320, 256), 384, crate::drm::VA_FOURCC_NV12); + let out = pack_two_plane(&image, &mapped, (320, 240), crate::drm::VA_FOURCC_NV12) + .expect("the walk must read a padded NV12 surface"); + assert_eq!(out.len(), 320 * 240 + 320 * 120); + assert_eq!( + out.len(), + packed_len((320, 240), crate::drm::VA_FOURCC_NV12).unwrap() + ); + // No padding byte reached the output: 0xff is only ever a padding column. + assert!( + !out.contains(&0xff), + "a padding column leaked into the packed picture" + ); + // Luma row 3 is all 3s; chroma row 3 is all 131 — which is only true if the + // chroma plane was taken from offsets[1] and not from pitch * 240. + assert!(out[3 * 320..4 * 320].iter().all(|&b| b == 3)); + let chroma = 320 * 240; + assert!(out[chroma + 3 * 320..chroma + 4 * 320] + .iter() + .all(|&b| b == 131)); + } + + #[test] + fn reading_chroma_at_the_display_height_would_have_been_caught() { + // The counterfactual for the assertion above: an image that claims chroma + // starts at `pitch * display_height` — the 1088-row smear — hands back + // LUMA padding rows where chroma belongs, and the walk cannot tell. So the + // guarantee is that the walk uses the DRIVER's offset, and this proves the + // two answers actually differ on the shape the drivers hand out (they would + // coincide on an unpadded surface, which is why the test above uses one that + // is padded in BOTH axes). + let (mut image, mapped) = + padded_image((320, 240), (320, 256), 384, crate::drm::VA_FOURCC_NV12); + let right = pack_two_plane(&image, &mapped, (320, 240), crate::drm::VA_FOURCC_NV12) + .expect("the driver's own offset reads"); + image.offsets[1] = 384 * 240; + let wrong = pack_two_plane(&image, &mapped, (320, 240), crate::drm::VA_FOURCC_NV12) + .expect("the wrong offset also reads — that is the point"); + assert_ne!( + right, wrong, + "chroma at the display height must differ from chroma at the driver's \ + offset, or this walk's central claim is untestable" + ); + } + + #[test] + fn ten_bit_rows_are_twice_as_wide() { + // P010's samples are 16 bits, so a 320-sample row is 640 bytes and the packed + // picture is exactly twice an NV12 one. A walk that assumed one byte per + // sample would produce a half-width picture of the right total length for + // some other resolution, which is the kind of thing a length check alone + // misses. + let (image, mapped) = padded_image((320, 240), (320, 256), 768, crate::drm::VA_FOURCC_P010); + let out = pack_two_plane(&image, &mapped, (320, 240), crate::drm::VA_FOURCC_P010) + .expect("the walk must read a padded P010 surface"); + assert_eq!(out.len(), 320 * 2 * 240 + 320 * 2 * 120); + assert_eq!( + out.len(), + packed_len((320, 240), crate::drm::VA_FOURCC_P010).unwrap() + ); + assert_eq!( + out.len(), + 2 * packed_len((320, 240), crate::drm::VA_FOURCC_NV12).unwrap() + ); + } + + #[test] + fn an_odd_height_keeps_its_half_chroma_row() { + let (image, mapped) = padded_image((16, 9), (16, 16), 32, crate::drm::VA_FOURCC_NV12); + let out = pack_two_plane(&image, &mapped, (16, 9), crate::drm::VA_FOURCC_NV12) + .expect("an odd height still reads"); + assert_eq!(out.len(), 16 * 9 + 16 * 5, "9 luma rows, 5 chroma rows"); + } + + #[test] + fn a_substituted_format_is_refused_rather_than_reinterpreted() { + let (mut image, mapped) = + padded_image((320, 240), (320, 256), 384, crate::drm::VA_FOURCC_NV12); + image.format.fourcc = crate::drm::VA_FOURCC_P010; + assert_eq!( + pack_two_plane(&image, &mapped, (320, 240), crate::drm::VA_FOURCC_NV12), + Err(ImageReadError::Fourcc { + got: crate::drm::VA_FOURCC_P010, + want: crate::drm::VA_FOURCC_NV12 + }) + ); + } + + #[test] + fn a_packed_or_opaque_image_is_refused() { + let (mut image, mapped) = + padded_image((320, 240), (320, 256), 384, crate::drm::VA_FOURCC_NV12); + image.num_planes = 1; + assert_eq!( + pack_two_plane(&image, &mapped, (320, 240), crate::drm::VA_FOURCC_NV12), + Err(ImageReadError::NotTwoPlane { planes: 1 }) + ); + } + + #[test] + fn an_image_smaller_than_the_picture_is_refused() { + let (image, mapped) = padded_image((320, 240), (320, 240), 384, crate::drm::VA_FOURCC_NV12); + assert_eq!( + pack_two_plane(&image, &mapped, (321, 240), crate::drm::VA_FOURCC_NV12), + Err(ImageReadError::TooSmall { + image: (320, 240), + display: (321, 240) + }) + ); + } + + #[test] + fn a_pitch_narrower_than_a_row_is_refused() { + let (mut image, mapped) = + padded_image((320, 240), (320, 256), 384, crate::drm::VA_FOURCC_NV12); + image.pitches[1] = 16; + assert_eq!( + pack_two_plane(&image, &mapped, (320, 240), crate::drm::VA_FOURCC_NV12), + Err(ImageReadError::Pitch { + plane: 1, + pitch: 16, + need: 320 + }) + ); + } + + #[test] + fn a_mapping_shorter_than_the_descriptor_claims_is_refused_not_truncated() { + // The failure mode that matters most: a short read must NOT silently produce + // a shorter picture, because its hash would then be a hash of something the + // decoder never wrote. + let (image, mapped) = padded_image((320, 240), (320, 256), 384, crate::drm::VA_FOURCC_NV12); + // One byte short of the LAST chroma row the picture needs. Cutting the tail + // of the allocation would not do it: the surface is padded past the picture, + // so there is slack after the last row this walk reads — which is itself + // worth pinning, since it is why a `data_size` check alone would not catch a + // driver whose offsets point outside its buffer. + let last_row_end = image.offsets[1] as usize + 119 * image.pitches[1] as usize + 320; + assert!( + last_row_end < mapped.len(), + "the padded surface must have slack after the picture's last chroma row" + ); + let err = pack_two_plane( + &image, + &mapped[..last_row_end - 1], + (320, 240), + crate::drm::VA_FOURCC_NV12, + ) + .expect_err("a short mapping must be refused"); + assert!( + matches!( + err, + ImageReadError::OutOfBounds { + plane: 1, + row: 119, + .. + } + ), + "expected the last chroma row to be refused, got {err}" + ); + } + + #[test] + fn an_unknown_fourcc_has_no_packed_length_and_no_walk() { + assert_eq!( + packed_len((320, 240), 0x3132_3449), + None, + "I421 is not ours" + ); + let (image, mapped) = padded_image((320, 240), (320, 256), 384, crate::drm::VA_FOURCC_NV12); + assert_eq!( + pack_two_plane(&image, &mapped, (320, 240), 0x3132_3449), + Err(ImageReadError::UnsupportedFourcc { + fourcc: 0x3132_3449 + }) + ); + } } From 2b167595aa51e138cecfb0f422b890fe98ff9239 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Sat, 8 Aug 2026 01:00:06 +0200 Subject: [PATCH 21/21] =?UTF-8?q?docs(client):=20the=20VAAPI=20rung=20has?= =?UTF-8?q?=20parity=20now=20=E2=80=94=20say=20what=20is=20actually=20left?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Its rows still read "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". That readback now exists, and all SEVEN legs came back bit-identical to libavcodec on RDNA3: vendored H.264 250/250, our host's low-delay H.264 120/120, vendored H.265 250/250, host low-delay H.265 120/120, HEVC Main 10 50/50 as P010, vendored AV1 250/250 of 274 decoded, and host low-delay 4K two-tile AV1 60/60. The two arms collapse into one, because the thing that split them — AV1 having evidence the other legs lacked — is gone. Every leg now has the same evidence. It stays `verified = false`, and the note says why in the words the unproven-rung test requires: it has NEVER run on a second vendor and has never been soaked. That is a real limit rather than a formality — every other verified pair in this table earned it on more than one part, and the D3D11VA AV1 row two entries up is a rung that passed on one vendor's driver while failing on another's. The second reason is not about evidence at all, and it belongs in the record rather than in a commit nobody reads later: flipping this flag is a ROUTING change. `native_rung_admitted` is `verified || !below.verified`, so a verified VAAPI outranks Vulkan Video on every Linux AMD and Intel client — the Steam Deck included. The parity result justifies that change; it should still be made on purpose, by someone who wants it, rather than arriving as a side effect of writing down a test result. --- crates/pf-client-core/src/video.rs | 36 +++++++++++++++++------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/crates/pf-client-core/src/video.rs b/crates/pf-client-core/src/video.rs index 1afe5e2e1..8326fe183 100644 --- a/crates/pf-client-core/src/video.rs +++ b/crates/pf-client-core/src/video.rs @@ -51,8 +51,7 @@ //! | 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), re-confirmed 250/250 (+ 50/50 Main 10) on an RTX 3500 Ada and an Intel Arc on 2026-08-07 | //! | native D3D11VA | | AV1 | **yes** — 250/250 delivered frames bit-identical to libavcodec on an RTX 3500 Ada AND an Intel Arc (2026-08-07). It got there from 186/250 and 245/250 DIVERGING frames on those same two GPUs: `plan_to_dxva_av1` released the picture this frame's own refresh displaces before assigning the decode target its slot, and `SlotMap::assign` hands back the slot just vacated — so 268 of the vector's 274 frames named one surface as both `CurrPicTextureIndex` and a `RefFrameMapTextureIndex` entry. Intel followed the aliased surface (structurally wrong from display frame 4); NVIDIA tolerated it until the `order_hint` wrap at 64 made one 16x24 luma block depend on it. ONE defect, two driver tolerances — the two unlike signatures were not two bugs. TWO vendors, still NO soak on the goldens: the 5-minute 4K60 soak this row used to cite measured throughput, and "streams cleanly" was true throughout the failure | -//! | 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 (Main / Main 10) | **not proven** — but all three have now DECODED, on `.25` (Radeon 780M, RDNA3, radeonsi, Mesa 26.0.3, VA-API 1.23) on 2026-08-07: every access unit of the vendored H.264 (250), H.265 (250) and HEVC Main 10 (50) vectors was accepted with no decode error, **250/250/50 frames delivered**, NV12 for the 8-bit legs and **P010** for Main 10, all on the same tiled AMD modifier, and `probe_this_machines_libva` reports VLD decode for H.264 High, HEVC Main and HEVC Main 10. The counts were 225/204/45 when these legs were first written, and that was the RUNG rather than the driver: `finish` showed `outputs.last()` and retired the rest unshown, and nothing flushed the DPB at end of stream. Both are fixed — `video_vaapi_native` grew a bounded deliverable queue and a flush, and the keyframe flag now travels with the PICTURE rather than with the access unit that displays it (the pump's post-loss re-anchor signal, mislabelled on every one of these legs' first frames). Never frame-hash parity-checked, for the same reason as AV1: the rung exports a tiled dmabuf with no CPU-readable image | +//! | native VAAPI | [`crate::video_vaapi_native`] | H.264, H.265 (Main / Main 10), AV1 | **frame-hash parity-proven, ON ONE VENDOR** — 7 legs bit-identical to libavcodec on `.25` (Radeon 780M, RDNA3, radeonsi, Mesa 26.0.3, VA-API 1.23) on 2026-08-08: vendored H.264 250/250, our host's low-delay H.264 120/120, vendored H.265 250/250, host low-delay H.265 120/120, HEVC Main 10 50/50 (P010), vendored AV1 250/250 of 274 decoded, host low-delay 4K two-tile AV1 60/60. The readback that made it possible is `vaDeriveImage` with a `vaCreateImage`+`vaGetImage` fallback, and it is `#[cfg(test)]`-only by construction — the production [`video_vaapi_native::Libva`] gains no entry point and a CPU test scans this crate's own source to keep it that way. **Not `verified`, and the reason is no longer parity**: one vendor and no soak. Flipping it moves `auto` on Linux AMD/Intel from Vulkan Video to VAAPI ([`native_rung_admitted`]) — an evidence-backed change, but a routing change, so it is made on purpose or not at all | //! | 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 @@ -1157,22 +1156,27 @@ pub fn native_evidence(rung: NativeRung, wire: u8) -> RungEvidence { // them and must not be printed. The arm stays split only because AV1's note carries // its own frame count; both halves say the same thing about parity. // - // 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)", - ), + // 2026-08-08: parity finally exists here. A `#[cfg(test)]` readback + // (`vaDeriveImage`, falling back to `vaCreateImage` + `vaGetImage`) hashes the + // decoded surface, and all SEVEN legs came back bit-identical to libavcodec — the + // conformance vectors and our own host's low-delay streams, H.264 through AV1. + // + // So the old reason for `false` is gone, and the arm is no longer split: every leg + // has the same evidence. What is left is narrower and worth stating exactly, because + // the D3D11VA AV1 row above is a rung that decoded 250 frames and produced wrong + // pixels for every one of them — parity is what separated them, and this rung now + // has it. It stays `false` on ONE VENDOR and NO SOAK, and because flipping it is a + // routing change, not a bookkeeping one: `native_rung_admitted` would then let `auto` + // pick VAAPI ahead of Vulkan Video on every Linux AMD and Intel client, the Steam + // Deck included. That is defensible on this evidence and should be done deliberately, + // not as a side effect of recording a parity result. (NativeRung::Vaapi, _) => ( false, - "decoded every access unit of the vendored H.264, H.265 and HEVC Main 10 \ - vectors on RDNA3 (Mesa 26.0.3, 2026-08-07) - their first frames on any \ - hardware - but has never been frame-hash parity-checked: the rung exports \ - a tiled dmabuf with no CPU-readable image (M6/M7)", + "7 legs bit-identical to libavcodec on RDNA3 (Mesa 26.0.3, 2026-08-08) - H.264, \ + H.265, HEVC Main 10 and AV1, on both the conformance vectors and our own host's \ + low-delay streams - but has NEVER run on a second vendor and has never been \ + soaked, and `verified` here would move `auto` off Vulkan Video on every Linux \ + AMD/Intel client (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

b!oPcn+%N8Dd2&_2+S08sWahPmGuA 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 |SYC{0L(KI=;Db%My3G>K~4 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%0AT=btRmgzL7JhrfV_<-qP-VrZ!sFG)PCjV<_5 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 hsM)K9Xyt Date: Fri, 7 Aug 2026 22:19:12 +0200 Subject: [PATCH 14/21] =?UTF-8?q?fix(dxvadec):=20the=20review=20round=20?= =?UTF-8?q?=E2=80=94=20a=20doc=20that=20had=20become=20false,=20a=20warn-s?= =?UTF-8?q?torm=20on=20renegotiation,=20and=20HEVC's=20exemption=20made=20?= =?UTF-8?q?falsifiable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four findings, all real. **`SlotMap`'s own docs had become false.** "feed it every `DpbUpdate` in decode order (via `Self::apply` or `plan_to_vk`, which applies internally)" — `plan_to_vk` no longer applies internally, which is the entire point of the change, and `release`'s docs named it as one of the two things that may free a slot. A reader following those docs would build the next caller wrong in exactly the way this commit's parent fixed. Both now say which conversions defer, which one does not, and why H.265 is the one that does not. **The deferred release warned on a legitimate event.** `release_deferred` warned per id when a deferred release found no slot — but a renegotiation replaces the whole `Session`, and with it the slot map, INSIDE `plan`, while the planner's own drain reports every drained picture in that same access unit's `removed`. Every one of those ids then misses, and nothing is wrong. `debug!`, with the legitimate cause named so the illegitimate one stays diagnosable. **HEVC's exemption was asserted only in its consequence.** `the_current_picture_is_ named_by_curr_pic_and_never_aliases_a_reference` checked that no reference shares the decode target's slot — which on the vendored vector holds whether or not the reasoning behind it does. That is precisely how the H.264 leg passed for two milestones. The test now also asserts the PLANNER property the exemption rests on (`removed ∩ dpb_refs = ∅`, falsified by moving `dpb_snapshot()` above `decode_rps`), and records that the low-delay measurement was 0 of 300 against H.264's 297 of 300 from the same host and the same run. It also records what is still missing: no low-delay HEVC stream is vendored, so HEVC's freedom is a re-derivable argument plus one measurement, not a standing hardware leg. **Two stale cross-references.** Both AV1 conversions told the reader the H.264/H.265 zero was "measured on reordering vectors and not a proof" — the open question this commit's parent closed. They now say what the answer was. --- .../pf-client-core/src/video_d3d11_native.rs | 14 +++-- crates/pf-dxvadec/src/pic_av1.rs | 14 +++-- crates/pf-dxvadec/src/pic_h265.rs | 51 +++++++++++++++++++ crates/pf-vkdecode/src/pic_av1.rs | 15 +++--- crates/pf-vkdecode/src/slots.rs | 19 +++++-- 5 files changed, 95 insertions(+), 18 deletions(-) diff --git a/crates/pf-client-core/src/video_d3d11_native.rs b/crates/pf-client-core/src/video_d3d11_native.rs index 6829aa93d..5323860c0 100644 --- a/crates/pf-client-core/src/video_d3d11_native.rs +++ b/crates/pf-client-core/src/video_d3d11_native.rs @@ -519,11 +519,15 @@ impl NativeD3d11Decoder { }; for &id in &sub.release_after_decode { if !session.slots.release(id) { - // Never fatal, and never silent: a deferred id that holds no slot - // means the conversion and the ledger disagree about the DPB, which - // is a bug in one of them rather than a stream this AU can do - // anything about. - tracing::warn!(id, "a deferred release named a picture holding no surface"); + // Never fatal, and never silent — but `debug!` rather than `warn!`, + // because there is a LEGITIMATE way to get here: a renegotiation + // replaces the whole `Session` (and with it the slot map) inside + // `plan`, while the planner's own drain reports every drained picture + // in the same AU's `removed`. Those ids belong to the map that no + // longer exists, so every one of them misses and nothing is wrong. + // Outside a rebuild it means the conversion and the ledger disagree + // about the DPB, which the surrounding rebuild log makes separable. + tracing::debug!(id, "a deferred release named a picture holding no surface"); } } } diff --git a/crates/pf-dxvadec/src/pic_av1.rs b/crates/pf-dxvadec/src/pic_av1.rs index 6cfe6e0b9..d8ce47c88 100644 --- a/crates/pf-dxvadec/src/pic_av1.rs +++ b/crates/pf-dxvadec/src/pic_av1.rs @@ -139,10 +139,16 @@ pub struct DecodePlanDxvaAv1 { /// breath: decode into the surface you are predicting from. /// /// Neither vendored H.264 nor H.265 vector ever produces that shape (measured: - /// zero on the 250-AU clips — ⚠ but see `plan_to_dxva`'s note, because that is a - /// measurement of two REORDERING vectors and not a proof about those codecs), - /// which is why the eager release survived two hardware-proven codecs and opened - /// on the first AV1 frame past the key frame's neighbourhood. The Vulkan rung + /// zero on the 250-AU clips), which is why the eager release survived two + /// codecs believed hardware-proven and opened on the first AV1 frame past the key + /// frame's neighbourhood. + /// + /// ⚠ That zero turned out to be a fact about the VECTORS. H.264 has the identical + /// defect on any low-delay stream — 117 of 120 access units of our own host's + /// output, wrong pixels on three GPUs — and now carries the identical deferral + /// ([`crate::pic::DecodePlanDxva::release_after_decode`], which records the + /// measurement). H.265 is the only one of the three that is genuinely safe, and + /// structurally: its planner snapshots `dpb_refs` after `decode_rps`. The Vulkan rung /// carries the same contract for the same reason /// (`pf_vkdecode::pic_av1::DecodePlanVkAv1::release_after_decode`), and this /// rung's constraint is the STRICTER of the two: Vulkan binds only the references diff --git a/crates/pf-dxvadec/src/pic_h265.rs b/crates/pf-dxvadec/src/pic_h265.rs index dbb322934..1c59d8050 100644 --- a/crates/pf-dxvadec/src/pic_h265.rs +++ b/crates/pf-dxvadec/src/pic_h265.rs @@ -916,8 +916,36 @@ mod tests { } } + /// HEVC's freedom from the aliasing that cost AV1 and H.264 a deferral, and the + /// PLANNER property that grants it. + /// + /// Both other codecs release a removed picture's slot and then let + /// [`SlotMap::assign`] hand it straight back to the decode target, so `CurrPic` + /// and a `RefPicList` entry name one surface. This conversion still releases its + /// whole `removed` list inline, and is safe doing so for one reason: `H265Planner` + /// snapshots `dpb_refs` AFTER `decode_rps` has updated the DPB, so a picture this + /// AU's RPS dropped is never in the set `RefPicList` is built from, and nothing + /// later in the AU unmarks anything. `H264Planner` and `Av1Planner` both snapshot + /// BEFORE their marking, and both needed the deferral. + /// + /// The second assertion is that argument made falsifiable. The first is only the + /// consequence, and on this vector the consequence would hold even if the argument + /// stopped being true — the vendored H.264 vector taught that lesson expensively + /// (it measured zero aliasing for two milestones while every stream we ship + /// aliased on 99% of its frames). Moving `dpb_snapshot()` above `decode_rps` would + /// leave the first assertion passing and break the second on the first AU whose + /// RPS drops a picture, which on this vector is most of them. + /// + /// ⚠ No low-delay HEVC stream is vendored, so unlike H.264 this is not backed by a + /// hardware leg on our own encoder's output. It was MEASURED once, 2026-08-07, on + /// a 300-picture 1080p low-delay HEVC stream from a punktfunk host: 0 access units + /// with a `removed ∩ dpb_refs` intersection, against 297 of 300 for H.264 from the + /// same host and the same run. Vendoring that stream is the way to make this a + /// standing guarantee rather than a re-derivable argument. #[test] fn the_current_picture_is_named_by_curr_pic_and_never_aliases_a_reference() { + let mut aus_with_removals = 0usize; + let mut both = 0usize; for (plan, dxva) in convert_stream(TEST_25FPS) { assert_eq!(dxva.pic_params.CurrPic.index(), dxva.setup_slot); assert!(!dxva.pic_params.CurrPic.associated()); @@ -928,7 +956,30 @@ mod tests { for r in &dxva.refs { assert_ne!(r.slot, dxva.setup_slot, "a reference aliases the target"); } + if !plan.dpb.removed.is_empty() { + aus_with_removals += 1; + } + both += plan + .dpb + .removed + .iter() + .filter(|id| plan.dpb_refs.iter().any(|r| r.id == **id)) + .count(); } + assert!( + aus_with_removals > 0, + "no AU of this vector removed anything, so the zero below would be empty \ + for a reason that has nothing to do with the property being asserted" + ); + assert_eq!( + both, 0, + "{both} picture(s) are in an AU's own reference set AND removed by it. \ + That is the H.264/AV1 aliasing precondition, and HEVC is supposed to be \ + structurally incapable of it — so the snapshot in `H265Planner` has moved \ + ahead of `decode_rps`. Restore the ordering, or give this conversion the \ + `release_after_decode` deferral the other two carry; do NOT relax this \ + number" + ); } #[test] diff --git a/crates/pf-vkdecode/src/pic_av1.rs b/crates/pf-vkdecode/src/pic_av1.rs index 28da7f115..6a4bd9ab5 100644 --- a/crates/pf-vkdecode/src/pic_av1.rs +++ b/crates/pf-vkdecode/src/pic_av1.rs @@ -834,12 +834,15 @@ mod tests { /// AV1 applies `refresh_frame_flags` after decoding (7.20), so `ref_frame_idx` /// resolves against the store as it stood BEFORE the frame. Cycling eight slots /// in a low-delay stream therefore means almost every frame displaces something - /// it is reading: **268 of this vector's 274 frames**, first at frame 6. The - /// H.264 and H.265 planners can produce the same shape — `plan_to_vk`'s own - /// docs name the sliding window evicting a picture the slices reference — but - /// neither vendored vector ever does it (measured: zero on the 250-AU H.264 - /// clip), which is why the hole survived two hardware-proven codecs and opened - /// on the first AV1 frame that was not a key frame's neighbour. + /// it is reading: **268 of this vector's 274 frames**, first at frame 6. + /// + /// The H.264 planner produces the same shape and the vendored vector never does + /// it (measured: zero on the 250-AU clip), which is why the hole survived a codec + /// believed hardware-proven and opened here first. That zero was a fact about the + /// vector: on a low-delay host stream H.264 aliases on 117 of 120 access units, + /// and `plan_to_vk` now carries the same deferral + /// ([`crate::pic::DecodePlanVk::release_after_decode`]). H.265 does not need one — + /// `H265Planner` snapshots `dpb_refs` after `decode_rps`. #[test] fn a_reference_this_frame_displaces_keeps_its_slot_until_after_the_decode() { let mut planner = Av1Planner::new(); diff --git a/crates/pf-vkdecode/src/slots.rs b/crates/pf-vkdecode/src/slots.rs index d7f1de213..34b730d4a 100644 --- a/crates/pf-vkdecode/src/slots.rs +++ b/crates/pf-vkdecode/src/slots.rs @@ -44,7 +44,19 @@ impl std::fmt::Display for SlotError { impl std::error::Error for SlotError {} /// The slot ledger. One per decode session; feed it every [`DpbUpdate`] in decode -/// order (via [`Self::apply`] or `plan_to_vk`, which applies internally). +/// order. +/// +/// [`Self::apply`] does that whole-update. `plan_to_vk` and `plan_to_vk_av1` do it in +/// two halves instead: they ASSIGN the stored picture and hand the removals back as a +/// `release_after_decode` list for the caller to apply once the decode op is issued. +/// The split is not a convenience — releasing a removal before the assignment lets +/// [`Self::assign`] return the slot this AU's own submission still names, which is a +/// picture decoding into one it predicts from. A caller that drops the list leaks a +/// slot per AU. +/// +/// `plan_to_vk_h265` still applies its removals internally, and that is safe rather +/// than lucky: `H265Planner` snapshots `dpb_refs` AFTER `decode_rps`, so a picture +/// this AU's RPS dropped is never in the set its reference lists are built from. /// /// Invariants (unit-tested): /// - a [`PicId`] keeps its slot from [`Self::assign`] until [`Self::release`]; @@ -134,8 +146,9 @@ impl SlotMap { /// as the planner's DPB holds the picture — as a reference OR as a decoded /// picture awaiting output — and that residency ends only when a /// [`DpbUpdate::removed`] entry reports it. This method is that report's - /// primitive: `plan_to_vk` and [`Self::apply`] call it with the planner's - /// `removed` ids and nothing else may release a slot. + /// primitive: [`Self::apply`] calls it with the planner's `removed` ids, and so + /// do the conversions' callers via `release_after_decode` — one AU's removals, + /// deferred until its decode op is issued. Nothing else may release a slot. /// /// Releasing is CPU-side bookkeeping (the slot becomes assignable to a later /// picture); keeping the released slot's IMAGE out of reuse until in-flight From f0702f3e0668a3b09ad9cd09505e0b038cf1fe6a Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 7 Aug 2026 22:53:22 +0200 Subject: [PATCH 15/21] feat(vkdecode): HEVC's exemption stops being an argument and becomes a vendored stream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `fd6241a2` made HEVC's freedom from the release-ordering defect falsifiable on CPU and recorded what was still missing: no low-delay HEVC stream was vendored, so the exemption rested on a structural argument plus one throwaway measurement. This vendors the stream, and the exemption HELD. **The fixture.** `lowdelay-640x480.h265`, 270 KB, 120 pictures — `punktfunk-host spike --source synthetic --codec h265 --width 640 --height 480 --fps 60 --seconds 2 --bitrate 1` on .21 (NVENC, RTX 5070 Ti, driver 610.57.04). Deliberately the H.264 sibling's resolution and frame count: the two are then directly comparable, 640 and 480 are both multiples of MinCbSizeY so there is no conformance window and a hash mismatch can only be decode rather than readback geometry, and 270 KB sits alongside the 282 KB already accepted for H.264. Goldens are libavcodec's software decode, cross-checked BIT-IDENTICAL across ffmpeg n8.1.2 (Arch, x86_64) and 8.1.1 (Homebrew, macOS arm64), 120 of 120 digests distinct. **The exemption held, measured rather than argued.** `sps_max_dec_pic_buffering_minus1 = 4` against the four pictures 8.3.2 keeps marked in steady state, `sps_max_num_reorder_pics = 0`, `numRefL0 = 1` — a five-picture DPB filled exactly by four references plus the current picture. 115 of the 120 access units retire a picture, and `removed ∩ dpb_refs` is **0 of 120**. A 300-picture 1080p stream from the same host reports the same shape: 295 retirements, 0 intersections. It is the encoder and not the resolution, exactly as for H.264. **A zero proves nothing on its own, so the fixture is pinned by its counterfactual.** `test-25fps.h264` reported zero for two milestones while every stream we ship aliased on 99% of its frames. So the guarantee here is not "we looked and it was fine": hand `plan_to_dxva_h265` the marked DPB as it stood BEFORE `decode_rps` — the mutation a snapshot move would cause, reconstructed exactly as `dpb_refs(N-1) ∪ {stored(N-1)}` — and the alias appears on **115 of 120** access units, driven through the real conversion rather than through planner arithmetic. If a regeneration ever produced a stream that reordered, or a DPB deeper than its reference count, that 115 collapses to 0 and the tests say so instead of continuing to pass. **The two rungs are exempt for different reasons, and the asymmetry is now a gate.** DXVA binds the whole marked DPB — `RefPicList` is spec-defined that way, and an RFI long-term anchor has to survive in it — so its exemption really is `H265Planner`'s snapshot ordering, one call away from being untrue. `plan_to_vk_h265` never reads `dpb_refs` at all: `pReferenceSlots` is the slots the operation uses, so it binds the current RPS sets, which `decode_rps` itself derives and which therefore cannot name a picture that same RPS just dropped. A new test feeds that conversion the identical widened snapshot and asserts nothing changes, so a future change making the Vulkan rung bind the marked DPB — a legitimate thing to want, since a *Foll* anchor invisible to the hardware is the RFI failure shape — fails loudly instead of silently acquiring the defect. What the Vulkan pixel leg adds is therefore NOT aliasing coverage, and its docs say so: it is the first HEVC frame either rung has decoded from our own encoder, under a DPB that retires and reissues a slot on 115 of 120 access units back to back, where the vendored vector's reordering keeps that eviction slack. Legs: `low_delay_host_h265_every_frame_hashes_bit_identical_to_libavcodec` on the Vulkan rung (10 ignored legs now, up from 9) and on the D3D11VA rung, plus three non-ignored CPU guards that run in ordinary CI. Verified: 10/10 Vulkan parity legs on .21 (RTX 5070 Ti, 610.57.04), the new one 120/120 bit-identical; workspace clippy `-D warnings` and `cargo fmt --all --check` clean on .21. --- .../pf-client-core/src/video_d3d11_native.rs | 61 +++++ crates/pf-dxvadec/src/pic_h265.rs | 218 +++++++++++++++- crates/pf-vkdecode/src/pic_h265.rs | 89 +++++++ .../data/lowdelay-640x480-h265.nv12.sha256 | 165 ++++++++++++ .../tests/data/lowdelay-640x480.h265 | Bin 0 -> 276657 bytes crates/pf-vkdecode/tests/gpu_parity.rs | 244 ++++++++++++++++++ 6 files changed, 771 insertions(+), 6 deletions(-) create mode 100644 crates/pf-vkdecode/tests/data/lowdelay-640x480-h265.nv12.sha256 create mode 100644 crates/pf-vkdecode/tests/data/lowdelay-640x480.h265 diff --git a/crates/pf-client-core/src/video_d3d11_native.rs b/crates/pf-client-core/src/video_d3d11_native.rs index 5323860c0..26fcd45bd 100644 --- a/crates/pf-client-core/src/video_d3d11_native.rs +++ b/crates/pf-client-core/src/video_d3d11_native.rs @@ -33,6 +33,15 @@ //! ([`pf_dxvadec::DecodePlanDxva::release_after_decode`]), and the stream is now //! vendored so `low_delay_host_h264_every_frame_hashes_bit_identical_to_libavcodec` //! holds the rung to what it streams rather than only to what it conforms to. +//! +//! HEVC is EXEMPT from that defect, and since 2026-08-07 that is a measurement rather +//! than an argument: `H265Planner` snapshots `dpb_refs` after `decode_rps`, so an +//! RPS-dropped picture never reaches `RefPicList`, and a vendored low-delay HEVC +//! stream from the same host confirms it — 115 of its 120 access units retire a +//! picture, 0 alias, and all 115 WOULD alias if the snapshot moved one call earlier. +//! `low_delay_host_h265_every_frame_hashes_bit_identical_to_libavcodec` is the pixel +//! leg; pf-dxvadec's `pic_h265` tests pin the numbers and drive the counterfactual +//! through the conversion. //! * **AV1** — wired in M7, and frame-hash parity on the SAME two GPUs since 2026-08-07: //! 250/250 delivered frames bit-identical to libavcodec on the RTX 3500 Ada and on the //! Intel Arc. It streams 4K60 on both with a clean 5-minute soak, but that is throughput @@ -1767,6 +1776,32 @@ mod parity { const GOLDENS_H265: &str = include_str!("../../pf-vkdecode/tests/data/test-25fps-h265.nv12.sha256"); + /// **Our own host's low-delay HEVC** and its goldens — the H.265 twin of + /// [`LOWDELAY_H264`], vendored for the opposite reason. + /// + /// The H.264 stream is here because this rung was WRONG and only that shape could + /// show it. This one is here because HEVC is believed RIGHT — `H265Planner` + /// snapshots `dpb_refs` after `decode_rps`, so an RPS-dropped picture is never in + /// the marked set `RefPicList` is built from, and `plan_to_dxva_h265` is the one + /// conversion of the three that still releases inline. 120 pictures of 640x480 + /// IPPP, `sps_max_num_reorder_pics = 0`, a five-picture DPB against four marked + /// references: 115 of the 120 access units retire a picture, `removed ∩ dpb_refs` + /// is 0 of 120, and all 115 would alias under the other snapshot ordering + /// (pf-dxvadec's `pic_h265` tests pin every one of those numbers, and drive the + /// counterfactual through the conversion itself). + /// + /// Provenance, the `punktfunk-host spike` command and the ffmpeg cross-check are in + /// the golden file's header. + const LOWDELAY_H265: &[u8] = + include_bytes!("../../pf-vkdecode/tests/data/lowdelay-640x480.h265"); + const GOLDENS_LOWDELAY_H265: &str = + include_str!("../../pf-vkdecode/tests/data/lowdelay-640x480-h265.nv12.sha256"); + + /// The HEVC low-delay stream's frame count. A separate constant from + /// [`LOWDELAY_FRAME_COUNT`] on purpose: two files, two encoder runs, and one + /// regenerated at another length must fail on its own leg. + const LOWDELAY_H265_FRAME_COUNT: usize = 120; + /// Both vendored vectors are 250 display frames. const FRAME_COUNT: usize = 250; @@ -2655,6 +2690,32 @@ mod parity { ); } + /// The HEVC twin of the low-delay H.264 leg — and the one that keeps HEVC's + /// exemption from the release-ordering defect a standing hardware fact. + /// + /// `h265_every_frame_hashes_bit_identical_to_libavcodec` decodes a vector that + /// REORDERS, so it never puts an RPS drop and the eviction it causes in one access + /// unit and cannot see this class at all. This stream does, on 115 of its 120 + /// access units — see [`LOWDELAY_H265`]. If a refactor ever moved `H265Planner`'s + /// snapshot ahead of `decode_rps` (where the other two planners take theirs), this + /// rung would name one surface as both `CurrPic` and a `RefPicList` entry on all + /// 115, and this leg is what would say so in pixels. + #[test] + #[ignore = "needs a Windows D3D11 video device (see module docs)"] + fn low_delay_host_h265_every_frame_hashes_bit_identical_to_libavcodec() { + let aus = split_h265_aus(LOWDELAY_H265); + let order = order_h265(&aus); + parity_run( + Codec::H265, + StreamFormat::SDR_420_8, + &aus, + &order, + &golden_hashes(GOLDENS_LOWDELAY_H265), + LOWDELAY_H265_FRAME_COUNT, + "H.265 (low-delay host stream)", + ); + } + /// The ten-bit path, which no golden set in this program covered until now. /// /// The HDR legs proved a Main10 session BUILDS and streams clean, which is a diff --git a/crates/pf-dxvadec/src/pic_h265.rs b/crates/pf-dxvadec/src/pic_h265.rs index 1c59d8050..7041c376a 100644 --- a/crates/pf-dxvadec/src/pic_h265.rs +++ b/crates/pf-dxvadec/src/pic_h265.rs @@ -690,6 +690,18 @@ mod tests { "../../pf-bitstream/vendor/cros-codecs/src/codec/h265/test_data/64x64-I-P-B-P.h265" ); + /// **Our own host's HEVC**, and the only stream in this repository that reaches + /// the DPB pressure HEVC's exemption is claimed against: low-delay IPPP, 120 + /// pictures of 640x480, `sps_max_num_reorder_pics = 0`, and a five-picture DPB + /// against the four pictures 8.3.2 keeps marked. + /// + /// Vendored beside the goldens the GPU legs decode it against (that file's header + /// carries the `punktfunk-host spike` command and the ffmpeg cross-check), the + /// same way `lowdelay-640x480.h264` is, and read from the same path by all three + /// crates that need it. + const LOWDELAY_640X480_H265: &[u8] = + include_bytes!("../../pf-vkdecode/tests/data/lowdelay-640x480.h265"); + /// Test-only AU splitter, mirroring pf-vkdecode's (which mirrors /// pf-bitstream's `#[cfg(test)]`-private helper). fn split_into_aus(stream: &[u8]) -> Vec<&[u8]> { @@ -936,12 +948,12 @@ mod tests { /// leave the first assertion passing and break the second on the first AU whose /// RPS drops a picture, which on this vector is most of them. /// - /// ⚠ No low-delay HEVC stream is vendored, so unlike H.264 this is not backed by a - /// hardware leg on our own encoder's output. It was MEASURED once, 2026-08-07, on - /// a 300-picture 1080p low-delay HEVC stream from a punktfunk host: 0 access units - /// with a `removed ∩ dpb_refs` intersection, against 297 of 300 for H.264 from the - /// same host and the same run. Vendoring that stream is the way to make this a - /// standing guarantee rather than a re-derivable argument. + /// This test covers the VENDORED VECTOR only, which reorders and therefore cannot + /// reach the DPB pressure the exemption is really claimed against. The stream that + /// can is [`LOWDELAY_640X480_H265`], and it carries its own pair of tests below — + /// [`the_low_delay_stream_reaches_the_dpb_pressure_and_hevc_still_does_not_alias`] + /// and [`the_low_delay_stream_would_alias_if_the_snapshot_moved_ahead_of_the_rps`], + /// the second of which drives the alias through this very conversion. #[test] fn the_current_picture_is_named_by_curr_pic_and_never_aliases_a_reference() { let mut aus_with_removals = 0usize; @@ -982,6 +994,200 @@ mod tests { ); } + /// The marked DPB as an access unit's `decode_rps` FINDS it — the set + /// `dpb_snapshot()` would return from the other side of that call. + /// + /// Exact, not approximate. `H265Planner::begin_picture` runs `decode_rps` → + /// `update_dpb_before_decoding` → `dpb_snapshot`, and the only thing between AU + /// N-1's snapshot and AU N's `decode_rps` is `finish_picture(N-1)` storing its + /// picture marked "used for short-term reference". So the pre-RPS marked set is + /// exactly `dpb_refs(N-1) ∪ {stored(N-1)}` — no DPB replay needed, and no + /// dependence on the planner internals staying reachable from a test. + /// + /// A sub-layer non-reference picture is stored but NOT marked, so it is excluded; + /// the callers assert their streams contain none, which keeps the reconstruction + /// honest rather than merely defensive. + fn pre_rps_marked(prev: Option<&AuPlan>) -> Vec { + let Some(prev) = prev else { + return Vec::new(); + }; + let mut marked = prev.dpb_refs.clone(); + if let Some(id) = prev.dpb.stored { + if prev.picture.is_reference { + marked.push(RefPic { + id, + pic_order_cnt: prev.picture.pic_order_cnt, + is_long_term: false, + }); + } + } + marked + } + + /// The exemption, measured on the stream that can actually falsify it. + /// + /// [`the_current_picture_is_named_by_curr_pic_and_never_aliases_a_reference`] runs + /// over `test-25fps.h265`, which REORDERS — a picture the RPS drops stays alive for + /// output past the access unit that dropped it, so the eviction and the unmarking + /// never land together and the vector cannot reach the precondition however hard it + /// is run. That is the same blindness that let the H.264 defect survive two + /// milestones behind a 250/250 green vector. + /// + /// This stream reaches it. Three numbers, and the third is what gives the second + /// its meaning: + /// + /// - **115 of 120** access units retire a picture (a five-picture DPB against four + /// marked references and `sps_max_num_reorder_pics = 0`); + /// - **0** of those retirements intersect the access unit's own `dpb_refs` — the + /// exemption, measured on our own encoder's output rather than argued; + /// - **115** of them intersect the PRE-RPS marked set, so a snapshot taken one call + /// earlier would alias on every single one. + /// + /// [`the_low_delay_stream_would_alias_if_the_snapshot_moved_ahead_of_the_rps`] + /// then drives that counterfactual through the conversion itself. + #[test] + fn the_low_delay_stream_reaches_the_dpb_pressure_and_hevc_still_does_not_alias() { + let converted = convert_stream(LOWDELAY_640X480_H265); + assert_eq!(converted.len(), 120, "the low-delay stream is 120 pictures"); + + let mut with_removals = 0usize; + let mut both = 0usize; + let mut would_alias = 0usize; + for (i, (plan, dxva)) in converted.iter().enumerate() { + // The consequence, over every access unit of the stream: the decode target + // is `CurrPic` and appears in no reference entry. + assert_eq!(dxva.pic_params.CurrPic.index(), dxva.setup_slot); + assert!(!dxva.pic_params.CurrPic.associated()); + for r in &dxva.refs { + assert_ne!( + r.slot, dxva.setup_slot, + "AU {i}: reference picture {} shares surface {} with the decode \ + target — HEVC has acquired the H.264/AV1 defect", + r.id, r.slot + ); + } + + assert!( + plan.picture.is_reference, + "AU {i}: this stream carries no sub-layer non-reference pictures, \ + which is what makes `pre_rps_marked` exact" + ); + if !plan.dpb.removed.is_empty() { + with_removals += 1; + } + both += plan + .dpb + .removed + .iter() + .filter(|id| plan.dpb_refs.iter().any(|r| r.id == **id)) + .count(); + let before = pre_rps_marked(i.checked_sub(1).map(|prev| &converted[prev].0)); + would_alias += plan + .dpb + .removed + .iter() + .filter(|id| before.iter().any(|r| r.id == **id)) + .count(); + } + + assert_eq!( + with_removals, 115, + "the stream must still retire a picture on nearly every access unit; \ + without that both numbers below are trivially zero" + ); + assert_eq!( + both, 0, + "{both} picture(s) are in an access unit's own marked DPB AND removed by \ + it — the H.264/AV1 aliasing precondition, which HEVC is supposed to be \ + structurally incapable of. `H265Planner`'s snapshot has moved ahead of \ + `decode_rps`. Restore the ordering, or give this conversion the \ + `release_after_decode` deferral the other two carry; do NOT relax this" + ); + assert_eq!( + would_alias, 115, + "the fixture must stay CAPABLE of exposing the defect it rules out. A \ + regenerated stream that reordered, or whose DPB was deeper than its \ + reference count, would report 0 here — and the zero above would then \ + prove exactly as much as `test-25fps.h264`'s zero proved, which was nothing" + ); + } + + /// The counterfactual driven through the CONVERSION, not just the planner's + /// arithmetic. + /// + /// `H265Planner` snapshotting one call earlier is a plausible refactor — it is + /// where `H264Planner` and `Av1Planner` both snapshot, and both needed + /// `release_after_decode` because of it. This test simulates exactly that by + /// handing `plan_to_dxva_h265` the PRE-RPS marked set as `dpb_refs` and nothing + /// else changed, then asserts the alias appears: the dropped picture enters + /// `RefPicList` as a *Foll* entry with its slot resolved BEFORE the removals are + /// released, `SlotMap::assign` hands that freed slot straight to `CurrPic`, and one + /// surface is named as both the decode target and a picture the frame predicts from. + /// + /// So the guarantee is not "we looked and it was fine". It is: this stream reaches + /// the shape, this conversion breaks on it under the other snapshot ordering, and + /// the ordering we ship is why it does not. + #[test] + fn the_low_delay_stream_would_alias_if_the_snapshot_moved_ahead_of_the_rps() { + let mut planner = H265Planner::new(); + let mut slots: Option = None; + let mut plans: Vec = Vec::new(); + let mut aliased = 0usize; + let mut converted = 0usize; + + for (i, au) in split_into_aus(LOWDELAY_640X480_H265) + .into_iter() + .enumerate() + { + let plan = planner.plan_au(au).expect("the low-delay stream plans"); + let map = slots.get_or_insert_with(|| SlotMap::new(plan.picture.max_dpb_frames)); + + // The ONE mutation: the marked DPB as it stood before this AU's RPS ran. + let mut as_if = plan.clone(); + as_if.dpb_refs = pre_rps_marked(plans.last()); + + // The reconstruction validates itself, so a planner change that broke the + // reasoning behind `pre_rps_marked` fails HERE with its reason rather than + // quietly turning the count below into a different measurement. Marking + // only ever grows between two access units' RPS derivations (the previous + // picture is stored marked; C.5.2.2's removal takes only already-unmarked + // pictures), so the pre-RPS set is a strict SUPERSET of the post-RPS one. + for rp in plan.dpb_refs.iter().chain( + as_if + .rps + .st_curr_before + .iter() + .chain(&as_if.rps.st_curr_after) + .chain(&as_if.rps.lt_curr), + ) { + assert!( + as_if.dpb_refs.iter().any(|r| r.id == rp.id), + "AU {i}: picture {} is in the post-RPS marked DPB (or a current \ + set) but not in the reconstructed pre-RPS one — marking is no \ + longer monotone across an access unit boundary, and this test is \ + measuring a different mutation than the one it documents", + rp.id + ); + } + + let dxva = plan_to_dxva_h265(&as_if, map, i as u32 + 1).expect("conversion"); + converted += 1; + if dxva.refs.iter().any(|r| r.slot == dxva.setup_slot) { + aliased += 1; + } + plans.push(plan); + } + + assert_eq!(converted, 120); + assert_eq!( + aliased, 115, + "the pre-RPS snapshot must alias on every access unit that retires a \ + picture. {aliased} of 120 did — if this is 0, the stream no longer \ + reaches the shape and the exemption asserted by the test above is \ + unfalsifiable again; regenerate the fixture rather than relaxing this" + ); + } + #[test] fn the_irap_picture_sets_all_three_picture_type_flags_and_the_others_set_none() { let converted = convert_stream(TEST_25FPS); diff --git a/crates/pf-vkdecode/src/pic_h265.rs b/crates/pf-vkdecode/src/pic_h265.rs index 896d1f80e..d6fe2c1ec 100644 --- a/crates/pf-vkdecode/src/pic_h265.rs +++ b/crates/pf-vkdecode/src/pic_h265.rs @@ -534,6 +534,95 @@ mod tests { aus } + /// **Our own host's low-delay HEVC**, vendored beside the goldens the GPU legs + /// decode it against (`tests/data/lowdelay-640x480-h265.nv12.sha256` carries the + /// `punktfunk-host spike` command and the ffmpeg cross-check). 120 pictures of + /// 640x480 IPPP, a five-picture DPB against four marked references and + /// `sps_max_num_reorder_pics = 0`, so 115 of its 120 access units retire a picture. + const LOWDELAY_640X480_H265: &[u8] = include_bytes!("../tests/data/lowdelay-640x480.h265"); + + /// This rung is immune to the release-ordering defect for a STRONGER reason than + /// the DXVA one, and this pins the difference instead of asserting it in prose. + /// + /// Both HEVC conversions release `removed` inline and let [`SlotMap::assign`] hand + /// the freed slot to the decode target. DXVA survives that because `H265Planner` + /// snapshots `dpb_refs` AFTER `decode_rps`, so the retired picture is not in the + /// marked DPB `RefPicList` is built from — a property of the PLANNER, one call + /// away from being untrue, which is why `pf_dxvadec::pic_h265` drives the + /// counterfactual through its conversion. + /// + /// This conversion never reads `dpb_refs` at all. `pReferenceSlots` is spec-defined + /// as the slots the decode operation USES, so it binds `plan.rps` — the three + /// current sets, which `decode_rps` derives and which therefore cannot name a + /// picture that same RPS just dropped. The test proves it the only way that means + /// anything: it hands the conversion a `dpb_refs` deliberately widened to the + /// PRE-RPS marked set (the mutation that makes the DXVA rung alias on 115 of these + /// 120 access units) and asserts nothing changes here. + /// + /// If this ever fails, someone has made this conversion bind the marked DPB — a + /// legitimate thing to want, since a *Foll* long-term anchor invisible to the + /// hardware is the RFI failure shape — and it now needs the `release_after_decode` + /// deferral the H.264 and AV1 conversions carry. + #[test] + fn a_pre_rps_marked_dpb_changes_nothing_here_because_the_current_sets_are_what_bind() { + let aus = split_into_aus(LOWDELAY_640X480_H265); + let mut planner = H265Planner::new(); + let mut slots: Option = None; + let mut prev: Option = None; + let mut converted = 0usize; + let mut widened = 0usize; + + for au in aus { + let plan = planner.plan_au(au).expect("the low-delay stream plans"); + let map = slots.get_or_insert_with(|| SlotMap::new(plan.picture.max_dpb_frames)); + + // The marked DPB as this AU's `decode_rps` found it: the previous AU's + // snapshot plus the picture it stored. Exactly what `dpb_snapshot()` would + // return from above `decode_rps` instead of below it. + let mut as_if = plan.clone(); + as_if.dpb_refs = match &prev { + None => Vec::new(), + Some(prev) => { + let mut marked = prev.dpb_refs.clone(); + if let Some(id) = prev.dpb.stored { + assert!( + prev.picture.is_reference, + "this stream carries no sub-layer non-reference pictures" + ); + marked.push(RefPic { + id, + pic_order_cnt: prev.picture.pic_order_cnt, + is_long_term: false, + }); + } + marked + } + }; + if as_if.dpb_refs.len() > plan.dpb_refs.len() { + widened += 1; + } + + let vk = plan_to_vk_h265(&as_if, map).expect("conversion"); + converted += 1; + for r in &vk.refs { + assert_ne!( + r.slot, vk.setup_slot, + "a reference aliases the setup slot even though this conversion \ + binds only the current RPS sets — it has started reading \ + `dpb_refs`, and now needs a deferred release" + ); + } + prev = Some(plan); + } + + assert_eq!(converted, 120); + assert_eq!( + widened, 115, + "the mutation must actually widen the marked set on the access units that \ + retire a picture, else this test asserts nothing about anything" + ); + } + #[test] fn the_full_25fps_vector_converts_with_stable_slots_and_start_code_offsets() { let aus = split_into_aus(TEST_25FPS); diff --git a/crates/pf-vkdecode/tests/data/lowdelay-640x480-h265.nv12.sha256 b/crates/pf-vkdecode/tests/data/lowdelay-640x480-h265.nv12.sha256 new file mode 100644 index 000000000..be51684d7 --- /dev/null +++ b/crates/pf-vkdecode/tests/data/lowdelay-640x480-h265.nv12.sha256 @@ -0,0 +1,165 @@ +# SHA-256 per decoded frame of lowdelay-640x480.h265, DISPLAY order — 120 frames. +# Each frame is the full 640x480 picture as tightly packed NV12: +# Y plane 640*480 bytes, then interleaved UV 640*240 bytes = 460800 bytes/frame. +# (No conformance window: 640 and 480 are both multiples of MinCbSizeY, so the +# coded size IS the display size — the same reason the H.264 sibling picked 640x480.) +# +# THE STREAM IS OURS, not a conformance vector, and that is the point of it. +# `punktfunk-host spike` on .21 (NVENC, RTX 5070 Ti, driver 610.57.04, +# punktfunk-host 0.25.0-0.00011708), 2026-08-07: +# +# punktfunk-host spike --source synthetic --codec h265 --width 640 --height 480 \ +# --fps 60 --seconds 2 --bitrate 1 --no-loopback --out lowdelay-640x480.h265 +# +# Main profile, 4:2:0, 8-bit, level 3.1. LOW-DELAY IPPP: one IDR then 119 trailing P +# pictures, ONE slice segment each, no B pictures, `sps_max_num_reorder_pics = 0`, so +# a picture is output the moment it decodes. Its SPS says +# `sps_max_dec_pic_buffering_minus1 = 4` — a five-picture DPB — and 8.3.2 leaves FOUR +# pictures marked in steady state (116 of 120 access units), so four references plus +# the current picture fill the DPB exactly. Each P names exactly one of them +# (`numRefL0 = 1`); the other three are 8.3.2 *Foll* pictures the RFI window keeps. +# +# That is the H.264 fixture's shape in HEVC's vocabulary, and it is what puts an RPS +# drop and the eviction it causes in ONE access unit: 115 of these 120 access units +# remove exactly one picture. `test-25fps.h265` cannot reach it — it reorders. +# +# ⭐ HEVC is nonetheless EXEMPT from the aliasing that cost H.264 and AV1 a deferred +# slot release, and this stream exists to keep PROVING that rather than to be the +# stream that breaks it. `H265Planner` snapshots `dpb_refs` AFTER `decode_rps`, so +# the 115 dropped pictures are never in the marked set `RefPicList` is built from: +# measured `removed ∩ dpb_refs` = 0 of 120 access units. Move that snapshot above +# `decode_rps` and the SAME 115 access units alias — the counterfactual is measured, +# not argued (pf-dxvadec's `pic_h265` tests pin both numbers). HEVC's freedom is a +# property of the planner, not of the streams we happened to test. +# +# Goldens from libavcodec's SOFTWARE decoder, the same ground truth every other +# golden set here uses (H.265 decoding is exactly specified, so every conformant +# decoder is bit-identical): +# +# ffmpeg -i lowdelay-640x480.h265 -f rawvideo -pix_fmt nv12 \ +# -fps_mode passthrough ref.yuv +# # then split ref.yuv into 460800-byte frames and sha256 each +# +# ffmpeg version n8.1.2 (Arch/CachyOS, gcc 16, x86_64) — cross-checked BIT-IDENTICAL +# against ffmpeg 8.1.1 (Homebrew, clang, macOS arm64), all 120 frames. 120 of 120 +# digests distinct. +9f9c0286da4917dd02897f75d61142f0e6e950c2795e835f6afcc849d6778e5b +269e68e0917f62050cc6e134a9355d621dbb6dd1ab162f809b86c44df3d8fef0 +3297b14670d010b01f7c5afba4a1309055965d4b1cbdcf644f3ae59540330e79 +f6f7b529995b9d658a7a30a2b8df8381a494b18d4a92441c350591e2221656cd +c32d3e28e7322d08a5a4c2044b3d6f6a8a5b093818e378e9f68ecbf787c3e89e +51b534a44bd5292dbfe189108f0cd2ceaeafd35433762113d25ad17aee95ee9f +fe23e229a7154d5ddd965e7131a3db1928fbcc91525b96d06bf354ecb390cd36 +a18313577c9f658af750ac5deb71275b7cb31847030064b7aa1eabfa1bca8b6d +bd372841ca00e7ed8cf88bd5b47f1d2aee0a87c9c40fc1d921ce5135c5ff0782 +baa5e7a7a2c86ac428dfe3ed3bfdbe399ff8f339186c48c181840a738010e6a7 +43e84f559307c12c0a4d8acfd259cc3a73aa6d9e2951dcf05c45e4185becf75b +c85779050cbd80826c554e62d5b9435f6e6cc31c0013d288045260fe576dfe8a +93f5292b0436b37290e644f0e45aa987e39c97666f182e54709eae42512e40b7 +163fa586ec9813079ed0427bfa046f846359c540763b4437062c181a219d64b2 +2851fa698eb3889235d16a7e9497153b57f6e1ba79133f038586c13623b1893f +3ca46e9b37fe4c0330893f5d91ed9592bfa65725d703be58d00bb57d2aedaa8d +30a70c37b70f224198c09b7606464fa9f2ed578cb33a467832b2864db01bafe6 +7cd5448fc7d149f311fabef84893bb91b5127c22fdba429eabee1b10a266eb0b +79bda1e5dd8136eb2dc5cfc607dcb1bdd2a3b58a3c85133f52e54f21bb138e99 +9aa3f7dc22d68dbf76cb1a71fd476f5f8b272b7dd6676383945c37611ce26f56 +75b1151b63f8e071cadd3656edae21e6755cdef194730e4d9d364df67ff63b56 +a6fe1a709aa69329fa96ab90aa4561a57b42cdca1d6dc3f41b00b8466bbd2bfc +a309129ad54517d8839ca4ebf12bdcc4c563049fe991d8a7c0fa57ba8c75c6ee +dff37bb18055c1f4739168c882ec694b027998b960e341d6815a47cd87942a14 +ff334206b81c8664cf2a59df91d0cd3fd3bc8f45f6ec918312df75e61d6adf74 +916f50363a424907d97dd1914808770ee06716f70c6040a44d0c1c9c66004b3c +a2bdd4550eb5a43742d667a1538102838dd81e7930c76b607df23dee4d0c2c03 +e5f70c0a5df394b9dbe49bb6cefb0c0ce799496ee1cb02ba85366415376d9863 +f154dac657645f5aff1e0f47aa8ffd66b7205808fdf345c9b153a70770eb0702 +5d83483a3135908c233f9c8719115b823ca4c384cc3d48f9d43b2ce174e55c12 +60ea060bd5f80d2ed291f7fd87a317adc8a8eab5f2ea0bcda130e06f0bc4b21b +3cbd406035c302f48874c509f0a2f32f2a3ee6072d87a97ad91728abe9778176 +43c319ed065cb55d59a6d9fe98a70884563ce11109e54dfd99d5a4ce695ef355 +a6416844bc2d08e80415350ea6a77b37e0a8c5ea0a13bb284ac6f4f3a54882a4 +b49fc7a97971eef0c7ce3264a5245de9c323fdb8c1f8d5410f5ffda6bca6c187 +23754bb46f0938a29cf3b58cb78530701b3ed7e8a2d6fc55a168b2b0e6c9bf19 +9bd927da6114b539327c3c9010e01a19bea941fac296acc7cea4ff59ee2e21cb +774b5b0f9872c4d95f071ea16dc848d99c7bbafa22228d6530501d1e9c405e22 +ee244d9663d700085033571e63649beb169f3464b5c47b435beb5e56b6513033 +c552bc82788cb0b793361a3beed54b0f1ccbb03c1a34d29491a07acea615b980 +31a92d0c3a8906c742034f36720481d50d2b7913f2f8225f78ef6ab8fb104a8a +3f0581af032d83dd7cc2cf0afff04d225a48a42dae3e3cfb6cc72b1218b49dc8 +6f0bb48a98e7a971238bb4d90432ffc675c55bab2571387e0fdc9f2d8acfdc7e +4f51f9aaa83108587eb3871a22c7ebb34a7f66dbdd81689de03f10f16f8e3207 +90a47f56c6aedd15de6dc3798bab53f394c4095c1fea933f2d8af83763d2431f +1e8c8505e4e9f7ada9a60b6d6f66d30961c3f5fe70777a71b4e110079a435164 +6909300c3db9cd6db449bd6e3002b94052da02000105f89bf38ad4babab4e339 +caf9794215bd2ceebbabcebbd7d3de3d86ac9fdced421ab30ef0cb1eea9f472b +232b867d97898839e331b9a9aa971e5593b4e360833ae1230272d426fd3796a5 +3472664dc348f434ffd0b58215cbdd17c9d18fb8112c0b1bd3781fd23da77edc +cdfb83932eecd4c5acaa12bad0d7f04d2a87dcd2812cd0a10ff3ddab3ced0484 +4862527f8256fa9c1817c7db32a9237ca9078554de336c7ec231cdf92fd8b481 +95ef6abd712678dec27238e9f5d983c632ea606c68a17db8c13db5f984106753 +2a37fa16ac36bdba0a934a4fd756bb0dc40685c4db63cb1c919f1737e2bcd497 +798d6c594aa1924205e947e8b48a49822dae5310f1938e31d7fba6b538bb0689 +f7e8fcbfe4074d0e8cffc88ac5be30cfcaae2bdb4d6f35b32c627ef1eb1e5b44 +87eedb3cd8a47c70cf23c7025a53176dac83d9928d66bbc9750169b15df65fee +e3f24c0c33a4442c7f3ccc4fdc4d42116a92c1afa2a108d9867493c25971c6c6 +6fba290349aea3ad2f0b189ddf608c07e3c7062e5659464453e9198f80fb1a06 +fb1503234b54566ab4d4fb8f155ec779e6e83c14eaa2ebbc51502636b8142362 +25a2d7396d612601b65306d166fa9353eaa63a820c88a4a2ba4891ebd6527c73 +52e600c978c89b4e0580ea63a4131edd445cb1a83d5978db99e09bf115795c43 +c576c1f537bd10065c93ce01bee7821b5d947d418c42f2941a65c106a8866ded +fd925cf61ffdd98fad5388c8bde2f3e4dfaaff0de0badd3e28998dcdaaa22790 +ed06b90b70480467e2bff3aa44fe26356065393b09916ca55b705147e79a4532 +bc311e2abeeceb28e409a5c117a303100a5b8bd72a9212da8370ed125d23267f +611f87e03ab96ef0e683968611f9ee9b6c0052457a513217f9a20cfadf98c0c8 +781aa26b54f6e3a941c973c1685323ebb6a4c8a024d575a219138d2f7500f12c +e1bf924e000066db999ea9df03f444544ef9b5ced6903fd6e11679b1bcde4288 +1dbbe9f9039f67d630e0474830a9b4908453dabc705a726990e5da33f8265ef0 +6b196b22ee1b5f5a41129fd6ea4c31217010d656d07eba1b519372483e225d5a +f98c6557358d7d69ad084a39780e46c3f6d2c882a95b14e196453cf5683f2e1e +2d1f2d2e730d80b0a0e9186cf706fee66e5919b724cc048fc26905766baeb4fb +fc6e7638091a5a8fea2c2fb93de326466d0b8b2791a137b6c326cf873f780091 +12ace0c4843c04cd1ab0b24d11374f92862300dfb04402c53dbb2a3846d3cd1e +ec3b094cf0a85e64794afb1a423fcf6ac2d4ac49bcd0eafaff93c87c5ea5da80 +4eabff56ae2893cb8d18b994e8922d98517f4bfc26f03572917ee04e9c1f2c21 +c836a55b5690dc31db03d5373b65660a7a56b3dd6ecb1335daecfdd798dbae49 +3513c96a153957e34cb5b4568fed9cbc9f008b40c7f7c8888419e280b9552e72 +98d993995f82e40736a96d78088f79ef5b1e23a2f2c8612a995581d2e4339c89 +a7e18f4986c14870c0f5703287acf0015e84265c59cd81ec4da249a156eaf344 +c67d1b2cdfd341d605fb1a0104fbf39dca7a220a20864f6e258c5261b363d2d6 +d49987c3cc626dea121b049f26ff92619fabc0c9ac2f4d7222fd5a4f3cd05106 +c1af9fdac737422cad1e70fae5660268f51c1fa2ad565617d9aef0c6567bc1f2 +988b41b171cbefd8844891dbf1dda9586f4f7d855fdedf9084ce1a35211bf4c7 +0c89259a251df6835d43f2d41b3ab680191ff8a2a1b52f38067f5ff6e64c46ef +463acd75505bf1c7f9cdbac61ffc447fe7dff36832dd6df2da8c6fb034a7ea5d +b87b3ae2d51b989010cbc44d020db3be1df563727cb1828ed20be75138b391ca +f224bab42976a46f976e3792ec15c7c143fd40c255577ced88c4dc423f9e521d +83248fdcccac574bf2550594894668e2294b60a51e02511bd7ed1a3d62a0a833 +e21a46b8aa370956a53c6059663c95846d6de9c21b911e9b35328642cd2b6a93 +1b25f894a5e039940be81195d73bc47b81dbe3de0a02172c47e8f3de53ff453e +b91a7d7f4545adec12ab1f1646a152aa7a411f52fe85ba6d0a05e26233bd9554 +76fb5d9740f2f31d27c017d14f2641dd71debd5ce1322f993e4d4525f86913e2 +f69e6a8e6d1f9022fa3b4d7e27cae866d827b0d70ed4a1475cec54703e3028be +89e799beb6d7196de0f1aaec171c0aa7c5ab5b5061e7ea6431a4b9ca2f9f305d +a48dd4d45122187814b66952700536336d2d86e3cf4c90499416ee4178c82df6 +716723e849b5a3fc13460c45b7b4f236bdb077a40eae3f1bd6f63970eccfd6d1 +6d6f03ccd7e223c150bf4dab4667ef4cca83ef24db79dda73c1d3d132cd8abec +f6b9a0795d6202a1f868768fead3a3072f5ac84a31bb741b1c2e6435bddea335 +93db9ff6332ea465c026c6d994e214d7fc035dac3b28cc5df5100633c4b3d355 +f2f7b1a021abe83b5635da215b72d341c58532fe0aaa6c72570e1ab4d8bd702c +e1cc57cae82018761af96d75485c9564c742b9c180709f41cdac28a08b4734c1 +bd8b75c1bddaca942b0645efd1774ca6cdf2b1254f65aa3bb52160a635c5820e +454d9a6027f89a23680378b0ff469b0ddaa277f1daa1fc953ffbf3a86c9b56e2 +52dd5395d96d8097515707287fe2f53c140e46addca3d4407b1b9f9685569ce3 +df850ef27b8e2db55f1f03e4fe686a22897b5266e37ba6d56cf97e9adc0a6e25 +d6ee7735b22fa97e1ee85fcbf7bea1609ed703e98a4076e1474e8d152794ec6a +4bfcb046b8175eed1732bf52d5abd0540d73333e1b3efbf86b8dfb1a76aeca57 +632c87450e3b5b763d87890b5ed5ce38775af5359b858f716cfbff651838c01e +2784dd87812c4df4793ff50409626d47bebd4149caa4b5098efb5ec63c59b5a1 +480b2f63b02ba2436a3d026701adafc27ed8fbc100ebe232257ec62c54166767 +81d1b8196e7bacd89ef90494b48772b1eef52afbee49aaf907eb264eeb11d660 +a045bf5565f399c293363f2d08315b60ae6bd6a524ca537668e3afb3b9893d9a +767ffcd6a3b2574f641c1f298c4606cce88b08f76ff5abbdbfd8c8cc8b88cb86 +54cb01c9f411e1994b5a2b2cea22032e232a1d76874b5b6a991474058c3b3dfa +feba0cc0e710d509963cf7f7c6bd62e6904d6c5446fb784bd3508520e1a3371a +ae0c6d458623d31eeb59e9170f983dec17ca5885614dbc82e67ae45b9ecb11c9 +e6d0f8d4ee60e94199ee39ed6f7b2f6c4d4835993c981086f5b022ba4bd1da6a +d70133bc51be4b64510a0411c07dc2479b04f18b1b561cce21b91f6a3a9508a2 diff --git a/crates/pf-vkdecode/tests/data/lowdelay-640x480.h265 b/crates/pf-vkdecode/tests/data/lowdelay-640x480.h265 new file mode 100644 index 0000000000000000000000000000000000000000..fb8bc79e6425026f7e7fc1428297f0ce9480d82b GIT binary patch literal 276657 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