From 2f9e61d1915fffa76893dead6665b42e30174a54 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Mon, 6 Jul 2026 10:33:30 +0000 Subject: [PATCH] feat(host): enable GPU zero-copy by default on all backends Now that the per-capture worker subprocess makes an NVENC EGL/CUDA driver fault survivable (design/zerocopy-worker-isolation.md), the reason the NVENC zero-copy path stayed opt-in is gone. zerocopy::enabled() now defaults ON for both GPU backends (was ON VAAPI / OFF NVENC). Fallbacks are intact: VAAPI's one-shot CPU auto-downgrade (VAAPI-gated, never trips for NVENC) and NVENC's per-capture fallback + worker-death latch. Reframe the shipped host.env examples and setup guides to rely on the default rather than force PUNKTFUNK_ZEROCOPY=1 (an explicit =1 skips the VAAPI auto-downgrade). Co-Authored-By: Claude Opus 4.8 --- .../punktfunk-host/src/linux/zerocopy/mod.rs | 21 +++++++++++-------- docs-site/content/docs/bazzite.md | 4 ++-- docs-site/content/docs/configuration.md | 2 +- docs-site/content/docs/gnome.md | 2 +- docs-site/content/docs/kde.md | 2 +- docs-site/content/docs/sway.md | 2 +- packaging/bazzite/README.md | 11 +++++----- packaging/bazzite/host.env | 5 +++-- packaging/kde/host.env | 2 +- scripts/headless/run-headless-kde.sh | 4 ++-- scripts/host.env.example | 5 +++-- 11 files changed, 33 insertions(+), 27 deletions(-) diff --git a/crates/punktfunk-host/src/linux/zerocopy/mod.rs b/crates/punktfunk-host/src/linux/zerocopy/mod.rs index f9ff25d..fdee370 100644 --- a/crates/punktfunk-host/src/linux/zerocopy/mod.rs +++ b/crates/punktfunk-host/src/linux/zerocopy/mod.rs @@ -51,18 +51,21 @@ pub fn vaapi_dmabuf_forced() -> bool { flag_opt("PUNKTFUNK_ZEROCOPY") == Some(true) } -/// Whether the zero-copy path is on. `PUNKTFUNK_ZEROCOPY` decides when set (truthy = on, else -/// off). Unset defaults **on for the VAAPI (AMD/Intel) backend** — the stock AMD/Intel install -/// gets the GPU dmabuf path, not three full-frame CPU touches — unless a failed negotiation -/// downgraded it ([`note_vaapi_dmabuf_failed`]); and **off for NVENC**, whose EGL→CUDA import -/// stays opt-in (Mutter+NVIDIA has known dmabuf-capture races; see `PUNKTFUNK_FORCE_SHM`). +/// Whether the zero-copy path is on. `PUNKTFUNK_ZEROCOPY` decides when set (truthy = on, else off). +/// **Unset defaults ON for both GPU backends** — the stock install gets the GPU dmabuf path, not +/// three full-frame CPU touches. This includes NVENC (previously opt-in): the EGL→CUDA (tiled) and +/// Vulkan (LINEAR) imports now run in a per-capture worker subprocess +/// (`design/zerocopy-worker-isolation.md`), so a driver fault on a producer-invalidated dmabuf kills +/// the worker and the host degrades to its capture-loss rebuild instead of dying — the reason the +/// NVENC path stayed opt-in is gone. Fallbacks stay in place: VAAPI has a one-shot CPU downgrade if +/// the LINEAR-dmabuf offer never negotiates ([`note_vaapi_dmabuf_failed`]); NVENC falls back per +/// capture when no importer/importable modifier is available and latches the import off after +/// repeated worker deaths. `PUNKTFUNK_ZEROCOPY=0` opts out; `PUNKTFUNK_FORCE_SHM` forces the +/// race-free SHM path. pub fn enabled() -> bool { match flag_opt("PUNKTFUNK_ZEROCOPY") { Some(v) => v, - None => { - crate::encode::linux_zero_copy_is_vaapi() - && !VAAPI_DMABUF_FAILED.load(Ordering::Relaxed) - } + None => !VAAPI_DMABUF_FAILED.load(Ordering::Relaxed), } } diff --git a/docs-site/content/docs/bazzite.md b/docs-site/content/docs/bazzite.md index 1ac2d47..0350664 100644 --- a/docs-site/content/docs/bazzite.md +++ b/docs-site/content/docs/bazzite.md @@ -85,13 +85,13 @@ cp /usr/share/punktfunk/host.env.bazzite ~/.config/punktfunk/host.env The template is deliberately minimal — it does **not** force a compositor, because the host auto-detects Gaming Mode (gamescope) vs Desktop (KWin) on every connect and follows the switch -mid-stream. The only settings that matter are the session anchors plus zero-copy: +mid-stream. The only settings that matter are the session anchors (GPU zero-copy is on by default): ```sh XDG_RUNTIME_DIR=/run/user/1000 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus PUNKTFUNK_VIDEO_SOURCE=virtual -PUNKTFUNK_ZEROCOPY=1 # GPU zero-copy (dmabuf → CUDA → NVENC); auto-falls back to CPU +# GPU zero-copy (dmabuf → CUDA → NVENC) is ON by default; auto-falls back to CPU. Set =0 to force CPU. PUNKTFUNK_GAMESCOPE_ATTACH=1 # Gaming Mode = attach to the box's own session (see below) ``` diff --git a/docs-site/content/docs/configuration.md b/docs-site/content/docs/configuration.md index c92cb64..878beff 100644 --- a/docs-site/content/docs/configuration.md +++ b/docs-site/content/docs/configuration.md @@ -36,7 +36,7 @@ On Linux the host **rewrites `WAYLAND_DISPLAY` / `XDG_CURRENT_DESKTOP` / `XDG_RU |---|---|---| | `PUNKTFUNK_COMPOSITOR` | `kwin` · `mutter` · `gamescope` · `wlroots` (aliases: `kde`/`plasma`, `gnome`, `sway`/`hyprland`) | Which backend creates the virtual display. **Leave unset to auto-detect;** set only to force one. | | `PUNKTFUNK_VIDEO_SOURCE` | `virtual` · `portal` | `virtual` creates a per-client display at the client's exact mode (the normal choice). `portal` captures an existing monitor instead. | -| `PUNKTFUNK_ZEROCOPY` | `1` · `0` | GPU zero-copy capture→encode (dmabuf → CUDA → NVENC, or D3D11 on Windows). Leave on; it falls back to a CPU path automatically. | +| `PUNKTFUNK_ZEROCOPY` | `1` · `0` *(default on)* | GPU zero-copy capture→encode (dmabuf → CUDA → NVENC, or D3D11 on Windows). **On by default** — no need to set it; it falls back to a CPU path automatically. Set `0` to force the CPU path. | | `PUNKTFUNK_INPUT_BACKEND` | `libei` · `gamescope` · `wlr` · `uinput` | How input is injected. `libei` for GNOME/KDE, `gamescope` for Bazzite/gamescope, `wlr` for Sway/wlroots. Auto-detected with the compositor. | | `PUNKTFUNK_ENCODER` | `auto` · `nvenc` · `vaapi` (Linux) · `amf` · `qsv` (Windows) · `software` | Encoder backend. `auto` (default) detects the GPU vendor: NVIDIA→NVENC, AMD→VAAPI/AMF, Intel→VAAPI/QSV. `software` (aliases `sw`/`openh264`) is the GPU-less H.264 path on both platforms — on Windows `auto` falls back to it when no GPU is found; on Linux it is **explicit-only** (`auto` never picks it). | | `PUNKTFUNK_RENDER_NODE` | path | Linux DRM render node for zero-copy (default `/dev/dri/renderD128`). Set on multi-GPU boxes to pick the right GPU. | diff --git a/docs-site/content/docs/gnome.md b/docs-site/content/docs/gnome.md index 83195fe..0434168 100644 --- a/docs-site/content/docs/gnome.md +++ b/docs-site/content/docs/gnome.md @@ -21,7 +21,7 @@ WAYLAND_DISPLAY=wayland-0 XDG_CURRENT_DESKTOP=GNOME PUNKTFUNK_COMPOSITOR=mutter PUNKTFUNK_VIDEO_SOURCE=virtual -PUNKTFUNK_ZEROCOPY=1 +# GPU zero-copy (dmabuf → CUDA → NVENC) is ON by default; auto-falls back to CPU. Set =0 to force CPU. PUNKTFUNK_INPUT_BACKEND=libei ``` diff --git a/docs-site/content/docs/kde.md b/docs-site/content/docs/kde.md index 9d2c1ed..6996574 100644 --- a/docs-site/content/docs/kde.md +++ b/docs-site/content/docs/kde.md @@ -20,7 +20,7 @@ WAYLAND_DISPLAY=wayland-0 XDG_CURRENT_DESKTOP=KDE PUNKTFUNK_COMPOSITOR=kwin PUNKTFUNK_VIDEO_SOURCE=virtual -PUNKTFUNK_ZEROCOPY=1 +# GPU zero-copy (dmabuf → CUDA → NVENC) is ON by default; auto-falls back to CPU. Set =0 to force CPU. PUNKTFUNK_INPUT_BACKEND=libei ``` diff --git a/docs-site/content/docs/sway.md b/docs-site/content/docs/sway.md index 81a2fc2..e737d3f 100644 --- a/docs-site/content/docs/sway.md +++ b/docs-site/content/docs/sway.md @@ -27,7 +27,7 @@ these in `~/.config/punktfunk/host.env`: PUNKTFUNK_COMPOSITOR=wlroots # aliases: sway, hyprland PUNKTFUNK_INPUT_BACKEND=wlr PUNKTFUNK_VIDEO_SOURCE=virtual -PUNKTFUNK_ZEROCOPY=1 # GPU zero-copy capture→encode; auto-falls back to CPU +# GPU zero-copy capture→encode is ON by default; auto-falls back to CPU. Set PUNKTFUNK_ZEROCOPY=0 to force CPU. ``` See [Configuration](/docs/configuration) for the full reference. diff --git a/packaging/bazzite/README.md b/packaging/bazzite/README.md index 3e1383a..cbebd42 100644 --- a/packaging/bazzite/README.md +++ b/packaging/bazzite/README.md @@ -242,8 +242,9 @@ PUNKTFUNK_GAMESCOPE_APP=steam -gamepadui # gamescope hosts its own EIS input socket — input lands in the nested session. PUNKTFUNK_INPUT_BACKEND=gamescope -# GPU zero-copy capture (dmabuf -> CUDA -> NVENC). Auto-falls back to CPU if unavailable. -PUNKTFUNK_ZEROCOPY=1 +# GPU zero-copy capture (dmabuf -> CUDA -> NVENC) is ON by default and auto-falls back to CPU if +# unavailable. No need to set it. Set to 0 only to force the CPU path. +# PUNKTFUNK_ZEROCOPY=0 #RUST_LOG=info ``` @@ -257,7 +258,7 @@ PUNKTFUNK_ZEROCOPY=1 | `PUNKTFUNK_VIDEO_SOURCE` | `virtual` | Create a per-client virtual output at the client's exact WxH@Hz (the flagship "native resolution, no scaling" mode), vs. `portal` which captures an existing monitor. | | `PUNKTFUNK_GAMESCOPE_APP` | `steam -gamepadui` | The command launched **inside** the nested gamescope — here, a SteamOS-style couch UI. Set it to whatever you want the session to run. | | `PUNKTFUNK_INPUT_BACKEND` | `gamescope` | Inject mouse/keyboard/gamepad into the nested gamescope via its own EIS socket. | -| `PUNKTFUNK_ZEROCOPY` | `1` | GPU zero-copy capture (dmabuf → CUDA → NVENC). Falls back to CPU automatically if unavailable. | +| `PUNKTFUNK_ZEROCOPY` | `on` *(default)* | GPU zero-copy capture (dmabuf → CUDA → NVENC), on by default. Falls back to CPU automatically if unavailable; set `0` to force the CPU path. | | `RUST_LOG` | (commented) | Uncomment `RUST_LOG=info` for verbose logs while debugging. | **Optional — a real DualSense for clients holding one:** add `PUNKTFUNK_GAMEPAD=dualsense` to present @@ -463,8 +464,8 @@ desktop viewer. after an `rpm-ostree`/`bootc` update, confirm the NVIDIA driver still loads (`nvidia-smi`) before blaming punktfunk. -- **`PUNKTFUNK_ZEROCOPY=1` but it falls back to CPU.** The zero-copy path needs working EGL/CUDA from - the NVIDIA driver. The code falls back to CPU automatically; check the log for the fallback line and +- **Zero-copy falls back to CPU.** The zero-copy path (on by default) needs working EGL/CUDA from the + NVIDIA driver. The code falls back to CPU automatically; check the log for the fallback line and verify the `-nvidia` image / driver is healthy. - **Wrong UID in `host.env`.** `XDG_RUNTIME_DIR=/run/user/1000` and the bus path assume UID 1000. Run diff --git a/packaging/bazzite/host.env b/packaging/bazzite/host.env index 44a1994..97e4cde 100644 --- a/packaging/bazzite/host.env +++ b/packaging/bazzite/host.env @@ -10,8 +10,9 @@ DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus PUNKTFUNK_VIDEO_SOURCE=virtual -# GPU zero-copy capture (dmabuf -> CUDA -> NVENC). Auto-falls back to CPU if unavailable. -PUNKTFUNK_ZEROCOPY=1 +# GPU zero-copy capture (dmabuf -> CUDA -> NVENC) is ON by default and auto-falls back to CPU if +# unavailable. No need to set it. Set to 0 only to force the CPU path. +# PUNKTFUNK_ZEROCOPY=0 #RUST_LOG=info diff --git a/packaging/kde/host.env b/packaging/kde/host.env index 5997740..c9ba2b7 100644 --- a/packaging/kde/host.env +++ b/packaging/kde/host.env @@ -8,7 +8,7 @@ WAYLAND_DISPLAY=wayland-kde XDG_CURRENT_DESKTOP=KDE PUNKTFUNK_COMPOSITOR=kwin PUNKTFUNK_VIDEO_SOURCE=virtual -PUNKTFUNK_ZEROCOPY=1 +# GPU zero-copy (dmabuf → CUDA → NVENC) is ON by default; auto-falls back to CPU. Set =0 to force CPU. PUNKTFUNK_INPUT_BACKEND=libei # UDP Generic Segmentation Offload on the send path: coalesce a frame's equal-size packets into # kernel super-buffers (one sendmsg per ~64 packets instead of one per packet) — the dominant diff --git a/scripts/headless/run-headless-kde.sh b/scripts/headless/run-headless-kde.sh index 63dcfc2..07df33c 100755 --- a/scripts/headless/run-headless-kde.sh +++ b/scripts/headless/run-headless-kde.sh @@ -13,8 +13,8 @@ # bash scripts/headless/run-headless-kde.sh [WxH] # default 1920x1080 # # Then in another shell: -# WAYLAND_DISPLAY=wayland-kde XDG_CURRENT_DESKTOP=KDE PUNKTFUNK_ZEROCOPY=1 \ -# punktfunk-host punktfunk1-host --source virtual --seconds 14400 +# WAYLAND_DISPLAY=wayland-kde XDG_CURRENT_DESKTOP=KDE \ +# punktfunk-host punktfunk1-host --source virtual --seconds 14400 # zero-copy is on by default set -euo pipefail RES="${1:-1920x1080}" diff --git a/scripts/host.env.example b/scripts/host.env.example index 9d26691..1bf1ee2 100644 --- a/scripts/host.env.example +++ b/scripts/host.env.example @@ -16,8 +16,9 @@ XDG_CURRENT_DESKTOP=KDE # resolution+refresh (the flagship mode); `portal` captures an existing monitor. PUNKTFUNK_VIDEO_SOURCE=virtual -# GPU zero-copy capture (EGL/Vulkan → CUDA → NVENC). Falls back to CPU automatically. -PUNKTFUNK_ZEROCOPY=1 +# GPU zero-copy capture (dmabuf → CUDA → NVENC / VAAPI / Vulkan) is ON by default and falls back to +# CPU automatically. No need to set it. Set to 0 only to force the CPU path. +# PUNKTFUNK_ZEROCOPY=0 # --- Bazzite / SteamOS-like host: host-managed Steam-Deck-UI session ----------------------- # The host LAUNCHES gamescope-session-plus headless AT THE CLIENT'S mode (so games see the