d6647b9183
The punktfunk-session Vulkan client (clients/linux-session, now clients/session) builds and runs on Windows; the WinUI shell spawns it for every stream. Verified live: 10-bit HEVC via Vulkan Video on both AMD (iGPU) and NVIDIA, 5120x1440 at 130 fps / 8 ms end-to-end on the RTX 4090. - pf-ffvk: Windows bindgen branch (FFMPEG_DIR + PF_FFVK_VULKAN_INCLUDE, no pkg-config); provisioning fetches Vulkan-Headers (pinned v1.4.309). - pf-client-core: builds on Windows — WASAPI audio (audio_wasapi.rs, cfg-swapped via #[path], same surface as the PipeWire twin), VAAPI/dmabuf gated inline (chain = vulkan -> software), trust reads the WinUI shell's %APPDATA% stores (parity tests pin both serialized shapes), Settings gains adapter/hdr_enabled (serde-defaulted; Linux stores unaffected). - pf-presenter: builds on Windows — dmabuf module Linux-gated; SDL keyboard grab while captured (Alt+Tab/Win reach the host); pick_device ranks discrete over integrated (device 0 was the iGPU on hybrid boxes — the silent footgun) and honors PUNKTFUNK_VK_ADAPTER (the Settings GPU pick, exported by the session). - run loop: block in one SDL wait woken by input AND decoded frames (a per- session forwarder pushes a FrameWake user event) instead of a 1 ms poll — measured 111%% -> 5%% of a core (NVIDIA), 86%% -> 3.5%% (AMD), stats unchanged. The pump's decode-fence wait became once-per-window sampling (no per-frame pipeline stall; the stat now shows true backlog). - pf-console-ui: builds on Windows (skia-safe msvc prebuilts); font lookup falls through fontconfig aliases to concrete DirectWrite families (Consolas/Segoe UI) — browse/coverflow works, verified against a live host. - WinUI shell: session-always via new src/spawn.rs (GTK spawn.rs port — CREATE_NO_WINDOW, stdout contract, kill handle); the Stream screen is a status card (chips + stage lines from the child's stats). The legacy in-process D3D11VA path stays behind Settings "Streaming engine" / PUNKTFUNK_BUILTIN_ STREAM=1 as the A/B baseline until Phase 8 deletes it. SessionParams.video_caps makes the HDR toggle real. - clients/linux-session renamed to clients/session (builds for both OSes). - CI/MSIX: both workflows build/test both bins with widened path filters; the MSIX ships punktfunk-session.exe. ARM64 session builds --no-default-features (rust-skia has no aarch64-pc-windows-msvc prebuilts; flip when it does). A/B on this box (5120x1440 HEVC vs home-worker-5): NVIDIA Vulkan 130 fps / 8 ms e2e / 1.6 ms decode — clearly better than the built-in path. The AMD iGPU VCN saturates at ~52 fps where its own D3D11VA does ~70 — Adrenalin Vulkan decode is slower on APU silicon; discrete RDNA validation gates Phase 8. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
95 lines
3.4 KiB
Rust
95 lines
3.4 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(any(target_os = "linux", windows))]
|
|
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
|
|
|
/// Conversions between the generated vulkan.h handle types and ash's.
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
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)
|
|
}
|
|
|
|
// bindgen's enum repr is target-dependent: u32 on Linux (clang default), i32 on
|
|
// MSVC — so the cast is required on one target and a same-type no-op on the other.
|
|
#[allow(clippy::unnecessary_cast)]
|
|
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, any(target_os = "linux", windows)))]
|
|
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);
|
|
}
|
|
}
|