diff --git a/packaging/windows/drivers/pf-vdisplay/src/callbacks.rs b/packaging/windows/drivers/pf-vdisplay/src/callbacks.rs index ba0b1d9..7001e45 100644 --- a/packaging/windows/drivers/pf-vdisplay/src/callbacks.rs +++ b/packaging/windows/drivers/pf-vdisplay/src/callbacks.rs @@ -25,13 +25,23 @@ pub unsafe extern "C" fn device_d0_entry( crate::adapter::init_adapter(device) } -/// Async completion of `IddCxAdapterInitAsync`: stash the adapter for later DDIs. STEP 4 also starts the -/// watchdog here. +/// Async completion of `IddCxAdapterInitAsync`: stash the adapter for later DDIs — IFF the init +/// actually SUCCEEDED. STEP 4 also starts the watchdog here. pub unsafe extern "C" fn adapter_init_finished( adapter: iddcx::IDDCX_ADAPTER, - _p_in: *const iddcx::IDARG_IN_ADAPTER_INIT_FINISHED, + p_in: *const iddcx::IDARG_IN_ADAPTER_INIT_FINISHED, ) -> NTSTATUS { - dbglog!("[pf-vd] adapter_init_finished"); + // SAFETY: the framework supplies a valid, live input-args pointer for the call. + let status = unsafe { (*p_in).AdapterInitStatus }; + dbglog!("[pf-vd] adapter_init_finished (AdapterInitStatus={status:#010x})"); + // The MS sample gates on NT_SUCCESS(AdapterInitStatus). An adapter whose async init FAILED is a + // husk the contract forbids using: monitors created on it arrive but are never activated (no + // swap-chain ever assigned) — every session then black-screens with no visible cause. Leaving + // the ADAPTER unset makes `create_monitor` fail the ADD cleanly (host-visible + retryable), and + // a re-entrant D0 retries the init (`init_adapter` only short-circuits once the stash is set). + if status < 0 { + return STATUS_SUCCESS; // the callback itself succeeded; the failure is in NOT adopting + } crate::adapter::set_adapter(adapter); crate::control::start_watchdog(); STATUS_SUCCESS diff --git a/packaging/windows/drivers/pf-vdisplay/src/direct_3d_device.rs b/packaging/windows/drivers/pf-vdisplay/src/direct_3d_device.rs index 57d2e27..49a3af8 100644 --- a/packaging/windows/drivers/pf-vdisplay/src/direct_3d_device.rs +++ b/packaging/windows/drivers/pf-vdisplay/src/direct_3d_device.rs @@ -150,7 +150,16 @@ pub fn pooled_device(luid: LUID) -> Option> { if let Some((k, dev)) = pool.as_ref() && *k == key { - return Some(dev.clone()); + // A TDR / driver reset REMOVES the pooled device permanently; handing it out again gives + // every future swap-chain a dead device (SetDevice fail-loop → black virtual display until + // device teardown). Detect and fall through to a fresh create instead. + // SAFETY: plain status query on the live pooled device. + match unsafe { dev.device.GetDeviceRemovedReason() } { + Ok(()) => return Some(dev.clone()), + Err(e) => { + dbglog!("[pf-vd] pooled D3D device was REMOVED ({e:?}) — recreating on {key:#x}"); + } + } } match Direct3DDevice::init(luid) { Ok(d) => { diff --git a/packaging/windows/drivers/pf-vdisplay/src/swap_chain_processor.rs b/packaging/windows/drivers/pf-vdisplay/src/swap_chain_processor.rs index 0163e5d..f19d707 100644 --- a/packaging/windows/drivers/pf-vdisplay/src/swap_chain_processor.rs +++ b/packaging/windows/drivers/pf-vdisplay/src/swap_chain_processor.rs @@ -117,9 +117,18 @@ impl SwapChainProcessor { // SAFETY: `w!("Distribution")` is a 'static null-terminated UTF-16 task name; `av_task` is a // valid local out-param. The returned handle is reverted with AvRevertMmThreadCharacteristics. let res = unsafe { AvSetMmThreadCharacteristicsW(w!("Distribution"), &mut av_task) }; - let Ok(av_handle) = res else { - dbglog!("[pf-vd] swap-chain: failed to prioritize thread: {res:?}"); - return; + // MMCSS can fail under the restricted WUDFHost token ('Distribution' task unregistered / + // service unavailable). The MS sample CONTINUES unprioritized — never abort: returning + // here would leave the assigned swap-chain undrained (the monitor stalls, DWM blocks on + // it) and leak the WDF swap-chain object until device teardown. + let av_handle = match res { + Ok(h) => Some(h), + Err(e) => { + dbglog!( + "[pf-vd] swap-chain: MMCSS prioritization failed ({e:?}) — continuing unprioritized" + ); + None + } }; Self::run_core( @@ -144,12 +153,14 @@ impl SwapChainProcessor { call_unsafe_wdf_function_binding!(WdfObjectDelete, swap_chain.0 as WDFOBJECT); } - // Revert the thread to normal once it's done. - // SAFETY: `av_handle` is the live characteristics handle returned by AvSetMmThreadCharacteristicsW - // above, reverted exactly once here at thread exit. - let res = unsafe { AvRevertMmThreadCharacteristics(av_handle) }; - if let Err(e) = res { - dbglog!("[pf-vd] swap-chain: failed to revert prioritized thread: {e:?}"); + // Revert the thread to normal once it's done (only if MMCSS was actually engaged). + if let Some(h) = av_handle { + // SAFETY: `h` is the live characteristics handle returned by + // AvSetMmThreadCharacteristicsW above, reverted exactly once here at thread exit. + let res = unsafe { AvRevertMmThreadCharacteristics(h) }; + if let Err(e) = res { + dbglog!("[pf-vd] swap-chain: failed to revert prioritized thread: {e:?}"); + } } });