58ccd530fc
ffmpeg-sys-next binds a curated header list that omits every hwcontext_*.h, so AVVulkanDeviceContext/AVVulkanFramesContext/AVVkFrame — the surface that lets FFmpeg's Vulkan Video decoder run on the presenter's own VkDevice and hand the decoded VkImages back — had no Rust bindings at all. pf-ffvk generates them at build time from the SYSTEM headers, which is the only ABI-safe option: the struct layout depends on FF_API_* deprecation gates resolved in libavutil/version.h (confirmed: FFmpeg 8.1 still carries the FIXED_QUEUES fields). Ships ash<->vulkan.h handle conversions; opaque AVHW*/AVFrame types keep ffmpeg-sys-next the owner of the core surface (pointer casts across). Needs vulkan-headers (Arch) / libvulkan-dev (CI, added); PF_FFVK_VULKAN_INCLUDE overrides for cross builds. Verified: bindgen layout tests pass, av_vk_frame_alloc links and zero-initializes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
92 lines
3.2 KiB
Rust
92 lines
3.2 KiB
Rust
//! FFmpeg's Vulkan hwcontext surface (`AVVulkanDeviceContext`, `AVVulkanFramesContext`,
|
|
//! `AVVkFrame`), bindgen-generated from the system headers at build time — see build.rs
|
|
//! for why this must not be hand-transcribed.
|
|
//!
|
|
//! The raw bindings use vulkan.h's own handle types (pointers on 64-bit). The [`ash`]
|
|
//! conversion helpers below cross between them and ash's u64-newtype handles; both sides
|
|
//! are the same underlying Vulkan object handles, so the casts are value-preserving.
|
|
|
|
#![allow(non_upper_case_globals)]
|
|
#![allow(non_camel_case_types)]
|
|
#![allow(non_snake_case)]
|
|
#![allow(clippy::missing_safety_doc)]
|
|
// bindgen's layout tests deref-null-pointer by design; silence the lints they trip.
|
|
#![allow(deref_nullptr)]
|
|
#![allow(unnecessary_transmutes)]
|
|
|
|
#[cfg(target_os = "linux")]
|
|
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
|
|
|
/// Conversions between the generated vulkan.h handle types and ash's.
|
|
#[cfg(target_os = "linux")]
|
|
pub mod ashx {
|
|
use super::*;
|
|
use ash::vk::Handle as _;
|
|
|
|
/// vulkan.h non-dispatchable handles are `*mut T` on 64-bit; ash's are `u64`
|
|
/// newtypes. Same bits either way.
|
|
pub fn image(h: VkImage) -> ash::vk::Image {
|
|
ash::vk::Image::from_raw(h as u64)
|
|
}
|
|
|
|
pub fn semaphore(h: VkSemaphore) -> ash::vk::Semaphore {
|
|
ash::vk::Semaphore::from_raw(h as u64)
|
|
}
|
|
|
|
pub fn image_layout(l: VkImageLayout) -> ash::vk::ImageLayout {
|
|
ash::vk::ImageLayout::from_raw(l as i32)
|
|
}
|
|
|
|
// --- ash → vulkan.h (filling AVVulkanDeviceContext) ---------------------------------
|
|
|
|
pub fn to_instance(h: ash::vk::Instance) -> VkInstance {
|
|
h.as_raw() as VkInstance
|
|
}
|
|
|
|
pub fn to_physical_device(h: ash::vk::PhysicalDevice) -> VkPhysicalDevice {
|
|
h.as_raw() as VkPhysicalDevice
|
|
}
|
|
|
|
pub fn to_device(h: ash::vk::Device) -> VkDevice {
|
|
h.as_raw() as VkDevice
|
|
}
|
|
|
|
/// ash's loader-level `vkGetInstanceProcAddr` as the header's PFN type. Both are the
|
|
/// same C ABI function pointer (`extern "system"` == `extern "C"` on the platforms
|
|
/// this crate builds for).
|
|
pub fn to_get_proc_addr(
|
|
f: unsafe extern "system" fn(
|
|
ash::vk::Instance,
|
|
*const std::ffi::c_char,
|
|
) -> ash::vk::PFN_vkVoidFunction,
|
|
) -> PFN_vkGetInstanceProcAddr {
|
|
unsafe { std::mem::transmute(f) }
|
|
}
|
|
}
|
|
|
|
#[cfg(all(test, target_os = "linux"))]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
/// The allocator runs (links against the system libavutil) and the struct is
|
|
/// readable at the offsets bindgen computed — sem_value zero-initialized.
|
|
#[test]
|
|
fn vk_frame_alloc_links_and_zeroes() {
|
|
unsafe {
|
|
let f = av_vk_frame_alloc();
|
|
assert!(!f.is_null(), "av_vk_frame_alloc returned NULL");
|
|
assert_eq!((*f).sem_value[0], 0);
|
|
assert_eq!((*f).queue_family[0], 0);
|
|
// Leak the one test frame rather than binding av_free here.
|
|
}
|
|
}
|
|
|
|
/// AV_NUM_DATA_POINTERS-sized arrays came through with the right length.
|
|
#[test]
|
|
fn frame_arrays_are_av_num_data_pointers() {
|
|
let f: AVVkFrame = unsafe { std::mem::zeroed() };
|
|
assert_eq!(f.img.len(), 8);
|
|
assert_eq!(f.sem_value.len(), 8);
|
|
}
|
|
}
|