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
wilder/Plasma/6.2
Xaver Hugl 2 years ago
parent 6f750f0aa5
commit 0672313c20
  1. 38
      src/core/renderloop.cpp
  2. 2
      src/core/renderloop_p.h

@ -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;

@ -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;

Loading…
Cancel
Save