feat(clients/windows): port the Vulkan session client to Windows — session-always
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>
This commit is contained in:
@@ -8,17 +8,26 @@ license.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
# Same Linux+Windows gating as the rest of the client stack.
|
||||
[target.'cfg(any(target_os = "linux", windows))'.dependencies]
|
||||
pf-presenter = { path = "../pf-presenter" }
|
||||
# MenuEvent/MenuPulse (the gamepad service's menu mode drives the library).
|
||||
pf-client-core = { path = "../pf-client-core" }
|
||||
|
||||
# Skia on the presenter's VkDevice (`vulkan`); `textlayout` = skparagraph/harfbuzz for
|
||||
# the typography the console library needs (~15 MB stripped, prebuilt binaries exist for
|
||||
# this feature set on x86_64-unknown-linux-gnu — a source build is never triggered).
|
||||
# this feature set on x86_64-unknown-linux-gnu AND x86_64-pc-windows-msvc — a source
|
||||
# build is never triggered on either).
|
||||
skia-safe = { version = "0.87", features = ["vulkan", "textlayout"] }
|
||||
ash = { version = "0.38", features = ["loaded"] }
|
||||
sdl3 = { version = "0.18", features = ["hidapi", "ash"] }
|
||||
|
||||
anyhow = "1"
|
||||
tracing = "0.1"
|
||||
|
||||
# Linux links the system SDL3; Windows builds it from source (same choice as the rest
|
||||
# of the workspace's Windows SDL consumers).
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
sdl3 = { version = "0.18", features = ["hidapi", "ash"] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
sdl3 = { version = "0.18", features = ["hidapi", "ash", "build-from-source"] }
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
//! purpose, it proves the whole shared-device pipeline. The gamepad library moves in
|
||||
//! next.
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(any(target_os = "linux", windows))]
|
||||
pub mod library;
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(any(target_os = "linux", windows))]
|
||||
mod library_ui;
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(any(target_os = "linux", windows))]
|
||||
mod skia_overlay;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(any(target_os = "linux", windows))]
|
||||
pub use library::{LibraryGame, LibraryPhase, LibraryShared};
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(any(target_os = "linux", windows))]
|
||||
pub use skia_overlay::SkiaOverlay;
|
||||
|
||||
@@ -655,11 +655,27 @@ impl Fonts {
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve the first available family. Generic aliases ("sans-serif", "monospace")
|
||||
/// resolve through fontconfig on Linux; Windows' DirectWrite-backed FontMgr has no
|
||||
/// generic aliases, so the list falls through to concrete family names there.
|
||||
pub(crate) fn match_first_family(
|
||||
mgr: &skia_safe::FontMgr,
|
||||
families: &[&str],
|
||||
style: FontStyle,
|
||||
) -> Option<Typeface> {
|
||||
families
|
||||
.iter()
|
||||
.find_map(|f| mgr.match_family_style(f, style))
|
||||
}
|
||||
|
||||
pub(crate) fn build_fonts() -> Result<Fonts> {
|
||||
let mgr = skia_safe::FontMgr::new();
|
||||
let sans = mgr
|
||||
.match_family_style("sans-serif", FontStyle::normal())
|
||||
.ok_or_else(|| anyhow!("no sans-serif typeface via fontconfig"))?;
|
||||
let sans = match_first_family(
|
||||
&mgr,
|
||||
&["sans-serif", "Segoe UI", "Arial"],
|
||||
FontStyle::normal(),
|
||||
)
|
||||
.ok_or_else(|| anyhow!("no sans-serif typeface (fontconfig alias or system family)"))?;
|
||||
let mut collection = FontCollection::new();
|
||||
collection.set_default_font_manager(mgr, None);
|
||||
Ok(Fonts {
|
||||
|
||||
@@ -146,9 +146,12 @@ impl Overlay for SkiaOverlay {
|
||||
.ok_or_else(|| anyhow!("Skia DirectContext over the shared device"))?;
|
||||
context.set_resource_cache_limit(RESOURCE_CACHE_BYTES);
|
||||
|
||||
let typeface = FontMgr::new()
|
||||
.match_family_style("monospace", skia_safe::FontStyle::normal())
|
||||
.context("no monospace typeface via fontconfig")?;
|
||||
let typeface = crate::library_ui::match_first_family(
|
||||
&FontMgr::new(),
|
||||
&["monospace", "Consolas", "Cascadia Mono", "Courier New"],
|
||||
skia_safe::FontStyle::normal(),
|
||||
)
|
||||
.context("no monospace typeface (fontconfig alias or system family)")?;
|
||||
self.font = Some(Font::new(typeface, 14.0));
|
||||
self.fonts = Some(build_fonts()?);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user