refactor(host/W6.2): extract the video encode backends into the pf-encode crate

encode.rs + encode/* (NVENC, VAAPI, native AMF, AMF/QSV ffmpeg, direct-SDK
NVENC/CUDA, raw Vulkan-Video, PyroWave, openh264) move into crates/pf-encode
behind one Encoder trait + open_video selector (plan §W6). The crate speaks the
shared frame vocabulary (pf-frame: CapturedFrame/PixelFormat + the DXGI identity
D3d11Frame/make_device) and pf-zerocopy (CUDA context/buffers), and NEVER
pf-capture — the capture→encode edge is one-way (ZeroCopyPolicy, prior commit).

Dep moves: the heavy encoder deps (ffmpeg-next, the NVENC SDK, openh264,
pyrowave-sys) move from the host to pf-encode; the host's
nvenc/amf-qsv/vulkan-encode/pyrowave features now FORWARD to pf-encode/*. The
host keeps a mod-encode shim (pub use pf_encode) so every crate::encode::* path
(negotiator + GameStream/native/mgmt planes) is unchanged.

resolve_render_adapter_luid moves from the host's windows/win_adapter.rs into
pf-gpu (both pf-encode and pf-capture need it as a peer of GPU selection); its 5
call sites (encode amf/nvenc, capture idd_push/synthetic_nv12, vdisplay manager)
rewire to pf_gpu::resolve_render_adapter_luid and win_adapter.rs is deleted.
pf-frame's make_device gains a # Safety section (public-unsafe-fn lint, latent
since the pf-frame carve — a full-workspace -D warnings clippy catches it).

Verified: Linux clippy -D warnings (pf-encode + host nvenc,vulkan-encode,pyrowave
--all-targets) + 13/13 pf-encode + 299/299 host tests; Windows clippy -D warnings
(pf-encode nvenc,amf-qsv --all-targets + host nvenc,amf-qsv --all-targets)
Finished exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-17 10:42:51 +02:00
parent 1de83ba51d
commit 9a36ea2132
31 changed files with 339 additions and 224 deletions
+36
View File
@@ -914,3 +914,39 @@ mod tests {
}
}
}
/// Pick the render GPU LUID the Windows pipeline is created on: the IDD-push capturer's
/// shared-texture ring, the IddCx `SET_RENDER_ADAPTER` pin, and (via the captured frame's device)
/// NVENC/AMF/QSV all follow this one decision — see [`selected_gpu`] for the precedence (operator
/// preference > `PUNKTFUNK_RENDER_ADAPTER` substring > max `DedicatedVideoMemory`). A configured
/// preference that doesn't match a present GPU falls back to auto selection (with a warning) rather
/// than returning `None`, so a stale preference never stops the host from streaming.
///
/// Lives here (not in a host module) so BOTH the capture and encode subsystem crates depend on it
/// as a peer of GPU selection instead of the orchestrator — the plan's `windows/adapter.rs`, folded
/// into `pf-gpu` (plan §W6). It was historically the SudoVDA backend's, then the host's
/// `win_adapter.rs`; the LUID-shaped view of [`selected_gpu`] plus the per-decision logging.
#[cfg(target_os = "windows")]
pub fn resolve_render_adapter_luid() -> Option<windows::Win32::Foundation::LUID> {
match selected_gpu() {
Some(sel) => {
tracing::info!(
adapter = sel.info.name,
vram_mb = sel.info.vram_bytes / (1024 * 1024),
source = sel.source.tag(),
"render adapter selected"
);
if sel.source == PickSource::PreferenceMissing {
tracing::warn!(
"the preferred GPU is not present — auto-selected the adapter above \
(fix or clear the preference in the web console)"
);
}
Some(sel.info.luid())
}
None => {
tracing::warn!("no suitable render adapter found for SET_RENDER_ADAPTER");
None
}
}
}