diff --git a/idd/LGIdd/CD3D12CommandQueue.h b/idd/LGIdd/CD3D12CommandQueue.h index fb8b6064..e9236e4f 100644 --- a/idd/LGIdd/CD3D12CommandQueue.h +++ b/idd/LGIdd/CD3D12CommandQueue.h @@ -24,6 +24,7 @@ #include #include #include +#include using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; @@ -40,7 +41,7 @@ class CD3D12CommandQueue ComPtr m_cmdList; ComPtr m_fence; - bool m_pending = false; + std::atomic m_pending = false; HandleT m_event; HANDLE m_waitHandle = INVALID_HANDLE_VALUE; UINT64 m_fenceValue = 0; diff --git a/idd/LGIdd/CD3D12Device.cpp b/idd/LGIdd/CD3D12Device.cpp index 813c881a..45b8868a 100644 --- a/idd/LGIdd/CD3D12Device.cpp +++ b/idd/LGIdd/CD3D12Device.cpp @@ -147,6 +147,22 @@ void CD3D12Device::DeInit() m_infoQueue.Reset(); } +void CD3D12Device::WaitForIdle() +{ + // A queue is ready once its GPU work has signalled and its completion + // callback has run (clearing the pending flag). Bound the wait so a + // removed/hung device cannot stall teardown indefinitely. + auto drain = [](CD3D12CommandQueue& queue) + { + for (int i = 0; i < 1000 && !queue.IsReady(); ++i) + Sleep(1); + }; + + for (int i = 0; i < ARRAYSIZE(m_copyQueue); ++i) + drain(m_copyQueue[i]); + drain(m_computeQueue); +} + bool CD3D12Device::HeapTest() { D3D12_RESOURCE_DESC desc = {}; diff --git a/idd/LGIdd/CD3D12Device.h b/idd/LGIdd/CD3D12Device.h index 3e1d0382..3828c07d 100644 --- a/idd/LGIdd/CD3D12Device.h +++ b/idd/LGIdd/CD3D12Device.h @@ -69,6 +69,11 @@ struct CD3D12Device InitResult Init(CIVSHMEM &ivshmem, UINT64 &alignSize); void DeInit(); + // Wait for all command queues to finish in-flight GPU work and run their + // completion callbacks. Used at swap-chain teardown so no callback touches + // resources we are about to release. + void WaitForIdle(); + ComPtr GetDevice() { return m_device; } ComPtr GetHeap() { return m_ivshmemHeap; } bool IsIndirectCopy() { return m_indirectCopy; } diff --git a/idd/LGIdd/CIndirectDeviceContext.cpp b/idd/LGIdd/CIndirectDeviceContext.cpp index 366284c4..c624a3a1 100644 --- a/idd/LGIdd/CIndirectDeviceContext.cpp +++ b/idd/LGIdd/CIndirectDeviceContext.cpp @@ -278,6 +278,17 @@ void CIndirectDeviceContext::InitAdapter() void CIndirectDeviceContext::FinishInit(UINT connectorIndex) { + // We support a single monitor; never create a second one if one already + // exists (a replug must clear m_monitor via departure first). + AcquireSRWLockExclusive(&m_stateLock); + bool haveMonitor = m_monitor != WDF_NO_HANDLE; + ReleaseSRWLockExclusive(&m_stateLock); + if (haveMonitor) + { + DEBUG_WARN("FinishInit skipped: a monitor already exists"); + return; + } + WDF_OBJECT_ATTRIBUTES attr; WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, CIndirectMonitorContextWrapper); @@ -332,18 +343,7 @@ void CIndirectDeviceContext::FinishInit(UINT connectorIndex) void CIndirectDeviceContext::ReplugMonitor() { - // Sample and mutate the replug state atomically. The monitor handle is - // published by FinishInit on an IddCx thread while this can run from the - // timer thread, so a plain check-then-act would race. AcquireSRWLockExclusive(&m_stateLock); - IDDCX_MONITOR monitor = m_monitor; - - if (monitor == WDF_NO_HANDLE) - { - ReleaseSRWLockExclusive(&m_stateLock); - FinishInit(0); - return; - } if (m_replugMonitor) { @@ -351,7 +351,21 @@ void CIndirectDeviceContext::ReplugMonitor() return; } + IDDCX_MONITOR monitor = m_monitor; + if (monitor == WDF_NO_HANDLE) + { + ReleaseSRWLockExclusive(&m_stateLock); + // Either no monitor yet, or one is already pending; build it now and + // cancel any queued rebuild so we do not create two. + InterlockedExchange(&m_finishInitQueued, 0); + FinishInit(0); + return; + } + + // Clear the handle before departing so nothing calls an IddCx monitor API on + // a departing/destroyed handle. FinishInit publishes the new one. m_replugMonitor = true; + m_monitor = nullptr; ReleaseSRWLockExclusive(&m_stateLock); DEBUG_TRACE("ReplugMonitor"); @@ -360,16 +374,28 @@ void CIndirectDeviceContext::ReplugMonitor() { AcquireSRWLockExclusive(&m_stateLock); m_replugMonitor = false; + m_monitor = monitor; ReleaseSRWLockExclusive(&m_stateLock); DEBUG_ERROR("IddCxMonitorDeparture Failed (0x%08x)", status); return; } + + // Always queue the rebuild here rather than relying on the unassign callback: + // if the monitor had no swap chain (e.g. recovering from a lost path) no + // unassign fires. The timer runs FinishInit off this thread. + InterlockedExchange(&m_finishInitQueued, 1); +} + +void CIndirectDeviceContext::OnMonitorDestroyed(IDDCX_MONITOR monitor) +{ + AcquireSRWLockExclusive(&m_stateLock); + if (m_monitor == monitor) + m_monitor = nullptr; + ReleaseSRWLockExclusive(&m_stateLock); } void CIndirectDeviceContext::OnAssignSwapChain() { - InterlockedExchange(&m_recoverModeUpdateSwapChain, 0); - AcquireSRWLockExclusive(&m_stateLock); bool doSetMode = m_doSetMode; CSettings::DisplayMode mode = m_setMode; @@ -380,40 +406,6 @@ void CIndirectDeviceContext::OnAssignSwapChain() g_pipe.SetDisplayMode(mode.width, mode.height, mode.refresh); } -void CIndirectDeviceContext::OnUnassignedSwapChain() -{ - InterlockedExchange(&m_replugMonitorQueued, 0); - InterlockedExchange(&m_recoverModeUpdateSwapChain, 0); - - AcquireSRWLockExclusive(&m_stateLock); - bool replug = m_replugMonitor; - 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) - InterlockedExchange(&m_finishInitQueued, 1); -} - -void CIndirectDeviceContext::OnSwapChainLost() -{ - // A mode update normally keeps the swap chain alive. If Windows instead - // reports the existing path disappeared before we see a frame at the new - // size, recover by scheduling the old replug path from the LGMP timer so we - // do not tear down the swap chain from one of its worker threads. - if (!InterlockedCompareExchange(&m_recoverModeUpdateSwapChain, 0, 0)) - return; - - if (InterlockedExchange(&m_replugMonitorQueued, 1)) - return; - - DEBUG_WARN("Swap chain was lost after a mode update, falling back to monitor replug"); -} - static inline void FillSignalInfo(DISPLAYCONFIG_VIDEO_SIGNAL_INFO & mode, DWORD width, DWORD height, DWORD vsync, bool monitorMode) { mode.totalSize.cx = mode.activeSize.cx = width; @@ -574,96 +566,6 @@ NTSTATUS CIndirectDeviceContext::MonitorQueryTargetModes2( } #endif -bool CIndirectDeviceContext::UpdateMonitorModes() -{ - AcquireSRWLockExclusive(&m_stateLock); - IDDCX_MONITOR monitor = m_monitor; - ReleaseSRWLockExclusive(&m_stateLock); - - if (!monitor) - return false; - - // Snapshot the mode list so we do not hold m_modeLock across the IddCx call - // below - IddCxMonitorUpdateModes can synchronously re-enter our mode - // enumeration callbacks (which also take m_modeLock). - CSettings::DisplayModes displayModes; - AcquireSRWLockShared(&m_modeLock); - displayModes = m_displayModes; - ReleaseSRWLockShared(&m_modeLock); - -#ifdef HAS_IDDCX_110 - if (CanUseIddCx110DDIs()) - { - IDDCX_TARGET_MODE2* modes = (IDDCX_TARGET_MODE2*)_malloca( - displayModes.size() * sizeof(IDDCX_TARGET_MODE2)); - if (!modes) - { - DEBUG_WARN("Failed to allocate memory for the mode list"); - return false; - } - - ZeroMemory(modes, displayModes.size() * sizeof(IDDCX_TARGET_MODE2)); - - auto* mode = modes; - for (auto it = displayModes.cbegin(); it != displayModes.cend(); ++it, ++mode) - { - mode->Size = sizeof(IDDCX_TARGET_MODE2); - mode->RequiredBandwidth = (UINT64)it->width * it->height * it->refresh * 32; - mode->BitsPerComponent = GetWireBitsPerComponent(CanUseIddCx110DDIs()); - FillSignalInfo(mode->TargetVideoSignalInfo.targetVideoSignalInfo, it->width, it->height, it->refresh, false); - } - - IDARG_IN_UPDATEMODES2 updateModes = {}; - updateModes.Reason = IDDCX_UPDATE_REASON_OTHER; - updateModes.TargetModeCount = (UINT)displayModes.size(); - updateModes.pTargetModes = modes; - - NTSTATUS status = IddCxMonitorUpdateModes2(monitor, &updateModes); - _freea(modes); - if (!NT_SUCCESS(status)) - { - DEBUG_WARN("IddCxMonitorUpdateModes2 Failed (0x%08x)", status); - return false; - } - - return true; - } -#endif - - IDDCX_TARGET_MODE* modes = (IDDCX_TARGET_MODE*)_malloca( - displayModes.size() * sizeof(IDDCX_TARGET_MODE)); - if (!modes) - { - DEBUG_WARN("Failed to allocate memory for the mode list"); - return false; - } - - ZeroMemory(modes, displayModes.size() * sizeof(IDDCX_TARGET_MODE)); - - auto* mode = modes; - for (auto it = displayModes.cbegin(); it != displayModes.cend(); ++it, ++mode) - { - mode->Size = sizeof(IDDCX_TARGET_MODE); - mode->RequiredBandwidth = (UINT64)it->width * it->height * it->refresh * 32; - FillSignalInfo(mode->TargetVideoSignalInfo.targetVideoSignalInfo, it->width, it->height, it->refresh, false); - } - - IDARG_IN_UPDATEMODES updateModes = {}; - updateModes.Reason = IDDCX_UPDATE_REASON_OTHER; - updateModes.TargetModeCount = (UINT)displayModes.size(); - updateModes.pTargetModes = modes; - - NTSTATUS status = IddCxMonitorUpdateModes(monitor, &updateModes); - _freea(modes); - if (!NT_SUCCESS(status)) - { - DEBUG_WARN("IddCxMonitorUpdateModes Failed (0x%08x)", status); - return false; - } - - return true; -} - void CIndirectDeviceContext::SetResolution(int width, int height) { CSettings::DisplayMode mode = {}; @@ -673,29 +575,17 @@ void CIndirectDeviceContext::SetResolution(int width, int height) mode.preferred = true; AcquireSRWLockExclusive(&m_stateLock); - m_setMode = mode; + m_setMode = mode; + m_doSetMode = true; ReleaseSRWLockExclusive(&m_stateLock); g_settings.SetExtraMode(mode); PopulateDefaultModes(); - if (UpdateMonitorModes()) - { - DEBUG_TRACE("Updated monitor modes without replugging"); - AcquireSRWLockExclusive(&m_stateLock); - m_doSetMode = false; - ReleaseSRWLockExclusive(&m_stateLock); - InterlockedExchange(&m_recoverModeUpdateSwapChain, 1); - g_pipe.SetDisplayMode(mode.width, mode.height, mode.refresh); - return; - } - - DEBUG_TRACE("Falling back to monitor replug for mode update"); - AcquireSRWLockExclusive(&m_stateLock); - m_doSetMode = true; - ReleaseSRWLockExclusive(&m_stateLock); - InterlockedExchange(&m_recoverModeUpdateSwapChain, 0); + // IddCxMonitorUpdateModes[2] does not invalidate Windows' cached mode list, + // so the only reliable way to apply a new mode is to depart and re-arrive the + // monitor, forcing Windows to rebuild the topology from the new mode list. ReplugMonitor(); } @@ -909,20 +799,13 @@ void CIndirectDeviceContext::DeInitLGMP() void CIndirectDeviceContext::LGMPTimer() { - if (InterlockedExchange(&m_replugMonitorQueued, 0)) - { - AcquireSRWLockExclusive(&m_stateLock); - m_doSetMode = true; - ReleaseSRWLockExclusive(&m_stateLock); - ReplugMonitor(); - return; - } - - // Rebuild the monitor deferred from the IddCx unassign callback, off that - // callback's thread and after its swap-chain teardown has fully unwound. + // Rebuild the monitor queued by ReplugMonitor, off the IddCx callback thread. if (InterlockedExchange(&m_finishInitQueued, 0)) { FinishInit(0); + AcquireSRWLockExclusive(&m_stateLock); + m_replugMonitor = false; + ReleaseSRWLockExclusive(&m_stateLock); return; } @@ -984,17 +867,6 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame if (!m_lgmp || !m_frameQueue) return result; - if (InterlockedCompareExchange(&m_recoverModeUpdateSwapChain, 0, 0)) - { - AcquireSRWLockShared(&m_stateLock); - const bool sizeMatches = - srcFormat.width == m_setMode.width && srcFormat.height == m_setMode.height; - ReleaseSRWLockShared(&m_stateLock); - - if (sizeMatches) - InterlockedExchange(&m_recoverModeUpdateSwapChain, 0); - } - if (m_width != dstFormat.desc.Width || m_height != dstFormat.desc.Height || m_pitch != pitch || diff --git a/idd/LGIdd/CIndirectDeviceContext.h b/idd/LGIdd/CIndirectDeviceContext.h index 7ca8079d..e4581a4e 100644 --- a/idd/LGIdd/CIndirectDeviceContext.h +++ b/idd/LGIdd/CIndirectDeviceContext.h @@ -140,7 +140,6 @@ private: void DeInitLGMP(); void LGMPTimer(); void ResendCursor() const; - bool UpdateMonitorModes(); // Guards m_displayModes and m_edid. The mode list is rebuilt on the LGMP // timer thread (SetResolution) while IddCx concurrently enumerates it on its @@ -153,13 +152,9 @@ private: CSettings::DisplayMode m_setMode = {}; 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. + + // Set by ReplugMonitor after a departure to rebuild the monitor from the LGMP + // timer, off the IddCx callback thread. volatile LONG m_finishInitQueued = 0; public: @@ -175,9 +170,8 @@ public: void FinishInit(UINT connectorIndex); void ReplugMonitor(); + void OnMonitorDestroyed(IDDCX_MONITOR monitor); void OnAssignSwapChain(); - void OnUnassignedSwapChain(); - void OnSwapChainLost(); NTSTATUS ParseMonitorDescription( const IDARG_IN_PARSEMONITORDESCRIPTION* inArgs, IDARG_OUT_PARSEMONITORDESCRIPTION* outArgs); diff --git a/idd/LGIdd/CIndirectMonitorContext.cpp b/idd/LGIdd/CIndirectMonitorContext.cpp index 0b384702..7906a359 100644 --- a/idd/LGIdd/CIndirectMonitorContext.cpp +++ b/idd/LGIdd/CIndirectMonitorContext.cpp @@ -32,50 +32,78 @@ CIndirectMonitorContext::CIndirectMonitorContext(_In_ IDDCX_MONITOR monitor, CIn CIndirectMonitorContext::~CIndirectMonitorContext() { UnassignSwapChain(); + m_devContext->OnMonitorDestroyed(m_monitor); } void CIndirectMonitorContext::AssignSwapChain(IDDCX_SWAPCHAIN swapChain, LUID renderAdapter, HANDLE newFrameEvent) { -reInit: UnassignSwapChain(); - m_dx11Device = std::make_shared(renderAdapter); - if (FAILED(m_dx11Device->Init())) - { - WdfObjectDelete(swapChain); - return; - } + // Build everything into locals so the members are never observed + // half-constructed by a concurrent unassign, and so the processor (which + // spawns a worker thread) is created outside the lock. + std::shared_ptr dx11Device; + std::shared_ptr dx12Device; - UINT64 alignSize = CPlatformInfo::GetPageSize(); - m_dx12Device = std::make_shared(renderAdapter); - switch (m_dx12Device->Init(m_devContext->GetIVSHMEM(), alignSize)) + for (;;) { - case CD3D12Device::SUCCESS: - break; - - case CD3D12Device::FAILURE: + dx11Device = std::make_shared(renderAdapter); + if (FAILED(dx11Device->Init())) + { WdfObjectDelete(swapChain); return; + } - case CD3D12Device::RETRY: - m_dx12Device.reset(); - m_dx11Device.reset(); - goto reInit; - } - - if (!m_devContext->SetupLGMP(alignSize)) - { - WdfObjectDelete(swapChain); - DEBUG_ERROR("SetupLGMP failed"); - return; + UINT64 alignSize = CPlatformInfo::GetPageSize(); + dx12Device = std::make_shared(renderAdapter); + CD3D12Device::InitResult r = dx12Device->Init(m_devContext->GetIVSHMEM(), alignSize); + if (r == CD3D12Device::RETRY) + { + dx12Device.reset(); + dx11Device.reset(); + continue; + } + if (r == CD3D12Device::FAILURE) + { + WdfObjectDelete(swapChain); + return; + } + + if (!m_devContext->SetupLGMP(alignSize)) + { + WdfObjectDelete(swapChain); + DEBUG_ERROR("SetupLGMP failed"); + return; + } + break; } - m_swapChain.reset(new CSwapChainProcessor(m_monitor, m_devContext, swapChain, m_dx11Device, m_dx12Device, newFrameEvent)); + std::unique_ptr processor(new CSwapChainProcessor( + m_monitor, m_devContext, swapChain, dx11Device, dx12Device, newFrameEvent)); + + AcquireSRWLockExclusive(&m_lock); + m_dx11Device = std::move(dx11Device); + m_dx12Device = std::move(dx12Device); + m_swapChain = std::move(processor); + ReleaseSRWLockExclusive(&m_lock); } void CIndirectMonitorContext::UnassignSwapChain() { - m_swapChain.reset(); - m_dx11Device.reset(); - m_dx12Device.reset(); + // Detach under the lock, then destroy outside it. Destroying the processor + // joins its worker thread, whose teardown (WdfObjectDelete) re-enters this + // method on another thread - holding the lock across that would deadlock. + std::unique_ptr processor; + std::shared_ptr dx11Device; + std::shared_ptr dx12Device; + + AcquireSRWLockExclusive(&m_lock); + processor = std::move(m_swapChain); + dx11Device = std::move(m_dx11Device); + dx12Device = std::move(m_dx12Device); + ReleaseSRWLockExclusive(&m_lock); + + processor.reset(); + dx11Device.reset(); + dx12Device.reset(); } \ No newline at end of file diff --git a/idd/LGIdd/CIndirectMonitorContext.h b/idd/LGIdd/CIndirectMonitorContext.h index 7a9fe5f4..9ac35a8d 100644 --- a/idd/LGIdd/CIndirectMonitorContext.h +++ b/idd/LGIdd/CIndirectMonitorContext.h @@ -35,6 +35,10 @@ class CIndirectMonitorContext private: IDDCX_MONITOR m_monitor; + // Guards the swap chain and device pointers. Assign and unassign can run + // concurrently (an unassign triggered by the worker's WdfObjectDelete can + // race the next assign), and shared_ptr copy/reset is not thread safe. + SRWLOCK m_lock = SRWLOCK_INIT; std::shared_ptr m_dx11Device; std::shared_ptr m_dx12Device; diff --git a/idd/LGIdd/CSwapChainProcessor.cpp b/idd/LGIdd/CSwapChainProcessor.cpp index bc1e4ae0..437cb362 100644 --- a/idd/LGIdd/CSwapChainProcessor.cpp +++ b/idd/LGIdd/CSwapChainProcessor.cpp @@ -38,7 +38,9 @@ CSwapChainProcessor::CSwapChainProcessor(IDDCX_MONITOR monitor, CIndirectDeviceC if (!m_postProcessor.Init(dx12Device)) DEBUG_ERROR("Failed to initialize post processor"); - m_terminateEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr)); + // Manual-reset: both worker threads wait on this, so it must stay signalled + // once set or only one thread would ever observe termination. + m_terminateEvent.Attach(CreateEvent(nullptr, TRUE, FALSE, nullptr)); m_thread[0].Attach(CreateThread(nullptr, 0, _SwapChainThread, this, 0, nullptr)); m_cursorDataEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr)); @@ -53,6 +55,11 @@ CSwapChainProcessor::~CSwapChainProcessor() if (m_thread[1].Get()) WaitForSingleObject(m_thread[1].Get(), INFINITE); + // Drain in-flight GPU work / completion callbacks before releasing the + // resources they reference. The swap chain was already released in the + // worker epilogue, so this does not hold an IddCx frame. + m_dx12Device->WaitForIdle(); + m_postProcessor.Reset(); m_resPool.Reset(); m_fbPool.Reset(); @@ -71,22 +78,26 @@ void CSwapChainProcessor::SwapChainThread() HANDLE avTaskHandle = AvSetMmThreadCharacteristicsW(L"Distribution", &avTask); DEBUG_INFO("Start Thread"); - SwapChainThreadCore(); - WdfObjectDelete((WDFOBJECT)m_hSwapChain); + // Only delete the swap chain if we took ownership of it (SetDevice + // succeeded). If SetDevice failed IddCx still owns and tears it down, so + // deleting it here would double-free the WDF object. Releasing it when we do + // own it hands the acquired frame back to IddCx promptly. + if (SwapChainThreadCore()) + WdfObjectDelete((WDFOBJECT)m_hSwapChain); m_hSwapChain = nullptr; AvRevertMmThreadCharacteristics(avTaskHandle); } -void CSwapChainProcessor::SwapChainThreadCore() +bool CSwapChainProcessor::SwapChainThreadCore() { ComPtr dxgiDevice; HRESULT hr = m_dx11Device->GetDevice().As(&dxgiDevice); if (FAILED(hr)) { DEBUG_ERROR_HR(hr, "Failed to get the dxgiDevice"); - return; + return false; } if (IDD_IS_FUNCTION_AVAILABLE(IddCxSetRealtimeGPUPriority)) @@ -107,12 +118,17 @@ void CSwapChainProcessor::SwapChainThreadCore() IDARG_IN_SWAPCHAINSETDEVICE setDevice = {}; setDevice.pDevice = dxgiDevice.Get(); + // A failure here (commonly DXGI_ERROR_ACCESS_LOST on the first assignment) + // is not recoverable on this handle - IddCx reassigns a fresh swap chain, + // which is what actually succeeds. Bail cleanly and let that happen. hr = IddCxSwapChainSetDevice(m_hSwapChain, &setDevice); if (FAILED(hr)) { DEBUG_ERROR_HR(hr, "IddCxSwapChainSetDevice Failed"); - return; + return false; } + // Past this point SetDevice succeeded: we own the swap chain and are + // responsible for deleting it. IDARG_IN_SETUP_HWCURSOR c = {}; c.CursorInfo.Size = sizeof(c.CursorInfo); @@ -125,7 +141,7 @@ void CSwapChainProcessor::SwapChainThreadCore() if (!NT_SUCCESS(status)) { DEBUG_ERROR("IddCxMonitorSetupHardwareCursor Failed (0x%08x)", status); - return; + return true; } m_lastShapeId = 0; @@ -206,9 +222,9 @@ void CSwapChainProcessor::SwapChainThreadCore() hr = IddCxSwapChainFinishedProcessingFrame(m_hSwapChain); if (FAILED(hr)) { - if (hr == STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY) - m_devContext->OnSwapChainLost(); - else + // A lost path is normal (mode change/topology rebuild); Windows + // reassigns a fresh swap chain. Just exit and let it. + if (hr != STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY) DEBUG_ERROR_HR(hr, "IddCxSwapChainFinishedProcessingFrame Failed"); break; } @@ -216,12 +232,10 @@ void CSwapChainProcessor::SwapChainThreadCore() } } else - { - if (hr == STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY) - m_devContext->OnSwapChainLost(); break; - } } + + return true; } void CSwapChainProcessor::CompletionFunction( @@ -584,7 +598,6 @@ bool CSwapChainProcessor::QueryHWCursor() // this occurs if the display went away (ie, screen blanking or disabled) if (status == STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY) { - m_devContext->OnSwapChainLost(); SetEvent(m_terminateEvent.Get()); return false; } diff --git a/idd/LGIdd/CSwapChainProcessor.h b/idd/LGIdd/CSwapChainProcessor.h index 50f9faa4..bd380b26 100644 --- a/idd/LGIdd/CSwapChainProcessor.h +++ b/idd/LGIdd/CSwapChainProcessor.h @@ -62,7 +62,7 @@ private: static DWORD CALLBACK _SwapChainThread(LPVOID arg); void SwapChainThread(); - void SwapChainThreadCore(); + bool SwapChainThreadCore(); static DWORD CALLBACK _CursorThread(LPVOID arg); bool QueryHWCursor(); diff --git a/idd/LGIdd/Device.cpp b/idd/LGIdd/Device.cpp index a03bdabd..8335ba46 100644 --- a/idd/LGIdd/Device.cpp +++ b/idd/LGIdd/Device.cpp @@ -199,7 +199,6 @@ NTSTATUS LGIddMonitorUnassignSwapChain(IDDCX_MONITOR monitor) { auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor); wrapper->context->UnassignSwapChain(); - wrapper->context->GetDeviceContext()->OnUnassignedSwapChain(); return STATUS_SUCCESS; }