Files
punktfunk/crates/pf-vkdecode/src/lib.rs
T
enricobuehler 6331ae7fd9 fix(pf-vkdecode): zero-copy pool model + the two faults the first hardware run found
WP-D leg 1 (.25 RADV, distinct mode) root causes, both real:
1. Output starvation: the fixed 4-deep ring lost to a stream that keeps
   max_dpb_frames+1 = 8 pictures pending. Zero-copy fix (user
   requirement, no copies): one picture pool of required_slots +
   HOLD_HEADROOM(8) images decoupled from DPB slots — a re-activated
   slot binds a fresh free image, so a delivered picture is never a
   decode target; the WP-B pin layer became dead and is deleted.
   Per-image timeline semaphores carry the AVVkFrame contract: decode
   signals value+1, the presenter waits and signals back, later decodes
   wait the image's latest value — layout traffic ordered against
   reference reads with no copy anywhere.
2. RESULT_STATUS queries HANG RADV's VCN firmware (ring timeout,
   DEVICE_LOST): queryResultStatusSupport=false on the decode family.
   Queries are now caps-gated; without them poll/wait degrade to
   timeline-completion verdicts (FFmpeg parity — and the likely reason
   upstream never wired nb_queries). The Ally-X-class detection runs
   where drivers advertise the query; .173 probes NVIDIA/Windows-AMD.

Also: slice-only bitstream feeding (the field-proven consumer shape),
graveyarded pool retirement keyed by release tokens + generation,
decode-current-AU-before-status attribution, take_ready drained,
H264-bit gating, teardown short-circuit on disconnected channel.

On-glass: 48 AUs green on .25 holding 4 frames like the real client.
Gates: fmt clean, container clippy -D warnings zero, 27+121+52 green
both platforms.
2026-08-05 19:12:20 +02:00

102 lines
4.6 KiB
Rust

//! Native Vulkan Video H.264 decode for the clients — M2 of the native-decode program
//! (design/client-native-decode.md §3.2).
//!
//! This crate sits between [`pf_bitstream`]'s per-AU planning and Vulkan Video
//! submission.
//!
//! WP-A — the CPU-testable half, everything runs without a GPU:
//!
//! - [`params`]: the vendored parser's `Sps`/`Pps` converted into the
//! `StdVideoH264*ParameterSet` structs session parameters are created from, behind
//! owning wrappers ([`OwnedStdSps`]/[`OwnedStdPps`]) because the Std structs embed
//! raw pointers.
//! - [`slots`]: [`SlotMap`], the hardware DPB slot ledger keyed by
//! [`pf_bitstream::h264::PicId`]. pf-bitstream's DPB decides what lives and dies;
//! this map only translates ids to slot indices and refuses to guess.
//! - [`pic`]: [`plan_to_vk`], one [`pf_bitstream::h264::AuPlan`] converted into the
//! `StdVideoDecodeH264PictureInfo`/`StdVideoDecodeH264ReferenceInfo` set plus slice
//! offsets and slot bindings a `vkCmdDecodeVideoKHR` call wants.
//!
//! WP-B — the GPU half, built ON the borrowed presenter device (this crate never
//! creates or destroys a VkDevice) with the decision logic split out pure so it
//! stays CPU-testable:
//!
//! - [`device`]: [`DeviceHandles`] (the borrowed handle bundle + its liveness
//! contract), the [`QueueLock`] trait every submit runs under, [`DecodeDevice`].
//! - [`caps`]: one thin driver query + [`derive_caps`], the pure
//! coincide/distinct/layered decision table ([`DecodeCaps`]).
//! - [`session`]: `VkVideoSessionKHR` + versioned session parameters (pure ledger
//! decides Add-vs-Recreate; extent/DPB renegotiation rebuilds the session).
//! - [`images`]: the picture pool DECOUPLED from DPB slots (the zero-copy FFmpeg
//! pool model — a re-activated slot binds a fresh free image, so delivered
//! pictures are never decode targets), per-image timeline semaphores with the
//! presenter `value+1` write-back, per-plane views, [`HOLD_HEADROOM`] sizing.
//! - [`ring`]: the host-visible bitstream upload ring (pure alignment/growth
//! math) — SLICE NALUs only (feeding a whole AU hangs VCN firmware).
//! - [`decoder`]: [`VkH264Decoder`] — plan → convert → upload → record → submit,
//! with a per-op `RESULT_STATUS_ONLY` query ([`VkH264Decoder::poll_status`]) so
//! driver-reported corruption is finally observable (the Ally X class) —
//! caps-gated per queue family: where `queryResultStatusSupport` is absent
//! (RADV), verdicts degrade to timeline completion, FFmpeg parity.
//!
//! Unsafe posture: unlike pf-bitstream (which forbids unsafe outright), this crate
//! cannot — the `ash::vk::native` bindgen structs are zero-initialized the way the
//! encode side does it (`pf-encode/src/enc/linux/vk_build.rs`), and the GPU half is
//! Vulkan FFI. Every unsafe block therefore carries a written `// SAFETY:` proof,
//! enforced (and unlike the encoder there is NO file-level
//! `unsafe_op_in_unsafe_fn` exemption — every operation is individually fenced):
#![deny(clippy::undocumented_unsafe_blocks)]
pub mod caps;
pub mod decoder;
pub mod device;
pub mod images;
pub mod params;
pub mod pic;
pub mod ring;
pub mod session;
pub mod slots;
/// Re-exported for the integration layer (WP-C): [`DecodedVkFrame`]'s handle fields are
/// ash types, and the consumer (pf-client-core, whose own `ash` is optional/feature-gated)
/// flattens them to raw `u64`s through `ash::vk::Handle` — via THIS instance of ash, so
/// the versions can never skew.
pub use ash;
/// Re-exported so [`VkH264Decoder::take_warnings`] consumers name the warning type —
/// and [`DecodedVkFrame::crop`]'s type — without growing a pf-bitstream dependency of
/// their own.
pub use pf_bitstream::h264::DisplayCrop;
pub use pf_bitstream::h264::PlanWarning;
pub use caps::derive_caps;
pub use caps::CapsError;
pub use caps::DecodeCaps;
pub use caps::RawH264Caps;
pub use caps::VideoFormat;
pub use decoder::DecodeStatus;
pub use decoder::DecodedVkFrame;
pub use decoder::VkDecodeError;
pub use decoder::VkH264Decoder;
pub use device::DecodeDevice;
pub use device::DeviceHandles;
pub use device::NoopQueueLock;
pub use device::QueueLock;
pub use device::QueueSubmitGuard;
pub use images::plan_pools;
pub use images::PoolPlan;
pub use images::HOLD_HEADROOM;
pub use params::pps_to_std;
pub use params::sps_to_std;
pub use params::OwnedStdPps;
pub use params::OwnedStdSps;
pub use params::ParamsError;
pub use pic::plan_to_vk;
pub use pic::DecodePlanVk;
pub use pic::PlanToVkError;
pub use pic::VkRef;
pub use ring::RingLayout;
pub use session::ParamsAction;
pub use session::SessionConfig;
pub use slots::SlotError;
pub use slots::SlotMap;