File indexing completed on 2024-11-10 04:56:40
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 ×tamp, 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"