feat(host/windows): WGC capture backend (overlay/HDR-correct) with watchdog'd DDA fallback
android / android (push) Failing after 46s
apple / swift (push) Successful in 54s
ci / rust (push) Failing after 1m16s
ci / web (push) Successful in 31s
ci / docs-site (push) Successful in 27s
deb / build-publish (push) Successful in 2m23s
decky / build-publish (push) Successful in 10s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 6s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 5s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
ci / bench (push) Successful in 4m31s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 8m15s
docker / deploy-docs (push) Successful in 18s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 7m50s
android / android (push) Failing after 46s
apple / swift (push) Successful in 54s
ci / rust (push) Failing after 1m16s
ci / web (push) Successful in 31s
ci / docs-site (push) Successful in 27s
deb / build-publish (push) Successful in 2m23s
decky / build-publish (push) Successful in 10s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 6s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 5s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
ci / bench (push) Successful in 4m31s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 8m15s
docker / deploy-docs (push) Successful in 18s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 7m50s
The capture-architecture reset from the research: add a Windows.Graphics.Capture (WGC) backend that captures the COMPOSED desktop — including the overlay/independent-flip/MPO planes DXGI Desktop Duplication misses — which structurally fixes the frozen HDR animations + video (proven live: a WGC frame decodes to the real 5120x1440 HDR content DDA freezes on). It reuses the whole pipeline unchanged: the WGC frame's GPU texture → same scRGB→BT.2020-PQ shader → NVENC zero-copy; the OS composites the cursor (IsCursorCaptureEnabled) so no manual cursor pass. crates/punktfunk-host/src/ capture/wgc.rs; find_output/make_device/HdrConverter/nudge_cursor_onto made pub(crate) for reuse. Reliability findings + mitigations (live on the RTX 4090): - WGC can't activate under the SYSTEM account (0x80070424) — it needs the interactive user token. The host must run as the user for WGC (run.cmd: drop PsExec -s). DDA still needs SYSTEM for the secure desktop — that token reconciliation (impersonation) is the remaining task. - WGC's Direct3D11CaptureFramePool::CreateFreeThreaded intermittently HANGS on the headless SudoVDA (IddCx) display, correlated with accumulated SudoVDA churn (failed REMOVEs leaving lingering displays); clean-state opens reliably. Since it's a blocking hang, capture_virtual_output runs WGC open on a watchdog thread with a 5s timeout and falls back to DDA on hang/error — the session is NEVER left black: WGC when it opens (fixed animations), DDA otherwise. First-frame nudge added (WGC fires FrameArrived on change; a static desktop otherwise never delivers the first frame). - Default WGC; PUNKTFUNK_CAPTURE=dda forces DDA. DDA path unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -92,7 +92,7 @@ fn depad_bgra(src: &[u8], pitch: usize, w: usize, h: usize) -> Vec<u8> {
|
||||
/// Re-find the live `IDXGIOutput1` for a GDI name across all adapters (the SudoVDA monitor is
|
||||
/// enumerated under the rendering GPU). Used to recover after ACCESS_LOST, where the cached handle
|
||||
/// may be stale.
|
||||
unsafe fn find_output(gdi_name: &str) -> Result<(IDXGIAdapter1, IDXGIOutput1)> {
|
||||
pub(crate) unsafe fn find_output(gdi_name: &str) -> Result<(IDXGIAdapter1, IDXGIOutput1)> {
|
||||
let factory: IDXGIFactory1 = CreateDXGIFactory1().context("CreateDXGIFactory1")?;
|
||||
let mut i = 0u32;
|
||||
while let Ok(a) = factory.EnumAdapters1(i) {
|
||||
@@ -113,7 +113,9 @@ unsafe fn find_output(gdi_name: &str) -> Result<(IDXGIAdapter1, IDXGIOutput1)> {
|
||||
/// adapter). Used at open and on every ACCESS_LOST: a device created on one desktop cannot sustain a
|
||||
/// duplication on a *different* desktop (perpetual ACCESS_LOST), so the secure-desktop switch needs a
|
||||
/// device made while the thread is attached to that desktop.
|
||||
unsafe fn make_device(adapter: &IDXGIAdapter1) -> Result<(ID3D11Device, ID3D11DeviceContext)> {
|
||||
pub(crate) unsafe fn make_device(
|
||||
adapter: &IDXGIAdapter1,
|
||||
) -> Result<(ID3D11Device, ID3D11DeviceContext)> {
|
||||
let mut device: Option<ID3D11Device> = None;
|
||||
let mut context: Option<ID3D11DeviceContext> = None;
|
||||
D3D11CreateDevice(
|
||||
@@ -179,7 +181,7 @@ unsafe fn attach_input_desktop() {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn nudge_cursor_onto(output: &IDXGIOutput1) {
|
||||
pub(crate) unsafe fn nudge_cursor_onto(output: &IDXGIOutput1) {
|
||||
if let Ok(od) = output.GetDesc() {
|
||||
let r = od.DesktopCoordinates;
|
||||
let _ = SetCursorPos(r.left + 8, r.top + 8);
|
||||
@@ -495,14 +497,14 @@ float4 main(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_TARGET {
|
||||
/// scRGB FP16 → BT.2020 PQ 10-bit conversion pass. One per capture device (rebuilt on device
|
||||
/// recreate, like [`CursorCompositor`]). A single fullscreen draw samples the FP16 source SRV and
|
||||
/// writes PQ-encoded BT.2020 to the bound R10G10B10A2 render target.
|
||||
struct HdrConverter {
|
||||
pub(crate) struct HdrConverter {
|
||||
vs: ID3D11VertexShader,
|
||||
ps: ID3D11PixelShader,
|
||||
sampler: ID3D11SamplerState,
|
||||
}
|
||||
|
||||
impl HdrConverter {
|
||||
unsafe fn new(device: &ID3D11Device) -> Result<Self> {
|
||||
pub(crate) unsafe fn new(device: &ID3D11Device) -> Result<Self> {
|
||||
let vsb = compile_shader(HDR_VS, s!("main"), s!("vs_5_0"))?;
|
||||
let psb = compile_shader(HDR_PS, s!("main"), s!("ps_5_0"))?;
|
||||
let mut vs = None;
|
||||
@@ -528,7 +530,7 @@ impl HdrConverter {
|
||||
}
|
||||
|
||||
/// Convert `src_srv` (FP16 scRGB) into `dst_rtv` (R10G10B10A2 PQ BT.2020). Opaque pass, no blend.
|
||||
unsafe fn convert(
|
||||
pub(crate) unsafe fn convert(
|
||||
&self,
|
||||
ctx: &ID3D11DeviceContext,
|
||||
src_srv: &ID3D11ShaderResourceView,
|
||||
|
||||
Reference in New Issue
Block a user