File indexing completed on 2024-11-10 04:57:16
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 "compositor.h" 0057 #include "core/output.h" 0058 #include "core/renderbackend.h" 0059 #include "core/renderlayer.h" 0060 #include "core/renderloop.h" 0061 #include "core/renderviewport.h" 0062 #include "effect/effecthandler.h" 0063 #include "internalwindow.h" 0064 #include "scene/dndiconitem.h" 0065 #include "scene/itemrenderer.h" 0066 #include "scene/shadowitem.h" 0067 #include "scene/surfaceitem.h" 0068 #include "scene/windowitem.h" 0069 #include "shadow.h" 0070 #include "wayland/seat.h" 0071 #include "wayland/surface.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 , m_containerItem(std::make_unique<Item>(this)) 0089 { 0090 setGeometry(workspace()->geometry()); 0091 connect(workspace(), &Workspace::geometryChanged, this, [this]() { 0092 setGeometry(workspace()->geometry()); 0093 }); 0094 0095 if (waylandServer()) { 0096 connect(waylandServer()->seat(), &SeatInterface::dragStarted, this, &WorkspaceScene::createDndIconItem); 0097 connect(waylandServer()->seat(), &SeatInterface::dragEnded, this, &WorkspaceScene::destroyDndIconItem); 0098 } 0099 } 0100 0101 WorkspaceScene::~WorkspaceScene() 0102 { 0103 } 0104 0105 void WorkspaceScene::createDndIconItem() 0106 { 0107 DragAndDropIcon *dragIcon = waylandServer()->seat()->dragIcon(); 0108 if (!dragIcon) { 0109 return; 0110 } 0111 m_dndIcon = std::make_unique<DragAndDropIconItem>(dragIcon, this); 0112 if (waylandServer()->seat()->isDragPointer()) { 0113 auto updatePosition = [this]() { 0114 const auto pointerPos = waylandServer()->seat()->pointerPos(); 0115 m_dndIcon->setPosition(pointerPos); 0116 m_dndIcon->setOutput(workspace()->outputAt(pointerPos)); 0117 }; 0118 0119 updatePosition(); 0120 connect(waylandServer()->seat(), &SeatInterface::pointerPosChanged, m_dndIcon.get(), updatePosition); 0121 } else if (waylandServer()->seat()->isDragTouch()) { 0122 auto updatePosition = [this]() { 0123 const auto touchPos = waylandServer()->seat()->firstTouchPointPosition(); 0124 m_dndIcon->setPosition(touchPos); 0125 m_dndIcon->setOutput(workspace()->outputAt(touchPos)); 0126 }; 0127 0128 updatePosition(); 0129 connect(waylandServer()->seat(), &SeatInterface::touchMoved, m_dndIcon.get(), updatePosition); 0130 } 0131 } 0132 0133 void WorkspaceScene::destroyDndIconItem() 0134 { 0135 m_dndIcon.reset(); 0136 } 0137 0138 Item *WorkspaceScene::containerItem() const 0139 { 0140 return m_containerItem.get(); 0141 } 0142 0143 static SurfaceItem *findTopMostSurface(SurfaceItem *item) 0144 { 0145 const QList<Item *> children = item->childItems(); 0146 if (children.isEmpty()) { 0147 return item; 0148 } else { 0149 return findTopMostSurface(static_cast<SurfaceItem *>(children.constLast())); 0150 } 0151 } 0152 0153 SurfaceItem *WorkspaceScene::scanoutCandidate() const 0154 { 0155 if (!waylandServer()) { 0156 return nullptr; 0157 } 0158 SurfaceItem *candidate = nullptr; 0159 if (!effects->blocksDirectScanout()) { 0160 for (int i = stacking_order.count() - 1; i >= 0; i--) { 0161 WindowItem *windowItem = stacking_order[i]; 0162 Window *window = windowItem->window(); 0163 if (window->isOnOutput(painted_screen) && window->opacity() > 0) { 0164 if (!window->isClient() || !window->isFullScreen() || window->opacity() != 1.0) { 0165 break; 0166 } 0167 if (!windowItem->surfaceItem()) { 0168 break; 0169 } 0170 SurfaceItem *topMost = findTopMostSurface(windowItem->surfaceItem()); 0171 // the subsurface has to be able to cover the whole window 0172 if (topMost->position() != QPoint(0, 0)) { 0173 break; 0174 } 0175 // and it has to be completely opaque 0176 if (!topMost->opaque().contains(QRect(0, 0, window->width(), window->height()))) { 0177 break; 0178 } 0179 candidate = topMost; 0180 break; 0181 } 0182 } 0183 } 0184 return candidate; 0185 } 0186 0187 void WorkspaceScene::frame(SceneDelegate *delegate, OutputFrame *frame) 0188 { 0189 if (waylandServer()) { 0190 Output *output = delegate->output(); 0191 const std::chrono::milliseconds frameTime = 0192 std::chrono::duration_cast<std::chrono::milliseconds>(output->renderLoop()->lastPresentationTimestamp()); 0193 0194 const QList<Item *> items = m_containerItem->sortedChildItems(); 0195 for (Item *item : items) { 0196 if (!item->isVisible()) { 0197 continue; 0198 } 0199 Window *window = static_cast<WindowItem *>(item)->window(); 0200 if (!window->isOnOutput(output)) { 0201 continue; 0202 } 0203 if (auto surface = window->surface()) { 0204 surface->traverseTree([&frameTime, &frame, &output](SurfaceInterface *surface) { 0205 surface->frameRendered(frameTime.count()); 0206 if (auto feedback = surface->takePresentationFeedback(output)) { 0207 frame->addFeedback(std::move(feedback)); 0208 } 0209 }); 0210 } 0211 } 0212 0213 if (m_dndIcon) { 0214 if (auto surface = m_dndIcon->surface()) { 0215 surface->traverseTree([&frameTime, &frame, &output](SurfaceInterface *surface) { 0216 surface->frameRendered(frameTime.count()); 0217 if (auto feedback = surface->takePresentationFeedback(output)) { 0218 frame->addFeedback(std::move(feedback)); 0219 } 0220 }); 0221 } 0222 } 0223 } 0224 } 0225 0226 QRegion WorkspaceScene::prePaint(SceneDelegate *delegate) 0227 { 0228 createStackingOrder(); 0229 0230 painted_delegate = delegate; 0231 if (kwinApp()->operationMode() == Application::OperationModeX11) { 0232 painted_screen = workspace()->outputs().constFirst(); 0233 } else { 0234 painted_screen = painted_delegate->output(); 0235 } 0236 0237 const RenderLoop *renderLoop = painted_screen->renderLoop(); 0238 const std::chrono::milliseconds presentTime = 0239 std::chrono::duration_cast<std::chrono::milliseconds>(renderLoop->nextPresentationTimestamp()); 0240 0241 if (presentTime > m_expectedPresentTimestamp) { 0242 m_expectedPresentTimestamp = presentTime; 0243 } 0244 0245 // preparation step 0246 effects->startPaint(); 0247 0248 ScreenPrePaintData prePaintData; 0249 prePaintData.mask = 0; 0250 prePaintData.screen = painted_screen; 0251 0252 effects->makeOpenGLContextCurrent(); 0253 Q_EMIT preFrameRender(); 0254 0255 effects->prePaintScreen(prePaintData, m_expectedPresentTimestamp); 0256 m_paintContext.damage = prePaintData.paint; 0257 m_paintContext.mask = prePaintData.mask; 0258 m_paintContext.phase2Data.clear(); 0259 0260 if (m_paintContext.mask & (PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS)) { 0261 preparePaintGenericScreen(); 0262 } else { 0263 preparePaintSimpleScreen(); 0264 } 0265 0266 return m_paintContext.damage.translated(-delegate->viewport().topLeft()); 0267 } 0268 0269 static void resetRepaintsHelper(Item *item, SceneDelegate *delegate) 0270 { 0271 item->resetRepaints(delegate); 0272 0273 const auto childItems = item->childItems(); 0274 for (Item *childItem : childItems) { 0275 resetRepaintsHelper(childItem, delegate); 0276 } 0277 } 0278 0279 static void accumulateRepaints(Item *item, SceneDelegate *delegate, QRegion *repaints) 0280 { 0281 *repaints += item->repaints(delegate); 0282 item->resetRepaints(delegate); 0283 0284 const auto childItems = item->childItems(); 0285 for (Item *childItem : childItems) { 0286 accumulateRepaints(childItem, delegate, repaints); 0287 } 0288 } 0289 0290 void WorkspaceScene::preparePaintGenericScreen() 0291 { 0292 for (WindowItem *windowItem : std::as_const(stacking_order)) { 0293 resetRepaintsHelper(windowItem, painted_delegate); 0294 0295 WindowPrePaintData data; 0296 data.mask = m_paintContext.mask; 0297 data.paint = infiniteRegion(); // no clipping, so doesn't really matter 0298 0299 effects->prePaintWindow(windowItem->effectWindow(), data, m_expectedPresentTimestamp); 0300 m_paintContext.phase2Data.append(Phase2Data{ 0301 .item = windowItem, 0302 .region = infiniteRegion(), 0303 .opaque = data.opaque, 0304 .mask = data.mask, 0305 }); 0306 } 0307 0308 m_paintContext.damage = infiniteRegion(); 0309 } 0310 0311 void WorkspaceScene::preparePaintSimpleScreen() 0312 { 0313 for (WindowItem *windowItem : std::as_const(stacking_order)) { 0314 Window *window = windowItem->window(); 0315 WindowPrePaintData data; 0316 data.mask = m_paintContext.mask; 0317 accumulateRepaints(windowItem, painted_delegate, &data.paint); 0318 0319 // Clip out the decoration for opaque windows; the decoration is drawn in the second pass. 0320 if (window->opacity() == 1.0) { 0321 const SurfaceItem *surfaceItem = windowItem->surfaceItem(); 0322 if (Q_LIKELY(surfaceItem)) { 0323 data.opaque = surfaceItem->mapToGlobal(surfaceItem->opaque()); 0324 } 0325 0326 const DecorationItem *decorationItem = windowItem->decorationItem(); 0327 if (decorationItem) { 0328 data.opaque += decorationItem->mapToGlobal(decorationItem->opaque()); 0329 } 0330 } 0331 0332 effects->prePaintWindow(windowItem->effectWindow(), data, m_expectedPresentTimestamp); 0333 m_paintContext.phase2Data.append(Phase2Data{ 0334 .item = windowItem, 0335 .region = data.paint, 0336 .opaque = data.opaque, 0337 .mask = data.mask, 0338 }); 0339 } 0340 0341 // Perform an occlusion cull pass, remove surface damage occluded by opaque windows. 0342 QRegion opaque; 0343 for (int i = m_paintContext.phase2Data.size() - 1; i >= 0; --i) { 0344 const auto &paintData = m_paintContext.phase2Data.at(i); 0345 m_paintContext.damage += paintData.region - opaque; 0346 if (!(paintData.mask & (PAINT_WINDOW_TRANSLUCENT | PAINT_WINDOW_TRANSFORMED))) { 0347 opaque += paintData.opaque; 0348 } 0349 } 0350 0351 if (m_dndIcon) { 0352 accumulateRepaints(m_dndIcon.get(), painted_delegate, &m_paintContext.damage); 0353 } 0354 } 0355 0356 void WorkspaceScene::postPaint() 0357 { 0358 for (WindowItem *w : std::as_const(stacking_order)) { 0359 effects->postPaintWindow(w->effectWindow()); 0360 } 0361 0362 effects->postPaintScreen(); 0363 0364 clearStackingOrder(); 0365 } 0366 0367 void WorkspaceScene::paint(const RenderTarget &renderTarget, const QRegion ®ion) 0368 { 0369 Output *output = kwinApp()->operationMode() == Application::OperationMode::OperationModeX11 ? nullptr : painted_screen; 0370 RenderViewport viewport(output ? output->geometryF() : workspace()->geometry(), output ? output->scale() : 1, renderTarget); 0371 0372 m_renderer->beginFrame(renderTarget, viewport); 0373 0374 effects->paintScreen(renderTarget, viewport, m_paintContext.mask, region, painted_screen); 0375 m_paintScreenCount = 0; 0376 Q_EMIT frameRendered(); 0377 0378 m_renderer->endFrame(); 0379 } 0380 0381 // the function that'll be eventually called by paintScreen() above 0382 void WorkspaceScene::finalPaintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion ®ion, Output *screen) 0383 { 0384 m_paintScreenCount++; 0385 if (mask & (PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS)) { 0386 paintGenericScreen(renderTarget, viewport, mask, screen); 0387 } else { 0388 paintSimpleScreen(renderTarget, viewport, mask, region); 0389 } 0390 } 0391 0392 // The generic painting code that can handle even transformations. 0393 // It simply paints bottom-to-top. 0394 void WorkspaceScene::paintGenericScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int, Output *screen) 0395 { 0396 if (m_paintContext.mask & PAINT_SCREEN_BACKGROUND_FIRST) { 0397 if (m_paintScreenCount == 1) { 0398 m_renderer->renderBackground(renderTarget, viewport, infiniteRegion()); 0399 } 0400 } else { 0401 m_renderer->renderBackground(renderTarget, viewport, infiniteRegion()); 0402 } 0403 0404 for (const Phase2Data &paintData : std::as_const(m_paintContext.phase2Data)) { 0405 paintWindow(renderTarget, viewport, paintData.item, paintData.mask, paintData.region); 0406 } 0407 } 0408 0409 // The optimized case without any transformations at all. 0410 // It can paint only the requested region and can use clipping 0411 // to reduce painting and improve performance. 0412 void WorkspaceScene::paintSimpleScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int, const QRegion ®ion) 0413 { 0414 // This is the occlusion culling pass 0415 QRegion visible = region; 0416 for (int i = m_paintContext.phase2Data.size() - 1; i >= 0; --i) { 0417 Phase2Data *data = &m_paintContext.phase2Data[i]; 0418 data->region = visible; 0419 0420 if (!(data->mask & PAINT_WINDOW_TRANSFORMED)) { 0421 data->region &= data->item->mapToGlobal(data->item->boundingRect()).toAlignedRect(); 0422 0423 if (!(data->mask & PAINT_WINDOW_TRANSLUCENT)) { 0424 visible -= data->opaque; 0425 } 0426 } 0427 } 0428 0429 m_renderer->renderBackground(renderTarget, viewport, visible); 0430 0431 for (const Phase2Data &paintData : std::as_const(m_paintContext.phase2Data)) { 0432 paintWindow(renderTarget, viewport, paintData.item, paintData.mask, paintData.region); 0433 } 0434 0435 if (m_dndIcon) { 0436 const QRegion repaint = region & m_dndIcon->mapToGlobal(m_dndIcon->boundingRect()).toRect(); 0437 if (!repaint.isEmpty()) { 0438 m_renderer->renderItem(renderTarget, viewport, m_dndIcon.get(), 0, repaint, WindowPaintData(viewport.projectionMatrix())); 0439 } 0440 } 0441 } 0442 0443 void WorkspaceScene::createStackingOrder() 0444 { 0445 QList<Item *> items = m_containerItem->sortedChildItems(); 0446 for (Item *item : std::as_const(items)) { 0447 WindowItem *windowItem = static_cast<WindowItem *>(item); 0448 if (windowItem->isVisible()) { 0449 stacking_order.append(windowItem); 0450 } 0451 } 0452 } 0453 0454 void WorkspaceScene::clearStackingOrder() 0455 { 0456 stacking_order.clear(); 0457 } 0458 0459 void WorkspaceScene::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, WindowItem *item, int mask, const QRegion ®ion) 0460 { 0461 if (region.isEmpty()) { // completely clipped 0462 return; 0463 } 0464 0465 WindowPaintData data(viewport.projectionMatrix()); 0466 effects->paintWindow(renderTarget, viewport, item->effectWindow(), mask, region, data); 0467 } 0468 0469 // the function that'll be eventually called by paintWindow() above 0470 void WorkspaceScene::finalPaintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion ®ion, WindowPaintData &data) 0471 { 0472 effects->drawWindow(renderTarget, viewport, w, mask, region, data); 0473 } 0474 0475 // will be eventually called from drawWindow() 0476 void WorkspaceScene::finalDrawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion ®ion, WindowPaintData &data) 0477 { 0478 m_renderer->renderItem(renderTarget, viewport, w->windowItem(), mask, region, data); 0479 } 0480 0481 bool WorkspaceScene::makeOpenGLContextCurrent() 0482 { 0483 return false; 0484 } 0485 0486 void WorkspaceScene::doneOpenGLContextCurrent() 0487 { 0488 } 0489 0490 bool WorkspaceScene::supportsNativeFence() const 0491 { 0492 return false; 0493 } 0494 0495 } // namespace 0496 0497 #include "moc_workspacescene.cpp"