Require several consecutive long render times to drop a frame

This commit is contained in:
Cameron Gutman
2019-01-21 17:58:42 -08:00
parent 58b77cf589
commit 1f972b1828
2 changed files with 37 additions and 16 deletions
@@ -12,6 +12,7 @@
#endif #endif
#define FRAME_HISTORY_ENTRIES 8 #define FRAME_HISTORY_ENTRIES 8
#define RENDER_TIME_HISTORY_ENTRIES 8
// We may be woken up slightly late so don't go all the way // We may be woken up slightly late so don't go all the way
// up to the next V-sync since we may accidentally step into // up to the next V-sync since we may accidentally step into
@@ -22,7 +23,6 @@
Pacer::Pacer(IFFmpegRenderer* renderer, PVIDEO_STATS videoStats) : Pacer::Pacer(IFFmpegRenderer* renderer, PVIDEO_STATS videoStats) :
m_FrameQueueLock(0), m_FrameQueueLock(0),
m_DropNextFrame(false),
m_VsyncSource(nullptr), m_VsyncSource(nullptr),
m_VsyncRenderer(renderer), m_VsyncRenderer(renderer),
m_MaxVideoFps(0), m_MaxVideoFps(0),
@@ -65,8 +65,8 @@ void Pacer::vsyncCallback(int timeUntilNextVsyncMillis)
// frame history to drop frames only if consistently above the // frame history to drop frames only if consistently above the
// one queued frame mark. // one queued frame mark.
if (m_MaxVideoFps >= m_DisplayFps) { if (m_MaxVideoFps >= m_DisplayFps) {
for (int i = 0; i < m_FrameQueueHistory.count(); i++) { for (int queueHistoryEntry : m_FrameQueueHistory) {
if (m_FrameQueueHistory[i] <= 1) { if (queueHistoryEntry <= 1) {
// Be lenient as long as the queue length // Be lenient as long as the queue length
// resolves before the end of frame history // resolves before the end of frame history
frameDropTarget = 3; frameDropTarget = 3;
@@ -104,7 +104,7 @@ void Pacer::vsyncCallback(int timeUntilNextVsyncMillis)
} }
// Nothing to render at this time. This counts as a drop. // Nothing to render at this time. This counts as a drop.
m_DropNextFrame = false; addRenderTimeToHistory(0);
return; return;
} }
@@ -148,13 +148,40 @@ bool Pacer::initialize(SDL_Window* window, int maxVideoFps, bool enablePacing)
return true; return true;
} }
void Pacer::addRenderTimeToHistory(int renderTime)
{
if (m_RenderTimeHistory.count() == RENDER_TIME_HISTORY_ENTRIES) {
m_RenderTimeHistory.dequeue();
}
m_RenderTimeHistory.enqueue(renderTime);
}
void Pacer::renderFrame(AVFrame* frame) void Pacer::renderFrame(AVFrame* frame)
{ {
bool dropFrame = !m_RenderTimeHistory.isEmpty();
// If rendering consistently takes longer than an entire V-sync interval,
// there must have been frames pending in the pipeline that are blocking us.
// Drop our this frame to clear the backed up display pipeline.
for (int renderTime : m_RenderTimeHistory) {
if (renderTime <= 1000 / m_DisplayFps) {
dropFrame = false;
break;
}
}
// Drop this frame if we believe we've run into V-sync waits // Drop this frame if we believe we've run into V-sync waits
if (m_DropNextFrame) { if (dropFrame) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Render time (%d ms) consistently exceeded V-sync period; dropping frame to reduce latency",
m_RenderTimeHistory[0]);
// Add 0 render time to history to account for the drop
addRenderTimeToHistory(0);
m_VideoStats->pacerDroppedFrames++; m_VideoStats->pacerDroppedFrames++;
av_frame_free(&frame); av_frame_free(&frame);
m_DropNextFrame = false;
return; return;
} }
@@ -166,15 +193,7 @@ void Pacer::renderFrame(AVFrame* frame)
m_VsyncRenderer->renderFrameAtVsync(frame); m_VsyncRenderer->renderFrameAtVsync(frame);
Uint32 afterRender = SDL_GetTicks(); Uint32 afterRender = SDL_GetTicks();
// If rendering took longer than an entire V-sync interval, addRenderTimeToHistory(afterRender - beforeRender);
// there must have been a frame pending display that blocked us.
// Drop our next frame to clear the backed up display pipeline.
if (afterRender - beforeRender > 1000 / m_DisplayFps) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Render time exceeded V-sync period (%d ms); dropping next frame to reduce latency",
afterRender - beforeRender);
m_DropNextFrame = true;
}
m_VideoStats->totalRenderTime += afterRender - beforeRender; m_VideoStats->totalRenderTime += afterRender - beforeRender;
m_VideoStats->renderedFrames++; m_VideoStats->renderedFrames++;
@@ -27,12 +27,14 @@ public:
bool isUsingFrameQueue(); bool isUsingFrameQueue();
private: private:
void addRenderTimeToHistory(int renderTime);
void renderFrame(AVFrame* frame); void renderFrame(AVFrame* frame);
QQueue<AVFrame*> m_FrameQueue; QQueue<AVFrame*> m_FrameQueue;
QQueue<int> m_FrameQueueHistory; QQueue<int> m_FrameQueueHistory;
QQueue<int> m_RenderTimeHistory;
SDL_SpinLock m_FrameQueueLock; SDL_SpinLock m_FrameQueueLock;
bool m_DropNextFrame;
IVsyncSource* m_VsyncSource; IVsyncSource* m_VsyncSource;
IFFmpegRenderer* m_VsyncRenderer; IFFmpegRenderer* m_VsyncRenderer;