KWin
Loading...
Searching...
No Matches
renderloop.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "renderloop.h"
8#include "options.h"
9#include "renderloop_p.h"
10#include "scene/surfaceitem.h"
11#include "utils/common.h"
12#include "window.h"
13#include "workspace.h"
14
15namespace KWin
16{
17
18template<typename T>
19T alignTimestamp(const T &timestamp, const T &alignment)
20{
21 return timestamp + ((alignment - (timestamp % alignment)) % alignment);
22}
23
25{
26 return loop->d.get();
27}
28
30 : q(q)
31 , output(output)
32{
33 compositeTimer.setSingleShot(true);
34 QObject::connect(&compositeTimer, &QTimer::timeout, q, [this]() {
35 dispatch();
36 });
37}
38
40{
41 if (kwinApp()->isTerminating() || compositeTimer.isActive()) {
42 return;
43 }
44 const std::chrono::nanoseconds vblankInterval(1'000'000'000'000ull / refreshRate);
45 const std::chrono::nanoseconds currentTime(std::chrono::steady_clock::now().time_since_epoch());
46
47 // Estimate when the next presentation will occur. Note that this is a prediction.
51 + alignTimestamp(currentTime - lastPresentationTimestamp, vblankInterval);
52 }
53
54 // Estimate when it's a good time to perform the next compositing cycle.
55 // the 1ms on top of the safety margin is required for timer and scheduler inaccuracies
56 std::chrono::nanoseconds nextRenderTimestamp = nextPresentationTimestamp - renderJournal.result() - safetyMargin - std::chrono::milliseconds(1);
57
58 // If we can't render the frame before the deadline, start compositing immediately.
59 if (nextRenderTimestamp < currentTime) {
60 nextRenderTimestamp = currentTime;
61 }
62
64 compositeTimer.start(0);
65 } else {
66 const std::chrono::nanoseconds waitInterval = nextRenderTimestamp - currentTime;
67 compositeTimer.start(std::chrono::duration_cast<std::chrono::milliseconds>(waitInterval));
68 }
69}
70
75
83
93
94void RenderLoopPrivate::notifyFrameCompleted(std::chrono::nanoseconds timestamp, std::chrono::nanoseconds renderTime, PresentationMode mode)
95{
96 Q_ASSERT(pendingFrameCount > 0);
98
99 notifyVblank(timestamp);
100
101 renderJournal.add(renderTime, timestamp);
102 if (!inhibitCount) {
104 }
105
106 Q_EMIT q->framePresented(q, timestamp, mode);
107}
108
109void RenderLoopPrivate::notifyVblank(std::chrono::nanoseconds timestamp)
110{
111 if (lastPresentationTimestamp <= timestamp) {
112 lastPresentationTimestamp = timestamp;
113 } else {
114 qCDebug(KWIN_CORE,
115 "Got invalid presentation timestamp: %lld (current %lld)",
116 static_cast<long long>(timestamp.count()),
117 static_cast<long long>(lastPresentationTimestamp.count()));
118 lastPresentationTimestamp = std::chrono::steady_clock::now().time_since_epoch();
119 }
120}
121
123{
124 // On X11, we want to ignore repaints that are scheduled by windows right before
125 // the Compositor starts repainting.
126 pendingRepaint = true;
127
128 Q_EMIT q->frameRequested(q);
129
130 // The Compositor may decide to not repaint when the frameRequested() signal is
131 // emitted, in which case the pending repaint flag has to be reset manually.
132 pendingRepaint = false;
133}
134
136{
137 pendingReschedule = false;
139 compositeTimer.stop();
140}
141
143 : d(std::make_unique<RenderLoopPrivate>(this, output))
144{
145}
146
150
152{
153 d->inhibitCount++;
154
155 if (d->inhibitCount == 1) {
156 d->compositeTimer.stop();
157 }
158}
159
161{
162 Q_ASSERT(d->inhibitCount > 0);
163 d->inhibitCount--;
164
165 if (d->inhibitCount == 0) {
166 d->maybeScheduleRepaint();
167 }
168}
169
171{
172 d->pendingFrameCount++;
173}
174
176{
177 d->pendingRepaint = false;
178}
179
181{
182 return d->refreshRate;
183}
184
185void RenderLoop::setRefreshRate(int refreshRate)
186{
187 if (d->refreshRate == refreshRate) {
188 return;
189 }
190 d->refreshRate = refreshRate;
191 Q_EMIT refreshRateChanged();
192}
193
194void RenderLoop::setPresentationSafetyMargin(std::chrono::nanoseconds safetyMargin)
195{
196 d->safetyMargin = safetyMargin;
197}
198
200{
201 if (d->pendingRepaint) {
202 return;
203 }
204 const bool vrr = d->presentationMode == PresentationMode::AdaptiveSync || d->presentationMode == PresentationMode::AdaptiveAsync;
205 if (vrr && workspace()->activeWindow() && d->output) {
206 Window *const activeWindow = workspace()->activeWindow();
207 if (activeWindow->isOnOutput(d->output) && activeWindow->surfaceItem() && item != activeWindow->surfaceItem() && activeWindow->surfaceItem()->frameTimeEstimation() <= std::chrono::nanoseconds(1'000'000'000) / 30) {
208 return;
209 }
210 }
211 if (!d->pendingFrameCount && !d->inhibitCount) {
212 d->scheduleRepaint();
213 } else {
214 d->delayScheduleRepaint();
215 }
216}
217
218std::chrono::nanoseconds RenderLoop::lastPresentationTimestamp() const
219{
220 return d->lastPresentationTimestamp;
221}
222
223std::chrono::nanoseconds RenderLoop::nextPresentationTimestamp() const
224{
225 return d->nextPresentationTimestamp;
226}
227
229{
230 d->presentationMode = mode;
231}
232
233} // namespace KWin
234
235#include "moc_renderloop.cpp"
void add(std::chrono::nanoseconds renderTime, std::chrono::nanoseconds presentationTimestamp)
std::chrono::nanoseconds result() const
int refreshRate() const
void frameRequested(RenderLoop *loop)
void setPresentationMode(PresentationMode mode)
void framePresented(RenderLoop *loop, std::chrono::nanoseconds timestamp, PresentationMode mode)
~RenderLoop() override
void scheduleRepaint(Item *item=nullptr)
void refreshRateChanged()
std::chrono::nanoseconds lastPresentationTimestamp() const
std::chrono::nanoseconds nextPresentationTimestamp() const
void setRefreshRate(int refreshRate)
void setPresentationSafetyMargin(std::chrono::nanoseconds safetyMargin)
RenderLoop(Output *output)
static RenderLoopPrivate * get(RenderLoop *loop)
std::chrono::nanoseconds nextPresentationTimestamp
RenderLoop *const q
std::chrono::nanoseconds lastPresentationTimestamp
void notifyFrameCompleted(std::chrono::nanoseconds timestamp, std::chrono::nanoseconds renderTime, PresentationMode mode=PresentationMode::VSync)
PresentationMode presentationMode
void notifyVblank(std::chrono::nanoseconds timestamp)
std::chrono::nanoseconds safetyMargin
RenderJournal renderJournal
RenderLoopPrivate(RenderLoop *q, Output *output)
std::chrono::nanoseconds frameTimeEstimation() const
SurfaceItem * surfaceItem() const
Definition window.cpp:289
bool isOnOutput(Output *output) const
Definition window.cpp:254
Window * activeWindow() const
Definition workspace.h:767
PresentationMode
Definition globals.h:276
Workspace * workspace()
Definition workspace.h:830
T alignTimestamp(const T &timestamp, const T &alignment)