diff --git a/idd/LGIdd/CIndirectDeviceContext.cpp b/idd/LGIdd/CIndirectDeviceContext.cpp index 9a103793..08be3ace 100644 --- a/idd/LGIdd/CIndirectDeviceContext.cpp +++ b/idd/LGIdd/CIndirectDeviceContext.cpp @@ -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"); diff --git a/idd/LGIdd/CIndirectDeviceContext.h b/idd/LGIdd/CIndirectDeviceContext.h index 69f5ab51..c348ed33 100644 --- a/idd/LGIdd/CIndirectDeviceContext.h +++ b/idd/LGIdd/CIndirectDeviceContext.h @@ -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; diff --git a/idd/LGIdd/CSwapChainProcessor.cpp b/idd/LGIdd/CSwapChainProcessor.cpp index 3090d4dc..068d0f30 100644 --- a/idd/LGIdd/CSwapChainProcessor.cpp +++ b/idd/LGIdd/CSwapChainProcessor.cpp @@ -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 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 texture; HRESULT hr = acquiredBuffer.As(&texture); if (FAILED(hr)) @@ -532,8 +537,10 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr 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);