File indexing completed on 2024-11-10 04:57:41
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 ®ion) 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"