[idd] pipeline: overlap independent frame copies

This commit is contained in:
Geoffrey McRae
2026-08-06 17:32:17 +10:00
parent ea6e155bbf
commit 5e7d944607
2 changed files with 113 additions and 12 deletions

View File

@@ -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, static bool FrameMetadataChanged(const D12FrameFormat& previous,
const D12FrameFormat& current) const D12FrameFormat& current)
{ {
@@ -92,6 +147,7 @@ CSwapChainProcessor::CSwapChainProcessor(CIndirectMonitorContext * monitorContex
m_candidateEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr)); m_candidateEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr));
m_candidateAvailableEvent.Attach( m_candidateAvailableEvent.Attach(
CreateEvent(nullptr, FALSE, FALSE, nullptr)); CreateEvent(nullptr, FALSE, FALSE, nullptr));
m_copySubmitEvent.Attach(CreateEvent(nullptr, TRUE, TRUE, nullptr));
m_publishTimer.Attach(CreateWaitableTimerExW(nullptr, nullptr, m_publishTimer.Attach(CreateWaitableTimerExW(nullptr, nullptr,
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS)); CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS));
if (!m_publishTimer.Get()) if (!m_publishTimer.Get())
@@ -104,8 +160,8 @@ CSwapChainProcessor::CSwapChainProcessor(CIndirectMonitorContext * monitorContex
bool CSwapChainProcessor::Start() bool CSwapChainProcessor::Start()
{ {
if (!m_terminateEvent.Get() || !m_candidateEvent.Get() || if (!m_terminateEvent.Get() || !m_candidateEvent.Get() ||
!m_candidateAvailableEvent.Get() || !m_publishTimer.Get() || !m_candidateAvailableEvent.Get() || !m_copySubmitEvent.Get() ||
!m_cursorDataEvent.Get() || !m_shapeBuffer) !m_publishTimer.Get() || !m_cursorDataEvent.Get() || !m_shapeBuffer)
{ {
DEBUG_ERROR("Failed to initialize swap chain worker resources"); DEBUG_ERROR("Failed to initialize swap chain worker resources");
return false; return false;
@@ -1203,13 +1259,49 @@ void CSwapChainProcessor::SignalCandidateState()
SetEvent(m_candidateAvailableEvent.Get()); 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( bool CSwapChainProcessor::PublishNewestCandidate(
const CFrameScheduler::Schedule& schedule, bool periodic, const CFrameScheduler::Schedule& schedule, bool periodic,
uint64_t publishStart) uint64_t publishStart)
{ {
// Once a deadline is due, submit transport work before allowing another // Once a deadline is due, prevent newly recorded preparation work from
// preparation to enqueue on the same physical copy queue. // being submitted ahead of the transport copy. The short submission gate
CSRWExclusiveLock pipelineLock(&m_pipelineLock); // 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; int selectedCandidate = -1;
uint64_t newestSequence = 0; uint64_t newestSequence = 0;
@@ -1394,7 +1486,9 @@ bool CSwapChainProcessor::PublishNewestCandidate(
} }
ReleaseSRWLockExclusive(&m_damageLock); 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 // The logical damage state was advanced before submission. Force a full
// repair whether submission failed or its callback reported the failure. // repair whether submission failed or its callback reported the failure.
@@ -1778,7 +1872,7 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
FrameCandidate& candidate = m_candidates[candidateIndex]; FrameCandidate& candidate = m_candidates[candidateIndex];
CSRWExclusiveLock pipelineLock(&m_pipelineLock); CSRWSharedLock pipelineLock(&m_pipelineLock);
CPostProcessor& postProcessor = m_postProcessors[candidateIndex]; CPostProcessor& postProcessor = m_postProcessors[candidateIndex];
const D12FrameFormat& dstFormat = postProcessor.GetOutputFormat(); const D12FrameFormat& dstFormat = postProcessor.GetOutputFormat();
@@ -1935,7 +2029,7 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
copySrcResource.Get()); copySrcResource.Get());
copySlot->EndTiming(); copySlot->EndTiming();
if (!copySlot->Execute()) if (!ExecuteCandidateCopy(copySlot))
{ {
if (!copySlot->HasSubmittedWork()) if (!copySlot->HasSubmittedWork())
{ {

View File

@@ -99,15 +99,21 @@ private:
// An active tail records only damage received after its candidate snapshot. // An active tail records only damage received after its candidate snapshot.
FrameCandidate m_candidates[LGMP_Q_FRAME_LEN]; FrameCandidate m_candidates[LGMP_Q_FRAME_LEN];
CandidateDamageTail m_candidateDamageTail[LGMP_Q_FRAME_LEN]; CandidateDamageTail m_candidateDamageTail[LGMP_Q_FRAME_LEN];
SRWLOCK m_candidateLock = SRWLOCK_INIT; SRWLOCK m_candidateLock = SRWLOCK_INIT;
SRWLOCK m_damageLock = SRWLOCK_INIT; SRWLOCK m_damageLock = SRWLOCK_INIT;
SRWLOCK m_pipelineLock = SRWLOCK_INIT; // Reconfiguration is exclusive while per-candidate recording is shared.
uint64_t m_candidateSequence = 0; 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<Wrappers::HandleTraits::HANDLENullTraits> m_thread[3]; Wrappers::HandleT<Wrappers::HandleTraits::HANDLENullTraits> m_thread[3];
Wrappers::Event m_terminateEvent; Wrappers::Event m_terminateEvent;
Wrappers::Event m_candidateEvent; Wrappers::Event m_candidateEvent;
Wrappers::Event m_candidateAvailableEvent; Wrappers::Event m_candidateAvailableEvent;
Wrappers::Event m_copySubmitEvent;
Wrappers::HandleT<Wrappers::HandleTraits::HANDLENullTraits> m_publishTimer; Wrappers::HandleT<Wrappers::HandleTraits::HANDLENullTraits> m_publishTimer;
Wrappers::Event m_cursorDataEvent; Wrappers::Event m_cursorDataEvent;
@@ -154,6 +160,7 @@ private:
unsigned candidateIndex, size_t frameSize); unsigned candidateIndex, size_t frameSize);
void ResetCandidates(); void ResetCandidates();
void SignalCandidateState(); void SignalCandidateState();
bool ExecuteCandidateCopy(CD3D12CommandSlot * copySlot);
static DWORD CALLBACK _CursorThread(LPVOID arg); static DWORD CALLBACK _CursorThread(LPVOID arg);
bool QueryHWCursor(); bool QueryHWCursor();