File indexing completed on 2025-03-23 11:13:33
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 ×tamp, 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