From f24379c2f888cd32f29bf30c8d94ae2b4bb3f132 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Wed, 15 Jul 2026 13:53:13 +0200 Subject: [PATCH] feat(host,clients): PyroWave ships in default builds; NVIDIA hosts advertise it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flip the `pyrowave` cargo feature into the default set across punktfunk-host, pf-client-core, pf-presenter and the session client — every packaged build (flatpak, arch/rpm/copr, windows x64 client) now carries the codec. Selection stays strictly per-session opt-in: a client must pick "PyroWave (wired LAN)" in Settings (or PUNKTFUNK_PREFER_PYROWAVE=1); nothing changes for normal HEVC/AV1 sessions. The Windows ARM64 client leg builds --no-default-features and keeps skipping it (decode is Linux-native + Apple Metal today). Advertisement no longer waits for the PUNKTFUNK_ENCODER=pyrowave lab override on NVIDIA: host_wire_caps sets the bit whenever the feature is present and the host isn't the GPU-less software pref, and SessionPlan::output_format flips a PyroWave session on the NVIDIA-auto capture path to CPU RGB frames (the EGL→CUDA import only NVENC consumes; the wavelet backend ingests raw dmabufs or CPU RGB). AMD/Intel keep their raw-dmabuf zero-copy unchanged; per-session raw-dmabuf passthrough on NVIDIA (true zero-copy without the env's global capture policy) stays a follow-up. On-glass on .21 (RTX 5070 Ti, default-features binaries, NO env overrides): host advertises + negotiates PyroWave, the CPU-capture fallback engages, 60 fps at e2e 3.2-5.7 ms p50, and a mid-stream 1080p→720p resize rides on top cleanly. Workspace clippy --locked clean; 33 client + 314 host tests. Co-Authored-By: Claude Fable 5 --- clients/session/Cargo.toml | 8 +++++--- crates/pf-client-core/Cargo.toml | 6 ++++-- crates/pf-presenter/Cargo.toml | 3 ++- crates/punktfunk-host/Cargo.toml | 9 +++++--- crates/punktfunk-host/src/encode.rs | 25 ++++++++++------------- crates/punktfunk-host/src/session_plan.rs | 20 ++++++++++++++++++ docs-site/content/docs/pyrowave.md | 12 +++++------ 7 files changed, 54 insertions(+), 29 deletions(-) diff --git a/clients/session/Cargo.toml b/clients/session/Cargo.toml index 1de25b18..e57559ef 100644 --- a/clients/session/Cargo.toml +++ b/clients/session/Cargo.toml @@ -13,9 +13,11 @@ name = "punktfunk-session" path = "src/main.rs" [features] -default = ["ui"] -# PyroWave client decode (the opt-in wired-LAN wavelet codec) — enables the decode -# backend + the planar present path; runtime opt-in stays PUNKTFUNK_PREFER_PYROWAVE=1. +default = ["ui", "pyrowave"] +# PyroWave client decode (the wired-LAN wavelet codec) — enables the decode backend + the +# planar present path. ON by default; each session still opts in explicitly (the Settings +# codec pick, or PUNKTFUNK_PREFER_PYROWAVE=1). The Windows ARM64 leg builds +# --no-default-features and so skips it (video decode is Linux-only anyway). pyrowave = ["pf-client-core/pyrowave", "pf-presenter/pyrowave"] # The Skia console UI (stats OSD, capture HUD, later the gamepad library). Dropping it # (`--no-default-features`) is the ~15 MB-smaller power-user build: same streaming, diff --git a/crates/pf-client-core/Cargo.toml b/crates/pf-client-core/Cargo.toml index 62c4b56d..ac5a03c9 100644 --- a/crates/pf-client-core/Cargo.toml +++ b/crates/pf-client-core/Cargo.toml @@ -64,6 +64,8 @@ windows = { git = "https://github.com/microsoft/windows-rs", rev = "a4f7b2cb7c63 ] } [features] -# PyroWave client decode — OFF by default (the flatpak/default builds stay unchanged); -# the Linux session client turns it on together with the host-side feature. +# PyroWave client decode ships in every default build (flatpak included; pyrowave-sys is a +# vendored in-repo tree, offline-safe, and an empty stub off Linux/Windows). The codec is +# still strictly per-session opt-in (Settings codec pick / PUNKTFUNK_PREFER_PYROWAVE=1). +default = ["pyrowave"] pyrowave = ["dep:pyrowave-sys", "dep:ash"] diff --git a/crates/pf-presenter/Cargo.toml b/crates/pf-presenter/Cargo.toml index 68f0024c..7c2e3ad3 100644 --- a/crates/pf-presenter/Cargo.toml +++ b/crates/pf-presenter/Cargo.toml @@ -46,5 +46,6 @@ windows-sys = { version = "0.61", features = [ [features] # PyroWave planar present path (the wired-LAN wavelet codec) — forwards to the decode -# backend in pf-client-core; OFF by default. +# backend in pf-client-core; ON by default, matching pf-client-core's default. +default = ["pyrowave"] pyrowave = ["pf-client-core/pyrowave"] diff --git a/crates/punktfunk-host/Cargo.toml b/crates/punktfunk-host/Cargo.toml index e07bece1..2cf384e3 100644 --- a/crates/punktfunk-host/Cargo.toml +++ b/crates/punktfunk-host/Cargo.toml @@ -251,6 +251,9 @@ pf-driver-proto = { path = "../pf-driver-proto" } bytemuck = { version = "1.19", features = ["derive"] } [features] +# PyroWave ships in every default build (the codec stays strictly opt-in per session — a client +# must explicitly prefer CODEC_PYROWAVE; nothing changes for normal HEVC/AV1 sessions). +default = ["pyrowave"] # NVENC hardware encode (Windows). OFF by default (it pulls the NVENC SDK crate); nothing is # needed at link time — the entry points are resolved at RUNTIME from the driver's # nvEncodeAPI64.dll (encode/windows/nvenc.rs `load_api`), so the same binary starts fine on @@ -269,9 +272,9 @@ amf-qsv = ["dep:ffmpeg-next"] # PUNKTFUNK_VULKAN_ENCODE (opt-in for now). Build the AMD/Intel RFI host with `--features vulkan-encode`. vulkan-encode = [] # PyroWave — the opt-in wired-LAN intra-only wavelet codec (design/pyrowave-codec-plan.md). -# Builds the vendored codec from source (crates/pyrowave-sys, CMake + bindgen; Linux/Windows). -# OFF by default; runtime-gated further behind an explicit PUNKTFUNK_ENCODER=pyrowave. -# EXPERIMENTAL until CODEC_PYROWAVE negotiation lands (plan Phase 2). +# Builds the vendored codec from source (crates/pyrowave-sys, CMake + bindgen; Linux/Windows — +# the encoder backend itself is Linux-only, the Windows host just carries the library). ON by +# default (see `default` above); sessions reach it only through explicit client opt-in. pyrowave = ["dep:pyrowave-sys"] # Build-time icon/version-info embedding (build.rs; Windows dev/CI hosts only — Linux packaging diff --git a/crates/punktfunk-host/src/encode.rs b/crates/punktfunk-host/src/encode.rs index e3ba51bf..e5683188 100644 --- a/crates/punktfunk-host/src/encode.rs +++ b/crates/punktfunk-host/src/encode.rs @@ -108,21 +108,18 @@ impl Codec { /// [`punktfunk_core::quic::resolve_codec`] against the client's advertised codecs. pub fn host_wire_caps() -> u8 { // PyroWave rides ON TOP of whatever H.26x set resolves below: feature-gated, Linux-only - // for now (the Windows host leg is blocked on the .173 D3D11 interop debt), and inert in - // negotiation unless the client explicitly prefers it (resolve_codec ignores the bit in - // its ladder). Advertised only when the capture side would actually deliver frames the - // backend ingests (raw-dmabuf passthrough / CPU RGB): `linux_zero_copy_is_vaapi()` — - // true on AMD/Intel auto and under an explicit PUNKTFUNK_ENCODER=pyrowave. On an NVIDIA - // host with `auto`, capture resolves to the EGL→CUDA import the backend can't consume, - // so the bit stays off until the OutputFormat plumbing carries a per-session - // raw-dmabuf decision (Phase 3); the operator opts in with the env instead (plan §3). + // for now (the Windows host encoder is future work), and inert in negotiation unless the + // client explicitly prefers it (resolve_codec ignores the bit in its ladder). Advertised + // whenever the backend could open: AMD/Intel capture hands raw dmabufs it imports + // directly, and an NVIDIA-auto host's PyroWave sessions flip capture to CPU RGB + // per-session instead ([`crate::session_plan::SessionPlan::output_format`]) — the EGL→CUDA + // frames the `auto` GPU path would deliver are NVENC-only. Only a software/GPU-less pref + // keeps the bit off (no Vulkan device to open). #[cfg(all(target_os = "linux", feature = "pyrowave"))] - let pyro = if linux_zero_copy_is_vaapi() - && !matches!( - crate::config::config().encoder_pref.as_str(), - // A software pref usually means a GPU-less box — no Vulkan device to open. - "software" | "sw" | "openh264" - ) { + let pyro = if !matches!( + crate::config::config().encoder_pref.as_str(), + "software" | "sw" | "openh264" + ) { punktfunk_core::quic::CODEC_PYROWAVE } else { 0u8 diff --git a/crates/punktfunk-host/src/session_plan.rs b/crates/punktfunk-host/src/session_plan.rs index f203bffb..0144b4a0 100644 --- a/crates/punktfunk-host/src/session_plan.rs +++ b/crates/punktfunk-host/src/session_plan.rs @@ -153,6 +153,26 @@ impl SessionPlan { } gpu && !force_cpu_for_nvenc_444 }; + // PyroWave on an NVIDIA-auto host: the `gpu` capture path resolves to the EGL→CUDA + // import that only NVENC can consume — the wavelet backend ingests raw dmabufs + // (the AMD/Intel path) or CPU RGB. Flip THIS session to CPU RGB capture; the + // Phase-2 exit sessions ran exactly this shape at 60 fps (the encode itself stays + // sub-ms GPU compute). Per-session raw-dmabuf passthrough on NVIDIA (true + // zero-copy without the PUNKTFUNK_ENCODER=pyrowave capture policy) is the + // follow-up; the AMD/Intel dmabuf path is untouched. + #[cfg(target_os = "linux")] + let gpu = { + let pyro_needs_cpu = self.codec == crate::encode::Codec::PyroWave + && !crate::encode::linux_zero_copy_is_vaapi(); + if gpu && pyro_needs_cpu { + tracing::info!( + "PyroWave session on the NVIDIA capture path: GPU (CUDA) capture disabled \ + for this session — frames arrive as CPU RGB and upload to the wavelet \ + encoder (raw-dmabuf zero-copy on NVIDIA is a follow-up)" + ); + } + gpu && !pyro_needs_cpu + }; crate::capture::OutputFormat { gpu, hdr: self.hdr, diff --git a/docs-site/content/docs/pyrowave.md b/docs-site/content/docs/pyrowave.md index 15a38392..4299eff4 100644 --- a/docs-site/content/docs/pyrowave.md +++ b/docs-site/content/docs/pyrowave.md @@ -44,13 +44,13 @@ setting. ## Turning it on -1. **Host** (Linux): build/install a host with the `pyrowave` feature. On an NVIDIA host the - capture path additionally needs `PUNKTFUNK_ENCODER=pyrowave` in `host.env` for the codec to - be advertised; AMD/Intel hosts advertise it automatically when the feature is present. +1. **Host** (Linux): nothing to do — default builds ship the codec and every Linux GPU host + advertises it. AMD/Intel hosts encode from the capture dmabuf zero-copy; on an NVIDIA host + a PyroWave session currently captures via CPU RGB (a modest host-side cost that only + affects sessions that picked this codec — everything else keeps its zero-copy path). 2. **Client**: - - Linux session client (with the `pyrowave` feature): set **Settings → Video codec → - PyroWave (wired LAN)** in the gamepad console, or launch with - `PUNKTFUNK_PREFER_PYROWAVE=1`. + - Linux session client: set **Settings → Video codec → PyroWave (wired LAN)** in the + gamepad console, or launch with `PUNKTFUNK_PREFER_PYROWAVE=1`. - Apple (Mac, Apple TV 4K, iPad — wired networking strongly recommended): set **Settings → Codec → PyroWave (wired LAN)**. The option appears only on devices whose GPU passes the decode probe (Apple Silicon and A13-class or newer); picking it forces