refactor(host): extract encode/nvenc_core.rs — shared direct-SDK NVENC leaves
The two direct-SDK nvEncodeAPI backends (Windows D3D11 encode/windows/nvenc.rs, Linux CUDA encode/linux/nvenc_cuda.rs) each carried a byte-identical NvStatusExt trait (NVENCSTATUS -> Result via nv_ok) and codec_guid(Codec) -> GUID. Hoist both into a new encode/nvenc_core.rs, the platform-agnostic sibling of the existing encode/nvenc_status.rs (same cfg gate: any(linux,windows) + nvenc). Each backend now imports them via super::nvenc_core; call sites (.nv_ok() ×16/20, the one codec_guid() struct-init) are unchanged. The per-platform machinery — entry-table load (nvEncodeAPI64.dll/LoadLibrary vs libnvidia-encode.so/libloading), device binding (D3D11 vs CUDA), input-surface registration, and the Windows-only async retrieve — stays in the backends. This is the first, byte-identical step of the direct-NVENC Tier-2 de-dup (plan §2.2); the larger build_config authoring is a later, carefully-diffed step. Verified on BOTH platforms: Linux clippy 0/0 (nvenc,vulkan-encode,pyrowave, RTX 5070 Ti) and Windows clippy 0/0 (nvenc,amf-qsv, RTX 4090 / .173). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1033,6 +1033,11 @@ mod nvenc_cuda;
|
|||||||
#[cfg(all(any(target_os = "linux", target_os = "windows"), feature = "nvenc"))]
|
#[cfg(all(any(target_os = "linux", target_os = "windows"), feature = "nvenc"))]
|
||||||
#[path = "encode/nvenc_status.rs"]
|
#[path = "encode/nvenc_status.rs"]
|
||||||
mod nvenc_status;
|
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
|
// 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).
|
// 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")))]
|
#[cfg(any(target_os = "linux", all(target_os = "windows", feature = "amf-qsv")))]
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
// Every `unsafe` block / impl in this file carries a `// SAFETY:` proof; enforce it.
|
// Every `unsafe` block / impl in this file carries a `// SAFETY:` proof; enforce it.
|
||||||
#![deny(clippy::undocumented_unsafe_blocks)]
|
#![deny(clippy::undocumented_unsafe_blocks)]
|
||||||
|
|
||||||
|
use super::nvenc_core::{codec_guid, NvStatusExt};
|
||||||
use super::nvenc_status;
|
use super::nvenc_status;
|
||||||
use super::{ChromaFormat, Codec, EncodedFrame, Encoder, EncoderCaps};
|
use super::{ChromaFormat, Codec, EncodedFrame, Encoder, EncoderCaps};
|
||||||
use crate::capture::{CapturedFrame, FramePayload};
|
use crate::capture::{CapturedFrame, FramePayload};
|
||||||
@@ -106,20 +107,6 @@ struct EncodeApi {
|
|||||||
invalidate_ref_frames: unsafe extern "C" fn(*mut c_void, u64) -> nv::NVENCSTATUS,
|
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,
|
/// 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/
|
/// or a driver older than our headers) — [`NvencCudaEncoder::open`] gates on it and the VAAPI/
|
||||||
/// software backends carry on.
|
/// software backends carry on.
|
||||||
@@ -226,16 +213,6 @@ const POOL: usize = 8;
|
|||||||
/// P-frame single-reference for low latency.
|
/// P-frame single-reference for low latency.
|
||||||
const RFI_DPB: u32 = 5;
|
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-
|
/// 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
|
/// 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.
|
/// CSC. 10-bit is never produced on Linux today (Phase 5.1), so everything is 8-bit.
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,6 +36,7 @@
|
|||||||
// Every `unsafe` block / impl in this file carries a `// SAFETY:` proof; enforce it.
|
// Every `unsafe` block / impl in this file carries a `// SAFETY:` proof; enforce it.
|
||||||
#![deny(clippy::undocumented_unsafe_blocks)]
|
#![deny(clippy::undocumented_unsafe_blocks)]
|
||||||
|
|
||||||
|
use super::nvenc_core::{codec_guid, NvStatusExt};
|
||||||
use super::nvenc_status;
|
use super::nvenc_status;
|
||||||
use super::{ChromaFormat, Codec, EncodedFrame, Encoder, EncoderCaps};
|
use super::{ChromaFormat, Codec, EncodedFrame, Encoder, EncoderCaps};
|
||||||
use crate::capture::{CapturedFrame, FramePayload, PixelFormat};
|
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,
|
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
|
/// 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
|
/// 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
|
/// ([`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.
|
/// `numRefL0 = 1` keeps each P-frame single-reference for low latency.
|
||||||
const RFI_DPB: u32 = 5;
|
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
|
/// 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
|
/// 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
|
/// (`design/windows-parallel-virtual-displays.md` §4.5). Kept in ONE place so admitting a parallel
|
||||||
|
|||||||
Reference in New Issue
Block a user