feat(linux/vulkan-encode): default-on native NV12 capture + RGB true-extent
ci / web (push) Successful in 48s
ci / docs-site (push) Successful in 1m5s
apple / swift (push) Successful in 1m20s
decky / build-publish (push) Successful in 21s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 25s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 42s
ci / bench (push) Successful in 5m59s
apple / screenshots (push) Successful in 6m17s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 6m29s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 5m12s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m16s
deb / build-publish (push) Successful in 12m58s
deb / build-publish-host (push) Successful in 13m40s
windows-host / package (push) Successful in 10m46s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 6m52s
android / android (push) Successful in 16m18s
arch / build-publish (push) Successful in 16m20s
docker / deploy-docs (push) Successful in 30s
ci / rust (push) Successful in 19m25s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 14m19s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m19s
ci / web (push) Successful in 48s
ci / docs-site (push) Successful in 1m5s
apple / swift (push) Successful in 1m20s
decky / build-publish (push) Successful in 21s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 25s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 42s
ci / bench (push) Successful in 5m59s
apple / screenshots (push) Successful in 6m17s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 6m29s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 5m12s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m16s
deb / build-publish (push) Successful in 12m58s
deb / build-publish-host (push) Successful in 13m40s
windows-host / package (push) Successful in 10m46s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 6m52s
android / android (push) Successful in 16m18s
arch / build-publish (push) Successful in 16m20s
docker / deploy-docs (push) Successful in 30s
ci / rust (push) Successful in 19m25s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 14m19s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m19s
Flip both zero-copy levers from opt-in to default: - The capture negotiation now PREFERS gamescope's producer-side NV12 pod by default (PUNKTFUNK_PIPEWIRE_NV12=0 restores the packed-RGB negotiation). Codec-aware gating rides a new OutputFormat::nv12_native -> ZeroCopyPolicy::native_nv12_session edge resolved by the host facade from the session plan (pf_encode::linux_native_nv12_ok): only H265/AV1 sessions whose backend can open the raw Vulkan Video encoder ever see the NV12 pod -- an H264/Moonlight session (libav VAAPI, which would misread the two-plane buffer) keeps today's BGRx negotiation, as do the GameStream-resolve and portal-mirror paths, PyroWave, and NVENC prefs. - RGB-direct's unaligned modes default to the true-extent direct import (PUNKTFUNK_VULKAN_RGB_TRUE_EXTENT=0 restores the padded-copy staging). Guarded-tested on Van Gogh with the kernel journal watched: clean, and at 5.38 ms p50 the fastest 1080p encode path measured on that hardware. The EFC only exists on Mesa >= 26, where the codedExtent-driven session_init padding is guaranteed (verified back to Mesa 24.2). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -84,13 +84,14 @@ fn rgb_request() -> Option<bool> {
|
||||
}
|
||||
}
|
||||
|
||||
/// `PUNKTFUNK_VULKAN_RGB_TRUE_EXTENT=1` (bring-up): at unaligned modes, replace RGB-direct's
|
||||
/// padded-copy staging with a direct import carrying the TRUE-SIZE source `codedExtent` — RADV
|
||||
/// derives nonzero VCN firmware padding from it, so the EFC is told the source lacks the
|
||||
/// alignment rows (see [`RgbDirect::true_extent`]). Off by default until the EFC front-end is
|
||||
/// proven to honor the padding like the YUV fetch path does.
|
||||
/// True-extent RGB-direct at unaligned modes (default ON; `PUNKTFUNK_VULKAN_RGB_TRUE_EXTENT=0`
|
||||
/// restores the padded-copy staging): direct-import the visible-size capture with the TRUE-SIZE
|
||||
/// source `codedExtent` — RADV derives nonzero VCN firmware padding from it, so the EFC is told
|
||||
/// the source lacks the alignment rows (see [`RgbDirect::true_extent`]). Guarded-tested on Van
|
||||
/// Gogh 2026-07-21 (kernel clean, and the fastest 1080p encode path measured); the EFC only
|
||||
/// exists on Mesa ≥ 26, where the `codedExtent`-driven `session_init` is guaranteed.
|
||||
fn rgb_true_extent_request() -> bool {
|
||||
std::env::var("PUNKTFUNK_VULKAN_RGB_TRUE_EXTENT").as_deref() == Ok("1")
|
||||
std::env::var("PUNKTFUNK_VULKAN_RGB_TRUE_EXTENT").as_deref() != Ok("0")
|
||||
}
|
||||
|
||||
/// Live RGB-direct session config: the chroma-siting bits the session was created with
|
||||
@@ -105,13 +106,13 @@ struct RgbDirect {
|
||||
/// with the edge rows/columns duplicated into the padding (transfer-only, no shader) and
|
||||
/// encoded from there. Aligned modes keep the true zero-copy import.
|
||||
padded: bool,
|
||||
/// `PUNKTFUNK_VULKAN_RGB_TRUE_EXTENT=1` bring-up alternative to `padded` at unaligned
|
||||
/// modes: direct-import the visible-size buffer and pass the TRUE-SIZE source
|
||||
/// The default unaligned-mode source strategy (`PUNKTFUNK_VULKAN_RGB_TRUE_EXTENT=0` falls
|
||||
/// back to `padded`): direct-import the visible-size buffer and pass the TRUE-SIZE source
|
||||
/// `codedExtent` — RADV then programs nonzero firmware padding from it (Mesa ≥ 24.2
|
||||
/// derives `session_init` padding from `srcPictureResource.codedExtent`; see
|
||||
/// [`VulkanVideoEncoder::native_nv12`]), telling the VCN the source lacks the alignment
|
||||
/// rows. The session/SPS/DPB stay app-aligned. Field-proven for YUV fetches; the EFC RGB
|
||||
/// front-end honoring the padding is what this gate exists to prove.
|
||||
/// rows, which the hardware edge-extends internally. The session/SPS/DPB stay app-aligned.
|
||||
/// Guarded-tested on Van Gogh (kernel clean; fastest 1080p path measured).
|
||||
true_extent: bool,
|
||||
}
|
||||
|
||||
@@ -696,7 +697,7 @@ impl VulkanVideoEncoder {
|
||||
) =>
|
||||
"active(true-extent: unaligned mode, direct import with the true-size \
|
||||
source codedExtent — RADV firmware padding covers the alignment rows; \
|
||||
PUNKTFUNK_VULKAN_RGB_TRUE_EXTENT bring-up)",
|
||||
PUNKTFUNK_VULKAN_RGB_TRUE_EXTENT=0 restores the padded copy)",
|
||||
(_, _, Some(RgbDirect { padded: false, .. })) => "active",
|
||||
(_, _, Some(RgbDirect { padded: true, .. })) =>
|
||||
"active(padded-copy: mode is not 64x16-aligned — staging blit + edge \
|
||||
|
||||
@@ -351,8 +351,8 @@ fn open_video_backend(
|
||||
Err(e) if format == PixelFormat::Nv12 => {
|
||||
return Err(e.context(
|
||||
"Vulkan Video open failed on a native-NV12 capture \
|
||||
(PUNKTFUNK_PIPEWIRE_NV12=1) — no VAAPI fallback exists; unset the \
|
||||
flag to restore the packed-RGB pipeline",
|
||||
— no VAAPI fallback exists; set PUNKTFUNK_PIPEWIRE_NV12=0 to \
|
||||
restore the packed-RGB negotiation",
|
||||
));
|
||||
}
|
||||
Err(e) => tracing::warn!(
|
||||
@@ -365,10 +365,10 @@ fn open_video_backend(
|
||||
// PUNKTFUNK_VULKAN_ENCODE=0, or a build without the feature).
|
||||
if format == PixelFormat::Nv12 {
|
||||
anyhow::bail!(
|
||||
"native NV12 capture (PUNKTFUNK_PIPEWIRE_NV12=1) requires the Vulkan Video \
|
||||
encoder (HEVC/AV1 session, --features vulkan-encode, \
|
||||
PUNKTFUNK_VULKAN_ENCODE not 0) — this session resolved to libav VAAPI; \
|
||||
unset the flag"
|
||||
"native NV12 capture requires the Vulkan Video encoder (HEVC/AV1 \
|
||||
session, --features vulkan-encode, PUNKTFUNK_VULKAN_ENCODE not 0) — this \
|
||||
session resolved to libav VAAPI; set PUNKTFUNK_PIPEWIRE_NV12=0 to restore \
|
||||
the packed-RGB negotiation"
|
||||
);
|
||||
}
|
||||
vaapi::VaapiEncoder::open(
|
||||
@@ -841,6 +841,33 @@ fn vulkan_encode_enabled() -> bool {
|
||||
.unwrap_or(true)
|
||||
}
|
||||
|
||||
/// Whether THIS session's encoder can ingest a producer-native NV12 capture: only the raw
|
||||
/// Vulkan Video backend does (libav VAAPI would misread the two-plane buffer as packed RGB —
|
||||
/// [`open_video`] refuses the combination), so the session's codec must be one it encodes and
|
||||
/// the backend must be eligible to open. The host facade threads the verdict into the capture
|
||||
/// negotiation (`OutputFormat::nv12_native` → `ZeroCopyPolicy::native_nv12_session`), which
|
||||
/// then PREFERS gamescope's producer-side NV12 pod (default-on; `PUNKTFUNK_PIPEWIRE_NV12=0`
|
||||
/// escapes at the capture gate).
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn linux_native_nv12_ok(codec: Codec) -> bool {
|
||||
#[cfg(feature = "vulkan-encode")]
|
||||
{
|
||||
matches!(codec, Codec::H265 | Codec::Av1)
|
||||
&& vulkan_encode_enabled()
|
||||
// NVENC/PyroWave prefs never open the Vulkan Video backend; every other pref
|
||||
// (auto/vaapi/amd/intel/vulkan) tries it first on AMD/Intel — see [`open_video`].
|
||||
&& !matches!(
|
||||
pf_host_config::config().encoder_pref.as_str(),
|
||||
"nvenc" | "nvidia" | "cuda" | "pyrowave"
|
||||
)
|
||||
}
|
||||
#[cfg(not(feature = "vulkan-encode"))]
|
||||
{
|
||||
let _ = codec;
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Cheap, side-effect-free NVIDIA-presence probe for the `auto` backend selector: the NVIDIA
|
||||
/// kernel driver exposes these device nodes, AMD/Intel boxes have neither. Deliberately does NOT
|
||||
/// create a CUDA context (that would allocate GPU state on every host that merely *might* be
|
||||
|
||||
Reference in New Issue
Block a user