[idd] avoid release frame timeouts during replug

Drop frames when the LGMP queue is full instead of blocking
while holding an IddCx surface. Always finish every
acquired frame, including duplicate frame numbers, so
replug teardown cannot trigger the release-frame watchdog.
This commit is contained in:
Geoffrey McRae
2026-07-18 03:53:24 +10:00
parent 3095a86556
commit 234f8623dc
3 changed files with 27 additions and 17 deletions

View File

@@ -923,13 +923,19 @@ void CIndirectDeviceContext::LGMPTimer()
ResendCursor();
}
bool CIndirectDeviceContext::FrameBufferAvailable() const
{
return m_lgmp && m_frameQueue &&
lgmpHostQueuePending(m_frameQueue) < LGMP_Q_FRAME_LEN;
}
CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrameBuffer(
unsigned pitch, const D12FrameFormat& srcFormat, const D12FrameFormat& dstFormat,
const RECT * dirtyRects, unsigned nbDirtyRects)
{
PreparedFrameBuffer result = {};
if (!m_lgmp || !m_frameQueue)
if (!FrameBufferAvailable())
return result;
if (m_width != dstFormat.desc.Width ||
@@ -988,10 +994,6 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
KVMFRFrame * fi = m_frame[m_frameIndex];
// wait until there is room in the queue
while (lgmpHostQueuePending(m_frameQueue) == LGMP_Q_FRAME_LEN)
Sleep(0);
if (dstFormat.format == FRAME_TYPE_INVALID)
{
DEBUG_ERROR("Unsupported frame format, skipping frame");

View File

@@ -210,6 +210,7 @@ public:
uint8_t* mem;
};
bool FrameBufferAvailable() const;
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;

View File

@@ -226,18 +226,18 @@ bool CSwapChainProcessor::SwapChainThreadCore()
lastFrameNumber = frameNumber;
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);
if (FAILED(hr))
{
// A lost path is normal (mode change/topology rebuild); Windows
// reassigns a fresh swap chain. Just exit and let it.
if (hr != STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY)
DEBUG_ERROR_HR(hr, "IddCxSwapChainFinishedProcessingFrame Failed");
break;
}
// Every acquired frame must be finished before the next acquire, even if
// its presentation number was a duplicate and no work was submitted.
hr = IddCxSwapChainFinishedProcessingFrame(m_hSwapChain);
if (FAILED(hr))
{
// A lost path is normal (mode change/topology rebuild); Windows
// reassigns a fresh swap chain. Just exit and let it.
if (hr != STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY)
DEBUG_ERROR_HR(hr, "IddCxSwapChainFinishedProcessingFrame Failed");
break;
}
}
else
@@ -339,6 +339,11 @@ static FrameType GetFrameType(DXGI_FORMAT format)
bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer, unsigned dirtyRectCount,
DXGI_COLOR_SPACE_TYPE colorSpace, UINT sdrWhiteLevel)
{
// Never hold an IddCx frame waiting for a slow or disconnected client. In
// particular, monitor departure cannot complete until the frame is released.
if (!m_devContext->FrameBufferAvailable())
return true;
ComPtr<ID3D11Texture2D> texture;
HRESULT hr = acquiredBuffer.As(&texture);
if (FAILED(hr))
@@ -532,8 +537,10 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
frameDirtyRects,
frameDirtyRectCount);
// The LGMP timer can fill the queue with a subscriber resend after the early
// availability check. Treat this as a dropped frame rather than an error.
if (!buffer.mem)
return false;
return true;
CFrameBufferResource * fbRes = m_fbPool.Get(buffer,
(size_t)layout.Footprint.RowPitch * dstFormat.desc.Height);