KWin
Loading...
Searching...
No Matches
compositor_wayland.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9
10#include "compositor_wayland.h"
11#include "core/output.h"
12#include "core/outputbackend.h"
13#include "core/renderbackend.h"
14#include "core/renderlayer.h"
16#include "main.h"
17#include "opengl/glplatform.h"
22#include "scene/cursorscene.h"
27#include "window.h"
28#include "workspace.h"
29
30#include <QQuickWindow>
31
32namespace KWin
33{
34
36{
37 Q_ASSERT(!s_compositor);
38 auto *compositor = new WaylandCompositor(parent);
39 s_compositor = compositor;
40 return compositor;
41}
42
43WaylandCompositor::WaylandCompositor(QObject *parent)
44 : Compositor(parent)
45{
46}
47
49{
50 Q_EMIT aboutToDestroy();
51 stop(); // this can't be called in the destructor of Compositor
52}
53
54bool WaylandCompositor::attemptOpenGLCompositing()
55{
56 std::unique_ptr<OpenGLBackend> backend = kwinApp()->outputBackend()->createOpenGLBackend();
57 if (!backend) {
58 return false;
59 }
60 if (!backend->isFailed()) {
61 backend->init();
62 }
63 if (backend->isFailed()) {
64 return false;
65 }
66
67 const QByteArray forceEnv = qgetenv("KWIN_COMPOSE");
68 if (!forceEnv.isEmpty()) {
69 if (qstrcmp(forceEnv, "O2") == 0 || qstrcmp(forceEnv, "O2ES") == 0) {
70 qCDebug(KWIN_CORE) << "OpenGL 2 compositing enforced by environment variable";
71 } else {
72 // OpenGL 2 disabled by environment variable
73 return false;
74 }
75 } else {
76 if (GLPlatform::instance()->recommendedCompositor() < OpenGLCompositing) {
77 qCDebug(KWIN_CORE) << "Driver does not recommend OpenGL compositing";
78 return false;
79 }
80 }
81
82 // We only support the OpenGL 2+ shader API, not GL_ARB_shader_objects
83 if (!hasGLVersion(2, 0)) {
84 qCDebug(KWIN_CORE) << "OpenGL 2.0 is not supported";
85 return false;
86 }
87
88 m_scene = std::make_unique<WorkspaceSceneOpenGL>(backend.get());
89 m_cursorScene = std::make_unique<CursorScene>(std::make_unique<ItemRendererOpenGL>());
90 m_backend = std::move(backend);
91
92 qCDebug(KWIN_CORE) << "OpenGL compositing has been successfully initialized";
93 return true;
94}
95
96bool WaylandCompositor::attemptQPainterCompositing()
97{
98 std::unique_ptr<QPainterBackend> backend(kwinApp()->outputBackend()->createQPainterBackend());
99 if (!backend || backend->isFailed()) {
100 return false;
101 }
102
103 m_scene = std::make_unique<WorkspaceSceneQPainter>(backend.get());
104 m_cursorScene = std::make_unique<CursorScene>(std::make_unique<ItemRendererQPainter>());
105 m_backend = std::move(backend);
106
107 qCDebug(KWIN_CORE) << "QPainter compositing has been successfully initialized";
108 return true;
109}
110
112{
113 if (kwinApp()->isTerminating()) {
114 return;
115 }
116 if (m_state != State::Off) {
117 return;
118 }
119
122
123 // If compositing has been restarted, try to use the last used compositing type.
124 const QList<CompositingType> availableCompositors = kwinApp()->outputBackend()->supportedCompositors();
125 QList<CompositingType> candidateCompositors;
126
127 if (m_selectedCompositor != NoCompositing) {
128 candidateCompositors.append(m_selectedCompositor);
129 } else {
130 candidateCompositors = availableCompositors;
131
132 const auto userConfigIt = std::find(candidateCompositors.begin(), candidateCompositors.end(), options->compositingMode());
133 if (userConfigIt != candidateCompositors.end()) {
134 candidateCompositors.erase(userConfigIt);
135 candidateCompositors.prepend(options->compositingMode());
136 } else {
137 qCWarning(KWIN_CORE) << "Configured compositor not supported by Platform. Falling back to defaults";
138 }
139 }
140
141 for (auto type : std::as_const(candidateCompositors)) {
142 bool stop = false;
143 switch (type) {
145 qCDebug(KWIN_CORE) << "Attempting to load the OpenGL scene";
146 stop = attemptOpenGLCompositing();
147 break;
149 qCDebug(KWIN_CORE) << "Attempting to load the QPainter scene";
150 stop = attemptQPainterCompositing();
151 break;
152 case NoCompositing:
153 qCDebug(KWIN_CORE) << "Starting without compositing...";
154 stop = true;
155 break;
156 }
157
158 if (stop) {
159 break;
160 } else if (qEnvironmentVariableIsSet("KWIN_COMPOSE")) {
161 qCCritical(KWIN_CORE) << "Could not fulfill the requested compositing mode in KWIN_COMPOSE:" << type << ". Exiting.";
162 qApp->quit();
163 }
164 }
165
166 if (!m_backend) {
168
169 qCCritical(KWIN_CORE) << "The used windowing system requires compositing";
170 qCCritical(KWIN_CORE) << "We are going to quit KWin now as it is broken";
171 qApp->quit();
172 return;
173 }
174
175 if (m_selectedCompositor == NoCompositing) {
176 m_selectedCompositor = m_backend->compositingType();
177
178 switch (m_selectedCompositor) {
179 case NoCompositing:
180 break;
182 QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
183 break;
185 QQuickWindow::setGraphicsApi(QSGRendererInterface::Software);
186 break;
187 }
188 }
189
190 Q_EMIT sceneCreated();
191
192 const QList<Output *> outputs = workspace()->outputs();
193 for (Output *output : outputs) {
194 addOutput(output);
195 }
196 connect(workspace(), &Workspace::outputAdded, this, &WaylandCompositor::addOutput);
197 connect(workspace(), &Workspace::outputRemoved, this, &WaylandCompositor::removeOutput);
198
200
201 const auto windows = workspace()->windows();
202 for (Window *window : windows) {
203 window->setupCompositing();
204 }
205
206 // Sets also the 'effects' pointer.
207 kwinApp()->createEffectsHandler(this, m_scene.get());
208
209 Q_EMIT compositingToggled(true);
210}
211
213{
215 return;
216 }
219
220 // Some effects might need access to effect windows when they are about to
221 // be destroyed, for example to unreference deleted windows, so we have to
222 // make sure that effect windows outlive effects.
223 delete effects;
224 effects = nullptr;
225
226 if (Workspace::self()) {
227 const auto windows = workspace()->windows();
228 for (Window *window : windows) {
229 window->finishCompositing();
230 }
231 disconnect(workspace(), &Workspace::outputAdded, this, &WaylandCompositor::addOutput);
232 disconnect(workspace(), &Workspace::outputRemoved, this, &WaylandCompositor::removeOutput);
233 }
234
235 if (m_backend->compositingType() == OpenGLCompositing) {
236 // some layers need a context current for destruction
237 static_cast<OpenGLBackend *>(m_backend.get())->makeCurrent();
238 }
239
240 const auto superlayers = m_superlayers;
241 for (auto it = superlayers.begin(); it != superlayers.end(); ++it) {
242 removeSuperLayer(*it);
243 }
244
245 m_scene.reset();
246 m_cursorScene.reset();
247 m_backend.reset();
248
250 Q_EMIT compositingToggled(false);
251}
252
253void WaylandCompositor::addOutput(Output *output)
254{
255 if (output->isPlaceholder()) {
256 return;
257 }
258 auto workspaceLayer = new RenderLayer(output->renderLoop());
259 workspaceLayer->setDelegate(std::make_unique<SceneDelegate>(m_scene.get(), output));
260 workspaceLayer->setGeometry(output->rectF());
261 connect(output, &Output::geometryChanged, workspaceLayer, [output, workspaceLayer]() {
262 workspaceLayer->setGeometry(output->rectF());
263 });
264
265 auto cursorLayer = new RenderLayer(output->renderLoop());
266 cursorLayer->setVisible(false);
267 if (m_backend->compositingType() == OpenGLCompositing) {
268 cursorLayer->setDelegate(std::make_unique<CursorDelegateOpenGL>(output));
269 } else {
270 cursorLayer->setDelegate(std::make_unique<CursorDelegateQPainter>(output));
271 }
272 cursorLayer->setParent(workspaceLayer);
273 cursorLayer->setSuperlayer(workspaceLayer);
274
275 // Software cursor is forced for intel devices because there are screen stuttering issues with hardware cursor,
276 // possibly a kernel driver bug. Remove the workaround when https://gitlab.freedesktop.org/drm/intel/-/issues/9571 is fixed.
277 static bool forceSoftwareCursorIsSet;
278 static const bool forceSoftwareCursor = qEnvironmentVariableIntValue("KWIN_FORCE_SW_CURSOR", &forceSoftwareCursorIsSet) == 1
279 || (!forceSoftwareCursorIsSet && GLPlatform::instance() && GLPlatform::instance()->isIntel());
280
281 auto updateCursorLayer = [this, output, cursorLayer]() {
282 const Cursor *cursor = Cursors::self()->currentCursor();
283 const QRectF outputLocalRect = output->mapFromGlobal(cursor->geometry());
284 const auto outputLayer = m_backend->cursorLayer(output);
285 if (!cursor->isOnOutput(output)) {
286 if (outputLayer && outputLayer->isEnabled()) {
287 outputLayer->setEnabled(false);
288 output->updateCursorLayer();
289 }
290 cursorLayer->setVisible(false);
291 return true;
292 }
293 const auto renderHardwareCursor = [&]() {
294 if (!outputLayer || forceSoftwareCursor) {
295 return false;
296 }
297 QRectF nativeCursorRect = output->transform().map(scaledRect(outputLocalRect, output->scale()), output->pixelSize());
298 QSize bufferSize(std::ceil(nativeCursorRect.width()), std::ceil(nativeCursorRect.height()));
299 if (const auto fixedSize = outputLayer->fixedSize()) {
300 if (fixedSize->width() < bufferSize.width() || fixedSize->height() < bufferSize.height()) {
301 return false;
302 }
303 bufferSize = *fixedSize;
304 nativeCursorRect = output->transform().map(QRectF(outputLocalRect.topLeft() * output->scale(), bufferSize), output->pixelSize());
305 }
306 outputLayer->setPosition(nativeCursorRect.topLeft());
307 outputLayer->setHotspot(output->transform().map(cursor->hotspot() * output->scale(), bufferSize));
308 outputLayer->setSize(bufferSize);
309 if (auto beginInfo = outputLayer->beginFrame()) {
310 const RenderTarget &renderTarget = beginInfo->renderTarget;
311
312 RenderLayer renderLayer(output->renderLoop());
313 renderLayer.setDelegate(std::make_unique<SceneDelegate>(m_cursorScene.get(), output));
314 renderLayer.setOutputLayer(outputLayer);
315
316 renderLayer.delegate()->prePaint();
317 renderLayer.delegate()->paint(renderTarget, infiniteRegion());
318 renderLayer.delegate()->postPaint();
319
320 if (!outputLayer->endFrame(infiniteRegion(), infiniteRegion())) {
321 return false;
322 }
323 } else {
324 return false;
325 }
326 outputLayer->setEnabled(true);
327 return output->updateCursorLayer();
328 };
329 if (renderHardwareCursor()) {
330 cursorLayer->setVisible(false);
331 return true;
332 } else {
333 if (outputLayer && outputLayer->isEnabled()) {
334 outputLayer->setEnabled(false);
335 output->updateCursorLayer();
336 }
337 cursorLayer->setVisible(cursor->isOnOutput(output));
338 cursorLayer->setGeometry(outputLocalRect);
339 cursorLayer->addRepaintFull();
340 return false;
341 }
342 };
343 auto moveCursorLayer = [this, output, cursorLayer, updateCursorLayer]() {
344 const Cursor *cursor = Cursors::self()->currentCursor();
345 const QRectF outputLocalRect = output->mapFromGlobal(cursor->geometry());
346 const auto outputLayer = m_backend->cursorLayer(output);
347 bool hardwareCursor = false;
348 if (outputLayer) {
349 if (outputLayer->isEnabled()) {
350 const QRectF nativeCursorRect = output->transform()
351 .map(QRectF(outputLocalRect.topLeft() * output->scale(), outputLayer->size()), output->pixelSize());
352 outputLayer->setPosition(nativeCursorRect.topLeft());
353 hardwareCursor = output->updateCursorLayer();
354 } else if (!cursorLayer->isVisible() && !forceSoftwareCursor) {
355 // this is for the case that the cursor wasn't visible because it was on a different output before
356 hardwareCursor = updateCursorLayer();
357 }
358 }
359 cursorLayer->setVisible(cursor->isOnOutput(output) && !hardwareCursor);
360 cursorLayer->setGeometry(outputLocalRect);
361 cursorLayer->addRepaintFull();
362 };
363 updateCursorLayer();
364 connect(output, &Output::geometryChanged, cursorLayer, updateCursorLayer);
365 connect(Cursors::self(), &Cursors::currentCursorChanged, cursorLayer, updateCursorLayer);
366 connect(Cursors::self(), &Cursors::hiddenChanged, cursorLayer, updateCursorLayer);
367 connect(Cursors::self(), &Cursors::positionChanged, cursorLayer, moveCursorLayer);
368
369 addSuperLayer(workspaceLayer);
370}
371
372void WaylandCompositor::removeOutput(Output *output)
373{
374 if (output->isPlaceholder()) {
375 return;
376 }
377 removeSuperLayer(m_superlayers[output->renderLoop()]);
378}
379
380} // namespace KWin
381
382#include "moc_compositor_wayland.cpp"
void compositingToggled(bool active)
static Compositor * s_compositor
Definition compositor.h:128
std::unique_ptr< RenderBackend > m_backend
Definition compositor.h:154
std::unique_ptr< CursorScene > m_cursorScene
Definition compositor.h:153
void aboutToToggleCompositing()
void removeSuperLayer(RenderLayer *layer)
RenderBackend * backend() const
Definition compositor.h:68
std::unique_ptr< WorkspaceScene > m_scene
Definition compositor.h:152
QHash< RenderLoop *, RenderLayer * > m_superlayers
Definition compositor.h:155
void addSuperLayer(RenderLayer *layer)
static Cursors * self()
Definition cursor.cpp:35
void positionChanged(Cursor *cursor, const QPointF &position)
void currentCursorChanged(Cursor *cursor)
Cursor * currentCursor() const
Definition cursor.h:285
void hiddenChanged()
static GLPlatform * instance()
Definition glplatform.h:394
bool isIntel() const
The OpenGLBackend creates and holds the OpenGL context and is responsible for Texture from Pixmap.
int compositingMode
Definition options.h:181
bool isPlaceholder() const
Definition output.cpp:662
qreal scale() const
Definition output.cpp:455
void geometryChanged()
OutputTransform transform() const
Definition output.cpp:369
QRect mapFromGlobal(const QRect &rect) const
Definition output.cpp:425
virtual RenderLoop * renderLoop() const =0
QRectF rectF() const
Definition output.h:489
virtual bool updateCursorLayer()
Definition output.cpp:727
QSize pixelSize() const
Definition output.cpp:485
QSizeF map(const QSizeF &size) const
Definition output.cpp:242
static WaylandCompositor * create(QObject *parent=nullptr)
void outputAdded(KWin::Output *)
static Workspace * self()
Definition workspace.h:91
QList< Output * > outputs() const
Definition workspace.h:762
const QList< Window * > windows() const
Definition workspace.h:248
void outputRemoved(KWin::Output *)
KWIN_EXPORT QRect infiniteRegion()
Definition globals.h:234
Session::Type type
Definition session.cpp:17
bool hasGLVersion(int major, int minor, int release)
Definition glutils.cpp:133
Workspace * workspace()
Definition workspace.h:830
@ QPainterCompositing
Definition globals.h:39
@ NoCompositing
Definition globals.h:29
@ OpenGLCompositing
Definition globals.h:37
Options * options
Definition main.cpp:73
KWIN_EXPORT QRectF scaledRect(const QRectF &rect, qreal scale)
Definition globals.h:243
EffectsHandler * effects