feat(pf-vkdecode): the CPU half of HEVC decode — StdVideo H265 conversion + slot map

M3 WP-2, first half. params_h265.rs: VPS/SPS/PPS -> StdVideoH265* with
owned pointer-backing (the params.rs contract), scaling lists incl. the
32x32 two-matrix quirk and +8 DC convention, short-term RPS re-encoded
from the parser's RESOLVED DeltaPoc arrays back into delta_poc_sX_minus1
syntax under monotonicity checks, fallback_vps_from_sps for streams
whose VPS NALU was lost. pic_h265.rs: plan_to_vk_h265 — h265 AuPlan ->
StdVideoDecodeH265PictureInfo + per-reference infos; the binding set is
the union of the three current RPS sets with the Std index arrays
indexing into refs (0xFF unused; the GPU half must lay pReferenceSlots
out in refs order); NumDeltaPocsOfRefRpsIdx from the predicted-from
candidate; transactional SlotMap lifecycle identical to pic.rs. SlotMap
reused unmodified — HEVC's ceiling equals H.264's 16+1.

Envelope fails closed: Main/Main10/MainStill/RExt only, 4:2:0-8/10 +
4:4:4 only (separate_colour_plane_flag rejected — ChromaArrayType 0 in
disguise), SCC palette predictors out, >64 ST RPS sets / >16 per side /
>32 LT SPS candidates out, checked narrowing on every narrower Std
field. No panics on untrusted input.

Review round 9 (adversarial): RPS re-encode math, Std field-by-field
conformance, transactionality and slot ceiling verified clean; 6
findings fixed pre-commit. Headline (BLOCKING): long_term_ref_pics_
present_flag=1 with num=0 left pLongTermRefPicsSps NULL — the header
demands a valid pointer whenever the flag is set, and flag=1/num=0 is
exactly the punktfunk LTR/RFI recovery stream shape; the all-zero
backing now rides whenever the flag is set. Also: the slice_offsets doc
in BOTH pic modules claimed submit-as-planned while decoder.rs packs
slices-only and rebases (non-VCL NALUs in the decode range hang VCN
firmware) — reworded so the HEVC GPU half cannot implement the hang; a
concealment-produced ST/LT duplicate now ORs the long-term flag across
occurrences; NumDeltaPocs clamps became a typed error; dead
UnmappableLevelIdc variant dropped.

Deferred to the GPU half: HEVC caps/profile chain, session parameters
(VPS leg in the ledger), P010/4:4:4 pool selection, recording, and the
pReferenceSlots-in-refs-order contract consumption.

Gates: fmt clean; mac pf-vkdecode 80 + pf-bitstream 69 green, clippy
clean; container clippy -D warnings zero (pf-client-core, pf-presenter,
pf-vkdecode) + tests green (69/121/80).
This commit is contained in:
2026-08-06 00:41:23 +02:00
parent 370b0ab494
commit a34f4051fc
4 changed files with 2963 additions and 3 deletions
+27
View File
@@ -39,6 +39,18 @@
//! caps-gated per queue family: where `queryResultStatusSupport` is absent
//! (RADV), verdicts degrade to timeline completion, FFmpeg parity.
//!
//! M3 (HEVC) — the CPU half, over [`pf_bitstream::h265`]'s WP-1 planner:
//!
//! - [`params_h265`]: VPS/SPS/PPS into the `StdVideoH265*ParameterSet` structs
//! behind owning wrappers ([`OwnedStdH265Vps`]/[`OwnedStdH265Sps`]/
//! [`OwnedStdH265Pps`]) — Main/Main10/4:4:4 RExt fidelity carried through, the
//! rest of the envelope rejected typed.
//! - [`pic_h265`]: [`plan_to_vk_h265`], one [`pf_bitstream::h265::AuPlan`] into
//! `StdVideoDecodeH265PictureInfo`/`StdVideoDecodeH265ReferenceInfo` plus the
//! RPS index arrays, slice offsets and slot bindings — over the SAME
//! [`SlotMap`] (H.265's DPB ceiling is H.264's: 16 references + 1 setup). The
//! GPU half (session/images/recording for HEVC) is a later WP.
//!
//! 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
@@ -52,7 +64,9 @@ pub mod decoder;
pub mod device;
pub mod images;
pub mod params;
pub mod params_h265;
pub mod pic;
pub mod pic_h265;
pub mod ring;
pub mod session;
pub mod slots;
@@ -93,10 +107,23 @@ pub use params::sps_to_std;
pub use params::OwnedStdPps;
pub use params::OwnedStdSps;
pub use params::ParamsError;
pub use params_h265::fallback_vps_from_sps;
pub use params_h265::pps_to_std_h265;
pub use params_h265::sps_to_std_h265;
pub use params_h265::vps_to_std_h265;
pub use params_h265::H265ParamsError;
pub use params_h265::OwnedStdH265Pps;
pub use params_h265::OwnedStdH265Sps;
pub use params_h265::OwnedStdH265Vps;
pub use pic::plan_to_vk;
pub use pic::DecodePlanVk;
pub use pic::PlanToVkError;
pub use pic::VkRef;
pub use pic_h265::plan_to_vk_h265;
pub use pic_h265::DecodePlanVkH265;
pub use pic_h265::PlanToVkH265Error;
pub use pic_h265::VkRefH265;
pub use pic_h265::H265_RPS_LIST_SIZE;
pub use ring::RingLayout;
pub use session::ParamsAction;
pub use session::SessionConfig;
File diff suppressed because it is too large Load Diff
+7 -3
View File
@@ -31,9 +31,13 @@ pub struct VkRef {
#[derive(Debug, Clone)]
pub struct DecodePlanVk {
pub std_pic: hh::StdVideoDecodeH264PictureInfo,
/// Byte offset of each slice NALU in the submitted AU, START CODE INCLUDED
/// Vulkan's `pSliceOffsets` points at start codes within the bitstream buffer,
/// and punktfunk submits the AU exactly as planned.
/// Byte offset of each slice NALU in the AU as planned, START CODE INCLUDED.
/// AU-relative, NOT submission-final: the recording layer packs the SLICE
/// NALUs alone into the bitstream buffer and rebases these offsets while
/// doing so (non-VCL NALUs inside the decode range hang VCN firmware — see
/// the slices-only packing in `decoder.rs`); Vulkan's `pSliceOffsets`
/// receives the rebased offsets, each pointing at a start code within the
/// packed buffer.
pub slice_offsets: Vec<u32>,
/// The slot the decoded picture activates (`pSetupReferenceSlot`).
pub setup_slot: u8,
File diff suppressed because it is too large Load Diff