File indexing completed on 2024-05-19 05:32:22

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