core/renderloop: make repaint scheduling more robust against wrong render time estimations

When the render time estimation is much higher than the real render time, and triple buffering is
enabled, the previous logic would schedule frames multiple vblanks in advance, which could drop the
effective refresh rate by more than is necessary. This commit limits it to the second vblank after
the current time, which ensures that we hit the full refresh rate even when render time prediction
is wrong
This commit is contained in:
Xaver Hugl 2024-05-26 14:56:20 +02:00
parent 97c1d335e5
commit 804ecb0e22

View file

@ -32,13 +32,6 @@ RenderLoopPrivate::RenderLoopPrivate(RenderLoop *q, Output *output)
});
}
static std::chrono::nanoseconds estimateNextPageflip(std::chrono::nanoseconds earliestSubmitTime, std::chrono::nanoseconds lastPageflip, std::chrono::nanoseconds vblankInterval)
{
// the last pageflip may be in the future
const uint64_t pageflipsSince = earliestSubmitTime > lastPageflip ? (earliestSubmitTime - lastPageflip) / vblankInterval : 0;
return lastPageflip + vblankInterval * (pageflipsSince + 1);
}
void RenderLoopPrivate::scheduleNextRepaint()
{
if (kwinApp()->isTerminating() || compositeTimer.isActive()) {
@ -59,13 +52,11 @@ void RenderLoopPrivate::scheduleRepaint(std::chrono::nanoseconds lastTargetTimes
if (presentationMode == PresentationMode::VSync) {
// normal presentation: pageflips only happen at vblank
if (maxPendingFrameCount == 1) {
// keep the old behavior for backends not supporting triple buffering
nextPresentationTimestamp = estimateNextPageflip(currentTime, lastPresentationTimestamp, vblankInterval);
} else {
// estimate the next pageflip that can realistically be hit
nextPresentationTimestamp = estimateNextPageflip(std::max(lastTargetTimestamp, currentTime + expectedCompositingTime), lastPresentationTimestamp, vblankInterval);
}
const uint64_t pageflipsSince = std::max<int64_t>((currentTime - lastPresentationTimestamp) / vblankInterval, 0);
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);
nextPresentationTimestamp = lastPresentationTimestamp + std::max(pageflipsSince + pageflipsInAdvance, pageflipsSinceLastToTarget + 1) * vblankInterval;
} else if (presentationMode == PresentationMode::Async || presentationMode == PresentationMode::AdaptiveAsync) {
// tearing: pageflips happen ASAP
nextPresentationTimestamp = currentTime;