Three encode-time fixes from the 4K120 field analysis (sessions pinned ~107 of
120 fps, 14-15 ms reported encode):
- Ceiling truth: the codec-level bitrate clamp (binary search at session open)
now records its result in a process-lifetime advisory cache keyed by
GPU/config, so an ABR overshoot opens — or in-place reconfigures — straight
AT the ceiling instead of re-running the ~6-open search (and a full rebuild
+ IDR) on every overshoot. New `Encoder::applied_bitrate_bps()` exposes the
post-clamp rate so the session loop can stop pacing/acking a phantom
requested rate (consumed host-side in a follow-up).
- Clamp-search hygiene: only NVENC parameter/caps rejections steer the search
now (`NvCallError` keeps the raw status downcastable); a transient failure
(busy engine, session limit, OOM, driver skew) propagates instead of
shrinking into — and now caching — a bogus ceiling. The floor fallback also
records the split mode it actually opened with, so a later reconfigure
re-presents the real session params.
- Split-frame selection: one shared `resolve_split_mode` replaces the two
byte-identical direct-SDK copies; the force-2-way threshold moves to
`SPLIT_FORCE_PIXEL_RATE` (950 Mpix/s, shared with the libav path) because
4K120 = 995,328,000 px/s missed the old `> 1e9` gate by 0.47% and stayed on
AUTO — which never engages at 2160 px height, leaving the second NVENC
engine idle in exactly the mode the threshold existed for. The Linux
session-ready info! line now carries the final split mode (journals are
INFO+; this was undiagnosable from user logs).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>