File indexing completed on 2024-05-19 16:34:48
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 0010 /* 0011 Design: 0012 0013 When compositing is turned on, XComposite extension is used to redirect 0014 drawing of windows to pixmaps and XDamage extension is used to get informed 0015 about damage (changes) to window contents. This code is mostly in composite.cpp . 0016 0017 Compositor::performCompositing() starts one painting pass. Painting is done 0018 by painting the screen, which in turn paints every window. Painting can be affected 0019 using effects, which are chained. E.g. painting a screen means that actually 0020 paintScreen() of the first effect is called, which possibly does modifications 0021 and calls next effect's paintScreen() and so on, until Scene::finalPaintScreen() 0022 is called. 0023 0024 There are 3 phases of every paint (not necessarily done together): 0025 The pre-paint phase, the paint phase and the post-paint phase. 0026 0027 The pre-paint phase is used to find out about how the painting will be actually 0028 done (i.e. what the effects will do). For example when only a part of the screen 0029 needs to be updated and no effect will do any transformation it is possible to use 0030 an optimized paint function. How the painting will be done is controlled 0031 by the mask argument, see PAINT_WINDOW_* and PAINT_SCREEN_* flags in scene.h . 0032 For example an effect that decides to paint a normal windows as translucent 0033 will need to modify the mask in its prePaintWindow() to include 0034 the PAINT_WINDOW_TRANSLUCENT flag. The paintWindow() function will then get 0035 the mask with this flag turned on and will also paint using transparency. 0036 0037 The paint pass does the actual painting, based on the information collected 0038 using the pre-paint pass. After running through the effects' paintScreen() 0039 either paintGenericScreen() or optimized paintSimpleScreen() are called. 0040 Those call paintWindow() on windows (not necessarily all), possibly using 0041 clipping to optimize performance and calling paintWindow() first with only 0042 PAINT_WINDOW_OPAQUE to paint the opaque parts and then later 0043 with PAINT_WINDOW_TRANSLUCENT to paint the transparent parts. Function 0044 paintWindow() again goes through effects' paintWindow() until 0045 finalPaintWindow() is called, which calls the window's performPaint() to 0046 do the actual painting. 0047 0048 The post-paint can be used for cleanups and is also used for scheduling 0049 repaints during the next painting pass for animations. Effects wanting to 0050 repaint certain parts can manually damage them during post-paint and repaint 0051 of these parts will be done during the next paint pass. 0052 0053 */ 0054 0055 #include "scene/workspacescene.h" 0056 #include "composite.h" 0057 #include "core/output.h" 0058 #include "core/renderlayer.h" 0059 #include "core/renderloop.h" 0060 #include "deleted.h" 0061 #include "effects.h" 0062 #include "internalwindow.h" 0063 #include "scene/dndiconitem.h" 0064 #include "scene/itemrenderer.h" 0065 #include "scene/shadowitem.h" 0066 #include "scene/surfaceitem.h" 0067 #include "scene/windowitem.h" 0068 #include "shadow.h" 0069 #include "unmanaged.h" 0070 #include "wayland/seat_interface.h" 0071 #include "wayland/surface_interface.h" 0072 #include "wayland_server.h" 0073 #include "waylandwindow.h" 0074 #include "workspace.h" 0075 #include "x11window.h" 0076 0077 #include <QtMath> 0078 0079 namespace KWin 0080 { 0081 0082 //**************************************** 0083 // Scene 0084 //**************************************** 0085 0086 WorkspaceScene::WorkspaceScene(std::unique_ptr<ItemRenderer> renderer) 0087 : Scene(std::move(renderer)) 0088 { 0089 } 0090 0091 WorkspaceScene::~WorkspaceScene() 0092 { 0093 } 0094 0095 void WorkspaceScene::initialize() 0096 { 0097 connect(workspace(), &Workspace::stackingOrderChanged, this, &WorkspaceScene::addRepaintFull); 0098 0099 setGeometry(workspace()->geometry()); 0100 connect(workspace(), &Workspace::geometryChanged, this, [this]() { 0101 setGeometry(workspace()->geometry()); 0102 }); 0103 0104 if (waylandServer()) { 0105 connect(waylandServer()->seat(), &KWaylandServer::SeatInterface::dragStarted, this, &WorkspaceScene::createDndIconItem); 0106 connect(waylandServer()->seat(), &KWaylandServer::SeatInterface::dragEnded, this, &WorkspaceScene::destroyDndIconItem); 0107 } 0108 } 0109 0110 void WorkspaceScene::createDndIconItem() 0111 { 0112 KWaylandServer::DragAndDropIcon *dragIcon = waylandServer()->seat()->dragIcon(); 0113 if (!dragIcon) { 0114 return; 0115 } 0116 m_dndIcon = std::make_unique<DragAndDropIconItem>(dragIcon, this); 0117 if (waylandServer()->seat()->isDragPointer()) { 0118 m_dndIcon->setPosition(waylandServer()->seat()->pointerPos()); 0119 connect(waylandServer()->seat(), &KWaylandServer::SeatInterface::pointerPosChanged, m_dndIcon.get(), [this]() { 0120 m_dndIcon->setPosition(waylandServer()->seat()->pointerPos()); 0121 }); 0122 } else if (waylandServer()->seat()->isDragTouch()) { 0123 m_dndIcon->setPosition(waylandServer()->seat()->firstTouchPointPosition()); 0124 connect(waylandServer()->seat(), &KWaylandServer::SeatInterface::touchMoved, m_dndIcon.get(), [this]() { 0125 m_dndIcon->setPosition(waylandServer()->seat()->firstTouchPointPosition()); 0126 }); 0127 } 0128 } 0129 0130 void WorkspaceScene::destroyDndIconItem() 0131 { 0132 m_dndIcon.reset(); 0133 } 0134 0135 QRegion WorkspaceScene::damage() const 0136 { 0137 return m_paintContext.damage; 0138 } 0139 0140 static SurfaceItem *findTopMostSurface(SurfaceItem *item) 0141 { 0142 const QList<Item *> children = item->childItems(); 0143 if (children.isEmpty()) { 0144 return item; 0145 } else { 0146 return findTopMostSurface(static_cast<SurfaceItem *>(children.constLast())); 0147 } 0148 } 0149 0150 SurfaceItem *WorkspaceScene::scanoutCandidate() const 0151 { 0152 if (!waylandServer()) { 0153 return nullptr; 0154 } 0155 SurfaceItem *candidate = nullptr; 0156 if (!static_cast<EffectsHandlerImpl *>(effects)->blocksDirectScanout()) { 0157 for (int i = stacking_order.count() - 1; i >= 0; i--) { 0158 WindowItem *windowItem = stacking_order[i]; 0159 Window *window = windowItem->window(); 0160 if (window->isOnOutput(painted_screen) && window->opacity() > 0) { 0161 if (!window->isClient() || !window->isFullScreen() || window->opacity() != 1.0) { 0162 break; 0163 } 0164 if (!windowItem->surfaceItem()) { 0165 break; 0166 } 0167 SurfaceItem *topMost = findTopMostSurface(windowItem->surfaceItem()); 0168 // the subsurface has to be able to cover the whole window 0169 if (topMost->position() != QPoint(0, 0)) { 0170 break; 0171 } 0172 // and it has to be completely opaque 0173 if (!topMost->opaque().contains(QRect(0, 0, window->width(), window->height()))) { 0174 break; 0175 } 0176 candidate = topMost; 0177 break; 0178 } 0179 } 0180 } 0181 return candidate; 0182 } 0183 0184 void WorkspaceScene::prePaint(SceneDelegate *delegate) 0185 { 0186 createStackingOrder(); 0187 0188 painted_delegate = delegate; 0189 if (kwinApp()->operationMode() == Application::OperationModeX11) { 0190 painted_screen = workspace()->outputs().constFirst(); 0191 m_renderer->setRenderTargetRect(geometry()); 0192 m_renderer->setRenderTargetScale(1); 0193 } else { 0194 painted_screen = painted_delegate->output(); 0195 m_renderer->setRenderTargetRect(painted_screen->fractionalGeometry()); 0196 m_renderer->setRenderTargetScale(painted_screen->scale()); 0197 } 0198 0199 const RenderLoop *renderLoop = painted_screen->renderLoop(); 0200 const std::chrono::milliseconds presentTime = 0201 std::chrono::duration_cast<std::chrono::milliseconds>(renderLoop->nextPresentationTimestamp()); 0202 0203 if (Q_UNLIKELY(presentTime < m_expectedPresentTimestamp)) { 0204 qCDebug(KWIN_CORE, 0205 "Provided presentation timestamp is invalid: %lld (current: %lld)", 0206 static_cast<long long>(presentTime.count()), 0207 static_cast<long long>(m_expectedPresentTimestamp.count())); 0208 } else { 0209 m_expectedPresentTimestamp = presentTime; 0210 } 0211 0212 // preparation step 0213 auto effectsImpl = static_cast<EffectsHandlerImpl *>(effects); 0214 effectsImpl->startPaint(); 0215 0216 ScreenPrePaintData prePaintData; 0217 prePaintData.mask = 0; 0218 prePaintData.screen = EffectScreenImpl::get(painted_screen); 0219 0220 effects->makeOpenGLContextCurrent(); 0221 Q_EMIT preFrameRender(); 0222 0223 effects->prePaintScreen(prePaintData, m_expectedPresentTimestamp); 0224 m_paintContext.damage = prePaintData.paint; 0225 m_paintContext.mask = prePaintData.mask; 0226 m_paintContext.phase2Data.clear(); 0227 0228 if (m_paintContext.mask & (PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS)) { 0229 preparePaintGenericScreen(); 0230 } else { 0231 preparePaintSimpleScreen(); 0232 } 0233 } 0234 0235 static void resetRepaintsHelper(Item *item, SceneDelegate *delegate) 0236 { 0237 item->resetRepaints(delegate); 0238 0239 const auto childItems = item->childItems(); 0240 for (Item *childItem : childItems) { 0241 resetRepaintsHelper(childItem, delegate); 0242 } 0243 } 0244 0245 static void accumulateRepaints(Item *item, SceneDelegate *delegate, QRegion *repaints, const bool padDamage) 0246 { 0247 if (!padDamage) { 0248 *repaints += item->repaints(delegate); 0249 } else { 0250 const auto padding = 1; 0251 0252 for (const QRect region : item->repaints(delegate)) { 0253 if (region.isEmpty() || region == infiniteRegion()) { 0254 *repaints += region; 0255 continue; 0256 } 0257 0258 *repaints += QRect{region.x() - padding, region.y() - padding, 0259 region.width() + 2 * padding, region.height() + 2 * padding}; 0260 } 0261 } 0262 0263 item->resetRepaints(delegate); 0264 0265 const auto childItems = item->childItems(); 0266 for (Item *childItem : childItems) { 0267 accumulateRepaints(childItem, delegate, repaints, padDamage); 0268 } 0269 } 0270 0271 void WorkspaceScene::preparePaintGenericScreen() 0272 { 0273 for (WindowItem *windowItem : std::as_const(stacking_order)) { 0274 resetRepaintsHelper(windowItem, painted_delegate); 0275 0276 WindowPrePaintData data; 0277 data.mask = m_paintContext.mask; 0278 data.paint = infiniteRegion(); // no clipping, so doesn't really matter 0279 0280 effects->prePaintWindow(windowItem->window()->effectWindow(), data, m_expectedPresentTimestamp); 0281 m_paintContext.phase2Data.append(Phase2Data{ 0282 .item = windowItem, 0283 .region = infiniteRegion(), 0284 .opaque = data.opaque, 0285 .mask = data.mask, 0286 }); 0287 } 0288 0289 m_paintContext.damage = m_renderer->renderTargetRect(); 0290 } 0291 0292 void WorkspaceScene::preparePaintSimpleScreen() 0293 { 0294 // if a fractional scale factor is used, pad the damage to avoid visual 0295 // glitches due to rounding errors (floating point arithmetic) and/or the 0296 // use of GL_LINEAR in the pipeline 0297 const auto scale = painted_screen->scale(); 0298 const bool padDamage = std::trunc(scale) != scale; 0299 0300 for (WindowItem *windowItem : std::as_const(stacking_order)) { 0301 Window *window = windowItem->window(); 0302 WindowPrePaintData data; 0303 data.mask = m_paintContext.mask; 0304 accumulateRepaints(windowItem, painted_delegate, &data.paint, padDamage); 0305 0306 // Clip out the decoration for opaque windows; the decoration is drawn in the second pass. 0307 if (window->opacity() == 1.0) { 0308 const SurfaceItem *surfaceItem = windowItem->surfaceItem(); 0309 if (Q_LIKELY(surfaceItem)) { 0310 data.opaque = surfaceItem->mapToGlobal(surfaceItem->opaque()); 0311 } 0312 0313 const DecorationItem *decorationItem = windowItem->decorationItem(); 0314 if (decorationItem) { 0315 data.opaque |= decorationItem->mapToGlobal(decorationItem->opaque()); 0316 } 0317 } 0318 0319 effects->prePaintWindow(window->effectWindow(), data, m_expectedPresentTimestamp); 0320 m_paintContext.phase2Data.append(Phase2Data{ 0321 .item = windowItem, 0322 .region = data.paint, 0323 .opaque = data.opaque, 0324 .mask = data.mask, 0325 }); 0326 } 0327 0328 // Perform an occlusion cull pass, remove surface damage occluded by opaque windows. 0329 QRegion opaque; 0330 for (int i = m_paintContext.phase2Data.size() - 1; i >= 0; --i) { 0331 const auto &paintData = m_paintContext.phase2Data.at(i); 0332 m_paintContext.damage += paintData.region - opaque; 0333 if (!(paintData.mask & (PAINT_WINDOW_TRANSLUCENT | PAINT_WINDOW_TRANSFORMED))) { 0334 opaque += paintData.opaque; 0335 } 0336 } 0337 0338 if (m_dndIcon) { 0339 accumulateRepaints(m_dndIcon.get(), painted_delegate, &m_paintContext.damage, padDamage); 0340 } 0341 } 0342 0343 void WorkspaceScene::postPaint() 0344 { 0345 for (WindowItem *w : std::as_const(stacking_order)) { 0346 effects->postPaintWindow(w->window()->effectWindow()); 0347 } 0348 0349 effects->postPaintScreen(); 0350 0351 if (waylandServer()) { 0352 const std::chrono::milliseconds frameTime = 0353 std::chrono::duration_cast<std::chrono::milliseconds>(painted_screen->renderLoop()->lastPresentationTimestamp()); 0354 0355 for (WindowItem *windowItem : std::as_const(stacking_order)) { 0356 Window *window = windowItem->window(); 0357 if (!window->isOnOutput(painted_screen)) { 0358 continue; 0359 } 0360 if (auto surface = window->surface()) { 0361 surface->frameRendered(frameTime.count()); 0362 } 0363 } 0364 0365 if (m_dndIcon) { 0366 m_dndIcon->frameRendered(frameTime.count()); 0367 } 0368 } 0369 0370 clearStackingOrder(); 0371 } 0372 0373 void WorkspaceScene::paint(RenderTarget *renderTarget, const QRegion ®ion) 0374 { 0375 m_renderer->beginFrame(renderTarget); 0376 0377 ScreenPaintData data(m_renderer->renderTargetProjectionMatrix(), EffectScreenImpl::get(painted_screen)); 0378 effects->paintScreen(m_paintContext.mask, region, data); 0379 m_paintScreenCount = 0; 0380 Q_EMIT frameRendered(); 0381 0382 m_renderer->endFrame(); 0383 } 0384 0385 // the function that'll be eventually called by paintScreen() above 0386 void WorkspaceScene::finalPaintScreen(int mask, const QRegion ®ion, ScreenPaintData &data) 0387 { 0388 m_paintScreenCount++; 0389 if (mask & (PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS)) { 0390 paintGenericScreen(mask, data); 0391 } else { 0392 paintSimpleScreen(mask, region); 0393 } 0394 } 0395 0396 // The generic painting code that can handle even transformations. 0397 // It simply paints bottom-to-top. 0398 void WorkspaceScene::paintGenericScreen(int, const ScreenPaintData &) 0399 { 0400 if (m_paintContext.mask & PAINT_SCREEN_BACKGROUND_FIRST) { 0401 if (m_paintScreenCount == 1) { 0402 m_renderer->renderBackground(infiniteRegion()); 0403 } 0404 } else { 0405 m_renderer->renderBackground(infiniteRegion()); 0406 } 0407 0408 for (const Phase2Data &paintData : std::as_const(m_paintContext.phase2Data)) { 0409 paintWindow(paintData.item, paintData.mask, paintData.region); 0410 } 0411 } 0412 0413 // The optimized case without any transformations at all. 0414 // It can paint only the requested region and can use clipping 0415 // to reduce painting and improve performance. 0416 void WorkspaceScene::paintSimpleScreen(int, const QRegion ®ion) 0417 { 0418 // This is the occlusion culling pass 0419 QRegion visible = region; 0420 for (int i = m_paintContext.phase2Data.size() - 1; i >= 0; --i) { 0421 Phase2Data *data = &m_paintContext.phase2Data[i]; 0422 data->region = visible; 0423 0424 if (!(data->mask & PAINT_WINDOW_TRANSFORMED)) { 0425 data->region &= data->item->mapToGlobal(data->item->boundingRect()).toAlignedRect(); 0426 0427 if (!(data->mask & PAINT_WINDOW_TRANSLUCENT)) { 0428 visible -= data->opaque; 0429 } 0430 } 0431 } 0432 0433 m_renderer->renderBackground(visible); 0434 0435 for (const Phase2Data &paintData : std::as_const(m_paintContext.phase2Data)) { 0436 paintWindow(paintData.item, paintData.mask, paintData.region); 0437 } 0438 0439 if (m_dndIcon) { 0440 const QRegion repaint = region & m_dndIcon->mapToGlobal(m_dndIcon->boundingRect()).toRect(); 0441 if (!repaint.isEmpty()) { 0442 m_renderer->renderItem(m_dndIcon.get(), 0, repaint, WindowPaintData(m_renderer->renderTargetProjectionMatrix())); 0443 } 0444 } 0445 } 0446 0447 void WorkspaceScene::createStackingOrder() 0448 { 0449 // Create a list of all windows in the stacking order 0450 QList<Window *> windows = workspace()->stackingOrder(); 0451 0452 // Move elevated windows to the top of the stacking order 0453 const QList<EffectWindow *> elevatedList = static_cast<EffectsHandlerImpl *>(effects)->elevatedWindows(); 0454 for (EffectWindow *c : elevatedList) { 0455 Window *t = static_cast<EffectWindowImpl *>(c)->window(); 0456 windows.removeAll(t); 0457 windows.append(t); 0458 } 0459 0460 // Skip windows that are not yet ready for being painted and if screen is locked skip windows 0461 // that are neither lockscreen nor inputmethod windows. 0462 // 0463 // TODO? This cannot be used so carelessly - needs protections against broken clients, the 0464 // window should not get focus before it's displayed, handle unredirected windows properly and 0465 // so on. 0466 for (Window *window : std::as_const(windows)) { 0467 if (window->windowItem()->isVisible()) { 0468 stacking_order.append(window->windowItem()); 0469 } 0470 } 0471 } 0472 0473 void WorkspaceScene::clearStackingOrder() 0474 { 0475 stacking_order.clear(); 0476 } 0477 0478 void WorkspaceScene::paintWindow(WindowItem *item, int mask, const QRegion ®ion) 0479 { 0480 if (region.isEmpty()) { // completely clipped 0481 return; 0482 } 0483 0484 WindowPaintData data(m_renderer->renderTargetProjectionMatrix()); 0485 effects->paintWindow(item->window()->effectWindow(), mask, region, data); 0486 } 0487 0488 // the function that'll be eventually called by paintWindow() above 0489 void WorkspaceScene::finalPaintWindow(EffectWindowImpl *w, int mask, const QRegion ®ion, WindowPaintData &data) 0490 { 0491 effects->drawWindow(w, mask, region, data); 0492 } 0493 0494 // will be eventually called from drawWindow() 0495 void WorkspaceScene::finalDrawWindow(EffectWindowImpl *w, int mask, const QRegion ®ion, WindowPaintData &data) 0496 { 0497 m_renderer->renderItem(w->windowItem(), mask, region, data); 0498 } 0499 0500 bool WorkspaceScene::makeOpenGLContextCurrent() 0501 { 0502 return false; 0503 } 0504 0505 void WorkspaceScene::doneOpenGLContextCurrent() 0506 { 0507 } 0508 0509 bool WorkspaceScene::supportsNativeFence() const 0510 { 0511 return false; 0512 } 0513 0514 } // namespace