From 3211bc1d93e412b09f11775125646408c6b29907 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Wed, 3 Jun 2026 02:15:49 +1000 Subject: [PATCH] [idd] driver: improve resolution switching performance --- idd/LGIdd/CIndirectDeviceContext.cpp | 194 +++++++++++++++++---------- idd/LGIdd/CIndirectDeviceContext.h | 16 ++- idd/LGIdd/CSwapChainProcessor.cpp | 16 ++- 3 files changed, 148 insertions(+), 78 deletions(-) diff --git a/idd/LGIdd/CIndirectDeviceContext.cpp b/idd/LGIdd/CIndirectDeviceContext.cpp index d940fcdc..a3499e30 100644 --- a/idd/LGIdd/CIndirectDeviceContext.cpp +++ b/idd/LGIdd/CIndirectDeviceContext.cpp @@ -269,6 +269,8 @@ void CIndirectDeviceContext::ReplugMonitor() void CIndirectDeviceContext::OnAssignSwapChain() { + InterlockedExchange(&m_recoverModeUpdateSwapChain, 0); + if (m_doSetMode) { m_doSetMode = false; @@ -278,6 +280,9 @@ void CIndirectDeviceContext::OnAssignSwapChain() void CIndirectDeviceContext::OnUnassignedSwapChain() { + InterlockedExchange(&m_replugMonitorQueued, 0); + InterlockedExchange(&m_recoverModeUpdateSwapChain, 0); + if (m_replugMonitor) { m_replugMonitor = false; @@ -285,6 +290,21 @@ void CIndirectDeviceContext::OnUnassignedSwapChain() } } +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; @@ -421,6 +441,85 @@ NTSTATUS CIndirectDeviceContext::MonitorQueryTargetModes2( } #endif +bool CIndirectDeviceContext::UpdateMonitorModes() +{ + if (!m_monitor) + return false; + +#if defined(IDDCX_VERSION_MAJOR) && defined(IDDCX_VERSION_MINOR) && \ + (IDDCX_VERSION_MAJOR > 1 || (IDDCX_VERSION_MAJOR == 1 && IDDCX_VERSION_MINOR >= 10)) + if (CanUseIddCx110DDIs()) + { + IDDCX_TARGET_MODE2* modes = (IDDCX_TARGET_MODE2*)_malloca( + m_displayModes.size() * sizeof(IDDCX_TARGET_MODE2)); + if (!modes) + { + DEBUG_WARN("Failed to allocate memory for the mode list"); + return false; + } + + ZeroMemory(modes, m_displayModes.size() * sizeof(IDDCX_TARGET_MODE2)); + + auto* mode = modes; + for (auto it = m_displayModes.cbegin(); it != m_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)m_displayModes.size(); + updateModes.pTargetModes = modes; + + NTSTATUS status = IddCxMonitorUpdateModes2(m_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( + m_displayModes.size() * sizeof(IDDCX_TARGET_MODE)); + if (!modes) + { + DEBUG_WARN("Failed to allocate memory for the mode list"); + return false; + } + + ZeroMemory(modes, m_displayModes.size() * sizeof(IDDCX_TARGET_MODE)); + + auto* mode = modes; + for (auto it = m_displayModes.cbegin(); it != m_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)m_displayModes.size(); + updateModes.pTargetModes = modes; + + NTSTATUS status = IddCxMonitorUpdateModes(m_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) { m_setMode.width = width; @@ -430,77 +529,20 @@ void CIndirectDeviceContext::SetResolution(int width, int height) g_settings.SetExtraMode(m_setMode); PopulateDefaultModes(); + + if (UpdateMonitorModes()) + { + DEBUG_TRACE("Updated monitor modes without replugging"); + m_doSetMode = false; + InterlockedExchange(&m_recoverModeUpdateSwapChain, 1); + g_pipe.SetDisplayMode(m_setMode.width, m_setMode.height, m_setMode.refresh); + return; + } + + DEBUG_TRACE("Falling back to monitor replug for mode update"); m_doSetMode = true; - -#if 1 + InterlockedExchange(&m_recoverModeUpdateSwapChain, 0); ReplugMonitor(); -#else - - if (IDD_IS_FUNCTION_AVAILABLE(IddCxMonitorUpdateModes2)) - { - IDDCX_TARGET_MODE2* modes = (IDDCX_TARGET_MODE2*)_malloca( - m_displayModes.size() * sizeof(IDDCX_TARGET_MODE2)); - - if (!modes) - { - DEBUG_ERROR("Failed to allocate memory for the mode list"); - return; - } - - ZeroMemory(modes, m_displayModes.size() * sizeof(IDDCX_TARGET_MODE2)); - - IDARG_IN_UPDATEMODES2 um = {}; - um.Reason = IDDCX_UPDATE_REASON_OTHER; - um.TargetModeCount = (UINT)m_displayModes.size(); - um.pTargetModes = modes; - auto* mode = modes; - for (auto it = m_displayModes.cbegin(); it != m_displayModes.cend(); ++it, ++mode) - { - mode->Size = sizeof(IDDCX_TARGET_MODE2); - mode->RequiredBandwidth = (UINT64)(it->width * it->height * it->refresh * 32); - mode->BitsPerComponent.Rgb = IDDCX_BITS_PER_COMPONENT_8; - - FillSignalInfo(mode->TargetVideoSignalInfo.targetVideoSignalInfo, it->width, it->height, it->refresh, true); - } - - NTSTATUS status = IddCxMonitorUpdateModes2(m_monitor, &um); - if (!NT_SUCCESS(status)) - DEBUG_ERROR("IddCxMonitorUpdateModes2 Failed (0x%08x)", status); - - _freea(modes); - } - else - { - IDDCX_TARGET_MODE* modes = (IDDCX_TARGET_MODE*)_malloca( - m_displayModes.size() * sizeof(IDDCX_TARGET_MODE)); - - if (!modes) - { - DEBUG_ERROR("Failed to allocate memory for the mode list"); - return; - } - - IDARG_IN_UPDATEMODES um = {}; - um.Reason = IDDCX_UPDATE_REASON_OTHER; - um.TargetModeCount = (UINT)m_displayModes.size(); - um.pTargetModes = modes; - - auto* mode = modes; - for (auto it = m_displayModes.cbegin(); it != m_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, true); - } - - NTSTATUS status = IddCxMonitorUpdateModes(m_monitor, &um); - if (!NT_SUCCESS(status)) - DEBUG_ERROR("IddCxMonitorUpdateModes Failed (0x%08x)", status); - - _freea(modes); - } -#endif } bool CIndirectDeviceContext::SetupLGMP(size_t alignSize) @@ -704,6 +746,13 @@ void CIndirectDeviceContext::DeInitLGMP() void CIndirectDeviceContext::LGMPTimer() { + if (InterlockedExchange(&m_replugMonitorQueued, 0)) + { + m_doSetMode = true; + ReplugMonitor(); + return; + } + LGMP_STATUS status; if ((status = lgmpHostProcess(m_lgmp)) != LGMP_OK) { @@ -753,13 +802,18 @@ void CIndirectDeviceContext::LGMPTimer() ResendCursor(); } -CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrameBuffer(int width, int height, int pitch, DXGI_FORMAT format) +CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrameBuffer( + unsigned width, unsigned height, unsigned pitch, DXGI_FORMAT format) { PreparedFrameBuffer result = {}; if (!m_lgmp || !m_frameQueue) return result; + if (InterlockedCompareExchange(&m_recoverModeUpdateSwapChain, 0, 0) && + width == m_setMode.width && height == m_setMode.height) + InterlockedExchange(&m_recoverModeUpdateSwapChain, 0); + if (m_width != width || m_height != height || m_pitch != pitch || m_format != format) { m_width = width; diff --git a/idd/LGIdd/CIndirectDeviceContext.h b/idd/LGIdd/CIndirectDeviceContext.h index 468ca933..68f11546 100644 --- a/idd/LGIdd/CIndirectDeviceContext.h +++ b/idd/LGIdd/CIndirectDeviceContext.h @@ -79,9 +79,9 @@ private: KVMFRFrame * m_frame [LGMP_Q_FRAME_LEN] = {}; FrameBuffer * m_frameBuffer[LGMP_Q_FRAME_LEN] = {}; - int m_width = 0; - int m_height = 0; - int m_pitch = 0; + unsigned m_width = 0; + unsigned m_height = 0; + unsigned m_pitch = 0; DXGI_FORMAT m_format = DXGI_FORMAT_UNKNOWN; bool m_hasFrame = false; @@ -94,12 +94,15 @@ private: void DeInitLGMP(); void LGMPTimer(); void ResendCursor(); + bool UpdateMonitorModes(); CSettings::DisplayModes m_displayModes; CEdid m_edid; - CSettings::DisplayMode m_setMode; - bool m_doSetMode; + CSettings::DisplayMode m_setMode = {}; + bool m_doSetMode = false; + volatile LONG m_replugMonitorQueued = 0; + volatile LONG m_recoverModeUpdateSwapChain = 0; public: CIndirectDeviceContext(_In_ WDFDEVICE wdfDevice) : @@ -116,6 +119,7 @@ public: void OnAssignSwapChain(); void OnUnassignedSwapChain(); + void OnSwapChainLost(); NTSTATUS ParseMonitorDescription( const IDARG_IN_PARSEMONITORDESCRIPTION* inArgs, IDARG_OUT_PARSEMONITORDESCRIPTION* outArgs); @@ -144,7 +148,7 @@ public: uint8_t* mem; }; - PreparedFrameBuffer PrepareFrameBuffer(int width, int height, int pitch, DXGI_FORMAT format); + PreparedFrameBuffer PrepareFrameBuffer(unsigned width, unsigned height, unsigned pitch, DXGI_FORMAT format); void WriteFrameBuffer(unsigned frameIndex, void* src, size_t offset, size_t len, bool setWritePos); void FinalizeFrameBuffer(unsigned frameIndex); diff --git a/idd/LGIdd/CSwapChainProcessor.cpp b/idd/LGIdd/CSwapChainProcessor.cpp index 510c0a86..c38bd4ab 100644 --- a/idd/LGIdd/CSwapChainProcessor.cpp +++ b/idd/LGIdd/CSwapChainProcessor.cpp @@ -130,6 +130,9 @@ void CSwapChainProcessor::SwapChainThreadCore() UINT lastFrameNumber = 0; for (;;) { + if (WaitForSingleObject(m_terminateEvent.Get(), 0) == WAIT_OBJECT_0) + break; + UINT frameNumber = 0; ComPtr surface; @@ -193,7 +196,10 @@ void CSwapChainProcessor::SwapChainThreadCore() hr = IddCxSwapChainFinishedProcessingFrame(m_hSwapChain); if (FAILED(hr)) { - DEBUG_ERROR_HR(hr, "IddCxSwapChainFinishedProcessingFrame Failed"); + if (hr == STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY) + m_devContext->OnSwapChainLost(); + else + DEBUG_ERROR_HR(hr, "IddCxSwapChainFinishedProcessingFrame Failed"); break; } @@ -201,6 +207,8 @@ void CSwapChainProcessor::SwapChainThreadCore() } else { + if (hr == STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY) + m_devContext->OnSwapChainLost(); break; } } @@ -349,8 +357,12 @@ bool CSwapChainProcessor::QueryHWCursor() if (FAILED(status)) { // this occurs if the display went away (ie, screen blanking or disabled) - if (status == ERROR_GRAPHICS_PATH_NOT_IN_TOPOLOGY) + if (status == STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY) + { + m_devContext->OnSwapChainLost(); + SetEvent(m_terminateEvent.Get()); return false; + } DEBUG_ERROR("IddCxMonitorQueryHardwareCursor failed (0x%08x)", status); return false;