From 929428c2734442a2831f58221911799e9a0ffeca Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Wed, 3 Jun 2026 04:17:23 +1000 Subject: [PATCH] [idd] driver: implement dirty rect tracking --- idd/LGIdd/CD3D12CommandQueue.h | 4 +- idd/LGIdd/CEdid.cpp | 2 +- idd/LGIdd/CIndirectDeviceContext.cpp | 19 ++++-- idd/LGIdd/CIndirectDeviceContext.h | 14 ++--- idd/LGIdd/CInteropResource.cpp | 33 +++++++--- idd/LGIdd/CInteropResource.h | 5 +- idd/LGIdd/CSwapChainProcessor.cpp | 92 +++++++++++++++++++++++++--- idd/LGIdd/CSwapChainProcessor.h | 5 +- 8 files changed, 140 insertions(+), 34 deletions(-) diff --git a/idd/LGIdd/CD3D12CommandQueue.h b/idd/LGIdd/CD3D12CommandQueue.h index 0040a1b2..5cc8716a 100644 --- a/idd/LGIdd/CD3D12CommandQueue.h +++ b/idd/LGIdd/CD3D12CommandQueue.h @@ -90,8 +90,8 @@ class CD3D12CommandQueue bool Execute(); //void Wait(); - bool IsReady () { return !m_pending; } - HANDLE GetEvent() { return m_event.Get(); } + bool IsReady () const { return !m_pending ; } + HANDLE GetEvent() const { return m_event.Get(); } ComPtr GetCmdQueue() { return m_queue; } ComPtr GetGfxList() { return m_gfxList; } diff --git a/idd/LGIdd/CEdid.cpp b/idd/LGIdd/CEdid.cpp index 68935783..f844a702 100644 --- a/idd/LGIdd/CEdid.cpp +++ b/idd/LGIdd/CEdid.cpp @@ -529,7 +529,7 @@ bool CEdid::WriteDetailedTiming(BYTE* dtd, const CSettings::DisplayMode& mode) void CEdid::Build(const CSettings::DisplayModes& modes) { - m_data.assign(EDID_BLOCK_SIZE * 2, 0); + m_data.assign(static_cast>::size_type>(EDID_BLOCK_SIZE) * 2, 0); EdidBaseBlock baseBlock = {}; InitEdidBaseBlock(baseBlock); diff --git a/idd/LGIdd/CIndirectDeviceContext.cpp b/idd/LGIdd/CIndirectDeviceContext.cpp index a3499e30..3b338a9d 100644 --- a/idd/LGIdd/CIndirectDeviceContext.cpp +++ b/idd/LGIdd/CIndirectDeviceContext.cpp @@ -803,7 +803,7 @@ void CIndirectDeviceContext::LGMPTimer() } CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrameBuffer( - unsigned width, unsigned height, unsigned pitch, DXGI_FORMAT format) + unsigned width, unsigned height, unsigned pitch, DXGI_FORMAT format, const RECT * dirtyRects, unsigned nbDirtyRects) { PreparedFrameBuffer result = {}; @@ -863,6 +863,17 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame fi->flags = 0; fi->rotation = FRAME_ROT_0; fi->damageRectsCount = 0; + if (nbDirtyRects <= ARRAYSIZE(fi->damageRects)) + { + fi->damageRectsCount = nbDirtyRects; + for (unsigned i = 0; i < nbDirtyRects; ++i) + { + fi->damageRects[i].x = dirtyRects[i].left; + fi->damageRects[i].y = dirtyRects[i].top; + fi->damageRects[i].width = dirtyRects[i].right - dirtyRects[i].left; + fi->damageRects[i].height = dirtyRects[i].bottom - dirtyRects[i].top; + } + } FrameBuffer* fb = m_frameBuffer[m_frameIndex]; fb->wp = 0; @@ -876,7 +887,7 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame return result; } -void CIndirectDeviceContext::WriteFrameBuffer(unsigned frameIndex, void* src, size_t offset, size_t len, bool setWritePos) +void CIndirectDeviceContext::WriteFrameBuffer(unsigned frameIndex, void* src, size_t offset, size_t len, bool setWritePos) const { FrameBuffer * fb = m_frameBuffer[frameIndex]; @@ -889,7 +900,7 @@ void CIndirectDeviceContext::WriteFrameBuffer(unsigned frameIndex, void* src, si fb->wp = (uint32_t)(offset + len); } -void CIndirectDeviceContext::FinalizeFrameBuffer(unsigned frameIndex) +void CIndirectDeviceContext::FinalizeFrameBuffer(unsigned frameIndex) const { FrameBuffer * fb = m_frameBuffer[frameIndex]; fb->wp = m_height * m_pitch; @@ -965,7 +976,7 @@ void CIndirectDeviceContext::SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info, co } } -void CIndirectDeviceContext::ResendCursor() +void CIndirectDeviceContext::ResendCursor() const { PLGMPMemory mem = m_pointerShape; if (!mem) diff --git a/idd/LGIdd/CIndirectDeviceContext.h b/idd/LGIdd/CIndirectDeviceContext.h index 68f11546..dc2260a4 100644 --- a/idd/LGIdd/CIndirectDeviceContext.h +++ b/idd/LGIdd/CIndirectDeviceContext.h @@ -93,7 +93,7 @@ private: void DeInitLGMP(); void LGMPTimer(); - void ResendCursor(); + void ResendCursor() const; bool UpdateMonitorModes(); CSettings::DisplayModes m_displayModes; @@ -138,9 +138,9 @@ public: void SetResolution(int width, int height); - size_t GetAlignSize() { return m_alignSize; } - size_t GetMaxFrameSize() { return m_maxFrameSize; } - bool CanProcessFP16() const { return m_canProcessFP16; } + size_t GetAlignSize () const { return m_alignSize ; } + size_t GetMaxFrameSize() const { return m_maxFrameSize ; } + bool CanProcessFP16 () const { return m_canProcessFP16; } struct PreparedFrameBuffer { @@ -148,9 +148,9 @@ public: uint8_t* mem; }; - 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); + PreparedFrameBuffer PrepareFrameBuffer(unsigned width, unsigned height, unsigned pitch, DXGI_FORMAT format, const RECT * dirtyRects, unsigned nbDirtyRects); + void WriteFrameBuffer(unsigned frameIndex, void* src, size_t offset, size_t len, bool setWritePos) const; + void FinalizeFrameBuffer(unsigned frameIndex) const; void SendCursor(const IDARG_OUT_QUERY_HWCURSOR & info, const BYTE * data); diff --git a/idd/LGIdd/CInteropResource.cpp b/idd/LGIdd/CInteropResource.cpp index 7282904a..c0da165f 100644 --- a/idd/LGIdd/CInteropResource.cpp +++ b/idd/LGIdd/CInteropResource.cpp @@ -86,24 +86,26 @@ bool CInteropResource::Init(std::shared_ptr dx11Device, std::share m_dx12Device = dx12Device; memcpy(&m_format, &srcDesc, sizeof(m_format)); - m_srcTex = srcTex.Get(); - m_d12Res = dst; - m_d11Fence = d11Fence; - m_d12Fence = d12Fence; - m_fenceValue = 0; - m_ready = true; + m_srcTex = srcTex.Get(); + m_d12Res = dst; + m_d11Fence = d11Fence; + m_d12Fence = d12Fence; + m_fenceValue = 0; + m_nbDirtyRects = 0; + m_ready = true; return true; } void CInteropResource::Reset() { - m_ready = false; - m_fenceValue = 0; + m_ready = false; + m_fenceValue = 0; m_d12Fence.Reset(); m_d11Fence.Reset(); m_d12Res .Reset(); - m_srcTex = NULL; + m_srcTex = NULL; + m_nbDirtyRects = 0; memset(&m_format, 0, sizeof(m_format)); m_dx12Device.reset(); @@ -143,4 +145,15 @@ void CInteropResource::SetFullDamage() m_dirtyRects[0].right = m_format.Width; m_dirtyRects[0].bottom = m_format.Height; m_nbDirtyRects = 1; -} \ No newline at end of file +} +void CInteropResource::SetDirtyRects(const RECT * dirtyRects, unsigned nbDirtyRects) +{ + if (nbDirtyRects > LG_MAX_DIRTY_RECTS) + { + SetFullDamage(); + return; + } + + memcpy(m_dirtyRects, dirtyRects, nbDirtyRects * sizeof(*m_dirtyRects)); + m_nbDirtyRects = nbDirtyRects; +} diff --git a/idd/LGIdd/CInteropResource.h b/idd/LGIdd/CInteropResource.h index 4b577e0e..a67c0409 100644 --- a/idd/LGIdd/CInteropResource.h +++ b/idd/LGIdd/CInteropResource.h @@ -62,7 +62,10 @@ class CInteropResource void Signal(); void Sync(CD3D12CommandQueue& queue); void SetFullDamage(); + void SetDirtyRects(const RECT * dirtyRects, unsigned nbDirtyRects); const ComPtr& GetRes() { return m_d12Res; } const D3D11_TEXTURE2D_DESC& GetFormat() { return m_format; } -}; \ No newline at end of file + const RECT * GetDirtyRects() { return m_dirtyRects; } + unsigned GetDirtyRectCount() { return m_nbDirtyRects; } +}; diff --git a/idd/LGIdd/CSwapChainProcessor.cpp b/idd/LGIdd/CSwapChainProcessor.cpp index c38bd4ab..a1590992 100644 --- a/idd/LGIdd/CSwapChainProcessor.cpp +++ b/idd/LGIdd/CSwapChainProcessor.cpp @@ -88,7 +88,7 @@ void CSwapChainProcessor::SwapChainThreadCore() if (IDD_IS_FUNCTION_AVAILABLE(IddCxSetRealtimeGPUPriority)) { DEBUG_INFO("Using IddCxSetRealtimeGPUPriority"); - IDARG_IN_SETREALTIMEGPUPRIORITY arg; + IDARG_IN_SETREALTIMEGPUPRIORITY arg = {0}; arg.pDevice = dxgiDevice.Get(); hr = IddCxSetRealtimeGPUPriority(m_hSwapChain, &arg); if (FAILED(hr)) @@ -134,6 +134,7 @@ void CSwapChainProcessor::SwapChainThreadCore() break; UINT frameNumber = 0; + UINT dirtyRectCount = 0; ComPtr surface; #if defined(IDDCX_VERSION_MAJOR) && defined(IDDCX_VERSION_MINOR) && \ @@ -151,6 +152,7 @@ void CSwapChainProcessor::SwapChainThreadCore() if (SUCCEEDED(hr)) { frameNumber = buffer.MetaData.PresentationFrameNumber; + dirtyRectCount = buffer.MetaData.DirtyRectCount; surface = buffer.MetaData.pSurface; } } @@ -163,6 +165,7 @@ void CSwapChainProcessor::SwapChainThreadCore() if (SUCCEEDED(hr)) { frameNumber = buffer.MetaData.PresentationFrameNumber; + dirtyRectCount = buffer.MetaData.DirtyRectCount; surface = buffer.MetaData.pSurface; } } @@ -190,7 +193,7 @@ void CSwapChainProcessor::SwapChainThreadCore() if (frameNumber != lastFrameNumber) { lastFrameNumber = frameNumber; - SwapChainNewFrame(surface); + SwapChainNewFrame(surface, dirtyRectCount); // report that all GPU processing for this frame has been queued hr = IddCxSwapChainFinishedProcessingFrame(m_hSwapChain); @@ -237,7 +240,35 @@ void CSwapChainProcessor::CompletionFunction( sc->m_devContext->FinalizeFrameBuffer(fbRes->GetFrameIndex()); } -bool CSwapChainProcessor::SwapChainNewFrame(ComPtr acquiredBuffer) + +static bool IsFullDamage(const RECT * dirtyRects, unsigned nbDirtyRects, + const D3D12_RESOURCE_DESC& desc) +{ + return nbDirtyRects == 0 || + (nbDirtyRects == 1 && + dirtyRects[0].left == 0 && + dirtyRects[0].top == 0 && + dirtyRects[0].right == (LONG)desc.Width && + dirtyRects[0].bottom == (LONG)desc.Height); +} + +static void CopyDirtyRect(ComPtr list, + D3D12_TEXTURE_COPY_LOCATION * dstLoc, + D3D12_TEXTURE_COPY_LOCATION * srcLoc, + const RECT& rect) +{ + D3D12_BOX box = {}; + box.left = rect.left; + box.top = rect.top; + box.front = 0; + box.right = rect.right; + box.bottom = rect.bottom; + box.back = 1; + + list->CopyTextureRegion(dstLoc, box.left, box.top, 0, srcLoc, &box); +} + +bool CSwapChainProcessor::SwapChainNewFrame(ComPtr acquiredBuffer, unsigned dirtyRectCount) { ComPtr texture; HRESULT hr = acquiredBuffer.As(&texture); @@ -261,8 +292,27 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr acquiredBuffer */ srcRes->Signal(); - //FIXME: handle dirty rects - srcRes->SetFullDamage(); + RECT dirtyRects[LG_MAX_DIRTY_RECTS] = {0}; + if (dirtyRectCount > ARRAYSIZE(dirtyRects)) + { + srcRes->SetFullDamage(); + } + else + { + IDARG_IN_GETDIRTYRECTS dirtyIn = {}; + dirtyIn.DirtyRectInCount = dirtyRectCount; + dirtyIn.pDirtyRects = dirtyRects; + + IDARG_OUT_GETDIRTYRECTS dirtyOut = {}; + hr = IddCxSwapChainGetDirtyRects(m_hSwapChain, &dirtyIn, &dirtyOut); + if (FAILED(hr)) + { + DEBUG_ERROR_HR(hr, "IddCxSwapChainGetDirtyRects Failed"); + srcRes->SetFullDamage(); + } + else + srcRes->SetDirtyRects(dirtyRects, dirtyOut.DirtyRectOutCount); + } D3D12_RESOURCE_DESC desc = srcRes->GetRes()->GetDesc(); D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout; @@ -280,7 +330,9 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr acquiredBuffer (int)desc.Width, (int)desc.Height, (int)layout.Footprint.RowPitch, - desc.Format); + desc.Format, + srcRes->GetDirtyRects(), + srcRes->GetDirtyRectCount()); if (!buffer.mem) return false; @@ -314,8 +366,32 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr acquiredBuffer dstLoc.PlacedFootprint = layout; srcRes->Sync(*copyQueue); - copyQueue->GetGfxList()->CopyTextureRegion( - &dstLoc, 0, 0, 0, &srcLoc, NULL); + + const RECT * currentDirtyRects = srcRes->GetDirtyRects(); + unsigned nbDirtyRects = srcRes->GetDirtyRectCount(); + if (IsFullDamage(currentDirtyRects, nbDirtyRects, desc) || + nbDirtyRects > KVMFR_MAX_DAMAGE_RECTS || m_nbDirtyRects == 0) + { + copyQueue->GetGfxList()->CopyTextureRegion( + &dstLoc, 0, 0, 0, &srcLoc, NULL); + } + else if (m_nbDirtyRects + nbDirtyRects > LG_MAX_DIRTY_RECTS) + { + copyQueue->GetGfxList()->CopyTextureRegion( + &dstLoc, 0, 0, 0, &srcLoc, NULL); + } + else + { + for (const RECT * rect = m_dirtyRects; rect < m_dirtyRects + m_nbDirtyRects; ++rect) + CopyDirtyRect(copyQueue->GetGfxList(), &dstLoc, &srcLoc, *rect); + + for (const RECT * rect = currentDirtyRects; rect < currentDirtyRects + nbDirtyRects; ++rect) + CopyDirtyRect(copyQueue->GetGfxList(), &dstLoc, &srcLoc, *rect); + } + + memcpy(m_dirtyRects, currentDirtyRects, nbDirtyRects * sizeof(*m_dirtyRects)); + m_nbDirtyRects = nbDirtyRects; + copyQueue->Execute(); return true; diff --git a/idd/LGIdd/CSwapChainProcessor.h b/idd/LGIdd/CSwapChainProcessor.h index 6b1f3173..78028379 100644 --- a/idd/LGIdd/CSwapChainProcessor.h +++ b/idd/LGIdd/CSwapChainProcessor.h @@ -55,6 +55,9 @@ private: BYTE* m_shapeBuffer; DWORD m_lastShapeId = 0; + RECT m_dirtyRects[LG_MAX_DIRTY_RECTS] = {}; + unsigned m_nbDirtyRects = 0; + static DWORD CALLBACK _SwapChainThread(LPVOID arg); void SwapChainThread(); void SwapChainThreadCore(); @@ -65,7 +68,7 @@ private: static void CompletionFunction( CD3D12CommandQueue * queue, bool result, void * param1, void * param2); - bool SwapChainNewFrame(ComPtr acquiredBuffer); + bool SwapChainNewFrame(ComPtr acquiredBuffer, unsigned dirtyRectCount); public: CSwapChainProcessor(IDDCX_MONITOR monitor, CIndirectDeviceContext * devContext, IDDCX_SWAPCHAIN hSwapChain,