File indexing completed on 2024-12-01 13:37:21
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 #include "composite.h" 0010 0011 #include <config-kwin.h> 0012 0013 #include "core/output.h" 0014 #include "core/outputbackend.h" 0015 #include "core/outputlayer.h" 0016 #include "core/overlaywindow.h" 0017 #include "core/renderlayer.h" 0018 #include "core/renderloop.h" 0019 #include "cursordelegate_opengl.h" 0020 #include "cursordelegate_qpainter.h" 0021 #include "dbusinterface.h" 0022 #include "decorations/decoratedclient.h" 0023 #include "deleted.h" 0024 #include "effects.h" 0025 #include "ftrace.h" 0026 #include "internalwindow.h" 0027 #include "openglbackend.h" 0028 #include "qpainterbackend.h" 0029 #include "scene/cursorscene.h" 0030 #include "scene/itemrenderer_opengl.h" 0031 #include "scene/itemrenderer_qpainter.h" 0032 #include "scene/surfaceitem_x11.h" 0033 #include "scene/workspacescene_opengl.h" 0034 #include "scene/workspacescene_qpainter.h" 0035 #include "shadow.h" 0036 #include "unmanaged.h" 0037 #include "useractions.h" 0038 #include "utils/common.h" 0039 #include "utils/xcbutils.h" 0040 #include "wayland/surface_interface.h" 0041 #include "wayland_server.h" 0042 #include "workspace.h" 0043 #include "x11syncmanager.h" 0044 #include "x11window.h" 0045 0046 #include <kwinglplatform.h> 0047 #include <kwingltexture.h> 0048 0049 #include <KCrash> 0050 #include <KGlobalAccel> 0051 #include <KLocalizedString> 0052 #if KWIN_BUILD_NOTIFICATIONS 0053 #include <KNotification> 0054 #endif 0055 #include <KSelectionOwner> 0056 0057 #include <QDateTime> 0058 #include <QFutureWatcher> 0059 #include <QMenu> 0060 #include <QOpenGLContext> 0061 #include <QQuickWindow> 0062 #include <QTextStream> 0063 #include <QTimerEvent> 0064 #include <QtConcurrentRun> 0065 0066 #include <xcb/composite.h> 0067 #include <xcb/damage.h> 0068 0069 #include <cstdio> 0070 0071 Q_DECLARE_METATYPE(KWin::X11Compositor::SuspendReason) 0072 0073 namespace KWin 0074 { 0075 0076 Compositor *Compositor::s_compositor = nullptr; 0077 Compositor *Compositor::self() 0078 { 0079 return s_compositor; 0080 } 0081 0082 WaylandCompositor *WaylandCompositor::create(QObject *parent) 0083 { 0084 Q_ASSERT(!s_compositor); 0085 auto *compositor = new WaylandCompositor(parent); 0086 s_compositor = compositor; 0087 return compositor; 0088 } 0089 X11Compositor *X11Compositor::create(QObject *parent) 0090 { 0091 Q_ASSERT(!s_compositor); 0092 auto *compositor = new X11Compositor(parent); 0093 s_compositor = compositor; 0094 return compositor; 0095 } 0096 0097 class CompositorSelectionOwner : public KSelectionOwner 0098 { 0099 Q_OBJECT 0100 public: 0101 CompositorSelectionOwner(const char *selection) 0102 : KSelectionOwner(selection, kwinApp()->x11Connection(), kwinApp()->x11RootWindow()) 0103 , m_owning(false) 0104 { 0105 connect(this, &CompositorSelectionOwner::lostOwnership, 0106 this, [this]() { 0107 m_owning = false; 0108 }); 0109 } 0110 bool owning() const 0111 { 0112 return m_owning; 0113 } 0114 void setOwning(bool own) 0115 { 0116 m_owning = own; 0117 } 0118 0119 private: 0120 bool m_owning; 0121 }; 0122 0123 Compositor::Compositor(QObject *workspace) 0124 : QObject(workspace) 0125 { 0126 connect(options, &Options::configChanged, this, &Compositor::configChanged); 0127 connect(options, &Options::animationSpeedChanged, this, &Compositor::configChanged); 0128 0129 // 2 sec which should be enough to restart the compositor. 0130 static const int compositorLostMessageDelay = 2000; 0131 0132 m_releaseSelectionTimer.setSingleShot(true); 0133 m_releaseSelectionTimer.setInterval(compositorLostMessageDelay); 0134 connect(&m_releaseSelectionTimer, &QTimer::timeout, 0135 this, &Compositor::releaseCompositorSelection); 0136 0137 m_unusedSupportPropertyTimer.setInterval(compositorLostMessageDelay); 0138 m_unusedSupportPropertyTimer.setSingleShot(true); 0139 connect(&m_unusedSupportPropertyTimer, &QTimer::timeout, 0140 this, &Compositor::deleteUnusedSupportProperties); 0141 0142 // Delay the call to start by one event cycle. 0143 // The ctor of this class is invoked from the Workspace ctor, that means before 0144 // Workspace is completely constructed, so calling Workspace::self() would result 0145 // in undefined behavior. This is fixed by using a delayed invocation. 0146 QTimer::singleShot(0, this, &Compositor::start); 0147 0148 connect(kwinApp(), &Application::x11ConnectionChanged, this, &Compositor::initializeX11); 0149 connect(kwinApp(), &Application::x11ConnectionAboutToBeDestroyed, this, &Compositor::cleanupX11); 0150 0151 // register DBus 0152 new CompositorDBusInterface(this); 0153 FTraceLogger::create(); 0154 } 0155 0156 Compositor::~Compositor() 0157 { 0158 deleteUnusedSupportProperties(); 0159 destroyCompositorSelection(); 0160 s_compositor = nullptr; 0161 } 0162 0163 bool Compositor::attemptOpenGLCompositing() 0164 { 0165 // Some broken drivers crash on glXQuery() so to prevent constant KWin crashes: 0166 if (openGLCompositingIsBroken()) { 0167 qCWarning(KWIN_CORE) << "KWin has detected that your OpenGL library is unsafe to use"; 0168 return false; 0169 } 0170 0171 createOpenGLSafePoint(OpenGLSafePoint::PreInit); 0172 auto safePointScope = qScopeGuard([this]() { 0173 createOpenGLSafePoint(OpenGLSafePoint::PostInit); 0174 }); 0175 0176 std::unique_ptr<OpenGLBackend> backend = kwinApp()->outputBackend()->createOpenGLBackend(); 0177 if (!backend) { 0178 return false; 0179 } 0180 if (!backend->isFailed()) { 0181 backend->init(); 0182 } 0183 if (backend->isFailed()) { 0184 return false; 0185 } 0186 0187 const QByteArray forceEnv = qgetenv("KWIN_COMPOSE"); 0188 if (!forceEnv.isEmpty()) { 0189 if (qstrcmp(forceEnv, "O2") == 0 || qstrcmp(forceEnv, "O2ES") == 0) { 0190 qCDebug(KWIN_CORE) << "OpenGL 2 compositing enforced by environment variable"; 0191 } else { 0192 // OpenGL 2 disabled by environment variable 0193 return false; 0194 } 0195 } else { 0196 if (!backend->isDirectRendering()) { 0197 return false; 0198 } 0199 if (GLPlatform::instance()->recommendedCompositor() < OpenGLCompositing) { 0200 qCDebug(KWIN_CORE) << "Driver does not recommend OpenGL compositing"; 0201 return false; 0202 } 0203 } 0204 0205 // We only support the OpenGL 2+ shader API, not GL_ARB_shader_objects 0206 if (!hasGLVersion(2, 0)) { 0207 qCDebug(KWIN_CORE) << "OpenGL 2.0 is not supported"; 0208 return false; 0209 } 0210 0211 m_scene = std::make_unique<WorkspaceSceneOpenGL>(backend.get()); 0212 m_cursorScene = std::make_unique<CursorScene>(std::make_unique<ItemRendererOpenGL>()); 0213 m_backend = std::move(backend); 0214 0215 // set strict binding 0216 if (options->isGlStrictBindingFollowsDriver()) { 0217 options->setGlStrictBinding(!GLPlatform::instance()->supports(LooseBinding)); 0218 } 0219 0220 qCDebug(KWIN_CORE) << "OpenGL compositing has been successfully initialized"; 0221 return true; 0222 } 0223 0224 bool Compositor::attemptQPainterCompositing() 0225 { 0226 std::unique_ptr<QPainterBackend> backend(kwinApp()->outputBackend()->createQPainterBackend()); 0227 if (!backend || backend->isFailed()) { 0228 return false; 0229 } 0230 0231 m_scene = std::make_unique<WorkspaceSceneQPainter>(backend.get()); 0232 m_cursorScene = std::make_unique<CursorScene>(std::make_unique<ItemRendererQPainter>()); 0233 m_backend = std::move(backend); 0234 0235 qCDebug(KWIN_CORE) << "QPainter compositing has been successfully initialized"; 0236 return true; 0237 } 0238 0239 bool Compositor::setupStart() 0240 { 0241 if (kwinApp()->isTerminating()) { 0242 // Don't start while KWin is terminating. An event to restart might be lingering 0243 // in the event queue due to graphics reset. 0244 return false; 0245 } 0246 if (m_state != State::Off) { 0247 return false; 0248 } 0249 m_state = State::Starting; 0250 0251 options->reloadCompositingSettings(true); 0252 0253 initializeX11(); 0254 0255 // There might still be a deleted around, needs to be cleared before 0256 // creating the scene (BUG 333275). 0257 if (Workspace::self()) { 0258 while (!Workspace::self()->deletedList().isEmpty()) { 0259 Workspace::self()->deletedList().first()->discard(); 0260 } 0261 } 0262 0263 Q_EMIT aboutToToggleCompositing(); 0264 0265 const QVector<CompositingType> availableCompositors = kwinApp()->outputBackend()->supportedCompositors(); 0266 QVector<CompositingType> candidateCompositors; 0267 0268 // If compositing has been restarted, try to use the last used compositing type. 0269 if (m_selectedCompositor != NoCompositing) { 0270 candidateCompositors.append(m_selectedCompositor); 0271 } else { 0272 candidateCompositors = availableCompositors; 0273 0274 const auto userConfigIt = std::find(candidateCompositors.begin(), candidateCompositors.end(), options->compositingMode()); 0275 if (userConfigIt != candidateCompositors.end()) { 0276 candidateCompositors.erase(userConfigIt); 0277 candidateCompositors.prepend(options->compositingMode()); 0278 } else { 0279 qCWarning(KWIN_CORE) << "Configured compositor not supported by Platform. Falling back to defaults"; 0280 } 0281 } 0282 0283 for (auto type : std::as_const(candidateCompositors)) { 0284 bool stop = false; 0285 switch (type) { 0286 case OpenGLCompositing: 0287 qCDebug(KWIN_CORE) << "Attempting to load the OpenGL scene"; 0288 stop = attemptOpenGLCompositing(); 0289 break; 0290 case QPainterCompositing: 0291 qCDebug(KWIN_CORE) << "Attempting to load the QPainter scene"; 0292 stop = attemptQPainterCompositing(); 0293 break; 0294 case NoCompositing: 0295 qCDebug(KWIN_CORE) << "Starting without compositing..."; 0296 stop = true; 0297 break; 0298 } 0299 0300 if (stop) { 0301 break; 0302 } 0303 } 0304 0305 if (!m_backend) { 0306 m_state = State::Off; 0307 0308 if (auto *con = kwinApp()->x11Connection()) { 0309 xcb_composite_unredirect_subwindows(con, kwinApp()->x11RootWindow(), 0310 XCB_COMPOSITE_REDIRECT_MANUAL); 0311 } 0312 if (m_selectionOwner) { 0313 m_selectionOwner->setOwning(false); 0314 m_selectionOwner->release(); 0315 } 0316 if (!availableCompositors.contains(NoCompositing)) { 0317 qCCritical(KWIN_CORE) << "The used windowing system requires compositing"; 0318 qCCritical(KWIN_CORE) << "We are going to quit KWin now as it is broken"; 0319 qApp->quit(); 0320 } 0321 return false; 0322 } 0323 0324 m_selectedCompositor = m_backend->compositingType(); 0325 0326 if (!Workspace::self() && m_backend && m_backend->compositingType() == QPainterCompositing) { 0327 // Force Software QtQuick on first startup with QPainter. 0328 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0329 QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software); 0330 #else 0331 QQuickWindow::setGraphicsApi(QSGRendererInterface::Software); 0332 #endif 0333 } 0334 0335 Q_EMIT sceneCreated(); 0336 0337 return true; 0338 } 0339 0340 void Compositor::initializeX11() 0341 { 0342 xcb_connection_t *connection = kwinApp()->x11Connection(); 0343 if (!connection) { 0344 return; 0345 } 0346 0347 if (!m_selectionOwner) { 0348 m_selectionOwner = std::make_unique<CompositorSelectionOwner>("_NET_WM_CM_S0"); 0349 connect(m_selectionOwner.get(), &CompositorSelectionOwner::lostOwnership, this, &Compositor::stop); 0350 } 0351 if (!m_selectionOwner->owning()) { 0352 // Force claim ownership. 0353 m_selectionOwner->claim(true); 0354 m_selectionOwner->setOwning(true); 0355 } 0356 0357 xcb_composite_redirect_subwindows(connection, kwinApp()->x11RootWindow(), 0358 XCB_COMPOSITE_REDIRECT_MANUAL); 0359 } 0360 0361 void Compositor::cleanupX11() 0362 { 0363 m_selectionOwner.reset(); 0364 } 0365 0366 void Compositor::startupWithWorkspace() 0367 { 0368 Q_ASSERT(m_scene); 0369 m_scene->initialize(); 0370 m_cursorScene->initialize(); 0371 0372 const QList<Output *> outputs = workspace()->outputs(); 0373 if (kwinApp()->operationMode() == Application::OperationModeX11) { 0374 auto workspaceLayer = new RenderLayer(outputs.constFirst()->renderLoop()); 0375 workspaceLayer->setDelegate(std::make_unique<SceneDelegate>(m_scene.get())); 0376 workspaceLayer->setGeometry(workspace()->geometry()); 0377 connect(workspace(), &Workspace::geometryChanged, workspaceLayer, [workspaceLayer]() { 0378 workspaceLayer->setGeometry(workspace()->geometry()); 0379 }); 0380 addSuperLayer(workspaceLayer); 0381 } else { 0382 for (Output *output : outputs) { 0383 addOutput(output); 0384 } 0385 connect(workspace(), &Workspace::outputAdded, this, &Compositor::addOutput); 0386 connect(workspace(), &Workspace::outputRemoved, this, &Compositor::removeOutput); 0387 } 0388 0389 m_state = State::On; 0390 0391 for (X11Window *window : Workspace::self()->clientList()) { 0392 window->setupCompositing(); 0393 } 0394 for (Unmanaged *window : Workspace::self()->unmanagedList()) { 0395 window->setupCompositing(); 0396 } 0397 for (InternalWindow *window : workspace()->internalWindows()) { 0398 window->setupCompositing(); 0399 } 0400 0401 if (auto *server = waylandServer()) { 0402 const auto windows = server->windows(); 0403 for (Window *window : windows) { 0404 window->setupCompositing(); 0405 } 0406 } 0407 0408 // Sets also the 'effects' pointer. 0409 kwinApp()->createEffectsHandler(this, m_scene.get()); 0410 0411 Q_EMIT compositingToggled(true); 0412 0413 if (m_releaseSelectionTimer.isActive()) { 0414 m_releaseSelectionTimer.stop(); 0415 } 0416 } 0417 0418 Output *Compositor::findOutput(RenderLoop *loop) const 0419 { 0420 const auto outputs = workspace()->outputs(); 0421 for (Output *output : outputs) { 0422 if (output->renderLoop() == loop) { 0423 return output; 0424 } 0425 } 0426 return nullptr; 0427 } 0428 0429 void Compositor::addOutput(Output *output) 0430 { 0431 Q_ASSERT(kwinApp()->operationMode() != Application::OperationModeX11); 0432 0433 auto workspaceLayer = new RenderLayer(output->renderLoop()); 0434 workspaceLayer->setDelegate(std::make_unique<SceneDelegate>(m_scene.get(), output)); 0435 workspaceLayer->setGeometry(output->rect()); 0436 connect(output, &Output::geometryChanged, workspaceLayer, [output, workspaceLayer]() { 0437 workspaceLayer->setGeometry(output->rect()); 0438 }); 0439 0440 auto cursorLayer = new RenderLayer(output->renderLoop()); 0441 cursorLayer->setVisible(false); 0442 if (m_backend->compositingType() == OpenGLCompositing) { 0443 cursorLayer->setDelegate(std::make_unique<CursorDelegateOpenGL>()); 0444 } else { 0445 cursorLayer->setDelegate(std::make_unique<CursorDelegateQPainter>()); 0446 } 0447 cursorLayer->setParent(workspaceLayer); 0448 cursorLayer->setSuperlayer(workspaceLayer); 0449 0450 auto updateCursorLayer = [output, cursorLayer]() { 0451 const Cursor *cursor = Cursors::self()->currentCursor(); 0452 const QRect layerRect = output->mapFromGlobal(cursor->geometry()); 0453 bool usesHardwareCursor = false; 0454 if (!Cursors::self()->isCursorHidden()) { 0455 usesHardwareCursor = output->setCursor(cursor->source()) && output->moveCursor(layerRect.topLeft()); 0456 } else { 0457 usesHardwareCursor = output->setCursor(nullptr); 0458 } 0459 cursorLayer->setVisible(cursor->isOnOutput(output) && !usesHardwareCursor); 0460 cursorLayer->setGeometry(layerRect); 0461 cursorLayer->addRepaintFull(); 0462 }; 0463 auto moveCursorLayer = [output, cursorLayer]() { 0464 const Cursor *cursor = Cursors::self()->currentCursor(); 0465 const QRect layerRect = output->mapFromGlobal(cursor->geometry()); 0466 const bool usesHardwareCursor = output->moveCursor(layerRect.topLeft()); 0467 cursorLayer->setVisible(cursor->isOnOutput(output) && !usesHardwareCursor); 0468 cursorLayer->setGeometry(layerRect); 0469 cursorLayer->addRepaintFull(); 0470 }; 0471 updateCursorLayer(); 0472 connect(output, &Output::geometryChanged, cursorLayer, updateCursorLayer); 0473 connect(Cursors::self(), &Cursors::currentCursorChanged, cursorLayer, updateCursorLayer); 0474 connect(Cursors::self(), &Cursors::hiddenChanged, cursorLayer, updateCursorLayer); 0475 connect(Cursors::self(), &Cursors::positionChanged, cursorLayer, moveCursorLayer); 0476 0477 addSuperLayer(workspaceLayer); 0478 } 0479 0480 void Compositor::removeOutput(Output *output) 0481 { 0482 removeSuperLayer(m_superlayers[output->renderLoop()]); 0483 } 0484 0485 void Compositor::addSuperLayer(RenderLayer *layer) 0486 { 0487 m_superlayers.insert(layer->loop(), layer); 0488 connect(layer->loop(), &RenderLoop::frameRequested, this, &Compositor::handleFrameRequested); 0489 } 0490 0491 void Compositor::removeSuperLayer(RenderLayer *layer) 0492 { 0493 m_superlayers.remove(layer->loop()); 0494 disconnect(layer->loop(), &RenderLoop::frameRequested, this, &Compositor::handleFrameRequested); 0495 delete layer; 0496 } 0497 0498 void Compositor::scheduleRepaint() 0499 { 0500 for (auto it = m_superlayers.constBegin(); it != m_superlayers.constEnd(); ++it) { 0501 it.key()->scheduleRepaint(); 0502 } 0503 } 0504 0505 void Compositor::stop() 0506 { 0507 if (m_state == State::Off || m_state == State::Stopping) { 0508 return; 0509 } 0510 m_state = State::Stopping; 0511 Q_EMIT aboutToToggleCompositing(); 0512 0513 m_releaseSelectionTimer.start(); 0514 0515 // Some effects might need access to effect windows when they are about to 0516 // be destroyed, for example to unreference deleted windows, so we have to 0517 // make sure that effect windows outlive effects. 0518 delete effects; 0519 effects = nullptr; 0520 0521 if (Workspace::self()) { 0522 for (X11Window *window : Workspace::self()->clientList()) { 0523 window->finishCompositing(); 0524 } 0525 for (Unmanaged *window : Workspace::self()->unmanagedList()) { 0526 window->finishCompositing(); 0527 } 0528 for (InternalWindow *window : workspace()->internalWindows()) { 0529 window->finishCompositing(); 0530 } 0531 if (auto *con = kwinApp()->x11Connection()) { 0532 xcb_composite_unredirect_subwindows(con, kwinApp()->x11RootWindow(), 0533 XCB_COMPOSITE_REDIRECT_MANUAL); 0534 } 0535 while (!workspace()->deletedList().isEmpty()) { 0536 workspace()->deletedList().first()->discard(); 0537 } 0538 0539 disconnect(workspace(), &Workspace::outputAdded, this, &Compositor::addOutput); 0540 disconnect(workspace(), &Workspace::outputRemoved, this, &Compositor::removeOutput); 0541 } 0542 0543 if (waylandServer()) { 0544 const QList<Window *> toFinishCompositing = waylandServer()->windows(); 0545 for (Window *window : toFinishCompositing) { 0546 window->finishCompositing(); 0547 } 0548 } 0549 0550 const auto superlayers = m_superlayers; 0551 for (auto it = superlayers.begin(); it != superlayers.end(); ++it) { 0552 removeSuperLayer(*it); 0553 } 0554 0555 m_scene.reset(); 0556 m_cursorScene.reset(); 0557 m_backend.reset(); 0558 0559 m_state = State::Off; 0560 Q_EMIT compositingToggled(false); 0561 } 0562 0563 void Compositor::destroyCompositorSelection() 0564 { 0565 m_selectionOwner.reset(); 0566 } 0567 0568 void Compositor::releaseCompositorSelection() 0569 { 0570 switch (m_state) { 0571 case State::On: 0572 // We are compositing at the moment. Don't release. 0573 break; 0574 case State::Off: 0575 if (m_selectionOwner) { 0576 qCDebug(KWIN_CORE) << "Releasing compositor selection"; 0577 m_selectionOwner->setOwning(false); 0578 m_selectionOwner->release(); 0579 } 0580 break; 0581 case State::Starting: 0582 case State::Stopping: 0583 // Still starting or shutting down the compositor. Starting might fail 0584 // or after stopping a restart might follow. So test again later on. 0585 m_releaseSelectionTimer.start(); 0586 break; 0587 } 0588 } 0589 0590 void Compositor::keepSupportProperty(xcb_atom_t atom) 0591 { 0592 m_unusedSupportProperties.removeAll(atom); 0593 } 0594 0595 void Compositor::removeSupportProperty(xcb_atom_t atom) 0596 { 0597 m_unusedSupportProperties << atom; 0598 m_unusedSupportPropertyTimer.start(); 0599 } 0600 0601 void Compositor::deleteUnusedSupportProperties() 0602 { 0603 if (m_state == State::Starting || m_state == State::Stopping) { 0604 // Currently still maybe restarting the compositor. 0605 m_unusedSupportPropertyTimer.start(); 0606 return; 0607 } 0608 if (auto *con = kwinApp()->x11Connection()) { 0609 for (const xcb_atom_t &atom : std::as_const(m_unusedSupportProperties)) { 0610 // remove property from root window 0611 xcb_delete_property(con, kwinApp()->x11RootWindow(), atom); 0612 } 0613 m_unusedSupportProperties.clear(); 0614 } 0615 } 0616 0617 void Compositor::configChanged() 0618 { 0619 reinitialize(); 0620 } 0621 0622 void Compositor::reinitialize() 0623 { 0624 // Reparse config. Config options will be reloaded by start() 0625 kwinApp()->config()->reparseConfiguration(); 0626 0627 // Restart compositing 0628 stop(); 0629 start(); 0630 0631 if (effects) { // start() may fail 0632 effects->reconfigure(); 0633 } 0634 } 0635 0636 void Compositor::handleFrameRequested(RenderLoop *renderLoop) 0637 { 0638 composite(renderLoop); 0639 } 0640 0641 void Compositor::composite(RenderLoop *renderLoop) 0642 { 0643 if (m_backend->checkGraphicsReset()) { 0644 qCDebug(KWIN_CORE) << "Graphics reset occurred"; 0645 #if KWIN_BUILD_NOTIFICATIONS 0646 KNotification::event(QStringLiteral("graphicsreset"), i18n("Desktop effects were restarted due to a graphics reset")); 0647 #endif 0648 reinitialize(); 0649 return; 0650 } 0651 0652 Output *output = findOutput(renderLoop); 0653 OutputLayer *primaryLayer = m_backend->primaryLayer(output); 0654 fTraceDuration("Paint (", output->name(), ")"); 0655 0656 RenderLayer *superLayer = m_superlayers[renderLoop]; 0657 prePaintPass(superLayer); 0658 superLayer->setOutputLayer(primaryLayer); 0659 0660 SurfaceItem *scanoutCandidate = superLayer->delegate()->scanoutCandidate(); 0661 renderLoop->setFullscreenSurface(scanoutCandidate); 0662 output->setContentType(scanoutCandidate ? scanoutCandidate->contentType() : ContentType::None); 0663 0664 renderLoop->beginFrame(); 0665 bool directScanout = false; 0666 if (scanoutCandidate) { 0667 const auto sublayers = superLayer->sublayers(); 0668 const bool scanoutPossible = std::none_of(sublayers.begin(), sublayers.end(), [](RenderLayer *sublayer) { 0669 return sublayer->isVisible(); 0670 }); 0671 if (scanoutPossible && !output->directScanoutInhibited()) { 0672 directScanout = primaryLayer->scanout(scanoutCandidate); 0673 } 0674 } 0675 0676 if (!directScanout) { 0677 QRegion surfaceDamage = primaryLayer->repaints(); 0678 primaryLayer->resetRepaints(); 0679 preparePaintPass(superLayer, &surfaceDamage); 0680 0681 if (auto beginInfo = primaryLayer->beginFrame()) { 0682 auto &[renderTarget, repaint] = beginInfo.value(); 0683 renderTarget.setDevicePixelRatio(output->scale()); 0684 0685 const QRegion bufferDamage = surfaceDamage.united(repaint).intersected(superLayer->rect()); 0686 primaryLayer->aboutToStartPainting(bufferDamage); 0687 0688 paintPass(superLayer, &renderTarget, bufferDamage); 0689 primaryLayer->endFrame(bufferDamage, surfaceDamage); 0690 } 0691 } 0692 0693 postPaintPass(superLayer); 0694 renderLoop->endFrame(); 0695 0696 m_backend->present(output); 0697 0698 // TODO: Put it inside the cursor layer once the cursor layer can be backed by a real output layer. 0699 if (waylandServer()) { 0700 const std::chrono::milliseconds frameTime = 0701 std::chrono::duration_cast<std::chrono::milliseconds>(output->renderLoop()->lastPresentationTimestamp()); 0702 0703 if (!Cursors::self()->isCursorHidden()) { 0704 Cursor *cursor = Cursors::self()->currentCursor(); 0705 if (cursor->geometry().intersects(output->geometry())) { 0706 cursor->markAsRendered(frameTime); 0707 } 0708 } 0709 } 0710 } 0711 0712 void Compositor::prePaintPass(RenderLayer *layer) 0713 { 0714 layer->delegate()->prePaint(); 0715 const auto sublayers = layer->sublayers(); 0716 for (RenderLayer *sublayer : sublayers) { 0717 prePaintPass(sublayer); 0718 } 0719 } 0720 0721 void Compositor::postPaintPass(RenderLayer *layer) 0722 { 0723 layer->delegate()->postPaint(); 0724 const auto sublayers = layer->sublayers(); 0725 for (RenderLayer *sublayer : sublayers) { 0726 postPaintPass(sublayer); 0727 } 0728 } 0729 0730 void Compositor::preparePaintPass(RenderLayer *layer, QRegion *repaint) 0731 { 0732 // TODO: Cull opaque region. 0733 *repaint += layer->mapToGlobal(layer->repaints() + layer->delegate()->repaints()); 0734 layer->resetRepaints(); 0735 const auto sublayers = layer->sublayers(); 0736 for (RenderLayer *sublayer : sublayers) { 0737 if (sublayer->isVisible()) { 0738 preparePaintPass(sublayer, repaint); 0739 } 0740 } 0741 } 0742 0743 void Compositor::paintPass(RenderLayer *layer, RenderTarget *target, const QRegion ®ion) 0744 { 0745 layer->delegate()->paint(target, region); 0746 0747 const auto sublayers = layer->sublayers(); 0748 for (RenderLayer *sublayer : sublayers) { 0749 if (sublayer->isVisible()) { 0750 paintPass(sublayer, target, region); 0751 } 0752 } 0753 } 0754 0755 bool Compositor::isActive() 0756 { 0757 return m_state == State::On; 0758 } 0759 0760 bool Compositor::compositingPossible() const 0761 { 0762 return true; 0763 } 0764 0765 QString Compositor::compositingNotPossibleReason() const 0766 { 0767 return QString(); 0768 } 0769 0770 bool Compositor::openGLCompositingIsBroken() const 0771 { 0772 return false; 0773 } 0774 0775 void Compositor::createOpenGLSafePoint(OpenGLSafePoint safePoint) 0776 { 0777 } 0778 0779 WaylandCompositor::WaylandCompositor(QObject *parent) 0780 : Compositor(parent) 0781 { 0782 connect(kwinApp(), &Application::x11ConnectionAboutToBeDestroyed, 0783 this, &WaylandCompositor::destroyCompositorSelection); 0784 } 0785 0786 WaylandCompositor::~WaylandCompositor() 0787 { 0788 Q_EMIT aboutToDestroy(); 0789 stop(); // this can't be called in the destructor of Compositor 0790 } 0791 0792 void WaylandCompositor::toggleCompositing() 0793 { 0794 // For the shortcut. Not possible on Wayland because we always composite. 0795 } 0796 0797 void WaylandCompositor::start() 0798 { 0799 if (!Compositor::setupStart()) { 0800 // Internal setup failed, abort. 0801 return; 0802 } 0803 0804 if (Workspace::self()) { 0805 startupWithWorkspace(); 0806 } else { 0807 connect(kwinApp(), &Application::workspaceCreated, 0808 this, &WaylandCompositor::startupWithWorkspace); 0809 } 0810 } 0811 0812 X11Compositor::X11Compositor(QObject *parent) 0813 : Compositor(parent) 0814 , m_suspended(options->isUseCompositing() ? NoReasonSuspend : UserSuspend) 0815 { 0816 if (qEnvironmentVariableIsSet("KWIN_MAX_FRAMES_TESTED")) { 0817 m_framesToTestForSafety = qEnvironmentVariableIntValue("KWIN_MAX_FRAMES_TESTED"); 0818 } 0819 } 0820 0821 X11Compositor::~X11Compositor() 0822 { 0823 Q_EMIT aboutToDestroy(); 0824 if (m_openGLFreezeProtectionThread) { 0825 m_openGLFreezeProtectionThread->quit(); 0826 m_openGLFreezeProtectionThread->wait(); 0827 } 0828 stop(); // this can't be called in the destructor of Compositor 0829 } 0830 0831 X11SyncManager *X11Compositor::syncManager() const 0832 { 0833 return m_syncManager.get(); 0834 } 0835 0836 void X11Compositor::toggleCompositing() 0837 { 0838 if (m_suspended) { 0839 // Direct user call; clear all bits. 0840 resume(AllReasonSuspend); 0841 } else { 0842 // But only set the user one (sufficient to suspend). 0843 suspend(UserSuspend); 0844 } 0845 } 0846 0847 void X11Compositor::reinitialize() 0848 { 0849 // Resume compositing if suspended. 0850 m_suspended = NoReasonSuspend; 0851 Compositor::reinitialize(); 0852 } 0853 0854 void X11Compositor::configChanged() 0855 { 0856 if (m_suspended) { 0857 stop(); 0858 return; 0859 } 0860 Compositor::configChanged(); 0861 } 0862 0863 void X11Compositor::suspend(X11Compositor::SuspendReason reason) 0864 { 0865 Q_ASSERT(reason != NoReasonSuspend); 0866 m_suspended |= reason; 0867 0868 if (reason & ScriptSuspend) { 0869 // When disabled show a shortcut how the user can get back compositing. 0870 const auto shortcuts = KGlobalAccel::self()->shortcut( 0871 workspace()->findChild<QAction *>(QStringLiteral("Suspend Compositing"))); 0872 if (!shortcuts.isEmpty()) { 0873 // Display notification only if there is the shortcut. 0874 const QString message = 0875 i18n("Desktop effects have been suspended by another application.<br/>" 0876 "You can resume using the '%1' shortcut.", 0877 shortcuts.first().toString(QKeySequence::NativeText)); 0878 #if KWIN_BUILD_NOTIFICATIONS 0879 KNotification::event(QStringLiteral("compositingsuspendeddbus"), message); 0880 #endif 0881 } 0882 } 0883 stop(); 0884 } 0885 0886 void X11Compositor::resume(X11Compositor::SuspendReason reason) 0887 { 0888 Q_ASSERT(reason != NoReasonSuspend); 0889 m_suspended &= ~reason; 0890 start(); 0891 } 0892 0893 void X11Compositor::start() 0894 { 0895 if (m_suspended) { 0896 QStringList reasons; 0897 if (m_suspended & UserSuspend) { 0898 reasons << QStringLiteral("Disabled by User"); 0899 } 0900 if (m_suspended & BlockRuleSuspend) { 0901 reasons << QStringLiteral("Disabled by Window"); 0902 } 0903 if (m_suspended & ScriptSuspend) { 0904 reasons << QStringLiteral("Disabled by Script"); 0905 } 0906 qCInfo(KWIN_CORE) << "Compositing is suspended, reason:" << reasons; 0907 return; 0908 } else if (!compositingPossible()) { 0909 qCWarning(KWIN_CORE) << "Compositing is not possible"; 0910 return; 0911 } 0912 if (!Compositor::setupStart()) { 0913 // Internal setup failed, abort. 0914 return; 0915 } 0916 startupWithWorkspace(); 0917 m_syncManager.reset(X11SyncManager::create()); 0918 } 0919 0920 void X11Compositor::stop() 0921 { 0922 m_syncManager.reset(); 0923 Compositor::stop(); 0924 } 0925 0926 void X11Compositor::composite(RenderLoop *renderLoop) 0927 { 0928 if (backend()->overlayWindow() && !isOverlayWindowVisible()) { 0929 // Return since nothing is visible. 0930 return; 0931 } 0932 0933 QList<Window *> windows = workspace()->stackingOrder(); 0934 QList<SurfaceItemX11 *> dirtyItems; 0935 0936 // Reset the damage state of each window and fetch the damage region 0937 // without waiting for a reply 0938 for (Window *window : std::as_const(windows)) { 0939 SurfaceItemX11 *surfaceItem = static_cast<SurfaceItemX11 *>(window->surfaceItem()); 0940 if (surfaceItem->fetchDamage()) { 0941 dirtyItems.append(surfaceItem); 0942 } 0943 } 0944 0945 if (dirtyItems.count() > 0) { 0946 if (m_syncManager) { 0947 m_syncManager->triggerFence(); 0948 } 0949 xcb_flush(kwinApp()->x11Connection()); 0950 } 0951 0952 // Get the replies 0953 for (SurfaceItemX11 *item : std::as_const(dirtyItems)) { 0954 item->waitForDamage(); 0955 } 0956 0957 if (m_framesToTestForSafety > 0 && (backend()->compositingType() & OpenGLCompositing)) { 0958 createOpenGLSafePoint(OpenGLSafePoint::PreFrame); 0959 } 0960 0961 Compositor::composite(renderLoop); 0962 0963 if (m_syncManager) { 0964 if (!m_syncManager->endFrame()) { 0965 qCDebug(KWIN_CORE) << "Aborting explicit synchronization with the X command stream."; 0966 qCDebug(KWIN_CORE) << "Future frames will be rendered unsynchronized."; 0967 m_syncManager.reset(); 0968 } 0969 } 0970 0971 if (m_framesToTestForSafety > 0) { 0972 if (backend()->compositingType() & OpenGLCompositing) { 0973 createOpenGLSafePoint(OpenGLSafePoint::PostFrame); 0974 } 0975 m_framesToTestForSafety--; 0976 if (m_framesToTestForSafety == 0 && (backend()->compositingType() & OpenGLCompositing)) { 0977 createOpenGLSafePoint(OpenGLSafePoint::PostLastGuardedFrame); 0978 } 0979 } 0980 } 0981 0982 bool X11Compositor::checkForOverlayWindow(WId w) const 0983 { 0984 if (!backend()) { 0985 // No backend, so it cannot be the overlay window. 0986 return false; 0987 } 0988 if (!backend()->overlayWindow()) { 0989 // No overlay window, it cannot be the overlay. 0990 return false; 0991 } 0992 // Compare the window ID's. 0993 return w == backend()->overlayWindow()->window(); 0994 } 0995 0996 bool X11Compositor::isOverlayWindowVisible() const 0997 { 0998 if (!backend()) { 0999 return false; 1000 } 1001 if (!backend()->overlayWindow()) { 1002 return false; 1003 } 1004 return backend()->overlayWindow()->isVisible(); 1005 } 1006 1007 void X11Compositor::updateClientCompositeBlocking(X11Window *c) 1008 { 1009 if (c) { 1010 if (c->isBlockingCompositing()) { 1011 // Do NOT attempt to call suspend(true) from within the eventchain! 1012 if (!(m_suspended & BlockRuleSuspend)) { 1013 QMetaObject::invokeMethod( 1014 this, [this]() { 1015 suspend(BlockRuleSuspend); 1016 }, 1017 Qt::QueuedConnection); 1018 } 1019 } 1020 } else if (m_suspended & BlockRuleSuspend) { 1021 // If !c we just check if we can resume in case a blocking client was lost. 1022 bool shouldResume = true; 1023 1024 for (auto it = Workspace::self()->clientList().constBegin(); 1025 it != Workspace::self()->clientList().constEnd(); ++it) { 1026 if ((*it)->isBlockingCompositing()) { 1027 shouldResume = false; 1028 break; 1029 } 1030 } 1031 if (shouldResume) { 1032 // Do NOT attempt to call suspend(false) from within the eventchain! 1033 QMetaObject::invokeMethod( 1034 this, [this]() { 1035 resume(BlockRuleSuspend); 1036 }, 1037 Qt::QueuedConnection); 1038 } 1039 } 1040 } 1041 1042 X11Compositor *X11Compositor::self() 1043 { 1044 return qobject_cast<X11Compositor *>(Compositor::self()); 1045 } 1046 1047 bool X11Compositor::openGLCompositingIsBroken() const 1048 { 1049 auto timestamp = KConfigGroup(kwinApp()->config(), "Compositing").readEntry(QLatin1String("LastFailureTimestamp"), 0); 1050 if (timestamp > 0) { 1051 if (QDateTime::currentSecsSinceEpoch() - timestamp < 60) { 1052 return true; 1053 } 1054 } 1055 1056 return false; 1057 } 1058 1059 QString X11Compositor::compositingNotPossibleReason() const 1060 { 1061 // first off, check whether we figured that we'll crash on detection because of a buggy driver 1062 KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing"); 1063 if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && openGLCompositingIsBroken()) { 1064 return i18n("<b>OpenGL compositing (the default) has crashed KWin in the past.</b><br>" 1065 "This was most likely due to a driver bug." 1066 "<p>If you think that you have meanwhile upgraded to a stable driver,<br>" 1067 "you can reset this protection but <b>be aware that this might result in an immediate crash!</b></p>"); 1068 } 1069 1070 if (!Xcb::Extensions::self()->isCompositeAvailable() || !Xcb::Extensions::self()->isDamageAvailable()) { 1071 return i18n("Required X extensions (XComposite and XDamage) are not available."); 1072 } 1073 if (!Xcb::Extensions::self()->hasGlx()) { 1074 return i18n("GLX/OpenGL is not available."); 1075 } 1076 return QString(); 1077 } 1078 1079 bool X11Compositor::compositingPossible() const 1080 { 1081 // first off, check whether we figured that we'll crash on detection because of a buggy driver 1082 KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing"); 1083 if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && openGLCompositingIsBroken()) { 1084 qCWarning(KWIN_CORE) << "Compositing disabled: video driver seems unstable. If you think it's a false positive, please try again in a few minutes."; 1085 return false; 1086 } 1087 1088 if (!Xcb::Extensions::self()->isCompositeAvailable()) { 1089 qCWarning(KWIN_CORE) << "Compositing disabled: no composite extension available"; 1090 return false; 1091 } 1092 if (!Xcb::Extensions::self()->isDamageAvailable()) { 1093 qCWarning(KWIN_CORE) << "Compositing disabled: no damage extension available"; 1094 return false; 1095 } 1096 if (Xcb::Extensions::self()->hasGlx()) { 1097 return true; 1098 } 1099 if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { 1100 return true; 1101 } else if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) { 1102 return true; 1103 } 1104 qCWarning(KWIN_CORE) << "Compositing disabled: no OpenGL support"; 1105 return false; 1106 } 1107 1108 void X11Compositor::createOpenGLSafePoint(OpenGLSafePoint safePoint) 1109 { 1110 auto group = KConfigGroup(kwinApp()->config(), "Compositing"); 1111 switch (safePoint) { 1112 case OpenGLSafePoint::PreInit: 1113 // Explicitly write the failure timestamp so that if we crash during 1114 // OpenGL init, we know we should not try again. 1115 group.writeEntry(QLatin1String("LastFailureTimestamp"), QDateTime::currentSecsSinceEpoch()); 1116 group.sync(); 1117 // Deliberately continue with PreFrame 1118 Q_FALLTHROUGH(); 1119 case OpenGLSafePoint::PreFrame: 1120 if (m_openGLFreezeProtectionThread == nullptr) { 1121 Q_ASSERT(m_openGLFreezeProtection == nullptr); 1122 m_openGLFreezeProtectionThread = std::make_unique<QThread>(); 1123 m_openGLFreezeProtectionThread->setObjectName("FreezeDetector"); 1124 m_openGLFreezeProtectionThread->start(); 1125 m_openGLFreezeProtection = std::make_unique<QTimer>(); 1126 m_openGLFreezeProtection->setInterval(15000); 1127 m_openGLFreezeProtection->setSingleShot(true); 1128 m_openGLFreezeProtection->start(); 1129 const QString configName = kwinApp()->config()->name(); 1130 m_openGLFreezeProtection->moveToThread(m_openGLFreezeProtectionThread.get()); 1131 connect( 1132 m_openGLFreezeProtection.get(), &QTimer::timeout, m_openGLFreezeProtection.get(), 1133 [configName] { 1134 auto group = KConfigGroup(KSharedConfig::openConfig(configName), "Compositing"); 1135 group.writeEntry(QLatin1String("LastFailureTimestamp"), QDateTime::currentSecsSinceEpoch()); 1136 group.sync(); 1137 KCrash::setDrKonqiEnabled(false); 1138 qFatal("Freeze in OpenGL initialization detected"); 1139 }, 1140 Qt::DirectConnection); 1141 } else { 1142 Q_ASSERT(m_openGLFreezeProtection); 1143 QMetaObject::invokeMethod(m_openGLFreezeProtection.get(), QOverload<>::of(&QTimer::start), Qt::QueuedConnection); 1144 } 1145 break; 1146 case OpenGLSafePoint::PostInit: 1147 group.deleteEntry(QLatin1String("LastFailureTimestamp")); 1148 group.sync(); 1149 // Deliberately continue with PostFrame 1150 Q_FALLTHROUGH(); 1151 case OpenGLSafePoint::PostFrame: 1152 QMetaObject::invokeMethod(m_openGLFreezeProtection.get(), &QTimer::stop, Qt::QueuedConnection); 1153 break; 1154 case OpenGLSafePoint::PostLastGuardedFrame: 1155 m_openGLFreezeProtectionThread->quit(); 1156 m_openGLFreezeProtectionThread->wait(); 1157 m_openGLFreezeProtectionThread.reset(); 1158 m_openGLFreezeProtection.reset(); 1159 break; 1160 } 1161 } 1162 1163 } 1164 1165 // included for CompositorSelectionOwner 1166 #include "composite.moc"