From f6c72b0bc53cd3a391189cb8cadf109f4a64f71b Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Thu, 6 Aug 2026 14:02:39 +1000 Subject: [PATCH] [idd] capture: replay only completed frames Track the newest submitted and completed frame independently. Only retain or replay a frame after its copy callback succeeds. Keep the prior completed frame when a newer copy fails, and compare publication sequences so out-of-order callbacks cannot move backward. --- idd/LGIdd/CIndirectDeviceContext.cpp | 49 ++++++++++++++++++++-------- idd/LGIdd/CIndirectDeviceContext.h | 9 +++-- idd/LGIdd/CSwapChainProcessor.cpp | 2 +- 3 files changed, 42 insertions(+), 18 deletions(-) diff --git a/idd/LGIdd/CIndirectDeviceContext.cpp b/idd/LGIdd/CIndirectDeviceContext.cpp index d3ff0910..aa0dd74a 100644 --- a/idd/LGIdd/CIndirectDeviceContext.cpp +++ b/idd/LGIdd/CIndirectDeviceContext.cpp @@ -1187,7 +1187,8 @@ bool CIndirectDeviceContext::SetupLGMP(size_t alignSize) } m_maxFrameSize = maxFrameSize; - m_publishedFrameIndex.store(-1, std::memory_order_release); + m_submittedFrameIndex.store(-1, std::memory_order_release); + m_readyFrameIndex.store(-1, std::memory_order_release); m_framePublishSequence = 0; memset(m_frameLastPublishSequence, 0, sizeof(m_frameLastPublishSequence)); @@ -1241,7 +1242,8 @@ void CIndirectDeviceContext::DeInitLGMP() if (m_lgmp == nullptr) { m_frameScheduler.Reset(); - m_publishedFrameIndex.store(-1, std::memory_order_release); + m_submittedFrameIndex.store(-1, std::memory_order_release); + m_readyFrameIndex.store(-1, std::memory_order_release); m_framePublishSequence = 0; memset(m_frameLastPublishSequence, 0, sizeof(m_frameLastPublishSequence)); @@ -1261,7 +1263,8 @@ void CIndirectDeviceContext::DeInitLGMP() m_frameScheduler.Reset(); AcquireSRWLockExclusive(&m_framePublishLock); - m_publishedFrameIndex.store(-1, std::memory_order_release); + m_submittedFrameIndex.store(-1, std::memory_order_release); + m_readyFrameIndex.store(-1, std::memory_order_release); m_framePublishSequence = 0; memset(m_frameLastPublishSequence, 0, sizeof(m_frameLastPublishSequence)); @@ -1633,15 +1636,15 @@ bool CIndirectDeviceContext::HasMatchingOwnerDelivery( int CIndirectDeviceContext::FindAvailableFrameBuffer() const { - const LONG publishedFrameIndex = - m_publishedFrameIndex.load(std::memory_order_acquire); + const LONG readyFrameIndex = + m_readyFrameIndex.load(std::memory_order_acquire); int available = -1; uint64_t newestPublish = 0; for (unsigned frameIndex = 0; frameIndex < LGMP_Q_FRAME_BUFFER_LEN; ++frameIndex) { - if (static_cast(frameIndex) == publishedFrameIndex || + if (static_cast(frameIndex) == readyFrameIndex || m_frameInFlight[frameIndex].load(std::memory_order_acquire) || m_frameDelivery[frameIndex].ownerQueueMask || m_frameDelivery[frameIndex].sharedPending || @@ -1722,7 +1725,7 @@ bool CIndirectDeviceContext::GetSharedFrameTarget(uint64_t now, AcquireSRWLockShared(&m_framePublishLock); const LONG frameIndex = - m_publishedFrameIndex.load(std::memory_order_acquire); + m_readyFrameIndex.load(std::memory_order_acquire); if (frameIndex < 0 || m_frameInFlight[frameIndex].load(std::memory_order_acquire) || lgmpHostQueuePending(m_frameQueue) != 0) @@ -1752,7 +1755,7 @@ bool CIndirectDeviceContext::ReplaySharedFrame(uint64_t now, bool& retry) AcquireSRWLockExclusive(&m_framePublishLock); const LONG frameIndex = - m_publishedFrameIndex.load(std::memory_order_acquire); + m_readyFrameIndex.load(std::memory_order_acquire); if (frameIndex < 0 || m_frameInFlight[frameIndex].load(std::memory_order_acquire) || lgmpHostQueuePending(m_frameQueue) != 0) @@ -1992,7 +1995,7 @@ bool CIndirectDeviceContext::PublishFrameBuffer(unsigned frameIndex, if (published) { m_frameLastPublishSequence[frameIndex] = ++m_framePublishSequence; - m_publishedFrameIndex.store( + m_submittedFrameIndex.store( static_cast(frameIndex), std::memory_order_release); } ReleaseSRWLockExclusive(&m_framePublishLock); @@ -2024,7 +2027,7 @@ bool CIndirectDeviceContext::RepublishFrameBuffer( } const LONG frameIndex = - m_publishedFrameIndex.load(std::memory_order_acquire); + m_readyFrameIndex.load(std::memory_order_acquire); if (frameIndex < 0 || m_frameInFlight[frameIndex].load(std::memory_order_acquire)) { @@ -2150,13 +2153,31 @@ void CIndirectDeviceContext::FailFrameBuffer(unsigned frameIndex) InterlockedExchange((volatile LONG *)&m_frame[frameIndex]->timingValid, 0); FinalizeFrameBuffer(frameIndex); - CompleteFrameBuffer(frameIndex); + CompleteFrameBuffer(frameIndex, false); } -void CIndirectDeviceContext::CompleteFrameBuffer(unsigned frameIndex) +void CIndirectDeviceContext::CompleteFrameBuffer( + unsigned frameIndex, bool succeeded) { - if (frameIndex < LGMP_Q_FRAME_BUFFER_LEN) - m_frameInFlight[frameIndex].store(false, std::memory_order_release); + if (frameIndex >= LGMP_Q_FRAME_BUFFER_LEN) + return; + + AcquireSRWLockExclusive(&m_framePublishLock); + if (succeeded) + { + // Completion callbacks may run out of order. Never replace a newer ready + // frame with an older submission. + const uint64_t sequence = m_frameLastPublishSequence[frameIndex]; + const LONG readyFrameIndex = + m_readyFrameIndex.load(std::memory_order_acquire); + if (sequence && + (readyFrameIndex < 0 || + sequence > m_frameLastPublishSequence[readyFrameIndex])) + m_readyFrameIndex.store( + static_cast(frameIndex), std::memory_order_release); + } + m_frameInFlight[frameIndex].store(false, std::memory_order_release); + ReleaseSRWLockExclusive(&m_framePublishLock); } void CIndirectDeviceContext::SetFrameTiming(unsigned frameIndex, diff --git a/idd/LGIdd/CIndirectDeviceContext.h b/idd/LGIdd/CIndirectDeviceContext.h index 2f91fd24..2eaa377a 100644 --- a/idd/LGIdd/CIndirectDeviceContext.h +++ b/idd/LGIdd/CIndirectDeviceContext.h @@ -110,7 +110,10 @@ private: size_t m_alignSize = 0; size_t m_frameMemoryOffset = 0; size_t m_maxFrameSize = 0; - std::atomic m_publishedFrameIndex = -1; + // LGMP publication precedes copy completion, so only the ready index is safe + // to retain and replay. + std::atomic m_submittedFrameIndex = -1; + std::atomic m_readyFrameIndex = -1; std::atomic m_frameInFlight[LGMP_Q_FRAME_BUFFER_LEN] = {}; SRWLOCK m_framePublishLock = SRWLOCK_INIT; uint64_t m_framePublishSequence = 0; @@ -270,7 +273,7 @@ public: bool FrameBufferAvailable(const CFrameScheduler::Schedule& schedule); bool HasPublishedFrame() const { - return m_publishedFrameIndex.load(std::memory_order_acquire) >= 0; + return m_readyFrameIndex.load(std::memory_order_acquire) >= 0; } void ProcessFrameQueue(); bool GetSharedFrameTarget(uint64_t now, uint64_t& target); @@ -283,7 +286,7 @@ public: const CFrameScheduler::Schedule& schedule, bool periodic); void AbortFrameBuffer(unsigned frameIndex); void FailFrameBuffer(unsigned frameIndex); - void CompleteFrameBuffer(unsigned frameIndex); + void CompleteFrameBuffer(unsigned frameIndex, bool succeeded); void SetFrameTiming(unsigned frameIndex, uint64_t captureTime, uint64_t postProcessTime, uint64_t copyTime, uint64_t readyTime); void WriteFrameBuffer(unsigned frameIndex, void* src, size_t offset, size_t len, bool setWritePos) const; diff --git a/idd/LGIdd/CSwapChainProcessor.cpp b/idd/LGIdd/CSwapChainProcessor.cpp index 4cca454a..d3d56523 100644 --- a/idd/LGIdd/CSwapChainProcessor.cpp +++ b/idd/LGIdd/CSwapChainProcessor.cpp @@ -744,7 +744,7 @@ void CSwapChainProcessor::CompletionFunction( sc->m_devContext->RecordFrameTiming(readyEnd - publishStart); sc->m_devContext->SetFrameTiming(fbRes->GetFrameIndex(), fbRes->GetCaptureTime(), postProcessTime, copyTime, readyTime); - sc->m_devContext->CompleteFrameBuffer(fbRes->GetFrameIndex()); + sc->m_devContext->CompleteFrameBuffer(fbRes->GetFrameIndex(), true); sc->ReleaseCandidate(candidateIndex); }