From d0bc4f3e07e355e1c994ef22e644b529015818f1 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Fri, 17 Jul 2026 23:49:57 +1000 Subject: [PATCH] [idd] publish frames after copy submission Submit the D3D12 copy before exposing the frame through LGMP, preventing clients from waiting on work that has not yet been queued. Track the last published buffer separately for safe subscriber resends and handle submission failures. --- idd/LGIdd/CIndirectDeviceContext.cpp | 32 ++++++++++++++++++++++------ idd/LGIdd/CIndirectDeviceContext.h | 14 ++++++------ idd/LGIdd/CSwapChainProcessor.cpp | 8 ++++--- 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/idd/LGIdd/CIndirectDeviceContext.cpp b/idd/LGIdd/CIndirectDeviceContext.cpp index 3cb5f8ab..7a241500 100644 --- a/idd/LGIdd/CIndirectDeviceContext.cpp +++ b/idd/LGIdd/CIndirectDeviceContext.cpp @@ -820,7 +820,7 @@ bool CIndirectDeviceContext::SetupLGMP(size_t alignSize) void CIndirectDeviceContext::DeInitLGMP() { - m_hasFrame = false; + InterlockedExchange(&m_publishedFrameIndex, -1); // The retry timer callback dereferences this context, so make sure it is // stopped and drained before we tear anything down. Wait for any in-flight @@ -910,8 +910,10 @@ void CIndirectDeviceContext::LGMPTimer() if (lgmpHostQueueNewSubs(m_frameQueue) && m_monitor) { - if (m_hasFrame) - lgmpHostQueuePost(m_frameQueue, 0, m_frameMemory[m_frameIndex]); + const LONG frameIndex = + InterlockedCompareExchange(&m_publishedFrameIndex, 0, 0); + if (frameIndex >= 0) + lgmpHostQueuePost(m_frameQueue, 0, m_frameMemory[frameIndex]); } if (lgmpHostQueueNewSubs(m_pointerQueue)) @@ -1047,15 +1049,33 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame FrameBuffer* fb = m_frameBuffer[m_frameIndex]; fb->wp = 0; - lgmpHostQueuePost(m_frameQueue, 0, m_frameMemory[m_frameIndex]); - result.frameIndex = m_frameIndex; result.mem = fb->data; - m_hasFrame = true; return result; } +bool CIndirectDeviceContext::PublishFrameBuffer(unsigned frameIndex) +{ + if (!m_frameQueue || frameIndex >= LGMP_Q_FRAME_LEN) + return false; + + /* Make resends select this submitted frame before posting it. This prevents + * a new subscriber racing publication from receiving the previous frame + * after the new one. */ + InterlockedExchange(&m_publishedFrameIndex, (LONG)frameIndex); + + const LGMP_STATUS status = + lgmpHostQueuePost(m_frameQueue, 0, m_frameMemory[frameIndex]); + if (status != LGMP_OK) + { + DEBUG_ERROR("Failed to publish frame: %s", lgmpStatusString(status)); + return false; + } + + return true; +} + void CIndirectDeviceContext::WriteFrameBuffer(unsigned frameIndex, void* src, size_t offset, size_t len, bool setWritePos) const { FrameBuffer * fb = m_frameBuffer[frameIndex]; diff --git a/idd/LGIdd/CIndirectDeviceContext.h b/idd/LGIdd/CIndirectDeviceContext.h index 602fb1ae..60a7a7bd 100644 --- a/idd/LGIdd/CIndirectDeviceContext.h +++ b/idd/LGIdd/CIndirectDeviceContext.h @@ -91,11 +91,12 @@ private: bool m_cursorVisible = false; int m_cursorX = 0, m_cursorY = 0; - size_t m_alignSize = 0; - size_t m_maxFrameSize = 0; - int m_frameIndex = 0; - uint32_t m_formatVer = 0; - uint32_t m_frameSerial = 0; + size_t m_alignSize = 0; + size_t m_maxFrameSize = 0; + int m_frameIndex = 0; + volatile LONG m_publishedFrameIndex = -1; + uint32_t m_formatVer = 0; + uint32_t m_frameSerial = 0; PLGMPMemory m_frameMemory[LGMP_Q_FRAME_LEN] = {}; KVMFRFrame * m_frame [LGMP_Q_FRAME_LEN] = {}; FrameBuffer * m_frameBuffer[LGMP_Q_FRAME_LEN] = {}; @@ -105,8 +106,6 @@ private: unsigned m_pitch = 0; DXGI_FORMAT m_format = DXGI_FORMAT_UNKNOWN; FrameType m_frameType = FRAME_TYPE_INVALID; - bool m_hasFrame = false; - UINT m_iddCxVersion = 0; bool m_canProcessFP16 = false; @@ -211,6 +210,7 @@ public: }; PreparedFrameBuffer PrepareFrameBuffer(unsigned pitch, const D12FrameFormat& srcFormat, const D12FrameFormat& dstFormat, const RECT * dirtyRects, unsigned nbDirtyRects); + bool PublishFrameBuffer(unsigned frameIndex); void WriteFrameBuffer(unsigned frameIndex, void* src, size_t offset, size_t len, bool setWritePos) const; void FinalizeFrameBuffer(unsigned frameIndex) const; diff --git a/idd/LGIdd/CSwapChainProcessor.cpp b/idd/LGIdd/CSwapChainProcessor.cpp index e69e9817..3090d4dc 100644 --- a/idd/LGIdd/CSwapChainProcessor.cpp +++ b/idd/LGIdd/CSwapChainProcessor.cpp @@ -224,7 +224,8 @@ bool CSwapChainProcessor::SwapChainThreadCore() if (frameNumber != lastFrameNumber) { lastFrameNumber = frameNumber; - SwapChainNewFrame(surface, dirtyRectCount, colorSpace, sdrWhiteLevel); + if (!SwapChainNewFrame(surface, dirtyRectCount, colorSpace, sdrWhiteLevel)) + DEBUG_WARN("Failed to submit frame"); // report that all GPU processing for this frame has been queued hr = IddCxSwapChainFinishedProcessingFrame(m_hSwapChain); @@ -582,9 +583,10 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr acquiredBuffer memcpy(m_dirtyRects, currentDirtyRects, nbDirtyRects * sizeof(*m_dirtyRects)); m_nbDirtyRects = nbDirtyRects; - copyQueue->Execute(); + if (!copyQueue->Execute()) + return false; - return true; + return m_devContext->PublishFrameBuffer(buffer.frameIndex); } DWORD CALLBACK CSwapChainProcessor::_CursorThread(LPVOID arg)