fix(encode/nvenc): the host advertises what the driver lists, not a superset

Every NVIDIA host advertised a static H.264|HEVC|AV1 superset, so a 1st-gen
Maxwell (GTX 960M, no HEVC/AV1 encode) offered HEVC — a client that believed
it got ~15 s of blank video and a disconnect instead of a stream. Both OSes
now ask the driver itself (nvEncGetEncodeGUIDs) on one throwaway direct-SDK
session: Linux on the shared CUDA context, Windows on the selected render
adapter, wired into host_wire_caps AND the GameStream serverinfo mask (which
had been left on the superset for NVIDIA on both OSes). Fails open — an
unanswerable probe keeps the historical superset, so it can only ever narrow
the advertisement to codecs the GPU really encodes.

The HEVC 4:4:4 answer rides the same session on Linux instead of opening a
libav hevc_nvenc FREXT probe: that open is the prime suspect for the field
bug where one probe wedges NVENC process-wide (NV_ENC_ERR_INVALID_VERSION on
every later session until a host restart), and the direct backend re-checks
the same caps bit at session open anyway. The ffmpeg probe remains only for
hosts that really stream over libav (PUNKTFUNK_NVENC_DIRECT=0 or a build
without the nvenc feature), where ffmpeg's NVENC client runs regardless. The
10-bit probe deliberately stays libav — Linux HDR rides the libav P010 path.

On-hardware: .136 (RTX 5070 Ti) 14/14 nvenc tests in one process incl. the
probe followed by real sessions and dirty teardown; .173 (Windows RTX) probe
+ 47 release lib tests. The Windows probe test documents the pre-existing
MSVC debug-link failure (LNK2019 via the sdk crate's unused lazy loader) —
run it with --release, the same reason windows-host.yml gates with clippy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 0346ec8090568eb499e8cb7d735305b28471185e)
This commit is contained in:
2026-07-28 17:01:59 +02:00
parent 560e663aef
commit 188f55d3b1
5 changed files with 428 additions and 48 deletions
@@ -94,9 +94,20 @@ fn base_codec_mode_support() -> u32 {
return m;
}
}
// Windows AMD/Intel (AMF/QSV): advertise only what the GPU actually encodes (AV1 is narrow, an
// old iGPU might lack HEVC). AMF probes natively (no build feature needed); QSV needs the
// libavcodec build. NVENC and the GPU-less software path keep the static superset.
// Linux NVIDIA: the driver's own encode-GUID list (`nvenc_codec_support`, one throwaway
// direct-SDK session, cached) — the same probe `host_wire_caps` consults, so both planes
// stop advertising HEVC/AV1 on a chip without them (the GM107 dead-session field bug).
// Fail-open like every arm here: an unanswerable probe → `probed_mask` = None → superset.
#[cfg(all(target_os = "linux", feature = "nvenc"))]
if !crate::encode::linux_zero_copy_is_vaapi() {
if let Some(m) = probed_mask(crate::encode::nvenc_codec_support()) {
return m;
}
}
// Windows: advertise only what the GPU actually encodes (AV1 is narrow, an old iGPU might
// lack HEVC, a 1st-gen-Maxwell NVENC is H.264-only). AMF probes natively (no build feature
// needed); QSV needs the libavcodec or VPL build, NVENC the `nvenc` build. The GPU-less
// software path keeps the static superset.
#[cfg(target_os = "windows")]
if crate::encode::windows_backend_is_probed() {
if let Some(m) = probed_mask(crate::encode::windows_codec_support()) {