feat(host/windows): capture the secure desktop in HDR via DDA (no SDR drop)

The secure-desktop DDA leg went black with HDR on: legacy DuplicateOutput (the SDR-era
API) can't capture an FP16/HDR desktop, and dropping the SudoVDA out of HDR is denied on
the Winlogon desktop (so the SDR-drop attempt just churned and stayed black).

Instead capture HDR natively on the DDA path — the capturer already has the full
FP16→BT.2020 PQ→R10G10B10A2 conversion (hdr_fp16 path), it just never requested FP16.
Thread a want_hdr flag into duplicate_output: for an HDR session request
DuplicateOutput1 with FP16 first and retry it (5×) instead of bailing to the
HDR-incapable legacy fallback. The secure-desktop mux now reads the monitor's real HDR
state and opens DDA in HDR when set — no advanced-color toggling at all. The
normal-desktop DDA overlay/flip issues that pushed us to WGC don't apply to the composed
Winlogon UI. want_hdr is threaded through every (re)duplication incl. ACCESS_LOST recovery.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-16 22:11:07 +00:00
parent 012e67b885
commit b0c518c21b
3 changed files with 77 additions and 109 deletions
+5 -3
View File
@@ -286,7 +286,7 @@ pub fn capture_virtual_output(vout: crate::vdisplay::VirtualOutput) -> Result<Bo
.unwrap_or_default()
.to_ascii_lowercase();
if backend == "dda" || backend == "dxgi" || wgc_disabled() {
return dxgi::DuplCapturer::open(target, pref, keep)
return dxgi::DuplCapturer::open(target, pref, keep, false)
.map(|c| Box::new(c) as Box<dyn Capturer>);
}
// WGC default, with a watchdog'd DDA fallback. WGC's Direct3D11CaptureFramePool::CreateFreeThreaded
@@ -316,11 +316,13 @@ pub fn capture_virtual_output(vout: crate::vdisplay::VirtualOutput) -> Result<Bo
}
Ok(Err(e)) => {
tracing::warn!(error = %format!("{e:#}"), "WGC open failed — falling back to DDA");
dxgi::DuplCapturer::open(target, pref, keep).map(|c| Box::new(c) as Box<dyn Capturer>)
dxgi::DuplCapturer::open(target, pref, keep, false)
.map(|c| Box::new(c) as Box<dyn Capturer>)
}
Err(_) => {
tracing::warn!("WGC open timed out (CreateFreeThreaded hang on the virtual display) — falling back to DDA");
dxgi::DuplCapturer::open(target, pref, keep).map(|c| Box::new(c) as Box<dyn Capturer>)
dxgi::DuplCapturer::open(target, pref, keep, false)
.map(|c| Box::new(c) as Box<dyn Capturer>)
}
}
}