feat(pyrowave): Windows host encoder — separate-plane zero-copy D3D11→Vulkan
Wire PyroWave into the Windows host (design/pyrowave-windows-host-zerocopy.md). Before this a macOS client + Windows host that both selected PyroWave silently ran HEVC: the host never advertised CODEC_PYROWAVE and open_video_backend bailed. Approach (zero-copy, no GPU→CPU→GPU): pyrowave owns its own Vulkan device (create_device_by_compat, by render-GPU vendor/device-id — NOT LUID, invalid in Session 0). The capturer runs a BGRA→YUV BT.709-limited CSC (matching rgb2yuv.comp) into TWO SEPARATE shareable plane textures — full-res R8 Y + half-res R8G8 CbCr — which the encoder imports into pyrowave's device. Separate single/two-component textures import reliably on NVIDIA at any size; a single planar NV12 import does NOT (the vendored interop test: "only very specific resource sizes" — confirmed on-glass: 1024² fine, 720p/1080p/1440p garbage). A shared D3D11 fence, signalled after the CSC, is imported as a Vulkan timeline semaphore so the wavelet read is ordered after it. - pf-encode: enc/windows/pyrowave.rs (Encoder impl, two-plane import + Linux-style plane views); host_wire_caps advertises CODEC_PYROWAVE on Windows when the backend isn't Software; open_video_backend routes a negotiated PyroWave session first; pyrowave-sys on the Windows target; interop confirmed at open → clean HEVC fallback. - pf-encode: shared, unit-tested enc/pyrowave_wire.rs (single source of truth for the client-facing AU framing); Linux encoder uses it too. - pf-capture: dxgi.rs BgraToYuvPlanes CSC; idd_push.rs pyrowave mode — forces the virtual display SDR (the VideoProcessor can't ingest the FP16 HDR ring), a two-plane shareable out-ring, a shared fence passed every frame (so a rebuilt encoder re-imports it). Threaded via OutputFormat::pyrowave. - pf-frame: D3d11Frame::pyro carries the CbCr plane + fence; OutputFormat::pyrowave. Verified on .173 (RTX 4090): full-host build + clippy -D warnings (nvenc,amf-qsv) + fmt --all --check; pyrowave_wire unit tests; pyrowave_win_smoke GPU test round-trips distinct Y/Cb/Cr (100/180/60) exactly at 1024²/720p/1080p/1440p; Stage-0 interop validated in the real Session-0 service context on-glass. Deployed to the box. Owed: final on-glass picture/latency confirmation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -34,10 +34,35 @@ pub struct WinCaptureTarget {
|
||||
pub wudf_pid: u32,
|
||||
}
|
||||
|
||||
/// A GPU-resident captured texture (future NVENC-D3D11 zero-copy path).
|
||||
/// The PyroWave (Windows) zero-copy sharing payload attached to a captured frame: the SECOND plane
|
||||
/// texture + the cross-device fence the wavelet encoder needs (design/pyrowave-windows-host-
|
||||
/// zerocopy.md). The wavelet encoder ingests **two SEPARATE** shareable plane textures — the full-res
|
||||
/// `R8_UNORM` **Y** rides [`D3d11Frame::texture`], and the half-res `R8G8_UNORM` **CbCr** rides
|
||||
/// [`cbcr`](Self::cbcr) — because importing a single *planar* NV12 texture into Vulkan is unreliable
|
||||
/// on NVIDIA at arbitrary sizes; separate single/two-component textures import reliably. `None` on
|
||||
/// every non-PyroWave frame (NVENC/AMF/QSV encode the in-place NV12/BGRA and need no cross-device
|
||||
/// fence). The encoder makes each texture's shared handle on demand.
|
||||
pub struct PyroFrameShare {
|
||||
/// The half-res `R8G8_UNORM` interleaved CbCr plane (created `SHARED | SHARED_NTHANDLE`). The
|
||||
/// full-res Y plane is [`D3d11Frame::texture`].
|
||||
pub cbcr: ID3D11Texture2D,
|
||||
/// The shared D3D11/D3D12 **fence** NT handle (raw), passed on EVERY frame; the encoder imports
|
||||
/// it (duplicating) whenever it has no timeline yet (first frame or after an encoder rebuild).
|
||||
pub fence_handle: Option<isize>,
|
||||
/// The fence value the capturer signalled after THIS frame's convert. The encoder's Vulkan
|
||||
/// acquire waits on it, so the wavelet read is ordered after the D3D11 CSC.
|
||||
pub fence_value: u64,
|
||||
}
|
||||
|
||||
/// A GPU-resident captured texture (the Windows zero-copy path: NVENC/AMF/QSV encode it in place;
|
||||
/// the PyroWave backend imports it — plus the second plane in [`pyro`](Self::pyro) — into its own
|
||||
/// Vulkan device). For a PyroWave frame, `texture` is the full-res `R8_UNORM` Y plane.
|
||||
pub struct D3d11Frame {
|
||||
pub texture: ID3D11Texture2D,
|
||||
pub device: ID3D11Device,
|
||||
/// PyroWave zero-copy sharing info (the CbCr plane + fence); `None` unless this is a PyroWave
|
||||
/// session. See [`PyroFrameShare`].
|
||||
pub pyro: Option<PyroFrameShare>,
|
||||
}
|
||||
// SAFETY: `D3d11Frame` owns an `ID3D11Texture2D` + `ID3D11Device`, which are COM interface pointers.
|
||||
// D3D11 devices/resources use thread-safe (interlocked) COM reference counting, and the device is
|
||||
|
||||
Reference in New Issue
Block a user