From 5748706631a48cbc5a4cb0c7d8be595ae290e6d2 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Thu, 16 Jul 2026 14:58:03 +0200 Subject: [PATCH] refactor(host): hoist the direct-NVENC init-params authoring into nvenc_core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both backends' build_init_params authored an identical NV_ENC_INITIALIZE_PARAMS (P1/ULL preset, PTD, session dimensions/rate, split-encode mode) — the only difference was the Windows-only enableEncodeAsync flag (Linux is sync-only). Hoist it to nvenc_core::build_init_params(codec_guid, w, h, fps, cfg, split_mode, enable_async); Linux's two call sites pass enable_async=false (the field stays 0 as before), Windows passes its session_async through. Keeps open and in-place reconfigure presenting the SAME init params, now guaranteed identical across platforms too. 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 --- .../src/encode/linux/nvenc_cuda.rs | 50 +++++++----------- .../punktfunk-host/src/encode/nvenc_core.rs | 36 +++++++++++++ .../src/encode/windows/nvenc.rs | 51 ++++++------------- 3 files changed, 70 insertions(+), 67 deletions(-) diff --git a/crates/punktfunk-host/src/encode/linux/nvenc_cuda.rs b/crates/punktfunk-host/src/encode/linux/nvenc_cuda.rs index b58ed163..16765cf7 100644 --- a/crates/punktfunk-host/src/encode/linux/nvenc_cuda.rs +++ b/crates/punktfunk-host/src/encode/linux/nvenc_cuda.rs @@ -32,7 +32,7 @@ #![deny(clippy::undocumented_unsafe_blocks)] use super::nvenc_core::{ - apply_low_latency_config, codec_guid, LowLatencyConfig, NvStatusExt, RFI_DPB, + apply_low_latency_config, build_init_params, codec_guid, LowLatencyConfig, NvStatusExt, RFI_DPB, }; use super::nvenc_status; use super::{ChromaFormat, Codec, EncodedFrame, Encoder, EncoderCaps}; @@ -599,34 +599,6 @@ impl NvencCudaEncoder { Ok(cfg) } - /// Author the `NV_ENC_INITIALIZE_PARAMS` pointing at `cfg`. Shared by [`try_open_session`] - /// and [`Encoder::reconfigure_bitrate`] — a reconfigure must present the SAME init params as - /// the open. The returned struct borrows `cfg` raw; the caller keeps `cfg` alive across the - /// NVENC call it feeds this into. - fn build_init_params( - &self, - cfg: &mut nv::NV_ENC_CONFIG, - split_mode: u32, - ) -> nv::NV_ENC_INITIALIZE_PARAMS { - let mut init = nv::NV_ENC_INITIALIZE_PARAMS { - version: nv::NV_ENC_INITIALIZE_PARAMS_VER, - encodeGUID: self.codec_guid, - presetGUID: nv::NV_ENC_PRESET_P1_GUID, - tuningInfo: nv::NV_ENC_TUNING_INFO::NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY, - encodeWidth: self.width, - encodeHeight: self.height, - darWidth: self.width, - darHeight: self.height, - frameRateNum: self.fps, - frameRateDen: 1, - enablePTD: 1, - encodeConfig: cfg, - ..Default::default() - }; - init.set_splitEncodeMode(split_mode); - init - } - /// Open + configure + initialize ONE NVENC CUDA session at `bitrate` (bps) and `split_mode`. /// Returns the session handle, or destroys it and returns the error. unsafe fn try_open_session(&self, bitrate: u64, split_mode: u32) -> Result<*mut c_void> { @@ -654,7 +626,15 @@ impl NvencCudaEncoder { return Err(e); } }; - let mut init = self.build_init_params(&mut cfg, split_mode); + let mut init = build_init_params( + self.codec_guid, + self.width, + self.height, + self.fps, + &mut cfg, + split_mode, + false, + ); match (api().initialize_encoder)(enc, &mut init).nv_ok() { Ok(()) => Ok(enc), @@ -1225,7 +1205,15 @@ impl Encoder for NvencCudaEncoder { }; let mut params = nv::NV_ENC_RECONFIGURE_PARAMS { version: nv::NV_ENC_RECONFIGURE_PARAMS_VER, - reInitEncodeParams: self.build_init_params(&mut cfg, self.split_mode), + reInitEncodeParams: build_init_params( + self.codec_guid, + self.width, + self.height, + self.fps, + &mut cfg, + self.split_mode, + false, + ), ..Default::default() }; // Keep the encoder's RC state and reference chain: no reset, no IDR — the in-flight diff --git a/crates/punktfunk-host/src/encode/nvenc_core.rs b/crates/punktfunk-host/src/encode/nvenc_core.rs index ddeb5133..a119e6f0 100644 --- a/crates/punktfunk-host/src/encode/nvenc_core.rs +++ b/crates/punktfunk-host/src/encode/nvenc_core.rs @@ -68,6 +68,42 @@ pub(super) struct LowLatencyConfig { pub rfi_supported: bool, } +/// Author the shared `NV_ENC_INITIALIZE_PARAMS` (P1/ULL preset, PTD, the session dimensions/rate) +/// pointing at `cfg`. `enable_async` drives the Windows two-thread async retrieve — Linux is +/// sync-only and passes `false`, leaving `enableEncodeAsync` at 0 as before. The returned struct +/// borrows `cfg` as a raw pointer; the caller must keep `cfg` alive across the NVENC call it feeds +/// this into. Used at open and at in-place reconfigure, which must present the SAME init params. +#[allow(clippy::too_many_arguments)] +pub(super) fn build_init_params( + codec_guid: nv::GUID, + width: u32, + height: u32, + fps: u32, + cfg: &mut nv::NV_ENC_CONFIG, + split_mode: u32, + enable_async: bool, +) -> nv::NV_ENC_INITIALIZE_PARAMS { + let mut init = nv::NV_ENC_INITIALIZE_PARAMS { + version: nv::NV_ENC_INITIALIZE_PARAMS_VER, + encodeGUID: codec_guid, + presetGUID: nv::NV_ENC_PRESET_P1_GUID, + tuningInfo: nv::NV_ENC_TUNING_INFO::NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY, + encodeWidth: width, + encodeHeight: height, + darWidth: width, + darHeight: height, + frameRateNum: fps, + frameRateDen: 1, + enablePTD: 1, + enableEncodeAsync: enable_async as u32, + encodeConfig: cfg, + ..Default::default() + }; + // splitEncodeMode is a C bitfield — set via the generated accessor, not a struct field. + init.set_splitEncodeMode(split_mode); + init +} + /// Author the shared low-latency NVENC config onto a **preset-seeded** `cfg`: CBR + infinite GOP + /// P-only + ~1-frame VBV, per-codec tier/level, chroma + bit depth, unconditional colour signaling, /// and the RFI DPB. The caller seeds `cfg` from the P1/ULL preset first (that call needs the diff --git a/crates/punktfunk-host/src/encode/windows/nvenc.rs b/crates/punktfunk-host/src/encode/windows/nvenc.rs index 8ba39ab8..d0ebf0be 100644 --- a/crates/punktfunk-host/src/encode/windows/nvenc.rs +++ b/crates/punktfunk-host/src/encode/windows/nvenc.rs @@ -37,7 +37,7 @@ #![deny(clippy::undocumented_unsafe_blocks)] use super::nvenc_core::{ - apply_low_latency_config, codec_guid, LowLatencyConfig, NvStatusExt, RFI_DPB, + apply_low_latency_config, build_init_params, codec_guid, LowLatencyConfig, NvStatusExt, RFI_DPB, }; use super::nvenc_status; use super::{ChromaFormat, Codec, EncodedFrame, Encoder, EncoderCaps}; @@ -742,39 +742,6 @@ impl NvencD3d11Encoder { Ok(cfg) } - /// Author the `NV_ENC_INITIALIZE_PARAMS` pointing at `cfg`. Shared by [`try_open_session`] - /// and [`Encoder::reconfigure_bitrate`] — a reconfigure must present the SAME init params as - /// the open. The returned struct borrows `cfg` raw; the caller keeps `cfg` alive across the - /// NVENC call it feeds this into. - fn build_init_params( - &self, - cfg: &mut nv::NV_ENC_CONFIG, - split_mode: u32, - enable_async: bool, - ) -> nv::NV_ENC_INITIALIZE_PARAMS { - let mut init = nv::NV_ENC_INITIALIZE_PARAMS { - version: nv::NV_ENC_INITIALIZE_PARAMS_VER, - encodeGUID: self.codec_guid, - presetGUID: nv::NV_ENC_PRESET_P1_GUID, - tuningInfo: nv::NV_ENC_TUNING_INFO::NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY, - encodeWidth: self.width, - encodeHeight: self.height, - darWidth: self.width, - darHeight: self.height, - frameRateNum: self.fps, - frameRateDen: 1, - enablePTD: 1, - // Two-thread async retrieve (§5.B): completion events signal the retrieve thread - // instead of `lock_bitstream` blocking the submit thread. - enableEncodeAsync: enable_async as u32, - encodeConfig: cfg, - ..Default::default() - }; - // splitEncodeMode is a C bitfield — set via the generated accessor, not a struct field. - init.set_splitEncodeMode(split_mode); - init - } - /// Open + configure + initialize ONE NVENC session at `bitrate` (bps) and `split_mode`. Returns /// the session handle, or destroys it and returns the error. NVENC has no re-init after a failed /// `initialize_encoder`, so the bitrate-clamp search in `init_session` calls this once per probe. @@ -809,7 +776,15 @@ impl NvencD3d11Encoder { return Err(e); } }; - let mut init = self.build_init_params(&mut cfg, split_mode, enable_async); + let mut init = build_init_params( + self.codec_guid, + self.width, + self.height, + self.fps, + &mut cfg, + split_mode, + enable_async, + ); match (api().initialize_encoder)(enc, &mut init).nv_ok() { Ok(()) => Ok(enc), @@ -1541,7 +1516,11 @@ impl Encoder for NvencD3d11Encoder { }; let mut params = nv::NV_ENC_RECONFIGURE_PARAMS { version: nv::NV_ENC_RECONFIGURE_PARAMS_VER, - reInitEncodeParams: self.build_init_params( + reInitEncodeParams: build_init_params( + self.codec_guid, + self.width, + self.height, + self.fps, &mut cfg, self.split_mode, self.session_async,