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 {
// 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
// us in sync with VBlank.
// frames for us. If frame pacing is also enabled or we're in full-screen,
// our Vsync source will keep us in sync with VBlank.
flags = 0;
}
@ -993,8 +993,19 @@ bool D3D11VARenderer::checkDecoderSupport(IDXGIAdapter* adapter)
int D3D11VARenderer::getRendererAttributes()
{
int attributes = 0;
// 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()

View file

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

View file

@ -208,6 +208,7 @@ bool Pacer::initialize(SDL_Window* window, int maxVideoFps, bool enablePacing)
{
m_MaxVideoFps = maxVideoFps;
m_DisplayFps = StreamUtils::getDisplayRefreshRate(window);
m_RendererAttributes = m_VsyncRenderer->getRendererAttributes();
if (enablePacing) {
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.
#endif
SDL_assert(m_VsyncSource != nullptr || !(m_RendererAttributes & RENDERER_ATTRIBUTE_FORCE_PACING));
if (m_VsyncSource != nullptr && !m_VsyncSource->initialize(window, m_DisplayFps)) {
return false;
}
@ -259,22 +262,31 @@ void Pacer::renderFrame(AVFrame* frame)
// Drop frames if we have too many queued up for a while
m_FrameQueueLock.lock();
int 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;
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) {
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
if (m_RenderQueueHistory.count() == m_MaxVideoFps / 2) {
m_RenderQueueHistory.dequeue();
}
// Keep a rolling 500 ms window of render queue history
if (m_RenderQueueHistory.count() == m_MaxVideoFps / 2) {
m_RenderQueueHistory.dequeue();
}
m_RenderQueueHistory.enqueue(m_RenderQueue.count());
m_RenderQueueHistory.enqueue(m_RenderQueue.count());
}
// Catch up if we're several frames ahead
while (m_RenderQueue.count() > frameDropTarget) {

View file

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

View file

@ -110,7 +110,8 @@ private:
#define RENDERER_ATTRIBUTE_FULLSCREEN_ONLY 0x01
#define RENDERER_ATTRIBUTE_1080P_MAX 0x02
#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 {
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
if (!testFrame) {
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;
}
}