File indexing completed on 2024-05-12 05:31:23

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 "options.h"
0009 #include "renderloop_p.h"
0010 #include "scene/surfaceitem.h"
0011 #include "utils/common.h"
0012 #include "window.h"
0013 #include "workspace.h"
0014 
0015 namespace KWin
0016 {
0017 
0018 template<typename T>
0019 T alignTimestamp(const T &timestamp, const T &alignment)
0020 {
0021     return timestamp + ((alignment - (timestamp % alignment)) % alignment);
0022 }
0023 
0024 RenderLoopPrivate *RenderLoopPrivate::get(RenderLoop *loop)
0025 {
0026     return loop->d.get();
0027 }
0028 
0029 RenderLoopPrivate::RenderLoopPrivate(RenderLoop *q, Output *output)
0030     : q(q)
0031     , output(output)
0032 {
0033     compositeTimer.setSingleShot(true);
0034     QObject::connect(&compositeTimer, &QTimer::timeout, q, [this]() {
0035         dispatch();
0036     });
0037 }
0038 
0039 void RenderLoopPrivate::scheduleRepaint()
0040 {
0041     if (kwinApp()->isTerminating() || compositeTimer.isActive()) {
0042         return;
0043     }
0044     const std::chrono::nanoseconds vblankInterval(1'000'000'000'000ull / refreshRate);
0045     const std::chrono::nanoseconds currentTime(std::chrono::steady_clock::now().time_since_epoch());
0046 
0047     // Estimate when the next presentation will occur. Note that this is a prediction.
0048     nextPresentationTimestamp = lastPresentationTimestamp + vblankInterval;
0049     if (nextPresentationTimestamp < currentTime && presentationMode == PresentationMode::VSync) {
0050         nextPresentationTimestamp = lastPresentationTimestamp
0051             + alignTimestamp(currentTime - lastPresentationTimestamp, vblankInterval);
0052     }
0053 
0054     // Estimate when it's a good time to perform the next compositing cycle.
0055     // the 1ms on top of the safety margin is required for timer and scheduler inaccuracies
0056     std::chrono::nanoseconds nextRenderTimestamp = nextPresentationTimestamp - renderJournal.result() - safetyMargin - std::chrono::milliseconds(1);
0057 
0058     // If we can't render the frame before the deadline, start compositing immediately.
0059     if (nextRenderTimestamp < currentTime) {
0060         nextRenderTimestamp = currentTime;
0061     }
0062 
0063     if (presentationMode == PresentationMode::Async || presentationMode == PresentationMode::AdaptiveAsync) {
0064         compositeTimer.start(0);
0065     } else {
0066         const std::chrono::nanoseconds waitInterval = nextRenderTimestamp - currentTime;
0067         compositeTimer.start(std::chrono::duration_cast<std::chrono::milliseconds>(waitInterval));
0068     }
0069 }
0070 
0071 void RenderLoopPrivate::delayScheduleRepaint()
0072 {
0073     pendingReschedule = true;
0074 }
0075 
0076 void RenderLoopPrivate::maybeScheduleRepaint()
0077 {
0078     if (pendingReschedule) {
0079         scheduleRepaint();
0080         pendingReschedule = false;
0081     }
0082 }
0083 
0084 void RenderLoopPrivate::notifyFrameFailed()
0085 {
0086     Q_ASSERT(pendingFrameCount > 0);
0087     pendingFrameCount--;
0088 
0089     if (!inhibitCount) {
0090         maybeScheduleRepaint();
0091     }
0092 }
0093 
0094 void RenderLoopPrivate::notifyFrameCompleted(std::chrono::nanoseconds timestamp, std::chrono::nanoseconds renderTime, PresentationMode mode)
0095 {
0096     Q_ASSERT(pendingFrameCount > 0);
0097     pendingFrameCount--;
0098 
0099     notifyVblank(timestamp);
0100 
0101     renderJournal.add(renderTime, timestamp);
0102     if (!inhibitCount) {
0103         maybeScheduleRepaint();
0104     }
0105 
0106     Q_EMIT q->framePresented(q, timestamp, mode);
0107 }
0108 
0109 void RenderLoopPrivate::notifyVblank(std::chrono::nanoseconds timestamp)
0110 {
0111     if (lastPresentationTimestamp <= timestamp) {
0112         lastPresentationTimestamp = timestamp;
0113     } else {
0114         qCDebug(KWIN_CORE,
0115                 "Got invalid presentation timestamp: %lld (current %lld)",
0116                 static_cast<long long>(timestamp.count()),
0117                 static_cast<long long>(lastPresentationTimestamp.count()));
0118         lastPresentationTimestamp = std::chrono::steady_clock::now().time_since_epoch();
0119     }
0120 }
0121 
0122 void RenderLoopPrivate::dispatch()
0123 {
0124     // On X11, we want to ignore repaints that are scheduled by windows right before
0125     // the Compositor starts repainting.
0126     pendingRepaint = true;
0127 
0128     Q_EMIT q->frameRequested(q);
0129 
0130     // The Compositor may decide to not repaint when the frameRequested() signal is
0131     // emitted, in which case the pending repaint flag has to be reset manually.
0132     pendingRepaint = false;
0133 }
0134 
0135 void RenderLoopPrivate::invalidate()
0136 {
0137     pendingReschedule = false;
0138     pendingFrameCount = 0;
0139     compositeTimer.stop();
0140 }
0141 
0142 RenderLoop::RenderLoop(Output *output)
0143     : d(std::make_unique<RenderLoopPrivate>(this, output))
0144 {
0145 }
0146 
0147 RenderLoop::~RenderLoop()
0148 {
0149 }
0150 
0151 void RenderLoop::inhibit()
0152 {
0153     d->inhibitCount++;
0154 
0155     if (d->inhibitCount == 1) {
0156         d->compositeTimer.stop();
0157     }
0158 }
0159 
0160 void RenderLoop::uninhibit()
0161 {
0162     Q_ASSERT(d->inhibitCount > 0);
0163     d->inhibitCount--;
0164 
0165     if (d->inhibitCount == 0) {
0166         d->maybeScheduleRepaint();
0167     }
0168 }
0169 
0170 void RenderLoop::prepareNewFrame()
0171 {
0172     d->pendingFrameCount++;
0173 }
0174 
0175 void RenderLoop::beginPaint()
0176 {
0177     d->pendingRepaint = false;
0178 }
0179 
0180 int RenderLoop::refreshRate() const
0181 {
0182     return d->refreshRate;
0183 }
0184 
0185 void RenderLoop::setRefreshRate(int refreshRate)
0186 {
0187     if (d->refreshRate == refreshRate) {
0188         return;
0189     }
0190     d->refreshRate = refreshRate;
0191     Q_EMIT refreshRateChanged();
0192 }
0193 
0194 void RenderLoop::setPresentationSafetyMargin(std::chrono::nanoseconds safetyMargin)
0195 {
0196     d->safetyMargin = safetyMargin;
0197 }
0198 
0199 void RenderLoop::scheduleRepaint(Item *item)
0200 {
0201     if (d->pendingRepaint) {
0202         return;
0203     }
0204     const bool vrr = d->presentationMode == PresentationMode::AdaptiveSync || d->presentationMode == PresentationMode::AdaptiveAsync;
0205     if (vrr && workspace()->activeWindow() && d->output) {
0206         Window *const activeWindow = workspace()->activeWindow();
0207         if (activeWindow->isOnOutput(d->output) && activeWindow->surfaceItem() && item != activeWindow->surfaceItem() && activeWindow->surfaceItem()->frameTimeEstimation() <= std::chrono::nanoseconds(1'000'000'000) / 30) {
0208             return;
0209         }
0210     }
0211     if (!d->pendingFrameCount && !d->inhibitCount) {
0212         d->scheduleRepaint();
0213     } else {
0214         d->delayScheduleRepaint();
0215     }
0216 }
0217 
0218 std::chrono::nanoseconds RenderLoop::lastPresentationTimestamp() const
0219 {
0220     return d->lastPresentationTimestamp;
0221 }
0222 
0223 std::chrono::nanoseconds RenderLoop::nextPresentationTimestamp() const
0224 {
0225     return d->nextPresentationTimestamp;
0226 }
0227 
0228 void RenderLoop::setPresentationMode(PresentationMode mode)
0229 {
0230     d->presentationMode = mode;
0231 }
0232 
0233 } // namespace KWin
0234 
0235 #include "moc_renderloop.cpp"