ci / bun-nix (pull_request) Successful in 38s
apple / swift (pull_request) Successful in 1m31s
apple / screenshots (pull_request) Skipped
ci / web (pull_request) Successful in 2m11s
ci / docs-site (pull_request) Successful in 2m21s
windows / build (x86_64-pc-windows-msvc) (pull_request) Successful in 2m44s
windows / build (aarch64-pc-windows-msvc) (pull_request) Successful in 1m10s
ci / rust-arm64 (pull_request) Successful in 6m29s
android / android (pull_request) Successful in 7m44s
ci / rust (pull_request) Canceled after 9m40s
nix / flake (pull_request) Canceled after 9m38s
The Intel Arc refusal moved one step down the caps query and stopped again: the coincide NV12 entry does not advertise SAMPLED. That sentence is punktfunk's, not the driver's, and the last two times a conclusion was drawn from a sentence of ours the conclusion was wrong. So --probe-decode now prints the driver's own answers instead. For every profile the client can negotiate (H.264 High, H.265 Main and Main 10, AV1 Main 8- and 10-bit) it asks vkGetPhysicalDeviceVideoFormatPropertiesKHR in six usage combinations — the three the image pools really create with, plus DPB|DST without sampling, SAMPLED alone and DST alone, which are what localise a refusal to a half. Each answer is printed as the driver gave it: format, usage and create flags named AND in hex with unrecognised bits called out, image type, tiling. A failed query prints its VkResult rather than vanishing into an empty list. It goes through pf-vkdecode's own query rather than a copy of it, which meant splitting query_formats into a physical-device form — the call never needed the VkDevice the old signature demanded. VideoFormat gains imageType and imageTiling to carry the whole record; VUID-VkImageCreateInfo-pNext-06811 compares both for equality, so they were being assumed rather than read. And because a driver that under-reports usage would be indistinguishable from one that genuinely lacks it, the probe asks a second, independent question — vkGetPhysicalDeviceImageFormatProperties2 over the same profile list — and prints it only where the two disagree. A disagreement is the finding. No behaviour change to any decode path: derivation reads the same fields it did.
259 lines
12 KiB
Rust
259 lines
12 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.
|
|
//!
|
|
//! 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).
|
|
//!
|
|
//! M3 (HEVC) — the GPU half, sharing every codec-agnostic piece with H.264
|
|
//! (picture pool, bitstream ring, op ring, DPB settling, frame delivery) rather
|
|
//! than re-implementing them:
|
|
//!
|
|
//! - [`caps_h265`]: [`H265ProfileKey`] (the stream's profile idc, chroma format
|
|
//! and bit depths, which Vulkan wants stated on every object) and
|
|
//! [`derive_caps_h265`] — Main → NV12, Main 10 → P010, RExt 4:4:4 → the
|
|
//! two-plane 4:4:4 formats, with a device that cannot host the combination
|
|
//! refused BEFORE a session exists ([`CapsError::NoFormat`]).
|
|
//! - [`session_h265`]: the H.265 session and its THREE-array parameters ledger —
|
|
//! VPS included, with [`fallback_vps_from_sps`] standing in (and deduping
|
|
//! correctly) for streams joined after their VPS NALU.
|
|
//! - [`decoder_h265`]: [`VkH265Decoder`], mirroring [`VkH264Decoder`]'s public
|
|
//! surface method-for-method. Codec DISPATCH is the client wiring's job.
|
|
//!
|
|
//! M7 (AV1) — the CPU half, over [`pf_bitstream::av1`]'s planner:
|
|
//!
|
|
//! - [`params_av1`]: the sequence header into `StdVideoAV1SequenceHeader` behind an
|
|
//! owning wrapper ([`OwnedStdAv1SequenceHeader`]) — the ONE parameter set AV1 has.
|
|
//! - [`pic_av1`]: [`plan_to_vk_av1`], one [`pf_bitstream::av1::AuPlan`] into
|
|
//! `StdVideoDecodeAV1PictureInfo` and its eight per-frame sub-blocks, plus the
|
|
//! per-reference-NAME DPB SLOT table, the tile-group ranges and the slot bindings
|
|
//! — over the SAME [`SlotMap`] (AV1's ceiling is eight references + one setup).
|
|
//!
|
|
//! M7 (AV1) — the GPU half, sharing every codec-agnostic piece with the other two
|
|
//! (picture pool, bitstream ring, op ring, frame delivery, DPB settling) rather
|
|
//! than re-implementing them:
|
|
//!
|
|
//! - [`caps_av1`]: [`Av1ProfileKey`] — Std profile, sampling, bit depth AND the
|
|
//! sequence's film-grain flag, because `filmGrainSupport` is part of the Vulkan
|
|
//! decode PROFILE — and [`derive_caps_av1`]: 4:2:0 8-bit → NV12, 10-bit → P010,
|
|
//! 4:4:4 → the two-plane 4:4:4 pair, with a device that cannot host the
|
|
//! combination (film grain very much included) refused BEFORE a session exists.
|
|
//! - [`session_av1`]: the AV1 session and its ONE-set parameters ledger — no PPS,
|
|
//! no VPS, no update path at all, so a changed sequence header RECREATES.
|
|
//! - [`decoder_av1`]: [`VkAv1Decoder`], mirroring [`VkH265Decoder`]'s public
|
|
//! surface method-for-method, over temporal units that may carry several frames.
|
|
//!
|
|
//! M4 (status and telemetry) — three pure modules turning the signals above into
|
|
//! something a session, a user and a support engineer can act on:
|
|
//!
|
|
//! - [`recovery`]: [`RecoveryWatch`], the recovery point SEI folded into a
|
|
//! per-picture "the stream healed HERE" mark ([`RecoveryMark`], carried on
|
|
//! [`DecodedVkFrame::recovery`]). The only clean point an intra-refresh session
|
|
//! has — its wave emits no IDR — so without it a client freezes for its full
|
|
//! backstop and then forces the very IDR the wave exists to avoid.
|
|
//! - [`integrity`]: [`is_integrity_warning`] / [`is_integrity_warning_h265`] /
|
|
//! [`is_integrity_warning_av1`], the one list of warnings that mean the PICTURE
|
|
//! is damaged. Here rather than in the client so the fault harness asserts
|
|
//! against the predicate production conceals on.
|
|
//! - [`fault`]: [`AuFault`], deliberate decoder-input corruption
|
|
//! (`PUNKTFUNK_AU_FAULT`), inert unless armed. A detector nobody can fire is
|
|
//! exactly as trustworthy as no detector at all.
|
|
//!
|
|
//! Plus [`VkH264Decoder::status_queries`] / [`VkH265Decoder::status_queries`]: does
|
|
//! this device answer per-op `RESULT_STATUS` at all? Without that fact a clean
|
|
//! integrity report cannot be told apart from an unmeasured one — which is the
|
|
//! precise failure the program exists to end.
|
|
//!
|
|
//! 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 caps_av1;
|
|
pub mod caps_h265;
|
|
pub mod decoder;
|
|
pub mod decoder_av1;
|
|
pub mod decoder_h265;
|
|
pub mod device;
|
|
pub mod fault;
|
|
pub mod images;
|
|
pub mod integrity;
|
|
pub mod params;
|
|
pub mod params_av1;
|
|
pub mod params_h265;
|
|
pub mod pic;
|
|
pub mod pic_av1;
|
|
pub mod pic_h265;
|
|
pub mod probe;
|
|
pub mod recovery;
|
|
pub mod ring;
|
|
pub mod session;
|
|
pub mod session_av1;
|
|
pub mod session_h265;
|
|
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;
|
|
// The pf-bitstream types a [`DecodedVkFrame`] consumer names, re-exported so it
|
|
// doesn't grow a pf-bitstream dependency of its own:
|
|
/// [`VkAv1Decoder::take_warnings`]'s warning type — the AV1 twin of
|
|
/// [`PlanWarning`], renamed for the same reason [`H265PlanWarning`] is: the three
|
|
/// enums are genuinely different (AV1 has `MissingShowExisting`, and its
|
|
/// `MissingReference` needs no interpretation because no AV1 process empties a
|
|
/// reference slot behind the stream's back) and a consumer dispatching per codec
|
|
/// must be able to name all three.
|
|
pub use pf_bitstream::av1::PlanWarning as Av1PlanWarning;
|
|
/// [`DecodedVkFrame::colour`]'s type.
|
|
pub use pf_bitstream::h264::ColourDescription;
|
|
/// [`DecodedVkFrame::crop`]'s type.
|
|
pub use pf_bitstream::h264::DisplayCrop;
|
|
/// [`VkH264Decoder::take_warnings`]'s warning type.
|
|
pub use pf_bitstream::h264::PlanWarning;
|
|
/// [`VkH265Decoder::take_warnings`]'s warning type — the H.265 twin of
|
|
/// [`PlanWarning`], renamed rather than shadowed because the two enums are
|
|
/// genuinely different (H.264 has `FrameNumGap`/`Mmco5Rebase`, H.265 has
|
|
/// `NonZeroReorder`) and a consumer dispatching per codec must be able to name
|
|
/// BOTH. Without it the client could only render warnings as strings — and it has
|
|
/// to BRANCH on them: `NonZeroReorder` and `Mmco5Rebase` are spec-legal facts the
|
|
/// planner planned in full, not concealment, and dropping their frames would cost
|
|
/// a visible hitch at every SPS activation.
|
|
pub use pf_bitstream::h265::PlanWarning as H265PlanWarning;
|
|
|
|
pub use caps::derive_caps;
|
|
pub use caps::plane_formats;
|
|
pub use caps::CapsError;
|
|
pub use caps::DecodeCaps;
|
|
pub use caps::MaxLevelIdc;
|
|
pub use caps::RawH264Caps;
|
|
pub use caps::VideoFormat;
|
|
pub use caps::NV12;
|
|
pub use caps::OUTPUT_FORMATS;
|
|
pub use caps::P010;
|
|
pub use caps::YUV444_10;
|
|
pub use caps::YUV444_8;
|
|
pub use caps_av1::derive_caps_av1;
|
|
pub use caps_av1::Av1ProfileKey;
|
|
pub use caps_av1::RawAv1Caps;
|
|
pub use caps_h265::derive_caps_h265;
|
|
pub use caps_h265::output_format_for;
|
|
pub use caps_h265::H265ProfileKey;
|
|
pub use caps_h265::RawH265Caps;
|
|
pub use decoder::DecodeStatus;
|
|
pub use decoder::DecodedVkFrame;
|
|
pub use decoder::VkDecodeError;
|
|
pub use decoder::VkH264Decoder;
|
|
pub use decoder_av1::plan_bitstream;
|
|
pub use decoder_av1::Av1Bitstream;
|
|
pub use decoder_av1::Av1TileError;
|
|
pub use decoder_av1::VkAv1Decoder;
|
|
pub use decoder_h265::VkH265Decoder;
|
|
pub use device::DecodeDevice;
|
|
pub use device::DeviceHandles;
|
|
pub use device::NoopQueueLock;
|
|
pub use device::QueueLock;
|
|
pub use device::QueueSubmitGuard;
|
|
pub use fault::AuFault;
|
|
pub use fault::FaultAction;
|
|
pub use fault::FaultMode;
|
|
pub use fault::DEFAULT_FAULT_PERIOD;
|
|
pub use images::plan_pools;
|
|
pub use images::PoolPlan;
|
|
pub use images::HOLD_HEADROOM;
|
|
pub use integrity::is_integrity_warning;
|
|
pub use integrity::is_integrity_warning_av1;
|
|
pub use integrity::is_integrity_warning_h265;
|
|
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 params_av1::sequence_to_std;
|
|
pub use params_av1::OwnedStdAv1SequenceHeader;
|
|
pub use params_av1::ParamsAv1Error;
|
|
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_av1::plan_to_vk_av1;
|
|
pub use pic_av1::DecodePlanVkAv1;
|
|
pub use pic_av1::OwnedStdAv1PictureInfo;
|
|
pub use pic_av1::PlanToVkAv1Error;
|
|
pub use pic_av1::VkRefAv1;
|
|
pub use pic_av1::REFERENCE_NAME_UNUSED;
|
|
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 recovery::RecoveryMark;
|
|
pub use recovery::RecoveryWatch;
|
|
pub use ring::RingLayout;
|
|
pub use session::ParamsAction;
|
|
pub use session::SessionConfig;
|
|
pub use session_av1::ParamsActionAv1;
|
|
pub use session_av1::SessionConfigAv1;
|
|
pub use session_h265::ParamsActionH265;
|
|
pub use session_h265::SessionConfigH265;
|
|
pub use slots::SlotError;
|
|
pub use slots::SlotMap;
|