feat(encode): default-on the Linux Vulkan Video HEVC backend
ci / docs-site (push) Successful in 59s
ci / web (push) Successful in 1m7s
decky / build-publish (push) Successful in 18s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 8s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 8s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 8s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 44s
apple / swift (push) Successful in 4m13s
ci / bench (push) Successful in 7m13s
windows-host / package (push) Successful in 9m43s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 9m26s
docker / deploy-docs (push) Successful in 28s
android / android (push) Successful in 12m35s
deb / build-publish (push) Successful in 12m46s
arch / build-publish (push) Successful in 16m31s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 19m32s
apple / screenshots (push) Successful in 18m49s
ci / rust (push) Successful in 25m10s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 18m0s

On-glass validated 2026-07-12 on an AMD RADV 780M with a real Deck-class
client: the pipelined raw-Vulkan HEVC encoder ran a rock-solid 1080p@240
session and healed loss with clean P-frame recovery anchors (real RFI the
libav VAAPI path can't express). Ship it on by default, mirroring the NVENC
default-on.

- vulkan_encode_enabled() defaults ON; PUNKTFUNK_VULKAN_ENCODE=0 is the libav
  VAAPI escape hatch. A failed open still falls back to VAAPI, so a device
  without h265 Vulkan encode (or an untested Intel/ANV that misbehaves at open)
  degrades gracefully instead of breaking the stream.
- Ring depth defaults to 2 (one frame of overlap, lowest added latency — the
  on-glass-validated real-time setting); PUNKTFUNK_VULKAN_INFLIGHT still tunes it.
- Compile --features punktfunk-host/vulkan-encode into the arch/deb/rpm host
  builds (pure-Rust ash, no new lib / no link-time dep), alongside nvenc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-12 20:20:31 +02:00
parent e9d8f2bc04
commit 84329205eb
5 changed files with 30 additions and 16 deletions
+11 -9
View File
@@ -873,18 +873,20 @@ fn nvenc_direct_enabled() -> bool {
.unwrap_or(true)
}
/// Whether the raw Vulkan Video HEVC encode backend is active for AMD/Intel. **Opt-in for now**
/// (design/linux-vulkan-video-encode.md) — `PUNKTFUNK_VULKAN_ENCODE=1` (also `true`/`yes`/`on`)
/// selects it over libav VAAPI for an HEVC session; it gives real reference-frame invalidation
/// (a clean P-frame recovery anchor via explicit DPB reference slots) that the libavcodec VAAPI
/// path can't express. Will flip to default-on after on-glass validation, like
/// [`nvenc_direct_enabled`]. Only consulted with `--features vulkan-encode`; a failed open falls
/// back to VAAPI, so this can only improve recovery, never break a stream.
/// Whether the raw Vulkan Video HEVC encode backend is active for AMD/Intel. **Default ON** —
/// on-glass validated 2026-07-12 on an AMD RADV 780M with a real Deck-class client: the pipelined
/// encoder ran a rock-solid 1080p@240 HEVC session and healed loss with clean P-frame recovery
/// anchors (never IDR) via explicit DPB reference slots — real reference-frame invalidation the
/// libavcodec VAAPI path can't express (design/linux-vulkan-video-encode.md). `PUNKTFUNK_VULKAN_ENCODE=0`
/// (also `false`/`no`/`off`) is the libav-VAAPI escape hatch. Only consulted with
/// `--features vulkan-encode`, and a failed open falls back to VAAPI, so an unsupported device
/// (e.g. a Mesa without h265 encode, or an untested Intel/ANV where the path misbehaves at open)
/// degrades gracefully to the old backend rather than breaking the stream.
#[cfg(all(target_os = "linux", feature = "vulkan-encode"))]
fn vulkan_encode_enabled() -> bool {
std::env::var("PUNKTFUNK_VULKAN_ENCODE")
.map(|v| matches!(v.trim(), "1" | "true" | "yes" | "on"))
.unwrap_or(false)
.map(|v| !matches!(v.trim(), "0" | "false" | "no" | "off"))
.unwrap_or(true)
}
/// Cheap, side-effect-free NVIDIA-presence probe for the `auto` backend selector: the NVIDIA
@@ -24,10 +24,11 @@ const IMPORT_CACHE_CAP: usize = 16;
const CSC_SPV: &[u8] = include_bytes!("rgb2yuv.spv");
/// DPB ring depth (well under the RADV `maxDpbSlots=17`); also the RFI recovery window.
const DPB_SLOTS: u32 = 8;
/// In-flight frame ring: how many captures may have GPU work outstanding at once. 3 overlaps a
/// frame's CSC+encode with the next capture (the throughput win) while keeping added latency tiny
/// (backpressure kicks in at the 3rd unread frame). Distinct from `DPB_SLOTS` (reference pool).
const RING_DEFAULT: usize = 3;
/// In-flight frame ring: how many captures may have GPU work outstanding at once. 2 overlaps a
/// frame's CSC+encode with the next capture (the throughput win) at the lowest possible added
/// latency — on-glass validated as rock-solid at 1080p@240, so it is the real-time default;
/// backpressure kicks in at the 2nd unread frame. Distinct from `DPB_SLOTS` (reference pool).
const RING_DEFAULT: usize = 2;
/// Resolve the in-flight ring depth: `PUNKTFUNK_VULKAN_INFLIGHT` (clamped 2..=6), else `RING_DEFAULT`.
fn ring_depth() -> usize {