Wait on our D3D11 swapchain before rendering to reduce latency

This commit is contained in:
Cameron Gutman 2022-04-07 21:46:48 -05:00
parent 474591c6a5
commit 6d3d51553b
4 changed files with 27 additions and 20 deletions

View file

@ -464,10 +464,6 @@ bool D3D11VARenderer::initialize(PDECODER_PARAMETERS params)
m_FrameWaitableObject = m_SwapChain->GetFrameLatencyWaitableObject();
SDL_assert(m_FrameWaitableObject != nullptr);
// Wait for the swap chain to be ready. This is required because we don't
// we're waiting after presenting in the general case, not before.
WaitForSingleObjectEx(m_FrameWaitableObject, 1000, FALSE);
}
else {
IDXGIDevice1* dxgiDevice;
@ -582,6 +578,22 @@ void D3D11VARenderer::setHdrMode(bool enabled)
unlockContext(this);
}
void D3D11VARenderer::waitToRender()
{
if (m_FrameWaitableObject != nullptr) {
SDL_assert(m_Windowed);
SDL_assert(m_DecoderParams.enableVsync);
// Wait for the pipeline to be ready for the next frame in V-Sync mode.
//
// This callback happens before selecting the next frame to render, so
// we can wait for the previous frame to finish prior to picking the
// next one to display. This reduces the effective display latency
// by ensuring we always render the most recent frame immediately.
WaitForSingleObjectEx(m_FrameWaitableObject, 500, FALSE);
}
}
void D3D11VARenderer::renderFrame(AVFrame* frame)
{
// Acquire the context lock for rendering to prevent concurrent
@ -669,22 +681,6 @@ void D3D11VARenderer::renderFrame(AVFrame* frame)
SDL_PushEvent(&event);
return;
}
if (m_FrameWaitableObject != nullptr) {
SDL_assert(m_Windowed);
SDL_assert(m_DecoderParams.enableVsync);
// Wait for the pipeline to be ready for the next frame in V-Sync mode.
//
// MSDN advises us to wait *before* doing any rendering operations,
// however that assumes the a typical game which will latch inputs,
// run the engine, draw, etc. after WaitForSingleObjectEx(). In our case,
// we actually want wait *after* our rendering operations, because our AVFrame
// is already set in stone by the time we enter this function. Waiting after
// presenting allows a more recent frame to be received before renderFrame()
// is called again.
WaitForSingleObjectEx(m_FrameWaitableObject, 1000, FALSE);
}
}
void D3D11VARenderer::renderOverlay(Overlay::OverlayType type)

View file

@ -19,6 +19,7 @@ public:
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary**) override;
virtual bool prepareDecoderContextInGetFormat(AVCodecContext* context, AVPixelFormat pixelFormat) override;
virtual void renderFrame(AVFrame* frame) override;
virtual void waitToRender() override;
virtual void notifyOverlayUpdated(Overlay::OverlayType) override;
virtual void setHdrMode(bool enabled) override;
virtual int getRendererAttributes() override;

View file

@ -91,6 +91,9 @@ int Pacer::renderThread(void* context)
}
while (!me->m_Stopping) {
// Wait for the renderer to be ready for the next frame
me->m_VsyncRenderer->waitToRender();
// Acquire the frame queue lock to protect the queue and
// the not empty condition
me->m_FrameQueueLock.lock();

View file

@ -102,6 +102,13 @@ public:
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) = 0;
virtual void renderFrame(AVFrame* frame) = 0;
// Called for threaded renderers to allow them to wait prior to us latching
// the next frame for rendering (as opposed to waiting on buffer swap with
// an older frame already queued for display).
virtual void waitToRender() {
// Don't wait by default
}
// Called on the same thread as renderFrame() during destruction of the renderer
virtual void cleanupRenderContext() {
// Nothing