diff --git a/src/core/renderloop.cpp b/src/core/renderloop.cpp index 1c438854d2..0c6cd59175 100644 --- a/src/core/renderloop.cpp +++ b/src/core/renderloop.cpp @@ -62,17 +62,41 @@ void RenderLoopPrivate::scheduleRepaint(std::chrono::nanoseconds lastTargetTimes // -> take that into account and start compositing very early expectedCompositingTime = std::max(vblankInterval - 1us, expectedCompositingTime); } - const uint64_t pageflipsInAdvance = std::min(expectedCompositingTime / vblankInterval + 1, maxPendingFrameCount); const uint64_t pageflipsSinceLastToTarget = std::max(std::round((lastTargetTimestamp - lastPresentationTimestamp).count() / double(vblankInterval.count())), 0); + uint64_t pageflipsInAdvance = std::min(expectedCompositingTime / vblankInterval + 1, maxPendingFrameCount); + + // switching from double to triple buffering causes a frame drop + // -> apply some amount of hysteresis to avoid switching back and forth constantly + if (pageflipsInAdvance > 1) { + // immediately switch to triple buffering when needed + wasTripleBuffering = true; + doubleBufferingCounter = 0; + } else if (wasTripleBuffering) { + // but wait a bit before switching back to double buffering + if (doubleBufferingCounter >= 10) { + wasTripleBuffering = false; + } else if (expectedCompositingTime >= vblankInterval * 0.95) { + // also don't switch back if render times are just barely enough for double buffering + pageflipsInAdvance = 2; + doubleBufferingCounter = 0; + } else { + doubleBufferingCounter++; + pageflipsInAdvance = 2; + } + } nextPresentationTimestamp = lastPresentationTimestamp + std::max(pageflipsSince + pageflipsInAdvance, pageflipsSinceLastToTarget + 1) * vblankInterval; - } else if (presentationMode == PresentationMode::Async || presentationMode == PresentationMode::AdaptiveAsync) { - // tearing: pageflips happen ASAP - nextPresentationTimestamp = currentTime; } else { - // adaptive sync: pageflips happen after one vblank interval - // TODO read minimum refresh rate from the EDID and take it into account here - nextPresentationTimestamp = lastPresentationTimestamp + vblankInterval; + wasTripleBuffering = false; + doubleBufferingCounter = 0; + if (presentationMode == PresentationMode::Async || presentationMode == PresentationMode::AdaptiveAsync) { + // tearing: pageflips happen ASAP + nextPresentationTimestamp = currentTime; + } else { + // adaptive sync: pageflips happen after one vblank interval + // TODO read minimum refresh rate from the EDID and take it into account here + nextPresentationTimestamp = lastPresentationTimestamp + vblankInterval; + } } const std::chrono::nanoseconds nextRenderTimestamp = nextPresentationTimestamp - expectedCompositingTime; diff --git a/src/core/renderloop_p.h b/src/core/renderloop_p.h index dba8a69e44..ce4d528ab0 100644 --- a/src/core/renderloop_p.h +++ b/src/core/renderloop_p.h @@ -43,6 +43,8 @@ public: std::optional m_debugOutput; std::chrono::nanoseconds lastPresentationTimestamp = std::chrono::nanoseconds::zero(); std::chrono::nanoseconds nextPresentationTimestamp = std::chrono::nanoseconds::zero(); + bool wasTripleBuffering = false; + int doubleBufferingCounter = 0; QTimer compositeTimer; RenderJournal renderJournal; int refreshRate = 60000;