diff --git a/crates/punktfunk-host/src/encode.rs b/crates/punktfunk-host/src/encode.rs index 311fab2c..2e4806a8 100644 --- a/crates/punktfunk-host/src/encode.rs +++ b/crates/punktfunk-host/src/encode.rs @@ -1033,6 +1033,11 @@ mod nvenc_cuda; #[cfg(all(any(target_os = "linux", target_os = "windows"), feature = "nvenc"))] #[path = "encode/nvenc_status.rs"] mod nvenc_status; +// Platform-agnostic direct-SDK NVENC glue (`NvStatusExt`/`nv_ok`, `codec_guid`) shared by both +// `nvEncodeAPI` backends — the byte-identical Tier-2 leaves (plan §2.2). Sibling of `nvenc_status`. +#[cfg(all(any(target_os = "linux", target_os = "windows"), feature = "nvenc"))] +#[path = "encode/nvenc_core.rs"] +mod nvenc_core; // Shared libavcodec glue (`pixel_to_av`, swscale consts) for the three libav backends — Linux // NVENC + VAAPI and Windows AMF/QSV — so the byte-identical pieces live once (plan §2.2, Tier 2). #[cfg(any(target_os = "linux", all(target_os = "windows", feature = "amf-qsv")))] diff --git a/crates/punktfunk-host/src/encode/linux/nvenc_cuda.rs b/crates/punktfunk-host/src/encode/linux/nvenc_cuda.rs index e6ff467a..844f312e 100644 --- a/crates/punktfunk-host/src/encode/linux/nvenc_cuda.rs +++ b/crates/punktfunk-host/src/encode/linux/nvenc_cuda.rs @@ -31,6 +31,7 @@ // Every `unsafe` block / impl in this file carries a `// SAFETY:` proof; enforce it. #![deny(clippy::undocumented_unsafe_blocks)] +use super::nvenc_core::{codec_guid, NvStatusExt}; use super::nvenc_status; use super::{ChromaFormat, Codec, EncodedFrame, Encoder, EncoderCaps}; use crate::capture::{CapturedFrame, FramePayload}; @@ -106,20 +107,6 @@ struct EncodeApi { invalidate_ref_frames: unsafe extern "C" fn(*mut c_void, u64) -> nv::NVENCSTATUS, } -/// Local `NVENCSTATUS` → `Result` (the sdk's own helper lives in the `safe` module this file must -/// not pull in). The raw status's Debug repr is the error payload. -trait NvStatusExt { - fn nv_ok(self) -> std::result::Result<(), nv::NVENCSTATUS>; -} -impl NvStatusExt for nv::NVENCSTATUS { - fn nv_ok(self) -> std::result::Result<(), nv::NVENCSTATUS> { - match self { - nv::NVENCSTATUS::NV_ENC_SUCCESS => Ok(()), - err => Err(err), - } - } -} - /// Resolve the table once per process. `Err` = NVENC genuinely unavailable (no NVIDIA driver/.so, /// or a driver older than our headers) — [`NvencCudaEncoder::open`] gates on it and the VAAPI/ /// software backends carry on. @@ -226,16 +213,6 @@ const POOL: usize = 8; /// P-frame single-reference for low latency. const RFI_DPB: u32 = 5; -fn codec_guid(codec: Codec) -> nv::GUID { - match codec { - Codec::H264 => nv::NV_ENC_CODEC_H264_GUID, - Codec::H265 => nv::NV_ENC_CODEC_HEVC_GUID, - Codec::Av1 => nv::NV_ENC_CODEC_AV1_GUID, - // Guarded by the open_video dispatch: a PyroWave session never reaches NVENC. - Codec::PyroWave => unreachable!("PyroWave never opens the direct-NVENC backend"), - } -} - /// The NVENC input buffer format for a captured `DeviceBuffer`'s layout. NV12/YUV444 are the zero- /// copy worker's convert outputs; packed RGB (`ABGR`) is the fallback where NVENC does the internal /// CSC. 10-bit is never produced on Linux today (Phase 5.1), so everything is 8-bit. diff --git a/crates/punktfunk-host/src/encode/nvenc_core.rs b/crates/punktfunk-host/src/encode/nvenc_core.rs new file mode 100644 index 00000000..0917a655 --- /dev/null +++ b/crates/punktfunk-host/src/encode/nvenc_core.rs @@ -0,0 +1,36 @@ +//! Shared direct-SDK NVENC core — the platform-agnostic pieces of the two `nvEncodeAPI` backends, +//! Windows D3D11 (`encode/windows/nvenc.rs`) and Linux CUDA (`encode/linux/nvenc_cuda.rs`), so the +//! byte-identical glue lives once (plan §2.2, the direct-NVENC Tier-2). The per-platform parts — +//! the entry-table load (`nvEncodeAPI64.dll` via `LoadLibrary` vs `libnvidia-encode.so` via +//! `libloading`), the device binding (D3D11 vs CUDA), input-surface registration, and the +//! Windows-only async retrieve — stay in their backends. Sibling of [`super::nvenc_status`]. + +use super::Codec; +use nvidia_video_codec_sdk::sys::nvEncodeAPI as nv; + +/// Local `NVENCSTATUS` → `Result` (replaces the sdk's `result_without_string`, which lives in the +/// crate's `safe` module — code these backends must not pull in). The raw status's Debug repr +/// (`NV_ENC_ERR_INVALID_PARAM`, …) is the error payload; callers fold it through +/// [`super::nvenc_status`] for an operator-actionable cause. +pub(super) trait NvStatusExt { + fn nv_ok(self) -> std::result::Result<(), nv::NVENCSTATUS>; +} +impl NvStatusExt for nv::NVENCSTATUS { + fn nv_ok(self) -> std::result::Result<(), nv::NVENCSTATUS> { + match self { + nv::NVENCSTATUS::NV_ENC_SUCCESS => Ok(()), + err => Err(err), + } + } +} + +/// The NVENC codec GUID for a session [`Codec`]. PyroWave never opens the direct-NVENC backend +/// (guarded by the `open_video` dispatch), so it is unreachable here. +pub(super) fn codec_guid(codec: Codec) -> nv::GUID { + match codec { + Codec::H264 => nv::NV_ENC_CODEC_H264_GUID, + Codec::H265 => nv::NV_ENC_CODEC_HEVC_GUID, + Codec::Av1 => nv::NV_ENC_CODEC_AV1_GUID, + Codec::PyroWave => unreachable!("PyroWave never opens the direct-NVENC backend"), + } +} diff --git a/crates/punktfunk-host/src/encode/windows/nvenc.rs b/crates/punktfunk-host/src/encode/windows/nvenc.rs index 6c46cb80..dbb72288 100644 --- a/crates/punktfunk-host/src/encode/windows/nvenc.rs +++ b/crates/punktfunk-host/src/encode/windows/nvenc.rs @@ -36,6 +36,7 @@ // Every `unsafe` block / impl in this file carries a `// SAFETY:` proof; enforce it. #![deny(clippy::undocumented_unsafe_blocks)] +use super::nvenc_core::{codec_guid, NvStatusExt}; use super::nvenc_status; use super::{ChromaFormat, Codec, EncodedFrame, Encoder, EncoderCaps}; use crate::capture::{CapturedFrame, FramePayload, PixelFormat}; @@ -117,21 +118,6 @@ struct EncodeApi { invalidate_ref_frames: unsafe extern "C" fn(*mut c_void, u64) -> nv::NVENCSTATUS, } -/// Local `NVENCSTATUS` → `Result` (replaces the sdk's `result_without_string`, which lives in the -/// crate's `safe` module — code this file must not pull in, see [`EncodeApi`]). The raw status's -/// Debug repr (`NV_ENC_ERR_INVALID_PARAM`, …) is the error payload. -trait NvStatusExt { - fn nv_ok(self) -> std::result::Result<(), nv::NVENCSTATUS>; -} -impl NvStatusExt for nv::NVENCSTATUS { - fn nv_ok(self) -> std::result::Result<(), nv::NVENCSTATUS> { - match self { - nv::NVENCSTATUS::NV_ENC_SUCCESS => Ok(()), - err => Err(err), - } - } -} - /// Resolve the table once per process. `Err` = NVENC genuinely unavailable on this machine (no /// NVIDIA driver/DLL, or a driver older than our headers) — the entry points /// ([`NvencD3d11Encoder::open`], [`probe_can_encode_444`]) gate on it and the AMF/QSV/software @@ -240,15 +226,6 @@ const POOL: usize = 8; /// `numRefL0 = 1` keeps each P-frame single-reference for low latency. const RFI_DPB: u32 = 5; -fn codec_guid(codec: Codec) -> nv::GUID { - match codec { - Codec::H264 => nv::NV_ENC_CODEC_H264_GUID, - Codec::H265 => nv::NV_ENC_CODEC_HEVC_GUID, - Codec::Av1 => nv::NV_ENC_CODEC_AV1_GUID, - Codec::PyroWave => unreachable!("PyroWave never opens the direct-NVENC backend"), - } -} - /// Live NVENC hardware-session units held by THIS host process (a plain session = 1; a forced /// split-encode session occupies one session per engine = 2–3) — the Stage-W3 encoder budget /// (`design/windows-parallel-virtual-displays.md` §4.5). Kept in ONE place so admitting a parallel