diff --git a/idd/LGIdd/CIndirectDeviceContext.cpp b/idd/LGIdd/CIndirectDeviceContext.cpp index 5a3c07bd..366284c4 100644 --- a/idd/LGIdd/CIndirectDeviceContext.cpp +++ b/idd/LGIdd/CIndirectDeviceContext.cpp @@ -390,8 +390,13 @@ void CIndirectDeviceContext::OnUnassignedSwapChain() m_replugMonitor = false; ReleaseSRWLockExclusive(&m_stateLock); + // Do NOT rebuild the monitor from inside this callback. Creating and arriving + // a new monitor here re-enters IddCx while the old monitor's swap-chain + // teardown is still unwinding on this thread, which leaves the new swap + // chain's surfaces lost/abandoned (DXGI_ERROR_ACCESS_LOST). Defer to the LGMP + // timer, matching how the departure is scheduled. if (replug) - FinishInit(0); + InterlockedExchange(&m_finishInitQueued, 1); } void CIndirectDeviceContext::OnSwapChainLost() @@ -913,6 +918,14 @@ void CIndirectDeviceContext::LGMPTimer() return; } + // Rebuild the monitor deferred from the IddCx unassign callback, off that + // callback's thread and after its swap-chain teardown has fully unwound. + if (InterlockedExchange(&m_finishInitQueued, 0)) + { + FinishInit(0); + return; + } + LGMP_STATUS status; if ((status = lgmpHostProcess(m_lgmp)) != LGMP_OK) { diff --git a/idd/LGIdd/CIndirectDeviceContext.h b/idd/LGIdd/CIndirectDeviceContext.h index 6881f8e7..7ca8079d 100644 --- a/idd/LGIdd/CIndirectDeviceContext.h +++ b/idd/LGIdd/CIndirectDeviceContext.h @@ -155,6 +155,12 @@ private: bool m_doSetMode = false; volatile LONG m_replugMonitorQueued = 0; volatile LONG m_recoverModeUpdateSwapChain = 0; + // Set from the IddCx unassign callback to defer the monitor rebuild + // (FinishInit) onto the LGMP timer. Creating/arriving a new monitor from + // inside the old monitor's unassign callback re-enters IddCx while its + // swap-chain teardown is still unwinding, which leaves the new swap-chain + // surfaces in a lost/abandoned state. + volatile LONG m_finishInitQueued = 0; public: CIndirectDeviceContext(_In_ WDFDEVICE wdfDevice) :