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 &region)
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 &region, 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 &region)
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 &region)
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 &region, 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 &region, 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