File indexing completed on 2024-04-28 05:30:13

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 "compositor.h"
0010 
0011 #include <config-kwin.h>
0012 
0013 #include "core/output.h"
0014 #include "core/outputlayer.h"
0015 #include "core/renderbackend.h"
0016 #include "core/renderlayer.h"
0017 #include "core/renderloop.h"
0018 #include "cursor.h"
0019 #include "dbusinterface.h"
0020 #include "ftrace.h"
0021 #include "scene/cursorscene.h"
0022 #include "scene/surfaceitem.h"
0023 #include "scene/surfaceitem_wayland.h"
0024 #include "scene/workspacescene.h"
0025 #include "utils/common.h"
0026 #include "wayland/surface.h"
0027 #include "wayland_server.h"
0028 #include "window.h"
0029 #include "workspace.h"
0030 
0031 #include <KLocalizedString>
0032 #if KWIN_BUILD_NOTIFICATIONS
0033 #include <KNotification>
0034 #endif
0035 
0036 namespace KWin
0037 {
0038 
0039 Compositor *Compositor::s_compositor = nullptr;
0040 Compositor *Compositor::self()
0041 {
0042     return s_compositor;
0043 }
0044 
0045 Compositor::Compositor(QObject *workspace)
0046     : QObject(workspace)
0047 {
0048     // 2 sec which should be enough to restart the compositor.
0049     static const int compositorLostMessageDelay = 2000;
0050 
0051     m_unusedSupportPropertyTimer.setInterval(compositorLostMessageDelay);
0052     m_unusedSupportPropertyTimer.setSingleShot(true);
0053     connect(&m_unusedSupportPropertyTimer, &QTimer::timeout,
0054             this, &Compositor::deleteUnusedSupportProperties);
0055 
0056     // Delay the call to start by one event cycle.
0057     // The ctor of this class is invoked from the Workspace ctor, that means before
0058     // Workspace is completely constructed, so calling Workspace::self() would result
0059     // in undefined behavior. This is fixed by using a delayed invocation.
0060     QTimer::singleShot(0, this, &Compositor::start);
0061 
0062     // register DBus
0063     new CompositorDBusInterface(this);
0064     FTraceLogger::create();
0065 }
0066 
0067 Compositor::~Compositor()
0068 {
0069     deleteUnusedSupportProperties();
0070     s_compositor = nullptr;
0071 }
0072 
0073 Output *Compositor::findOutput(RenderLoop *loop) const
0074 {
0075     const auto outputs = workspace()->outputs();
0076     for (Output *output : outputs) {
0077         if (output->renderLoop() == loop) {
0078             return output;
0079         }
0080     }
0081     return nullptr;
0082 }
0083 
0084 void Compositor::addSuperLayer(RenderLayer *layer)
0085 {
0086     m_superlayers.insert(layer->loop(), layer);
0087     connect(layer->loop(), &RenderLoop::frameRequested, this, &Compositor::handleFrameRequested);
0088 }
0089 
0090 void Compositor::removeSuperLayer(RenderLayer *layer)
0091 {
0092     m_superlayers.remove(layer->loop());
0093     disconnect(layer->loop(), &RenderLoop::frameRequested, this, &Compositor::handleFrameRequested);
0094     delete layer;
0095 }
0096 
0097 void Compositor::keepSupportProperty(xcb_atom_t atom)
0098 {
0099     m_unusedSupportProperties.removeAll(atom);
0100 }
0101 
0102 void Compositor::removeSupportProperty(xcb_atom_t atom)
0103 {
0104     m_unusedSupportProperties << atom;
0105     m_unusedSupportPropertyTimer.start();
0106 }
0107 
0108 void Compositor::deleteUnusedSupportProperties()
0109 {
0110     if (m_state == State::Starting || m_state == State::Stopping) {
0111         // Currently still maybe restarting the compositor.
0112         m_unusedSupportPropertyTimer.start();
0113         return;
0114     }
0115     if (auto *con = kwinApp()->x11Connection()) {
0116         for (const xcb_atom_t &atom : std::as_const(m_unusedSupportProperties)) {
0117             // remove property from root window
0118             xcb_delete_property(con, kwinApp()->x11RootWindow(), atom);
0119         }
0120         m_unusedSupportProperties.clear();
0121     }
0122 }
0123 
0124 void Compositor::reinitialize()
0125 {
0126     // Restart compositing
0127     stop();
0128     start();
0129 }
0130 
0131 void Compositor::handleFrameRequested(RenderLoop *renderLoop)
0132 {
0133     composite(renderLoop);
0134 }
0135 
0136 void Compositor::composite(RenderLoop *renderLoop)
0137 {
0138     if (m_backend->checkGraphicsReset()) {
0139         qCDebug(KWIN_CORE) << "Graphics reset occurred";
0140 #if KWIN_BUILD_NOTIFICATIONS
0141         KNotification::event(QStringLiteral("graphicsreset"), i18n("Desktop effects were restarted due to a graphics reset"));
0142 #endif
0143         reinitialize();
0144         return;
0145     }
0146 
0147     Output *output = findOutput(renderLoop);
0148     OutputLayer *primaryLayer = m_backend->primaryLayer(output);
0149     fTraceDuration("Paint (", output->name(), ")");
0150 
0151     RenderLayer *superLayer = m_superlayers[renderLoop];
0152     superLayer->setOutputLayer(primaryLayer);
0153 
0154     renderLoop->prepareNewFrame();
0155     auto frame = std::make_shared<OutputFrame>(renderLoop);
0156 
0157     if (primaryLayer->needsRepaint() || superLayer->needsRepaint()) {
0158         renderLoop->beginPaint();
0159 
0160         QRegion surfaceDamage = primaryLayer->repaints();
0161         primaryLayer->resetRepaints();
0162         prePaintPass(superLayer, &surfaceDamage);
0163 
0164         Window *const activeWindow = workspace()->activeWindow();
0165         SurfaceItem *const activeFullscreenItem = activeWindow && activeWindow->isFullScreen() ? activeWindow->surfaceItem() : nullptr;
0166         frame->setContentType(activeWindow && activeFullscreenItem ? activeFullscreenItem->contentType() : ContentType::None);
0167 
0168         const bool vrr = (output->capabilities() & Output::Capability::Vrr) && (output->vrrPolicy() == VrrPolicy::Always || (output->vrrPolicy() == VrrPolicy::Automatic && activeFullscreenItem));
0169         const bool tearing = (output->capabilities() & Output::Capability::Tearing) && options->allowTearing() && activeFullscreenItem && activeFullscreenItem->presentationHint() == PresentationModeHint::Async;
0170         if (vrr) {
0171             frame->setPresentationMode(tearing ? PresentationMode::AdaptiveAsync : PresentationMode::AdaptiveSync);
0172         } else {
0173             frame->setPresentationMode(tearing ? PresentationMode::Async : PresentationMode::VSync);
0174         }
0175 
0176         bool directScanout = false;
0177         if (const auto scanoutCandidate = superLayer->delegate()->scanoutCandidate()) {
0178             const auto sublayers = superLayer->sublayers();
0179             const bool scanoutPossible = std::none_of(sublayers.begin(), sublayers.end(), [](RenderLayer *sublayer) {
0180                 return sublayer->isVisible();
0181             });
0182             if (scanoutPossible && !output->directScanoutInhibited()) {
0183                 directScanout = primaryLayer->scanout(scanoutCandidate);
0184             }
0185         }
0186 
0187         if (!directScanout) {
0188             if (auto beginInfo = primaryLayer->beginFrame()) {
0189                 auto &[renderTarget, repaint] = beginInfo.value();
0190 
0191                 const QRegion bufferDamage = surfaceDamage.united(repaint).intersected(superLayer->rect().toAlignedRect());
0192 
0193                 paintPass(superLayer, renderTarget, bufferDamage);
0194                 primaryLayer->endFrame(bufferDamage, surfaceDamage);
0195             }
0196         }
0197 
0198         postPaintPass(superLayer);
0199     }
0200 
0201     m_backend->present(output, frame);
0202 
0203     framePass(superLayer, frame.get());
0204 
0205     // TODO: Put it inside the cursor layer once the cursor layer can be backed by a real output layer.
0206     if (waylandServer()) {
0207         const std::chrono::milliseconds frameTime =
0208             std::chrono::duration_cast<std::chrono::milliseconds>(output->renderLoop()->lastPresentationTimestamp());
0209 
0210         if (!Cursors::self()->isCursorHidden()) {
0211             Cursor *cursor = Cursors::self()->currentCursor();
0212             if (cursor->geometry().intersects(output->geometry())) {
0213                 cursor->markAsRendered(frameTime);
0214             }
0215         }
0216     }
0217 }
0218 
0219 void Compositor::framePass(RenderLayer *layer, OutputFrame *frame)
0220 {
0221     layer->delegate()->frame(frame);
0222     const auto sublayers = layer->sublayers();
0223     for (RenderLayer *sublayer : sublayers) {
0224         framePass(sublayer, frame);
0225     }
0226 }
0227 
0228 void Compositor::prePaintPass(RenderLayer *layer, QRegion *damage)
0229 {
0230     if (const QRegion repaints = layer->repaints(); !repaints.isEmpty()) {
0231         *damage += layer->mapToGlobal(repaints);
0232         layer->resetRepaints();
0233     }
0234 
0235     const QRegion repaints = layer->delegate()->prePaint();
0236     if (!repaints.isEmpty()) {
0237         *damage += layer->mapToGlobal(repaints);
0238     }
0239 
0240     const auto sublayers = layer->sublayers();
0241     for (RenderLayer *sublayer : sublayers) {
0242         if (sublayer->isVisible()) {
0243             prePaintPass(sublayer, damage);
0244         }
0245     }
0246 }
0247 
0248 void Compositor::postPaintPass(RenderLayer *layer)
0249 {
0250     layer->delegate()->postPaint();
0251     const auto sublayers = layer->sublayers();
0252     for (RenderLayer *sublayer : sublayers) {
0253         if (sublayer->isVisible()) {
0254             postPaintPass(sublayer);
0255         }
0256     }
0257 }
0258 
0259 void Compositor::paintPass(RenderLayer *layer, const RenderTarget &renderTarget, const QRegion &region)
0260 {
0261     layer->delegate()->paint(renderTarget, region);
0262 
0263     const auto sublayers = layer->sublayers();
0264     for (RenderLayer *sublayer : sublayers) {
0265         if (sublayer->isVisible()) {
0266             paintPass(sublayer, renderTarget, region);
0267         }
0268     }
0269 }
0270 
0271 bool Compositor::isActive()
0272 {
0273     return m_state == State::On;
0274 }
0275 
0276 bool Compositor::compositingPossible() const
0277 {
0278     return true;
0279 }
0280 
0281 QString Compositor::compositingNotPossibleReason() const
0282 {
0283     return QString();
0284 }
0285 
0286 bool Compositor::openGLCompositingIsBroken() const
0287 {
0288     return false;
0289 }
0290 
0291 void Compositor::inhibit(Window *window)
0292 {
0293 }
0294 
0295 void Compositor::uninhibit(Window *window)
0296 {
0297 }
0298 
0299 } // namespace KWin
0300 
0301 #include "moc_compositor.cpp"