KWin
Loading...
Searching...
No Matches
compositor_x11.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_x11.h"
11#include "core/outputbackend.h"
12#include "core/overlaywindow.h"
13#include "core/renderbackend.h"
14#include "core/renderlayer.h"
16#include "opengl/glplatform.h"
17#include "options.h"
21#include "utils/common.h"
22#include "utils/xcbutils.h"
23#include "window.h"
24#include "workspace.h"
25#include "x11syncmanager.h"
26
27#include <KCrash>
28#include <KGlobalAccel>
29#include <KLocalizedString>
30#include <KSelectionOwner>
31
32#include <QAction>
33#include <QOpenGLContext>
34#include <QThread>
35
37
38namespace KWin
39{
40
41class X11CompositorSelectionOwner : public KSelectionOwner
42{
43 Q_OBJECT
44
45public:
46 X11CompositorSelectionOwner(const char *selection)
47 : KSelectionOwner(selection, kwinApp()->x11Connection(), kwinApp()->x11RootWindow())
48 , m_owning(false)
49 {
50 connect(this, &X11CompositorSelectionOwner::lostOwnership, this, [this]() {
51 m_owning = false;
52 });
53 }
54 bool owning() const
55 {
56 return m_owning;
57 }
58 void setOwning(bool own)
59 {
60 m_owning = own;
61 }
62
63private:
64 bool m_owning;
65};
66
68{
69 Q_ASSERT(!s_compositor);
70 auto *compositor = new X11Compositor(parent);
71 s_compositor = compositor;
72 return compositor;
73}
74
75X11Compositor::X11Compositor(QObject *parent)
76 : Compositor(parent)
77 , m_suspended(options->isUseCompositing() ? NoReasonSuspend : UserSuspend)
78{
79 if (qEnvironmentVariableIsSet("KWIN_MAX_FRAMES_TESTED")) {
80 m_framesToTestForSafety = qEnvironmentVariableIntValue("KWIN_MAX_FRAMES_TESTED");
81 }
82
83 connect(options, &Options::configChanged, this, [this]() {
84 if (m_suspended) {
85 stop();
86 } else {
88 }
89 });
90
91 m_releaseSelectionTimer.setSingleShot(true);
92 m_releaseSelectionTimer.setInterval(2000);
93 connect(&m_releaseSelectionTimer, &QTimer::timeout, this, &X11Compositor::releaseCompositorSelection);
94
95 QAction *toggleAction = new QAction(this);
96 toggleAction->setProperty("componentName", QStringLiteral("kwin"));
97 toggleAction->setObjectName("Suspend Compositing");
98 toggleAction->setText(i18n("Suspend Compositing"));
99 KGlobalAccel::self()->setDefaultShortcut(toggleAction, QList<QKeySequence>() << (Qt::SHIFT | Qt::ALT | Qt::Key_F12));
100 KGlobalAccel::self()->setShortcut(toggleAction, QList<QKeySequence>() << (Qt::SHIFT | Qt::ALT | Qt::Key_F12));
101 connect(toggleAction, &QAction::triggered, this, &X11Compositor::toggle);
102}
103
105{
106 Q_EMIT aboutToDestroy();
107 if (m_openGLFreezeProtectionThread) {
108 m_openGLFreezeProtectionThread->quit();
109 m_openGLFreezeProtectionThread->wait();
110 }
111 stop(); // this can't be called in the destructor of Compositor
112 destroyCompositorSelection();
113}
114
116{
117 return m_syncManager.get();
118}
119
121{
122 if (m_suspended) {
123 // Direct user call; clear all bits.
125 } else {
126 // But only set the user one (sufficient to suspend).
128 }
129}
130
132{
133 // Resume compositing if suspended.
134 m_suspended = NoReasonSuspend;
135 m_inhibitors.clear();
137}
138
140{
141 Q_ASSERT(reason != NoReasonSuspend);
142 m_suspended |= reason;
143 stop();
144}
145
147{
148 Q_ASSERT(reason != NoReasonSuspend);
149 m_suspended &= ~reason;
150 if (reason & BlockRuleSuspend) {
151 m_inhibitors.clear();
152 }
153 start();
154}
155
156void X11Compositor::destroyCompositorSelection()
157{
158 m_selectionOwner.reset();
159}
160
161void X11Compositor::releaseCompositorSelection()
162{
163 switch (m_state) {
164 case State::On:
165 // We are compositing at the moment. Don't release.
166 break;
167 case State::Off:
168 if (m_selectionOwner) {
169 qCDebug(KWIN_CORE) << "Releasing compositor selection";
170 m_selectionOwner->setOwning(false);
171 m_selectionOwner->release();
172 }
173 break;
174 case State::Starting:
175 case State::Stopping:
176 // Still starting or shutting down the compositor. Starting might fail
177 // or after stopping a restart might follow. So test again later on.
178 m_releaseSelectionTimer.start();
179 break;
180 }
181}
182
183bool X11Compositor::attemptOpenGLCompositing()
184{
185 // Some broken drivers crash on glXQuery() so to prevent constant KWin crashes:
187 qCWarning(KWIN_CORE) << "KWin has detected that your OpenGL library is unsafe to use";
188 return false;
189 }
190
192 auto safePointScope = qScopeGuard([this]() {
194 });
195
196 std::unique_ptr<OpenGLBackend> backend = kwinApp()->outputBackend()->createOpenGLBackend();
197 if (!backend) {
198 return false;
199 }
200 if (!backend->isFailed()) {
201 backend->init();
202 }
203 if (backend->isFailed()) {
204 return false;
205 }
206
207 const QByteArray forceEnv = qgetenv("KWIN_COMPOSE");
208 if (!forceEnv.isEmpty()) {
209 if (qstrcmp(forceEnv, "O2") == 0 || qstrcmp(forceEnv, "O2ES") == 0) {
210 qCDebug(KWIN_CORE) << "OpenGL 2 compositing enforced by environment variable";
211 } else {
212 // OpenGL 2 disabled by environment variable
213 return false;
214 }
215 } else {
216 if (GLPlatform::instance()->recommendedCompositor() < OpenGLCompositing) {
217 qCDebug(KWIN_CORE) << "Driver does not recommend OpenGL compositing";
218 return false;
219 }
220 }
221
222 // We only support the OpenGL 2+ shader API, not GL_ARB_shader_objects
223 if (!hasGLVersion(2, 0)) {
224 qCDebug(KWIN_CORE) << "OpenGL 2.0 is not supported";
225 return false;
226 }
227
228 m_scene = std::make_unique<WorkspaceSceneOpenGL>(backend.get());
229 m_backend = std::move(backend);
230
231 // set strict binding
234 }
235
236 qCDebug(KWIN_CORE) << "OpenGL compositing has been successfully initialized";
237 return true;
238}
239
241{
242 if (m_suspended) {
243 QStringList reasons;
244 if (m_suspended & UserSuspend) {
245 reasons << QStringLiteral("Disabled by User");
246 }
247 if (m_suspended & BlockRuleSuspend) {
248 reasons << QStringLiteral("Disabled by Window");
249 }
250 qCInfo(KWIN_CORE) << "Compositing is suspended, reason:" << reasons;
251 return;
252 } else if (!compositingPossible()) {
253 qCWarning(KWIN_CORE) << "Compositing is not possible";
254 return;
255 }
256
257 if (kwinApp()->isTerminating()) {
258 return;
259 }
260 if (m_state != State::Off) {
261 return;
262 }
263
266
267 // Claim special _NET_WM_CM_S0 selection and redirect child windows of the root window.
268 if (!m_selectionOwner) {
269 m_selectionOwner = std::make_unique<X11CompositorSelectionOwner>("_NET_WM_CM_S0");
270 connect(m_selectionOwner.get(), &X11CompositorSelectionOwner::lostOwnership, this, &X11Compositor::stop);
271 }
272 if (!m_selectionOwner->owning()) {
273 // Force claim ownership.
274 m_selectionOwner->claim(true);
275 m_selectionOwner->setOwning(true);
276 }
277
278 xcb_composite_redirect_subwindows(kwinApp()->x11Connection(),
279 kwinApp()->x11RootWindow(),
280 XCB_COMPOSITE_REDIRECT_MANUAL);
281
282 // Decide what compositing types can be used.
283 QList<CompositingType> candidateCompositors = kwinApp()->outputBackend()->supportedCompositors();
284 const auto userConfigIt = std::find(candidateCompositors.begin(), candidateCompositors.end(), options->compositingMode());
285 if (userConfigIt != candidateCompositors.end()) {
286 candidateCompositors.erase(userConfigIt);
287 candidateCompositors.prepend(options->compositingMode());
288 } else {
289 qCWarning(KWIN_CORE) << "Configured compositor not supported by Platform. Falling back to defaults";
290 }
291
292 for (auto type : std::as_const(candidateCompositors)) {
293 bool stop = false;
294 switch (type) {
296 qCDebug(KWIN_CORE) << "Attempting to load the OpenGL scene";
297 stop = attemptOpenGLCompositing();
298 break;
300 qCDebug(KWIN_CORE) << "QPainter compositing is unsupported on X11";
301 break;
302 case NoCompositing:
303 qCDebug(KWIN_CORE) << "Starting without compositing...";
304 stop = true;
305 break;
306 }
307
308 if (stop) {
309 break;
310 } else if (qEnvironmentVariableIsSet("KWIN_COMPOSE")) {
311 qCCritical(KWIN_CORE) << "Could not fulfill the requested compositing mode in KWIN_COMPOSE:" << type << ". Exiting.";
312 qApp->quit();
313 }
314 }
315
316 if (!m_backend) {
318
319 xcb_composite_unredirect_subwindows(kwinApp()->x11Connection(),
320 kwinApp()->x11RootWindow(),
321 XCB_COMPOSITE_REDIRECT_MANUAL);
322 if (m_selectionOwner) {
323 m_selectionOwner->setOwning(false);
324 m_selectionOwner->release();
325 }
326 return;
327 }
328
329 Q_EMIT sceneCreated();
330
331 kwinApp()->setX11CompositeWindow(backend()->overlayWindow()->window());
332
333 auto workspaceLayer = new RenderLayer(workspace()->outputs()[0]->renderLoop());
334 workspaceLayer->setDelegate(std::make_unique<SceneDelegate>(m_scene.get(), nullptr));
335 workspaceLayer->setGeometry(workspace()->geometry());
336 connect(workspace(), &Workspace::geometryChanged, workspaceLayer, [workspaceLayer]() {
337 workspaceLayer->setGeometry(workspace()->geometry());
338 });
339 addSuperLayer(workspaceLayer);
340
342
343 const auto windows = workspace()->windows();
344 for (Window *window : windows) {
345 window->setupCompositing();
346 }
347
348 // Sets also the 'effects' pointer.
349 kwinApp()->createEffectsHandler(this, m_scene.get());
350
351 m_syncManager.reset(X11SyncManager::create());
352 if (m_releaseSelectionTimer.isActive()) {
353 m_releaseSelectionTimer.stop();
354 }
355
356 Q_EMIT compositingToggled(true);
357}
358
360{
362 return;
363 }
366
367 m_releaseSelectionTimer.start();
368
369 // Some effects might need access to effect windows when they are about to
370 // be destroyed, for example to unreference deleted windows, so we have to
371 // make sure that effect windows outlive effects.
372 delete effects;
373 effects = nullptr;
374
375 if (Workspace::self()) {
376 const auto windows = workspace()->windows();
377 for (Window *window : windows) {
378 window->finishCompositing();
379 }
380 xcb_composite_unredirect_subwindows(kwinApp()->x11Connection(),
381 kwinApp()->x11RootWindow(),
382 XCB_COMPOSITE_REDIRECT_MANUAL);
383 }
384
385 if (m_backend->compositingType() == OpenGLCompositing) {
386 // some layers need a context current for destruction
387 static_cast<OpenGLBackend *>(m_backend.get())->makeCurrent();
388 }
389
390 const auto superlayers = m_superlayers;
391 for (auto it = superlayers.begin(); it != superlayers.end(); ++it) {
392 removeSuperLayer(*it);
393 }
394
395 m_syncManager.reset();
396 m_scene.reset();
397 m_backend.reset();
398
399 kwinApp()->setX11CompositeWindow(XCB_WINDOW_NONE);
400
402 Q_EMIT compositingToggled(false);
403}
404
406{
407 if (backend()->overlayWindow() && !backend()->overlayWindow()->isVisible()) {
408 // Return since nothing is visible.
409 return;
410 }
411
412 QList<Window *> windows = workspace()->stackingOrder();
413 QList<SurfaceItemX11 *> dirtyItems;
414
415 // Reset the damage state of each window and fetch the damage region
416 // without waiting for a reply
417 for (Window *window : std::as_const(windows)) {
418 SurfaceItemX11 *surfaceItem = static_cast<SurfaceItemX11 *>(window->surfaceItem());
419 if (surfaceItem->fetchDamage()) {
420 dirtyItems.append(surfaceItem);
421 }
422 }
423
424 if (dirtyItems.count() > 0) {
425 if (m_syncManager) {
426 m_syncManager->triggerFence();
427 }
428 xcb_flush(kwinApp()->x11Connection());
429 }
430
431 // Get the replies
432 for (SurfaceItemX11 *item : std::as_const(dirtyItems)) {
433 item->waitForDamage();
434 }
435
436 if (m_framesToTestForSafety > 0 && (backend()->compositingType() & OpenGLCompositing)) {
438 }
439
440 Compositor::composite(renderLoop);
441
442 if (m_syncManager) {
443 if (!m_syncManager->endFrame()) {
444 qCDebug(KWIN_CORE) << "Aborting explicit synchronization with the X command stream.";
445 qCDebug(KWIN_CORE) << "Future frames will be rendered unsynchronized.";
446 m_syncManager.reset();
447 }
448 }
449
450 if (m_framesToTestForSafety > 0) {
451 if (backend()->compositingType() & OpenGLCompositing) {
453 }
454 m_framesToTestForSafety--;
455 if (m_framesToTestForSafety == 0 && (backend()->compositingType() & OpenGLCompositing)) {
457 }
458 }
459}
460
462{
463 m_inhibitors.insert(window);
464 // Do NOT attempt to call suspend(true) from within the eventchain!
465 if (!(m_suspended & BlockRuleSuspend)) {
466 QMetaObject::invokeMethod(
467 this, [this]() {
469 },
470 Qt::QueuedConnection);
471 }
472}
473
475{
476 if (!m_inhibitors.remove(window)) {
477 return;
478 }
479 if (m_suspended & BlockRuleSuspend) {
480 if (m_inhibitors.isEmpty()) {
481 // Do NOT attempt to call suspend(false) from within the eventchain!
482 QMetaObject::invokeMethod(
483 this, [this]() {
485 },
486 Qt::QueuedConnection);
487 }
488 }
489}
490
492{
493 return qobject_cast<X11Compositor *>(Compositor::self());
494}
495
497{
498 auto timestamp = KConfigGroup(kwinApp()->config(), QStringLiteral("Compositing")).readEntry(QLatin1String("LastFailureTimestamp"), 0);
499 if (timestamp > 0) {
500 if (QDateTime::currentSecsSinceEpoch() - timestamp < 60) {
501 return true;
502 }
503 }
504
505 return false;
506}
507
509{
510 // first off, check whether we figured that we'll crash on detection because of a buggy driver
511 KConfigGroup gl_workaround_group(kwinApp()->config(), QStringLiteral("Compositing"));
512 if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && openGLCompositingIsBroken()) {
513 return i18n("<b>OpenGL compositing (the default) has crashed KWin in the past.</b><br>"
514 "This was most likely due to a driver bug."
515 "<p>If you think that you have meanwhile upgraded to a stable driver,<br>"
516 "you can reset this protection but <b>be aware that this might result in an immediate crash!</b></p>");
517 }
518
519 if (!Xcb::Extensions::self()->isCompositeAvailable() || !Xcb::Extensions::self()->isDamageAvailable()) {
520 return i18n("Required X extensions (XComposite and XDamage) are not available.");
521 }
522 if (!Xcb::Extensions::self()->hasGlx()) {
523 return i18n("GLX/OpenGL is not available.");
524 }
525 return QString();
526}
527
529{
530 // first off, check whether we figured that we'll crash on detection because of a buggy driver
531 KConfigGroup gl_workaround_group(kwinApp()->config(), QStringLiteral("Compositing"));
532 if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && openGLCompositingIsBroken()) {
533 qCWarning(KWIN_CORE) << "Compositing disabled: video driver seems unstable. If you think it's a false positive, please try again in a few minutes.";
534 return false;
535 }
536
537 if (!Xcb::Extensions::self()->isCompositeAvailable()) {
538 qCWarning(KWIN_CORE) << "Compositing disabled: no composite extension available";
539 return false;
540 }
541 if (!Xcb::Extensions::self()->isDamageAvailable()) {
542 qCWarning(KWIN_CORE) << "Compositing disabled: no damage extension available";
543 return false;
544 }
545 if (Xcb::Extensions::self()->hasGlx()) {
546 return true;
547 }
548 if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) {
549 return true;
550 } else if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) {
551 return true;
552 }
553 qCWarning(KWIN_CORE) << "Compositing disabled: no OpenGL support";
554 return false;
555}
556
558{
559 auto group = KConfigGroup(kwinApp()->config(), QStringLiteral("Compositing"));
560 switch (safePoint) {
562 // Explicitly write the failure timestamp so that if we crash during
563 // OpenGL init, we know we should not try again.
564 group.writeEntry(QLatin1String("LastFailureTimestamp"), QDateTime::currentSecsSinceEpoch());
565 group.sync();
566 // Deliberately continue with PreFrame
567 Q_FALLTHROUGH();
569 if (m_openGLFreezeProtectionThread == nullptr) {
570 Q_ASSERT(m_openGLFreezeProtection == nullptr);
571 m_openGLFreezeProtectionThread = std::make_unique<QThread>();
572 m_openGLFreezeProtectionThread->setObjectName("FreezeDetector");
573 m_openGLFreezeProtectionThread->start();
574 m_openGLFreezeProtection = std::make_unique<QTimer>();
575 m_openGLFreezeProtection->setInterval(15000);
576 m_openGLFreezeProtection->setSingleShot(true);
577 m_openGLFreezeProtection->start();
578 const QString configName = kwinApp()->config()->name();
579 m_openGLFreezeProtection->moveToThread(m_openGLFreezeProtectionThread.get());
580 connect(
581 m_openGLFreezeProtection.get(), &QTimer::timeout, m_openGLFreezeProtection.get(),
582 [configName] {
583 auto group = KConfigGroup(KSharedConfig::openConfig(configName), QStringLiteral("Compositing"));
584 group.writeEntry(QLatin1String("LastFailureTimestamp"), QDateTime::currentSecsSinceEpoch());
585 group.sync();
586 KCrash::setDrKonqiEnabled(false);
587 qFatal("Freeze in OpenGL initialization detected");
588 },
589 Qt::DirectConnection);
590 } else {
591 Q_ASSERT(m_openGLFreezeProtection);
592 QMetaObject::invokeMethod(m_openGLFreezeProtection.get(), QOverload<>::of(&QTimer::start), Qt::QueuedConnection);
593 }
594 break;
596 group.deleteEntry(QLatin1String("LastFailureTimestamp"));
597 group.sync();
598 // Deliberately continue with PostFrame
599 Q_FALLTHROUGH();
601 QMetaObject::invokeMethod(m_openGLFreezeProtection.get(), &QTimer::stop, Qt::QueuedConnection);
602 break;
604 m_openGLFreezeProtectionThread->quit();
605 m_openGLFreezeProtectionThread->wait();
606 m_openGLFreezeProtectionThread.reset();
607 m_openGLFreezeProtection.reset();
608 break;
609 }
610}
611
612} // namespace KWin
613
614#include "compositor_x11.moc"
615#include "moc_compositor_x11.cpp"
void compositingToggled(bool active)
static Compositor * s_compositor
Definition compositor.h:128
std::unique_ptr< RenderBackend > m_backend
Definition compositor.h:154
void aboutToToggleCompositing()
void removeSuperLayer(RenderLayer *layer)
virtual void composite(RenderLoop *renderLoop)
virtual void reinitialize()
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 Compositor * self()
static GLPlatform * instance()
Definition glplatform.h:394
The OpenGLBackend creates and holds the OpenGL context and is responsible for Texture from Pixmap.
int compositingMode
Definition options.h:181
bool isGlStrictBindingFollowsDriver() const
Definition options.h:661
void configChanged()
void setGlStrictBinding(bool glStrictBinding)
Definition options.cpp:584
const QList< Window * > & stackingOrder() const
Definition workspace.h:788
static Workspace * self()
Definition workspace.h:91
const QList< Window * > windows() const
Definition workspace.h:248
void geometryChanged()
void reinitialize() override
QString compositingNotPossibleReason() const override
void inhibit(Window *window) override
bool openGLCompositingIsBroken() const override
void resume(SuspendReason reason)
Resumes the Compositor if it is currently suspended.
void start() override
void createOpenGLSafePoint(OpenGLSafePoint safePoint)
static X11Compositor * create(QObject *parent=nullptr)
static X11Compositor * self()
X11SyncManager * syncManager() const
bool compositingPossible() const override
void uninhibit(Window *window) override
void composite(RenderLoop *renderLoop) override
void suspend(SuspendReason reason)
Suspends the Compositor if it is currently active.
X11CompositorSelectionOwner(const char *selection)
static X11SyncManager * create()
static Extensions * self()
Definition xcbutils.cpp:346
Q_DECLARE_METATYPE(KWin::SwitchEvent::State)
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
EffectsHandler * effects