core/renderloop: add some hysteresis to triple buffering

Switching to triple buffering requires dropping a frame, so if we constantly
switch back and forth between double and triple buffering, that can cause
very visible performance issues

CCBUG: 488843
This commit is contained in:
Xaver Hugl 2024-06-24 19:16:05 +02:00
parent 6f750f0aa5
commit 0672313c20
2 changed files with 33 additions and 7 deletions

View file

@ -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<int64_t>(expectedCompositingTime / vblankInterval + 1, maxPendingFrameCount);
const uint64_t pageflipsSinceLastToTarget = std::max<int64_t>(std::round((lastTargetTimestamp - lastPresentationTimestamp).count() / double(vblankInterval.count())), 0);
uint64_t pageflipsInAdvance = std::min<int64_t>(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;

View file

@ -43,6 +43,8 @@ public:
std::optional<std::fstream> 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;