File indexing completed on 2024-05-12 17:02:08

0001 /*
0002     SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "renderloop.h"
0008 #include "renderloop_p.h"
0009 #include "scene/surfaceitem.h"
0010 #include "scene/surfaceitem_wayland.h"
0011 #include "utils/common.h"
0012 #include "wayland/surface_interface.h"
0013 
0014 namespace KWin
0015 {
0016 
0017 template<typename T>
0018 T alignTimestamp(const T &timestamp, const T &alignment)
0019 {
0020     return timestamp + ((alignment - (timestamp % alignment)) % alignment);
0021 }
0022 
0023 RenderLoopPrivate *RenderLoopPrivate::get(RenderLoop *loop)
0024 {
0025     return loop->d.get();
0026 }
0027 
0028 RenderLoopPrivate::RenderLoopPrivate(RenderLoop *q)
0029     : q(q)
0030 {
0031     compositeTimer.setSingleShot(true);
0032     QObject::connect(&compositeTimer, &QTimer::timeout, q, [this]() {
0033         dispatch();
0034     });
0035 }
0036 
0037 void RenderLoopPrivate::scheduleRepaint()
0038 {
0039     if (kwinApp()->isTerminating() || (compositeTimer.isActive() && !allowTearing)) {
0040         return;
0041     }
0042     if (vrrPolicy == RenderLoop::VrrPolicy::Always || (vrrPolicy == RenderLoop::VrrPolicy::Automatic && fullscreenItem != nullptr)) {
0043         presentMode = allowTearing ? SyncMode::AdaptiveAsync : SyncMode::Adaptive;
0044     } else {
0045         presentMode = allowTearing ? SyncMode::Async : SyncMode::Fixed;
0046     }
0047     const std::chrono::nanoseconds vblankInterval(1'000'000'000'000ull / refreshRate);
0048     const std::chrono::nanoseconds currentTime(std::chrono::steady_clock::now().time_since_epoch());
0049 
0050     // Estimate when the next presentation will occur. Note that this is a prediction.
0051     nextPresentationTimestamp = lastPresentationTimestamp + vblankInterval;
0052     if (nextPresentationTimestamp < currentTime && presentMode == SyncMode::Fixed) {
0053         nextPresentationTimestamp = lastPresentationTimestamp
0054             + alignTimestamp(currentTime - lastPresentationTimestamp, vblankInterval);
0055     }
0056 
0057     // Estimate when it's a good time to perform the next compositing cycle.
0058     const std::chrono::nanoseconds safetyMargin = std::chrono::milliseconds(3);
0059 
0060     std::chrono::nanoseconds renderTime;
0061     switch (q->latencyPolicy()) {
0062     case LatencyExtremelyLow:
0063         renderTime = std::chrono::nanoseconds(long(vblankInterval.count() * 0.1));
0064         break;
0065     case LatencyLow:
0066         renderTime = std::chrono::nanoseconds(long(vblankInterval.count() * 0.25));
0067         break;
0068     case LatencyMedium:
0069         renderTime = std::chrono::nanoseconds(long(vblankInterval.count() * 0.5));
0070         break;
0071     case LatencyHigh:
0072         renderTime = std::chrono::nanoseconds(long(vblankInterval.count() * 0.75));
0073         break;
0074     case LatencyExtremelyHigh:
0075         renderTime = std::chrono::nanoseconds(long(vblankInterval.count() * 0.9));
0076         break;
0077     }
0078 
0079     switch (options->renderTimeEstimator()) {
0080     case RenderTimeEstimatorMinimum:
0081         renderTime = std::max(renderTime, renderJournal.minimum());
0082         break;
0083     case RenderTimeEstimatorMaximum:
0084         renderTime = std::max(renderTime, renderJournal.maximum());
0085         break;
0086     case RenderTimeEstimatorAverage:
0087         renderTime = std::max(renderTime, renderJournal.average());
0088         break;
0089     }
0090 
0091     std::chrono::nanoseconds nextRenderTimestamp = nextPresentationTimestamp - renderTime - safetyMargin;
0092 
0093     // If we can't render the frame before the deadline, start compositing immediately.
0094     if (nextRenderTimestamp < currentTime) {
0095         nextRenderTimestamp = currentTime;
0096     }
0097 
0098     if (presentMode == SyncMode::Async || presentMode == SyncMode::AdaptiveAsync) {
0099         compositeTimer.start(0);
0100     } else {
0101         const std::chrono::nanoseconds waitInterval = nextRenderTimestamp - currentTime;
0102         compositeTimer.start(std::chrono::duration_cast<std::chrono::milliseconds>(waitInterval));
0103     }
0104 }
0105 
0106 void RenderLoopPrivate::delayScheduleRepaint()
0107 {
0108     pendingReschedule = true;
0109 }
0110 
0111 void RenderLoopPrivate::maybeScheduleRepaint()
0112 {
0113     if (pendingReschedule) {
0114         scheduleRepaint();
0115         pendingReschedule = false;
0116     }
0117 }
0118 
0119 void RenderLoopPrivate::notifyFrameFailed()
0120 {
0121     Q_ASSERT(pendingFrameCount > 0);
0122     pendingFrameCount--;
0123 
0124     if (!inhibitCount) {
0125         maybeScheduleRepaint();
0126     }
0127 }
0128 
0129 void RenderLoopPrivate::notifyFrameCompleted(std::chrono::nanoseconds timestamp)
0130 {
0131     Q_ASSERT(pendingFrameCount > 0);
0132     pendingFrameCount--;
0133 
0134     if (lastPresentationTimestamp <= timestamp) {
0135         lastPresentationTimestamp = timestamp;
0136     } else {
0137         qCDebug(KWIN_CORE,
0138                 "Got invalid presentation timestamp: %lld (current %lld)",
0139                 static_cast<long long>(timestamp.count()),
0140                 static_cast<long long>(lastPresentationTimestamp.count()));
0141         lastPresentationTimestamp = std::chrono::steady_clock::now().time_since_epoch();
0142     }
0143 
0144     if (!inhibitCount) {
0145         maybeScheduleRepaint();
0146     }
0147 
0148     Q_EMIT q->framePresented(q, timestamp);
0149 }
0150 
0151 void RenderLoopPrivate::dispatch()
0152 {
0153     // On X11, we want to ignore repaints that are scheduled by windows right before
0154     // the Compositor starts repainting.
0155     pendingRepaint = true;
0156 
0157     Q_EMIT q->frameRequested(q);
0158 
0159     // The Compositor may decide to not repaint when the frameRequested() signal is
0160     // emitted, in which case the pending repaint flag has to be reset manually.
0161     pendingRepaint = false;
0162 }
0163 
0164 void RenderLoopPrivate::invalidate()
0165 {
0166     pendingReschedule = false;
0167     pendingFrameCount = 0;
0168     compositeTimer.stop();
0169 }
0170 
0171 RenderLoop::RenderLoop()
0172     : d(std::make_unique<RenderLoopPrivate>(this))
0173 {
0174 }
0175 
0176 RenderLoop::~RenderLoop()
0177 {
0178 }
0179 
0180 void RenderLoop::inhibit()
0181 {
0182     d->inhibitCount++;
0183 
0184     if (d->inhibitCount == 1) {
0185         d->compositeTimer.stop();
0186     }
0187 }
0188 
0189 void RenderLoop::uninhibit()
0190 {
0191     Q_ASSERT(d->inhibitCount > 0);
0192     d->inhibitCount--;
0193 
0194     if (d->inhibitCount == 0) {
0195         d->maybeScheduleRepaint();
0196     }
0197 }
0198 
0199 void RenderLoop::beginFrame()
0200 {
0201     d->pendingRepaint = false;
0202     d->pendingFrameCount++;
0203     d->renderJournal.beginFrame();
0204 }
0205 
0206 void RenderLoop::endFrame()
0207 {
0208     d->renderJournal.endFrame();
0209 }
0210 
0211 int RenderLoop::refreshRate() const
0212 {
0213     return d->refreshRate;
0214 }
0215 
0216 void RenderLoop::setRefreshRate(int refreshRate)
0217 {
0218     if (d->refreshRate == refreshRate) {
0219         return;
0220     }
0221     d->refreshRate = refreshRate;
0222     Q_EMIT refreshRateChanged();
0223 }
0224 
0225 void RenderLoop::scheduleRepaint(Item *item)
0226 {
0227     if (d->pendingRepaint || (d->fullscreenItem != nullptr && item != nullptr && item != d->fullscreenItem)) {
0228         return;
0229     }
0230     if (!d->pendingFrameCount && !d->inhibitCount) {
0231         d->scheduleRepaint();
0232     } else {
0233         d->delayScheduleRepaint();
0234     }
0235 }
0236 
0237 LatencyPolicy RenderLoop::latencyPolicy() const
0238 {
0239     return d->latencyPolicy.value_or(options->latencyPolicy());
0240 }
0241 
0242 void RenderLoop::setLatencyPolicy(LatencyPolicy policy)
0243 {
0244     d->latencyPolicy = policy;
0245 }
0246 
0247 void RenderLoop::resetLatencyPolicy()
0248 {
0249     d->latencyPolicy.reset();
0250 }
0251 
0252 std::chrono::nanoseconds RenderLoop::lastPresentationTimestamp() const
0253 {
0254     return d->lastPresentationTimestamp;
0255 }
0256 
0257 std::chrono::nanoseconds RenderLoop::nextPresentationTimestamp() const
0258 {
0259     return d->nextPresentationTimestamp;
0260 }
0261 
0262 void RenderLoop::setFullscreenSurface(Item *surfaceItem)
0263 {
0264     d->fullscreenItem = surfaceItem;
0265     if (SurfaceItemWayland *wayland = qobject_cast<SurfaceItemWayland *>(surfaceItem)) {
0266         d->allowTearing = d->canDoTearing && options->allowTearing() && wayland->surface()->presentationHint() == KWaylandServer::PresentationHint::Async;
0267     } else {
0268         d->allowTearing = false;
0269     }
0270 }
0271 
0272 RenderLoop::VrrPolicy RenderLoop::vrrPolicy() const
0273 {
0274     return d->vrrPolicy;
0275 }
0276 
0277 void RenderLoop::setVrrPolicy(VrrPolicy policy)
0278 {
0279     d->vrrPolicy = policy;
0280 }
0281 
0282 } // namespace KWin