feat(host/windows): DXGI Desktop Duplication capture backend

Windows Capturer via DXGI Desktop Duplication: create a D3D11 device on the SudoVDA adapter (by LUID), find the matching output (by GDI name), DuplicateOutput, and per AcquireNextFrame copy the desktop into a CPU-readable staging texture -> tightly-packed BGRA (FramePayload::Cpu, feeds the openh264 software encoder GPU-lessly). Handles WAIT_TIMEOUT (reuse last frame) and ACCESS_LOST (re-duplicate). Adds FramePayload::D3d11(D3d11Frame) for the future NVENC zero-copy path, and a VirtualOutput.win_capture identity (adapter LUID + GDI name) carried out of the SudoVDA backend. Pure helpers (pack_luid/gdi_name_matches/depad_bgra) unit-tested on the VM; the live duplication path needs a real GPU + an activated SudoVDA monitor. Compiles clean on Windows + Linux.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 01:06:21 +00:00
parent 05426e2f20
commit 249da9beb3
5 changed files with 384 additions and 2 deletions
+4
View File
@@ -35,6 +35,10 @@ pub struct VirtualOutput {
/// gamescope outputs are created at the exact size, so this just confirms it; **Mutter sizes
/// its virtual monitor FROM the negotiation**, so here it's what makes the client's mode real.
pub preferred_mode: Option<(u32, u32, u32)>,
/// Windows capture identity (DXGI adapter LUID + GDI output name) for the SudoVDA backend —
/// what [`crate::capture::capture_virtual_output`] needs to duplicate the right output.
#[cfg(target_os = "windows")]
pub win_capture: Option<crate::capture::dxgi::WinCaptureTarget>,
/// Keeps the output — and whatever connection/thread backs it — alive; dropped on teardown.
pub keepalive: Box<dyn Send>,
}