From 5e7d94460726bf1495a0aebb63c145885f26c6dd Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Thu, 6 Aug 2026 17:32:17 +1000 Subject: [PATCH] [idd] pipeline: overlap independent frame copies --- idd/LGIdd/CSwapChainProcessor.cpp | 110 +++++++++++++++++++++++++++--- idd/LGIdd/CSwapChainProcessor.h | 15 ++-- 2 files changed, 113 insertions(+), 12 deletions(-) diff --git a/idd/LGIdd/CSwapChainProcessor.cpp b/idd/LGIdd/CSwapChainProcessor.cpp index a8187943..ff63cda5 100644 --- a/idd/LGIdd/CSwapChainProcessor.cpp +++ b/idd/LGIdd/CSwapChainProcessor.cpp @@ -55,6 +55,61 @@ public: } }; +class CSRWSharedLock +{ +private: + SRWLOCK * m_lock; + +public: + explicit CSRWSharedLock(SRWLOCK * lock) : m_lock(lock) + { + AcquireSRWLockShared(m_lock); + } + + ~CSRWSharedLock() + { + ReleaseSRWLockShared(m_lock); + } +}; + +class CPublishPending +{ +private: + SRWLOCK * m_lock; + bool * m_pending; + HANDLE m_event; + bool m_active = true; + +public: + CPublishPending(SRWLOCK * lock, bool * pending, HANDLE event) : + m_lock(lock), + m_pending(pending), + m_event(event) + { + AcquireSRWLockExclusive(m_lock); + *m_pending = true; + ResetEvent(m_event); + ReleaseSRWLockExclusive(m_lock); + } + + ~CPublishPending() + { + Clear(); + } + + void Clear() + { + if (!m_active) + return; + + AcquireSRWLockExclusive(m_lock); + *m_pending = false; + SetEvent(m_event); + ReleaseSRWLockExclusive(m_lock); + m_active = false; + } +}; + static bool FrameMetadataChanged(const D12FrameFormat& previous, const D12FrameFormat& current) { @@ -92,6 +147,7 @@ CSwapChainProcessor::CSwapChainProcessor(CIndirectMonitorContext * monitorContex m_candidateEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr)); m_candidateAvailableEvent.Attach( CreateEvent(nullptr, FALSE, FALSE, nullptr)); + m_copySubmitEvent.Attach(CreateEvent(nullptr, TRUE, TRUE, nullptr)); m_publishTimer.Attach(CreateWaitableTimerExW(nullptr, nullptr, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS)); if (!m_publishTimer.Get()) @@ -104,8 +160,8 @@ CSwapChainProcessor::CSwapChainProcessor(CIndirectMonitorContext * monitorContex bool CSwapChainProcessor::Start() { if (!m_terminateEvent.Get() || !m_candidateEvent.Get() || - !m_candidateAvailableEvent.Get() || !m_publishTimer.Get() || - !m_cursorDataEvent.Get() || !m_shapeBuffer) + !m_candidateAvailableEvent.Get() || !m_copySubmitEvent.Get() || + !m_publishTimer.Get() || !m_cursorDataEvent.Get() || !m_shapeBuffer) { DEBUG_ERROR("Failed to initialize swap chain worker resources"); return false; @@ -1203,13 +1259,49 @@ void CSwapChainProcessor::SignalCandidateState() SetEvent(m_candidateAvailableEvent.Get()); } +bool CSwapChainProcessor::ExecuteCandidateCopy( + CD3D12CommandSlot * copySlot) +{ + HANDLE waitHandles[] = + { + m_terminateEvent.Get(), + m_copySubmitEvent.Get(), + }; + + for (;;) + { + AcquireSRWLockExclusive(&m_copySubmitLock); + if (!m_publishPending) + { + const bool result = copySlot->Execute(); + ReleaseSRWLockExclusive(&m_copySubmitLock); + return result; + } + ReleaseSRWLockExclusive(&m_copySubmitLock); + + const DWORD result = WaitForMultipleObjects( + ARRAYSIZE(waitHandles), waitHandles, FALSE, INFINITE); + if (result == WAIT_OBJECT_0 + 1) + continue; + + copySlot->Cancel(); + if (result != WAIT_OBJECT_0) + DEBUG_ERROR_HR(HRESULT_FROM_WIN32(GetLastError()), + "Failed while waiting to submit a frame candidate"); + return false; + } +} + bool CSwapChainProcessor::PublishNewestCandidate( const CFrameScheduler::Schedule& schedule, bool periodic, uint64_t publishStart) { - // Once a deadline is due, submit transport work before allowing another - // preparation to enqueue on the same physical copy queue. - CSRWExclusiveLock pipelineLock(&m_pipelineLock); + // Once a deadline is due, prevent newly recorded preparation work from + // being submitted ahead of the transport copy. The short submission gate + // allows a preparation which is already submitting to finish first. + CPublishPending publishPending( + &m_copySubmitLock, &m_publishPending, m_copySubmitEvent.Get()); + CSRWSharedLock pipelineLock(&m_pipelineLock); int selectedCandidate = -1; uint64_t newestSequence = 0; @@ -1394,7 +1486,9 @@ bool CSwapChainProcessor::PublishNewestCandidate( } ReleaseSRWLockExclusive(&m_damageLock); - if (!copySlot->Execute()) + const bool submitted = copySlot->Execute(); + publishPending.Clear(); + if (!submitted) { // The logical damage state was advanced before submission. Force a full // repair whether submission failed or its callback reported the failure. @@ -1778,7 +1872,7 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr acquiredBuffer FrameCandidate& candidate = m_candidates[candidateIndex]; - CSRWExclusiveLock pipelineLock(&m_pipelineLock); + CSRWSharedLock pipelineLock(&m_pipelineLock); CPostProcessor& postProcessor = m_postProcessors[candidateIndex]; const D12FrameFormat& dstFormat = postProcessor.GetOutputFormat(); @@ -1935,7 +2029,7 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr acquiredBuffer copySrcResource.Get()); copySlot->EndTiming(); - if (!copySlot->Execute()) + if (!ExecuteCandidateCopy(copySlot)) { if (!copySlot->HasSubmittedWork()) { diff --git a/idd/LGIdd/CSwapChainProcessor.h b/idd/LGIdd/CSwapChainProcessor.h index 311f9ad8..0695fafb 100644 --- a/idd/LGIdd/CSwapChainProcessor.h +++ b/idd/LGIdd/CSwapChainProcessor.h @@ -99,15 +99,21 @@ private: // An active tail records only damage received after its candidate snapshot. FrameCandidate m_candidates[LGMP_Q_FRAME_LEN]; CandidateDamageTail m_candidateDamageTail[LGMP_Q_FRAME_LEN]; - SRWLOCK m_candidateLock = SRWLOCK_INIT; - SRWLOCK m_damageLock = SRWLOCK_INIT; - SRWLOCK m_pipelineLock = SRWLOCK_INIT; - uint64_t m_candidateSequence = 0; + SRWLOCK m_candidateLock = SRWLOCK_INIT; + SRWLOCK m_damageLock = SRWLOCK_INIT; + // Reconfiguration is exclusive while per-candidate recording is shared. + SRWLOCK m_pipelineLock = SRWLOCK_INIT; + // Capture holds this only across submission; the publisher uses it to + // close the gate before recording deadline work. + SRWLOCK m_copySubmitLock = SRWLOCK_INIT; + uint64_t m_candidateSequence = 0; + bool m_publishPending = false; Wrappers::HandleT m_thread[3]; Wrappers::Event m_terminateEvent; Wrappers::Event m_candidateEvent; Wrappers::Event m_candidateAvailableEvent; + Wrappers::Event m_copySubmitEvent; Wrappers::HandleT m_publishTimer; Wrappers::Event m_cursorDataEvent; @@ -154,6 +160,7 @@ private: unsigned candidateIndex, size_t frameSize); void ResetCandidates(); void SignalCandidateState(); + bool ExecuteCandidateCopy(CD3D12CommandSlot * copySlot); static DWORD CALLBACK _CursorThread(LPVOID arg); bool QueryHWCursor();