Enhance frame pacing logic for HDR renderers

This commit is contained in:
Cameron Gutman 2022-04-24 17:43:35 -05:00
parent 5f682bb45f
commit 2bb2745f91
6 changed files with 47 additions and 18 deletions

View file

@ -581,8 +581,8 @@ void D3D11VARenderer::renderFrame(AVFrame* frame)
} }
else { else {
// Otherwise, we'll submit as fast as possible and DWM will discard excess // Otherwise, we'll submit as fast as possible and DWM will discard excess
// frames for us. If frame pacing is also enabled, our Vsync source will keep // frames for us. If frame pacing is also enabled or we're in full-screen,
// us in sync with VBlank. // our Vsync source will keep us in sync with VBlank.
flags = 0; flags = 0;
} }
@ -993,8 +993,19 @@ bool D3D11VARenderer::checkDecoderSupport(IDXGIAdapter* adapter)
int D3D11VARenderer::getRendererAttributes() int D3D11VARenderer::getRendererAttributes()
{ {
int attributes = 0;
// This renderer supports HDR // This renderer supports HDR
return RENDERER_ATTRIBUTE_HDR_SUPPORT; attributes |= RENDERER_ATTRIBUTE_HDR_SUPPORT;
// This renderer requires frame pacing to synchronize with VBlank when we're
// in full-screen. In windowed mode, we will render as fast we can and DWM
// will grab whatever is latest at the time unless the user opts for pacing.
if (SDL_GetWindowFlags(m_DecoderParams.window) & SDL_WINDOW_FULLSCREEN) {
attributes |= RENDERER_ATTRIBUTE_FORCE_PACING;
}
return attributes;
} }
bool D3D11VARenderer::needsTestFrame() bool D3D11VARenderer::needsTestFrame()

View file

@ -443,6 +443,9 @@ int DrmRenderer::getRendererAttributes()
// This renderer supports HDR // This renderer supports HDR
attributes |= RENDERER_ATTRIBUTE_HDR_SUPPORT; attributes |= RENDERER_ATTRIBUTE_HDR_SUPPORT;
// This renderer does not buffer any frames in the graphics pipeline
attributes |= RENDERER_ATTRIBUTE_NO_BUFFERING;
return attributes; return attributes;
} }

View file

@ -208,6 +208,7 @@ bool Pacer::initialize(SDL_Window* window, int maxVideoFps, bool enablePacing)
{ {
m_MaxVideoFps = maxVideoFps; m_MaxVideoFps = maxVideoFps;
m_DisplayFps = StreamUtils::getDisplayRefreshRate(window); m_DisplayFps = StreamUtils::getDisplayRefreshRate(window);
m_RendererAttributes = m_VsyncRenderer->getRendererAttributes();
if (enablePacing) { if (enablePacing) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
@ -225,6 +226,8 @@ bool Pacer::initialize(SDL_Window* window, int maxVideoFps, bool enablePacing)
// immediately like they used to. // immediately like they used to.
#endif #endif
SDL_assert(m_VsyncSource != nullptr || !(m_RendererAttributes & RENDERER_ATTRIBUTE_FORCE_PACING));
if (m_VsyncSource != nullptr && !m_VsyncSource->initialize(window, m_DisplayFps)) { if (m_VsyncSource != nullptr && !m_VsyncSource->initialize(window, m_DisplayFps)) {
return false; return false;
} }
@ -259,22 +262,31 @@ void Pacer::renderFrame(AVFrame* frame)
// Drop frames if we have too many queued up for a while // Drop frames if we have too many queued up for a while
m_FrameQueueLock.lock(); m_FrameQueueLock.lock();
int frameDropTarget = 0; int frameDropTarget;
for (int queueHistoryEntry : m_RenderQueueHistory) {
if (queueHistoryEntry == 0) { if (m_RendererAttributes & RENDERER_ATTRIBUTE_NO_BUFFERING) {
// Be lenient as long as the queue length // Renderers that don't buffer any frames but don't support waitToRender() need us to buffer
// resolves before the end of frame history // an extra frame to ensure they don't starve while waiting to present.
frameDropTarget = 2; frameDropTarget = 1;
break; }
else {
frameDropTarget = 0;
for (int queueHistoryEntry : m_RenderQueueHistory) {
if (queueHistoryEntry == 0) {
// Be lenient as long as the queue length
// resolves before the end of frame history
frameDropTarget = 2;
break;
}
} }
}
// Keep a rolling 500 ms window of render queue history // Keep a rolling 500 ms window of render queue history
if (m_RenderQueueHistory.count() == m_MaxVideoFps / 2) { if (m_RenderQueueHistory.count() == m_MaxVideoFps / 2) {
m_RenderQueueHistory.dequeue(); m_RenderQueueHistory.dequeue();
} }
m_RenderQueueHistory.enqueue(m_RenderQueue.count()); m_RenderQueueHistory.enqueue(m_RenderQueue.count());
}
// Catch up if we're several frames ahead // Catch up if we're several frames ahead
while (m_RenderQueue.count() > frameDropTarget) { while (m_RenderQueue.count() > frameDropTarget) {

View file

@ -52,4 +52,5 @@ private:
int m_MaxVideoFps; int m_MaxVideoFps;
int m_DisplayFps; int m_DisplayFps;
PVIDEO_STATS m_VideoStats; PVIDEO_STATS m_VideoStats;
int m_RendererAttributes;
}; };

View file

@ -110,7 +110,8 @@ private:
#define RENDERER_ATTRIBUTE_FULLSCREEN_ONLY 0x01 #define RENDERER_ATTRIBUTE_FULLSCREEN_ONLY 0x01
#define RENDERER_ATTRIBUTE_1080P_MAX 0x02 #define RENDERER_ATTRIBUTE_1080P_MAX 0x02
#define RENDERER_ATTRIBUTE_HDR_SUPPORT 0x04 #define RENDERER_ATTRIBUTE_HDR_SUPPORT 0x04
#define RENDERER_ATTRIBUTE_SELF_PACING 0x08 #define RENDERER_ATTRIBUTE_NO_BUFFERING 0x08
#define RENDERER_ATTRIBUTE_FORCE_PACING 0x10
class IFFmpegRenderer : public Overlay::IOverlayRenderer { class IFFmpegRenderer : public Overlay::IOverlayRenderer {
public: public:

View file

@ -290,7 +290,8 @@ bool FFmpegVideoDecoder::completeInitialization(const AVCodec* decoder, PDECODER
// Don't bother initializing Pacer if we're not actually going to render // Don't bother initializing Pacer if we're not actually going to render
if (!testFrame) { if (!testFrame) {
m_Pacer = new Pacer(m_FrontendRenderer, &m_ActiveWndVideoStats); m_Pacer = new Pacer(m_FrontendRenderer, &m_ActiveWndVideoStats);
if (!m_Pacer->initialize(params->window, params->frameRate, params->enableFramePacing)) { if (!m_Pacer->initialize(params->window, params->frameRate,
params->enableFramePacing || (params->enableVsync && (m_FrontendRenderer->getRendererAttributes() & RENDERER_ATTRIBUTE_FORCE_PACING)))) {
return false; return false;
} }
} }