16666aa938
Adds true HDR (BT.2020 PQ) and 10-bit (HEVC Main10) streaming, negotiated so an 8-bit/SDR client is never sent a stream it can't decode, plus a robust fix for the capture losing the stream across a secure-desktop transition. Protocol (punktfunk-core/quic.rs): - Hello gains `video_caps` (VIDEO_CAP_10BIT / VIDEO_CAP_HDR), Welcome gains `bit_depth`, both as optional trailing bytes (back-compat). client-rs advertises 10-bit via PUNKTFUNK_CLIENT_10BIT; the connector advertises 0 for now (in-band detection drives the native clients). Regenerated punktfunk_core.h. Windows host: - 10-bit Main10: host enables it only when the client advertised VIDEO_CAP_10BIT AND PUNKTFUNK_10BIT is set; threaded through open_video → NVENC (profile Main10, pixelBitDepthMinus8). - HDR: when the captured desktop is scRGB FP16 (R16G16B16A16_FLOAT, HDR on), copy it to an FP16 surface, composite the cursor there, convert scRGB → BT.2020 PQ 10-bit (R10G10B10A2) via a shader, and encode HEVC Main10 with the BT.2020/PQ colour VUI (ABGR10 input). Fixes the freeze + cursor-trail that came from feeding FP16 into the BGRA path. Reacts dynamically to the HDR toggle. - Capture recovery: rebuild is now a single NON-BLOCKING attempt, throttled to ~4×/s, repeating the last good frame between attempts (format-tagged last_present). During a secure-desktop dwell SudoVDA's output is gone; the old blocking 12 s retry starved the send loop for seconds so the client timed out and disconnected — now the session stays fed (frozen) until the desktop returns. Also seeds a black frame on recovery. Apple client (PunktfunkKit): - Detects HDR in-band from the stream VUI (PQ transfer function), decodes to 10-bit P010, and presents via an rgba16Float + BT.2020 PQ CAMetalLayer with EDR; SDR path unchanged. Switches automatically on a mid-session HDR toggle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
52 lines
2.3 KiB
Rust
52 lines
2.3 KiB
Rust
//! Zero-copy capture→encode (plan §9): the PipeWire dmabuf is imported into CUDA via EGL and
|
|
//! handed straight to NVENC, eliminating the per-frame CPU copies (at 5K the CPU-copy path
|
|
//! moves ~3.5 GB/s). Opt in with `PUNKTFUNK_ZEROCOPY=1`; the CPU-copy path stays the default and
|
|
//! the runtime fallback (foreign-allocator / no-dmabuf / import failure).
|
|
//!
|
|
//! Pieces: [`cuda`] (driver-API FFI + the shared `CUcontext` + device buffers), [`egl`] (the
|
|
//! headless EGLDisplay + dmabuf→`EGLImage`→CUDA import). The encoder's CUDA-frame path lives in
|
|
//! `encode/linux.rs`; the dmabuf negotiation lives in `capture/linux.rs`.
|
|
|
|
pub mod cuda;
|
|
pub mod egl;
|
|
pub mod vulkan;
|
|
|
|
pub use cuda::DeviceBuffer;
|
|
pub use egl::{DmabufPlane, EglImporter};
|
|
|
|
/// Whether the zero-copy path is opted in (`PUNKTFUNK_ZEROCOPY` truthy).
|
|
pub fn enabled() -> bool {
|
|
std::env::var("PUNKTFUNK_ZEROCOPY")
|
|
.map(|v| matches!(v.trim(), "1" | "true" | "yes" | "on"))
|
|
.unwrap_or(false)
|
|
}
|
|
|
|
/// DRM FourCC for a packed 32-bit format name (little-endian, e.g. `b"XR24"`).
|
|
const fn fourcc(c: &[u8; 4]) -> u32 {
|
|
(c[0] as u32) | ((c[1] as u32) << 8) | ((c[2] as u32) << 16) | ((c[3] as u32) << 24)
|
|
}
|
|
|
|
/// Map a SPA/our [`crate::capture::PixelFormat`] to the DRM FourCC EGL expects for import.
|
|
/// SPA byte order `BGRx` ⇒ DRM `XRGB8888` (memory B,G,R,X), etc.
|
|
pub fn drm_fourcc(format: crate::capture::PixelFormat) -> Option<u32> {
|
|
use crate::capture::PixelFormat::*;
|
|
Some(match format {
|
|
Bgrx => fourcc(b"XR24"), // DRM_FORMAT_XRGB8888
|
|
Bgra => fourcc(b"AR24"), // DRM_FORMAT_ARGB8888
|
|
Rgbx => fourcc(b"XB24"), // DRM_FORMAT_XBGR8888
|
|
Rgba => fourcc(b"AB24"), // DRM_FORMAT_ABGR8888
|
|
// 24-bit packed RGB/BGR have no straightforward dmabuf import here; use the CPU path.
|
|
// Rgb10a2 is the Windows HDR capture format — never produced by the Linux capturer.
|
|
Rgb | Bgr | Rgb10a2 => return None,
|
|
})
|
|
}
|
|
|
|
/// Standalone probe (the `zerocopy-probe` subcommand): initialize the EGL importer + CUDA
|
|
/// context and report. De-risks the FFI/linking/GPU-access without needing a capture session.
|
|
pub fn probe() -> anyhow::Result<()> {
|
|
let _importer = EglImporter::new()?;
|
|
let ctx = cuda::context()?;
|
|
tracing::info!(cuda_ctx = ?ctx, "zero-copy probe OK — EGL display + CUDA context initialized");
|
|
Ok(())
|
|
}
|