feat(encode): RGB-direct (EFC) is now the DEFAULT on capable hosts — gated by a session cursor-blend hint
apple / screenshots (push) Has been cancelled
apple / swift (push) Has been cancelled
android / android (push) Has been cancelled
ci / web (push) Successful in 55s
deb / build-publish (push) Has been cancelled
deb / build-publish-host (push) Has been cancelled
ci / docs-site (push) Successful in 55s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 11s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 12s
decky / build-publish (push) Successful in 20s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 10s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 10s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 13s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 56s
ci / bench (push) Successful in 7m8s
ci / rust (push) Failing after 9m14s
windows-host / package (push) Successful in 10m24s
arch / build-publish (push) Successful in 12m58s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 16m25s
docker / deploy-docs (push) Successful in 28s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Failing after 20m25s

B2 default-on (design/vulkan-rgb-direct-encode.md): wherever the probe
passes, Vulkan Video sessions now take the EFC RGB source by default —
direct import on aligned modes, padded-copy staging on unaligned ones
(1080p). PUNKTFUNK_VULKAN_RGB_DIRECT becomes the override: =0 disables,
=1 forces, unset = the default below.

The one session class that must NOT default on: cursor-as-metadata
captures (every non-gamescope compositor), where the CSC shader's blend
IS the visible pointer — the EFC cannot composite, and defaulting there
would silently drop the cursor from the stream. The hint rides the
existing plumbing:

- SessionPlan gains cursor_blend, resolved once where the compositor is
  known (gamescope embeds the pointer itself → false; kwin/mutter/
  wlroots/hyprland → true), and shows up in the logged plan line.
- open_video/open_video_backend thread it through (native pump: all
  three encoder-open sites read plan.cursor_blend; GameStream monitor
  capture: true — it negotiates metadata cursor; spike: false).
- VulkanVideoEncoder::open resolves: env override, else ON iff the
  session never hands us cursor bitmaps. The warn-once for a cursor on
  an RGB session (forced via =1) stays.

Verified on-hw box (Linux): pf-encode + punktfunk-host compile, clippy
clean, unit suite green. The GPU paths themselves are unchanged from the
smoke-validated 96e19986 — this commit only changes which sessions
select them by default.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
enricobuehler
2026-07-20 23:19:46 +02:00
parent 3736bbdc73
commit c6caeca5bc
6 changed files with 81 additions and 16 deletions
+31 -10
View File
@@ -69,12 +69,19 @@ fn quality_request() -> u32 {
.unwrap_or(0)
}
/// `PUNKTFUNK_VULKAN_RGB_DIRECT=1` opts into the RGB-direct encode source (B1,
/// design/vulkan-rgb-direct-encode.md): the captured RGB frame is handed to the encoder as-is
/// and the VCN EFC front-end does the 709-narrow CSC inline — no compute CSC, no plane copies,
/// one queue submit per frame. Default OFF until the color/latency A/B graduates it (B2).
fn rgb_request() -> bool {
std::env::var("PUNKTFUNK_VULKAN_RGB_DIRECT").is_ok_and(|v| v == "1")
/// `PUNKTFUNK_VULKAN_RGB_DIRECT` override for the RGB-direct encode source
/// (design/vulkan-rgb-direct-encode.md): the captured RGB frame is the encode source and the
/// VCN EFC front-end does the 709-narrow CSC inline — no compute CSC, no plane copies, one
/// queue submit per frame (unaligned modes go through the padded-copy staging blit). B2
/// default: ON wherever the probe passes, EXCEPT sessions that may need the CSC's cursor
/// blend (see [`VulkanVideoEncoder::open`]). `=0` disables outright; `=1` forces it even on
/// cursor-blend sessions (the pointer will be missing from the stream); unset = the default.
fn rgb_request() -> Option<bool> {
match std::env::var("PUNKTFUNK_VULKAN_RGB_DIRECT") {
Ok(v) if v == "0" => Some(false),
Ok(_) => Some(true),
Err(_) => None,
}
}
/// Live RGB-direct session config: the chroma-siting bits the session was created with
@@ -408,9 +415,21 @@ pub struct VulkanVideoEncoder {
unsafe impl Send for VulkanVideoEncoder {}
impl VulkanVideoEncoder {
/// Signature mirrors the other Linux backends' `open` (see `nvenc_cuda::NvencCudaEncoder::open`).
pub fn open(codec: Codec, width: u32, height: u32, fps: u32, bitrate_bps: u64) -> Result<Self> {
Self::open_opts(codec, width, height, fps, bitrate_bps, rgb_request())
/// Signature mirrors the other Linux backends' `open` plus `cursor_blend`: the session may
/// hand this encoder cursor bitmaps to composite (cursor-as-metadata captures — every
/// non-gamescope compositor). The EFC cannot blend, so such sessions default to the CSC
/// path; everywhere else the RGB-direct source is the DEFAULT wherever the probe passes
/// (B2). `PUNKTFUNK_VULKAN_RGB_DIRECT` overrides both ways (see [`rgb_request`]).
pub fn open(
codec: Codec,
width: u32,
height: u32,
fps: u32,
bitrate_bps: u64,
cursor_blend: bool,
) -> Result<Self> {
let want_rgb = rgb_request().unwrap_or(!cursor_blend);
Self::open_opts(codec, width, height, fps, bitrate_bps, want_rgb)
}
/// `open` with the RGB-direct request explicit instead of read from the env — the smoke
@@ -563,7 +582,9 @@ impl VulkanVideoEncoder {
(_, _, Some(RgbDirect { padded: true, .. })) =>
"active(padded-copy: mode is not 64x16-aligned — staging blit + edge \
duplication instead of the direct import)",
(Ok(_), false, None) => "available(off; set PUNKTFUNK_VULKAN_RGB_DIRECT=1)",
(Ok(_), false, None) =>
"available(off: PUNKTFUNK_VULKAN_RGB_DIRECT=0, or a cursor-blend session \
— =1 forces)",
(Err(e), _, None) => e,
// (Ok, wanted) always builds Some above.
(Ok(_), true, None) => unreachable!("rgb gate and cfg disagree"),