From cb423730e4a7ec3bd00aa42a2148ecde1e418ecd Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Fri, 28 Mar 2025 23:17:31 +0000 Subject: [PATCH] [idd] driver: implement dynamic mode switch support --- idd/LGIdd/CIndirectDeviceContext.cpp | 137 +++++++++++++++++++++++++- idd/LGIdd/CIndirectDeviceContext.h | 29 +++++- idd/LGIdd/CIndirectMonitorContext.cpp | 7 +- idd/LGIdd/CIndirectMonitorContext.h | 2 + idd/LGIdd/Device.cpp | 63 ++++-------- 5 files changed, 186 insertions(+), 52 deletions(-) diff --git a/idd/LGIdd/CIndirectDeviceContext.cpp b/idd/LGIdd/CIndirectDeviceContext.cpp index 7a5a5e23..4a1c48a0 100644 --- a/idd/LGIdd/CIndirectDeviceContext.cpp +++ b/idd/LGIdd/CIndirectDeviceContext.cpp @@ -114,6 +114,14 @@ void CIndirectDeviceContext::InitAdapter() } factory->Release(); + // setup some default display modes + DisplayMode m; + m.refresh = 120; + + m.width = 800 ; m.height = 600 ; m.preferred = false; m_displayModes.push_back(m); + m.width = 1024; m.height = 768 ; m.preferred = false; m_displayModes.push_back(m); + m.width = 1920; m.height = 1200; m.preferred = true ; m_displayModes.push_back(m); + auto * wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(m_adapter); wrapper->context = this; } @@ -180,6 +188,118 @@ void CIndirectDeviceContext::FinishInit(UINT connectorIndex) } } +void CIndirectDeviceContext::ReplugMonitor(UINT connectorIndex) +{ + if (m_monitor == WDF_NO_HANDLE) + { + FinishInit(connectorIndex); + return; + } + + if (m_replugMonitor) + return; + + DEBUG_TRACE("ReplugMonitor %u", connectorIndex); + m_replugMonitor = true; + NTSTATUS status = IddCxMonitorDeparture(m_monitor); + if (!NT_SUCCESS(status)) + { + m_replugMonitor = false; + DEBUG_ERROR("IddCxMonitorDeparture Failed (0x%08x)", status); + return; + } +} + +void CIndirectDeviceContext::UnassignSwapChain() +{ + if (m_replugMonitor) + { + m_replugMonitor = false; + FinishInit(0); + } +} + +static inline void FillSignalInfo(DISPLAYCONFIG_VIDEO_SIGNAL_INFO & mode, DWORD width, DWORD height, DWORD vsync, bool monitorMode) +{ + mode.totalSize.cx = mode.activeSize.cx = width; + mode.totalSize.cy = mode.activeSize.cy = height; + + mode.AdditionalSignalInfo.vSyncFreqDivider = monitorMode ? 0 : 1; + mode.AdditionalSignalInfo.videoStandard = 255; + + mode.vSyncFreq.Numerator = vsync; + mode.vSyncFreq.Denominator = 1; + mode.hSyncFreq.Numerator = vsync * height; + mode.hSyncFreq.Denominator = 1; + + mode.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE; + mode.pixelRate = ((UINT64)vsync) * ((UINT64)width) * ((UINT64)height); +} + +NTSTATUS CIndirectDeviceContext::ParseMonitorDescription( + const IDARG_IN_PARSEMONITORDESCRIPTION* inArgs, + IDARG_OUT_PARSEMONITORDESCRIPTION* outArgs) +{ + outArgs->MonitorModeBufferOutputCount = (UINT)m_displayModes.size(); + if (inArgs->MonitorModeBufferInputCount < (UINT)m_displayModes.size()) + return (inArgs->MonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS; + + auto * mode = inArgs->pMonitorModes; + for (auto it = m_displayModes.cbegin(); it != m_displayModes.cend(); ++it, ++mode) + { + mode->Size = sizeof(IDDCX_MONITOR_MODE); + mode->Origin = IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR; + FillSignalInfo(mode->MonitorVideoSignalInfo, it->width, it->height, it->refresh, true); + + if (it->preferred) + outArgs->PreferredMonitorModeIdx = + (UINT)std::distance(m_displayModes.cbegin(), it); + } + + return STATUS_SUCCESS; +} + +NTSTATUS CIndirectDeviceContext::MonitorGetDefaultModes( + const IDARG_IN_GETDEFAULTDESCRIPTIONMODES* inArgs, + IDARG_OUT_GETDEFAULTDESCRIPTIONMODES* outArgs) +{ + outArgs->DefaultMonitorModeBufferOutputCount = (UINT)m_displayModes.size(); + if (inArgs->DefaultMonitorModeBufferInputCount < (UINT)m_displayModes.size()) + return (inArgs->DefaultMonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS; + + auto* mode = inArgs->pDefaultMonitorModes; + for (auto it = m_displayModes.cbegin(); it != m_displayModes.cend(); ++it, ++mode) + { + mode->Size = sizeof(IDDCX_MONITOR_MODE); + mode->Origin = IDDCX_MONITOR_MODE_ORIGIN_DRIVER; + FillSignalInfo(mode->MonitorVideoSignalInfo, it->width, it->height, it->refresh, true); + + if (it->preferred) + outArgs->PreferredMonitorModeIdx = + (UINT)std::distance(m_displayModes.cbegin(), it); + } + + return STATUS_SUCCESS; +} + +NTSTATUS CIndirectDeviceContext::MonitorQueryTargetModes( + const IDARG_IN_QUERYTARGETMODES* inArgs, + IDARG_OUT_QUERYTARGETMODES* outArgs) +{ + outArgs->TargetModeBufferOutputCount = (UINT)m_displayModes.size(); + if (inArgs->TargetModeBufferInputCount < (UINT)m_displayModes.size()) + return (inArgs->TargetModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS; + + auto* mode = inArgs->pTargetModes; + for (auto it = m_displayModes.cbegin(); it != m_displayModes.cend(); ++it, ++mode) + { + mode->Size = sizeof(IDDCX_TARGET_MODE); + FillSignalInfo(mode->TargetVideoSignalInfo.targetVideoSignalInfo, it->width, it->height, it->refresh, false); + } + + return STATUS_SUCCESS; +} + bool CIndirectDeviceContext::SetupLGMP(size_t alignSize) { // this may get called multiple times as we need to delay calling it until @@ -194,7 +314,9 @@ bool CIndirectDeviceContext::SetupLGMP(size_t alignSize) KVMFR kvmfr = {}; memcpy_s(kvmfr.magic, sizeof(kvmfr.magic), KVMFR_MAGIC, sizeof(KVMFR_MAGIC) - 1); kvmfr.version = KVMFR_VERSION; - kvmfr.features = KVMFR_FEATURE_SETCURSORPOS; + kvmfr.features = + KVMFR_FEATURE_SETCURSORPOS | + KVMFR_FEATURE_WINDOWSIZE; strncpy_s(kvmfr.hostver, LG_VERSION_STR, sizeof(kvmfr.hostver) - 1); ss.write(reinterpret_cast(&kvmfr), sizeof(kvmfr)); } @@ -396,6 +518,19 @@ void CIndirectDeviceContext::LGMPTimer() g_pipe.SetCursorPos(sp->x, sp->y); break; } + + case KVMFR_MESSAGE_WINDOWSIZE: + { + KVMFRWindowSize* ws = (KVMFRWindowSize*)msg; + m_displayModes.clear(); + DisplayMode m; + m.width = ws->w; + m.height = ws->h; + m.refresh = 120; + m.preferred = true; + m_displayModes.push_back(m); + ReplugMonitor(0); + } } lgmpHostAckData(m_pointerQueue); diff --git a/idd/LGIdd/CIndirectDeviceContext.h b/idd/LGIdd/CIndirectDeviceContext.h index 49cb14c4..9577431e 100644 --- a/idd/LGIdd/CIndirectDeviceContext.h +++ b/idd/LGIdd/CIndirectDeviceContext.h @@ -23,6 +23,7 @@ #include #include #include +#include #include "CIVSHMEM.h" @@ -38,8 +39,9 @@ class CIndirectDeviceContext { private: WDFDEVICE m_wdfDevice; - IDDCX_ADAPTER m_adapter = nullptr; - IDDCX_MONITOR m_monitor = nullptr; + IDDCX_ADAPTER m_adapter = nullptr; + IDDCX_MONITOR m_monitor = nullptr; + bool m_replugMonitor = false; CIVSHMEM m_ivshmem; @@ -71,7 +73,16 @@ private: void DeInitLGMP(); void LGMPTimer(); - void ResendCursor(); + void ResendCursor(); + + struct DisplayMode + { + unsigned width; + unsigned height; + unsigned refresh; + bool preferred; + }; + std::vector m_displayModes; public: CIndirectDeviceContext(_In_ WDFDEVICE wdfDevice) : @@ -82,10 +93,18 @@ public: bool SetupLGMP(size_t alignSize); void InitAdapter(); - void FinishInit(UINT connectorIndex); + void ReplugMonitor(UINT connectorIndex); + void UnassignSwapChain(); - size_t GetAlignSize() { return m_alignSize; } + NTSTATUS ParseMonitorDescription( + const IDARG_IN_PARSEMONITORDESCRIPTION* inArgs, IDARG_OUT_PARSEMONITORDESCRIPTION* outArgs); + NTSTATUS MonitorGetDefaultModes( + const IDARG_IN_GETDEFAULTDESCRIPTIONMODES* inArgs, IDARG_OUT_GETDEFAULTDESCRIPTIONMODES* outArgs); + NTSTATUS MonitorQueryTargetModes( + const IDARG_IN_QUERYTARGETMODES* inArgs, IDARG_OUT_QUERYTARGETMODES* outArgs); + + size_t GetAlignSize() { return m_alignSize; } size_t GetMaxFrameSize() { return m_maxFrameSize; } struct PreparedFrameBuffer diff --git a/idd/LGIdd/CIndirectMonitorContext.cpp b/idd/LGIdd/CIndirectMonitorContext.cpp index c46cc280..bb9d046e 100644 --- a/idd/LGIdd/CIndirectMonitorContext.cpp +++ b/idd/LGIdd/CIndirectMonitorContext.cpp @@ -33,8 +33,7 @@ CIndirectMonitorContext::CIndirectMonitorContext(_In_ IDDCX_MONITOR monitor, CIn CIndirectMonitorContext::~CIndirectMonitorContext() { - m_swapChain.reset(); - SetEvent(m_terminateEvent.Get()); + UnassignSwapChain(); delete[] m_shapeBuffer; } @@ -84,6 +83,10 @@ void CIndirectMonitorContext::AssignSwapChain(IDDCX_SWAPCHAIN swapChain, LUID re void CIndirectMonitorContext::UnassignSwapChain() { + SetEvent(m_terminateEvent.Get()); + if (m_thread.IsValid()) + WaitForSingleObject(m_thread.Get(), INFINITE); + m_swapChain.reset(); m_dx11Device.reset(); m_dx12Device.reset(); diff --git a/idd/LGIdd/CIndirectMonitorContext.h b/idd/LGIdd/CIndirectMonitorContext.h index 4e6822b5..df2ce024 100644 --- a/idd/LGIdd/CIndirectMonitorContext.h +++ b/idd/LGIdd/CIndirectMonitorContext.h @@ -58,6 +58,8 @@ public: void AssignSwapChain(IDDCX_SWAPCHAIN swapChain, LUID renderAdapter, HANDLE newFrameEvent); void UnassignSwapChain(); + + CIndirectDeviceContext * GetDeviceContext() { return m_devContext; } }; struct CIndirectMonitorContextWrapper diff --git a/idd/LGIdd/Device.cpp b/idd/LGIdd/Device.cpp index 1b7faa67..c56b80b4 100644 --- a/idd/LGIdd/Device.cpp +++ b/idd/LGIdd/Device.cpp @@ -33,10 +33,11 @@ #include "CIndirectDeviceContext.h" #include "CIndirectMonitorContext.h" +WDFDEVICE l_wdfDevice = nullptr; + NTSTATUS LGIddDeviceD0Entry(WDFDEVICE device, WDF_POWER_DEVICE_STATE previousState) { UNREFERENCED_PARAMETER(previousState); - UNREFERENCED_PARAMETER(device); auto * wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(device); wrapper->context->InitAdapter(); @@ -81,60 +82,25 @@ static inline void FillSignalInfo(DISPLAYCONFIG_VIDEO_SIGNAL_INFO & mode, DWORD NTSTATUS LGIddParseMonitorDescription(const IDARG_IN_PARSEMONITORDESCRIPTION* inArgs, IDARG_OUT_PARSEMONITORDESCRIPTION* outArgs) { - outArgs->MonitorModeBufferOutputCount = ARRAYSIZE(DisplayModes); - if (inArgs->MonitorModeBufferInputCount < ARRAYSIZE(DisplayModes)) - return (inArgs->MonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS; - - for (UINT i = 0; i < ARRAYSIZE(DisplayModes); ++i) - { - inArgs->pMonitorModes[i].Size = sizeof(IDDCX_MONITOR_MODE); - inArgs->pMonitorModes[i].Origin = IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR; - FillSignalInfo(inArgs->pMonitorModes[i].MonitorVideoSignalInfo, - DisplayModes[i][0], DisplayModes[i][1], DisplayModes[i][2], true); - } + if (!l_wdfDevice) + return STATUS_INVALID_PARAMETER; - outArgs->PreferredMonitorModeIdx = PreferredDisplayMode; - return STATUS_SUCCESS; + auto* wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(l_wdfDevice); + return wrapper->context->ParseMonitorDescription(inArgs, outArgs); } NTSTATUS LGIddMonitorGetDefaultModes(IDDCX_MONITOR monitor, const IDARG_IN_GETDEFAULTDESCRIPTIONMODES * inArgs, IDARG_OUT_GETDEFAULTDESCRIPTIONMODES * outArgs) { - UNREFERENCED_PARAMETER(monitor); - - outArgs->DefaultMonitorModeBufferOutputCount = ARRAYSIZE(DisplayModes); - if (inArgs->DefaultMonitorModeBufferInputCount < ARRAYSIZE(DisplayModes)) - return (inArgs->DefaultMonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS; - - for (UINT i = 0; i < ARRAYSIZE(DisplayModes); ++i) - { - inArgs->pDefaultMonitorModes[i].Size = sizeof(IDDCX_MONITOR_MODE); - inArgs->pDefaultMonitorModes[i].Origin = IDDCX_MONITOR_MODE_ORIGIN_DRIVER; - FillSignalInfo(inArgs->pDefaultMonitorModes[i].MonitorVideoSignalInfo, - DisplayModes[i][0], DisplayModes[i][1], DisplayModes[i][2], true); - } - - outArgs->PreferredMonitorModeIdx = PreferredDisplayMode; - return STATUS_SUCCESS; + auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor); + return wrapper->context->GetDeviceContext()->MonitorGetDefaultModes(inArgs, outArgs); } NTSTATUS LGIddMonitorQueryTargetModes(IDDCX_MONITOR monitor, const IDARG_IN_QUERYTARGETMODES * inArgs, IDARG_OUT_QUERYTARGETMODES * outArgs) { - UNREFERENCED_PARAMETER(monitor); - - outArgs->TargetModeBufferOutputCount = ARRAYSIZE(DisplayModes); - if (inArgs->TargetModeBufferInputCount < ARRAYSIZE(DisplayModes)) - return (inArgs->TargetModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS; - - for (UINT i = 0; i < ARRAYSIZE(DisplayModes); ++i) - { - inArgs->pTargetModes[i].Size = sizeof(IDDCX_TARGET_MODE); - FillSignalInfo(inArgs->pTargetModes[i].TargetVideoSignalInfo.targetVideoSignalInfo, - DisplayModes[i][0], DisplayModes[i][1], DisplayModes[i][2], false); - } - - return STATUS_SUCCESS; + auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor); + return wrapper->context->GetDeviceContext()->MonitorQueryTargetModes(inArgs, outArgs); } NTSTATUS LGIddMonitorAssignSwapChain(IDDCX_MONITOR monitor, const IDARG_IN_SETSWAPCHAIN* inArgs) @@ -149,6 +115,7 @@ NTSTATUS LGIddMonitorUnassignSwapChain(IDDCX_MONITOR monitor) { auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor); wrapper->context->UnassignSwapChain(); + wrapper->context->GetDeviceContext()->UnassignSwapChain(); return STATUS_SUCCESS; } @@ -190,6 +157,7 @@ NTSTATUS LGIddCreateDevice(_Inout_ PWDFDEVICE_INIT deviceInit) auto * wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(object); if (wrapper) wrapper->Cleanup(); + l_wdfDevice = nullptr; }; WDFDEVICE device = nullptr; @@ -197,6 +165,13 @@ NTSTATUS LGIddCreateDevice(_Inout_ PWDFDEVICE_INIT deviceInit) if (!NT_SUCCESS(status)) return status; + /* + * Because we are limited to IddCx 1.5 to retain Windows 10 support we have + * no way of getting the device context in `LGIdddapterCommitModes`, as such + * we need to store this for later. + */ + l_wdfDevice = device; + status = IddCxDeviceInitialize(device); auto wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(device);