Files
punktfunk/crates/pf-encode/src/enc/linux/vk_valve_rgb.rs
T
enricobuehler a17413327b fix(encode/vulkan): give the vendored Vulkan ABI the layout guard it never had
`vk_av1_encode.rs` and `vk_valve_rgb.rs` are hand-copied `#[repr(C)]` structs handed to the
driver through raw `p_next` chains. Nothing in the type system relates them to the C
definitions any more, so an edit that inserts, drops, widens or re-pads a field is not a
compile error — it is the driver reading our bytes at the wrong offsets, silently. The
sibling vendored ABI in `amf.rs` has carried assertions for exactly this reason; these two
had none at all.

Adds size, alignment and per-field offset assertions for all 19 vendored structs. They are
`const` rather than `#[cfg(test)]` (the shape `amf.rs` uses) so they hold in every build
including the shipped one, and on any target the modules compile for.

Assertions cannot catch a swap of two same-typed fields — the offsets are unchanged — so
the field order was diffed field-by-field against the authoritative headers while writing
them: `vulkan_core.h` and `vk_video/vulkan_video_codec_av1std_encode.h` from Vulkan-Headers
`main` as of 2026-07-25. That diff covered every struct, every `ST_*` structure-type value,
every flag bit and both enum groups, and found no drift — the vendored copies are faithful.

The one remaining hand-copied table the compiler still cannot see is the bitfield member
order inside the three `*Flags` words, where a wrong index means the driver reads
`use_superres` where we meant `render_and_frame_size_different`. Three tests pin those to
the header by listing the members in C declaration order and asserting the Nth setter
writes bit N, with the trailing `reserved` field checked too — a dropped member shifts
`reserved` down and fails.

Verified with the canonical Linux gate (docker linux/amd64): fmt, clippy `--all-targets`
default and `nvenc,vulkan-encode,pyrowave`, and both test legs.
2026-07-25 12:46:24 +02:00

154 lines
6.0 KiB
Rust

//! Vendored `VK_VALVE_video_encode_rgb_conversion` bindings — the RGB→YCbCr encode-source
//! extension (Vulkan 1.4.327; RADV since Mesa 26.0, hardware-gated on the VCN EFC front-end
//! conversion block). Our pinned `ash 0.38.0+1.3.281` predates it entirely; same vendoring
//! rationale as [`vk_av1_encode`](super::vk_av1_encode) — definitions copied from the registry
//! so the layouts are correct-by-construction, chained via raw `p_next`. Consumed by
//! `vulkan_video.rs`: B0 probes + logs availability (design/vulkan-rgb-direct-encode.md);
//! B1 makes the captured BGRx dmabuf the direct encode source with EFC doing the 709-narrow CSC.
#![allow(dead_code)]
use ash::vk;
use std::ffi::{c_void, CStr};
pub const EXTENSION_NAME: &CStr = c"VK_VALVE_video_encode_rgb_conversion";
// ---------- struct-type (VkStructureType) values — construct via `stype` ----------
pub const ST_PHYSICAL_DEVICE_FEATURES: i32 = 1_000_390_000;
pub const ST_CAPABILITIES: i32 = 1_000_390_001;
pub const ST_PROFILE_INFO: i32 = 1_000_390_002;
pub const ST_SESSION_CREATE_INFO: i32 = 1_000_390_003;
// `VkVideoEncodeRgbModelConversionFlagBitsVALVE`
pub const MODEL_RGB_IDENTITY: u32 = 0x01;
pub const MODEL_YCBCR_IDENTITY: u32 = 0x02;
pub const MODEL_YCBCR_709: u32 = 0x04;
pub const MODEL_YCBCR_601: u32 = 0x08;
pub const MODEL_YCBCR_2020: u32 = 0x10;
// `VkVideoEncodeRgbRangeCompressionFlagBitsVALVE`
pub const RANGE_FULL: u32 = 0x01;
pub const RANGE_NARROW: u32 = 0x02;
// `VkVideoEncodeRgbChromaOffsetFlagBitsVALVE`
pub const CHROMA_OFFSET_COSITED_EVEN: u32 = 0x01;
pub const CHROMA_OFFSET_MIDPOINT: u32 = 0x02;
/// `VkPhysicalDeviceVideoEncodeRgbConversionFeaturesVALVE` — chain into
/// `VkPhysicalDeviceFeatures2` (query) / `VkDeviceCreateInfo` (enable).
#[repr(C)]
pub struct PhysicalDeviceVideoEncodeRgbConversionFeaturesVALVE {
pub s_type: vk::StructureType,
pub p_next: *mut c_void,
pub video_encode_rgb_conversion: vk::Bool32,
}
/// `VkVideoEncodeRgbConversionCapabilitiesVALVE` — chain into the
/// `vkGetPhysicalDeviceVideoCapabilitiesKHR` output when the queried profile carries
/// [`VideoEncodeProfileRgbConversionInfoVALVE`]; reports which conversions the HW does.
#[repr(C)]
pub struct VideoEncodeRgbConversionCapabilitiesVALVE {
pub s_type: vk::StructureType,
pub p_next: *mut c_void,
pub rgb_models: u32,
pub rgb_ranges: u32,
pub x_chroma_offsets: u32,
pub y_chroma_offsets: u32,
}
/// `VkVideoEncodeProfileRgbConversionInfoVALVE` — part of the video-profile *identity*: every
/// consumer of the profile (caps query, format query, session, image profile lists) must carry
/// the same chain.
#[repr(C)]
pub struct VideoEncodeProfileRgbConversionInfoVALVE {
pub s_type: vk::StructureType,
pub p_next: *const c_void,
pub perform_encode_rgb_conversion: vk::Bool32,
}
/// `VkVideoEncodeSessionRgbConversionCreateInfoVALVE` — chain into
/// `VkVideoSessionCreateInfoKHR`; single-bit selections of the conversion actually performed.
#[repr(C)]
pub struct VideoEncodeSessionRgbConversionCreateInfoVALVE {
pub s_type: vk::StructureType,
pub p_next: *const c_void,
pub rgb_model: u32,
pub rgb_range: u32,
pub x_chroma_offset: u32,
pub y_chroma_offset: u32,
}
/// `vk::StructureType` for a raw `ST_*` constant above.
#[inline]
pub fn stype(raw: i32) -> vk::StructureType {
vk::StructureType::from_raw(raw)
}
// ---------- ABI layout guard ----------
//
// These structs are hand-copied from the registry and handed to the driver through raw `p_next`
// chains, so nothing in the type system relates them to the C definitions any more: an edit that
// inserts, drops, widens or re-pads a field is not a compile error, it is the driver reading our
// bytes at the wrong offsets. The assertions below are the missing compile error. They are `const`
// rather than `#[cfg(test)]` (the shape `amf.rs` uses) so they hold in every build, including the
// shipped one, and on any target this module compiles for.
//
// What they catch: a changed field width, an inserted or removed field, a changed array length, a
// padding assumption that only holds on one target. What they CANNOT catch: swapping two fields of
// the same type — offsets are unchanged. That case is only caught by reading the registry, so the
// field order here was diffed against `vulkan_core.h` (Vulkan-Headers `main`, 2026-07-25) when
// these assertions were written, along with every `ST_*` and flag-bit value above.
//
// Deliberately duplicated in `vk_av1_encode.rs` rather than shared: both modules exist to be
// deleted wholesale once `ash` ships these bindings, and a shared helper would make deleting one
// break the other.
macro_rules! assert_abi_layout {
($t:ty { size: $size:expr, align: $align:expr $(, $field:ident @ $off:expr)* $(,)? }) => {
const _: () = {
assert!(
::core::mem::size_of::<$t>() == $size,
concat!(stringify!($t), ": size does not match the C ABI")
);
assert!(
::core::mem::align_of::<$t>() == $align,
concat!(stringify!($t), ": alignment does not match the C ABI")
);
$(assert!(
::core::mem::offset_of!($t, $field) == $off,
concat!(stringify!($t), ".", stringify!($field), ": offset does not match the C ABI")
);)*
};
};
}
assert_abi_layout!(PhysicalDeviceVideoEncodeRgbConversionFeaturesVALVE {
size: 24, align: 8,
s_type @ 0,
p_next @ 8,
video_encode_rgb_conversion @ 16,
});
assert_abi_layout!(VideoEncodeRgbConversionCapabilitiesVALVE {
size: 32, align: 8,
s_type @ 0,
p_next @ 8,
rgb_models @ 16,
rgb_ranges @ 20,
x_chroma_offsets @ 24,
y_chroma_offsets @ 28,
});
assert_abi_layout!(VideoEncodeProfileRgbConversionInfoVALVE {
size: 24, align: 8,
s_type @ 0,
p_next @ 8,
perform_encode_rgb_conversion @ 16,
});
assert_abi_layout!(VideoEncodeSessionRgbConversionCreateInfoVALVE {
size: 32, align: 8,
s_type @ 0,
p_next @ 8,
rgb_model @ 16,
rgb_range @ 20,
x_chroma_offset @ 24,
y_chroma_offset @ 28,
});