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>
140 lines
6.4 KiB
Rust
140 lines
6.4 KiB
Rust
//! Generate bindings for `libavutil/hwcontext_vulkan.h` against the SYSTEM headers.
|
|
//!
|
|
//! ffmpeg-sys-next binds a curated header list that omits every hwcontext_*.h; the
|
|
//! Vulkan hwcontext structs (`AVVulkanDeviceContext`, `AVVkFrame`) are what let us run
|
|
//! FFmpeg's Vulkan Video decoder on the presenter's own VkDevice and read the decoded
|
|
//! VkImages back. Their layout depends on compile-time FF_API_* deprecation gates in
|
|
//! libavutil/version.h, so bindgen over the installed header is the only ABI-safe
|
|
//! source of truth — hand transcription would silently skew on the next FFmpeg bump.
|
|
//!
|
|
//! Header discovery is per-OS: Linux asks pkg-config; Windows reuses the FFMPEG_DIR
|
|
//! tree ffmpeg-sys-next links against (BtbN trees ship no .pc files) plus an explicit
|
|
//! Vulkan-Headers include dir, since Windows has no system <vulkan/vulkan.h>. Other
|
|
//! targets get an empty file: the workspace builds on macOS (clients/apple is the
|
|
//! client there).
|
|
|
|
use std::env;
|
|
use std::path::PathBuf;
|
|
|
|
fn main() {
|
|
println!("cargo:rerun-if-changed=wrapper.h");
|
|
println!("cargo:rerun-if-env-changed=PF_FFVK_VULKAN_INCLUDE");
|
|
let out = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs");
|
|
|
|
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
|
|
let includes = match target_os.as_str() {
|
|
"linux" => linux_includes(),
|
|
"windows" => windows_includes(),
|
|
_ => {
|
|
std::fs::write(
|
|
&out,
|
|
"// pf-ffvk: Linux/Windows-only, empty on this target\n",
|
|
)
|
|
.unwrap();
|
|
return;
|
|
}
|
|
};
|
|
|
|
let mut builder = bindgen::Builder::default()
|
|
.header("wrapper.h")
|
|
// The whole point of this crate: the Vulkan hwcontext surface…
|
|
.allowlist_type("AVVulkan.*")
|
|
.allowlist_type("AVVkFrame.*")
|
|
.allowlist_function("av_vk_frame_alloc")
|
|
.allowlist_function("av_vkfmt_from_pixfmt")
|
|
// The feature structs chained into AVVulkanDeviceContext.device_features (plain
|
|
// vulkan.h types; generating them here keeps the chain in one type system).
|
|
.allowlist_type("VkPhysicalDeviceVulkan11Features")
|
|
.allowlist_type("VkPhysicalDeviceVulkan12Features")
|
|
.allowlist_type("VkPhysicalDeviceVulkan13Features")
|
|
// AVVulkanFramesContext.img_flags values (plane views need MUTABLE_FORMAT).
|
|
.allowlist_type("VkImageCreateFlagBits")
|
|
// Timeline-semaphore wait — the pump measures true GPU decode completion.
|
|
.allowlist_type("VkSemaphoreWaitInfo")
|
|
.allowlist_type("PFN_vkWaitSemaphores")
|
|
.allowlist_type("PFN_vkGetDeviceProcAddr")
|
|
// …plus nothing else of FFmpeg: the core types these structs reference only
|
|
// ever appear behind pointers here, so keep them opaque instead of duplicating
|
|
// ffmpeg-sys-next's definitions (callers cast pointers between the crates).
|
|
.opaque_type("AVHWDeviceContext")
|
|
.opaque_type("AVHWFramesContext")
|
|
.opaque_type("AVBufferRef")
|
|
.opaque_type("AVFrame")
|
|
.derive_debug(false)
|
|
.layout_tests(true);
|
|
for dir in &includes {
|
|
builder = builder.clang_arg(format!("-I{}", dir.display()));
|
|
}
|
|
let bindings = builder.generate().expect(
|
|
"bindgen over libavutil/hwcontext_vulkan.h failed — is `vulkan-headers` installed? \
|
|
(the header includes <vulkan/vulkan.h>)",
|
|
);
|
|
bindings.write_to_file(&out).unwrap();
|
|
|
|
// The av_vk_* symbols live in libavutil, which ffmpeg-sys-next already links into
|
|
// every consumer of this crate; no extra link flags needed. Emitting the lib anyway
|
|
// keeps `cargo test -p pf-ffvk` linking standalone — which on Windows also needs the
|
|
// import-lib search path (there is no system linker path for FFmpeg there).
|
|
if target_os == "windows" {
|
|
// windows_includes() already required FFMPEG_DIR.
|
|
let ff = PathBuf::from(env::var("FFMPEG_DIR").unwrap());
|
|
println!(
|
|
"cargo:rustc-link-search=native={}",
|
|
ff.join("lib").display()
|
|
);
|
|
}
|
|
println!("cargo:rustc-link-lib=avutil");
|
|
}
|
|
|
|
/// Include paths from pkg-config (libavutil for the hwcontext header; the Vulkan
|
|
/// headers usually live in /usr/include, but honor a registered vulkan.pc too).
|
|
/// PF_FFVK_VULKAN_INCLUDE prepends an explicit Vulkan-Headers include dir — for
|
|
/// cross builds and boxes without the system package.
|
|
fn linux_includes() -> Vec<PathBuf> {
|
|
let mut includes: Vec<PathBuf> = Vec::new();
|
|
if let Ok(dir) = env::var("PF_FFVK_VULKAN_INCLUDE") {
|
|
includes.push(PathBuf::from(dir));
|
|
}
|
|
let avutil = pkg_config::Config::new()
|
|
.cargo_metadata(false)
|
|
.probe("libavutil")
|
|
.expect("pkg-config: libavutil not found — install the FFmpeg dev package");
|
|
includes.extend(avutil.include_paths);
|
|
if let Ok(vk) = pkg_config::Config::new()
|
|
.cargo_metadata(false)
|
|
.probe("vulkan")
|
|
{
|
|
includes.extend(vk.include_paths);
|
|
}
|
|
includes
|
|
}
|
|
|
|
/// No pkg-config on Windows: headers come from the FFMPEG_DIR tree (the same BtbN
|
|
/// lgpl-shared tree ffmpeg-sys-next links against) plus an explicit Vulkan-Headers
|
|
/// dir — PF_FFVK_VULKAN_INCLUDE (provision-windows-punktfunk-extras.ps1 stages
|
|
/// C:\Users\Public\vulkan-headers) or an installed Vulkan SDK. Only headers are
|
|
/// needed at build time; the loader (vulkan-1.dll) is a GPU-driver component and is
|
|
/// never linked here.
|
|
fn windows_includes() -> Vec<PathBuf> {
|
|
println!("cargo:rerun-if-env-changed=FFMPEG_DIR");
|
|
println!("cargo:rerun-if-env-changed=VULKAN_SDK");
|
|
let mut includes: Vec<PathBuf> = Vec::new();
|
|
if let Ok(dir) = env::var("PF_FFVK_VULKAN_INCLUDE") {
|
|
includes.push(PathBuf::from(dir));
|
|
} else if let Ok(sdk) = env::var("VULKAN_SDK") {
|
|
includes.push(PathBuf::from(sdk).join("Include"));
|
|
} else {
|
|
panic!(
|
|
"pf-ffvk: no Vulkan headers — set PF_FFVK_VULKAN_INCLUDE to a Vulkan-Headers \
|
|
include dir (scripts/ci/provision-windows-punktfunk-extras.ps1 stages \
|
|
C:\\Users\\Public\\vulkan-headers\\include) or install the Vulkan SDK (VULKAN_SDK)"
|
|
);
|
|
}
|
|
let ff = env::var("FFMPEG_DIR").expect(
|
|
"pf-ffvk: FFMPEG_DIR not set — point it at the FFmpeg tree \
|
|
(scripts/ci/provision-windows-punktfunk-extras.ps1 stages C:\\Users\\Public\\ffmpeg)",
|
|
);
|
|
includes.push(PathBuf::from(ff).join("include"));
|
|
includes
|
|
}
|