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
@@ -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()
@@ -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;
} }
@@ -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,7 +262,15 @@ 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;
if (m_RendererAttributes & RENDERER_ATTRIBUTE_NO_BUFFERING) {
// Renderers that don't buffer any frames but don't support waitToRender() need us to buffer
// an extra frame to ensure they don't starve while waiting to present.
frameDropTarget = 1;
}
else {
frameDropTarget = 0;
for (int queueHistoryEntry : m_RenderQueueHistory) { for (int queueHistoryEntry : m_RenderQueueHistory) {
if (queueHistoryEntry == 0) { if (queueHistoryEntry == 0) {
// Be lenient as long as the queue length // Be lenient as long as the queue length
@@ -275,6 +286,7 @@ void Pacer::renderFrame(AVFrame* frame)
} }
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) {
@@ -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;
}; };
@@ -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:
+2 -1
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;
} }
} }