KWin
Loading...
Searching...
No Matches
workspacescene.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9
10/*
11 Design:
12
13 When compositing is turned on, XComposite extension is used to redirect
14 drawing of windows to pixmaps and XDamage extension is used to get informed
15 about damage (changes) to window contents. This code is mostly in composite.cpp .
16
17 Compositor::performCompositing() starts one painting pass. Painting is done
18 by painting the screen, which in turn paints every window. Painting can be affected
19 using effects, which are chained. E.g. painting a screen means that actually
20 paintScreen() of the first effect is called, which possibly does modifications
21 and calls next effect's paintScreen() and so on, until Scene::finalPaintScreen()
22 is called.
23
24 There are 3 phases of every paint (not necessarily done together):
25 The pre-paint phase, the paint phase and the post-paint phase.
26
27 The pre-paint phase is used to find out about how the painting will be actually
28 done (i.e. what the effects will do). For example when only a part of the screen
29 needs to be updated and no effect will do any transformation it is possible to use
30 an optimized paint function. How the painting will be done is controlled
31 by the mask argument, see PAINT_WINDOW_* and PAINT_SCREEN_* flags in scene.h .
32 For example an effect that decides to paint a normal windows as translucent
33 will need to modify the mask in its prePaintWindow() to include
34 the PAINT_WINDOW_TRANSLUCENT flag. The paintWindow() function will then get
35 the mask with this flag turned on and will also paint using transparency.
36
37 The paint pass does the actual painting, based on the information collected
38 using the pre-paint pass. After running through the effects' paintScreen()
39 either paintGenericScreen() or optimized paintSimpleScreen() are called.
40 Those call paintWindow() on windows (not necessarily all), possibly using
41 clipping to optimize performance and calling paintWindow() first with only
42 PAINT_WINDOW_OPAQUE to paint the opaque parts and then later
43 with PAINT_WINDOW_TRANSLUCENT to paint the transparent parts. Function
44 paintWindow() again goes through effects' paintWindow() until
45 finalPaintWindow() is called, which calls the window's performPaint() to
46 do the actual painting.
47
48 The post-paint can be used for cleanups and is also used for scheduling
49 repaints during the next painting pass for animations. Effects wanting to
50 repaint certain parts can manually damage them during post-paint and repaint
51 of these parts will be done during the next paint pass.
52
53*/
54
56#include "compositor.h"
57#include "core/output.h"
58#include "core/renderbackend.h"
59#include "core/renderlayer.h"
60#include "core/renderloop.h"
61#include "core/renderviewport.h"
63#include "internalwindow.h"
64#include "scene/dndiconitem.h"
65#include "scene/itemrenderer.h"
66#include "scene/shadowitem.h"
67#include "scene/surfaceitem.h"
68#include "scene/windowitem.h"
69#include "shadow.h"
70#include "wayland/seat.h"
71#include "wayland/surface.h"
72#include "wayland_server.h"
73#include "waylandwindow.h"
74#include "workspace.h"
75#include "x11window.h"
76
77#include <QtMath>
78
79namespace KWin
80{
81
82//****************************************
83// Scene
84//****************************************
85
86WorkspaceScene::WorkspaceScene(std::unique_ptr<ItemRenderer> renderer)
87 : Scene(std::move(renderer))
88 , m_containerItem(std::make_unique<Item>(this))
89{
91 connect(workspace(), &Workspace::geometryChanged, this, [this]() {
93 });
94
95 if (waylandServer()) {
96 connect(waylandServer()->seat(), &SeatInterface::dragStarted, this, &WorkspaceScene::createDndIconItem);
97 connect(waylandServer()->seat(), &SeatInterface::dragEnded, this, &WorkspaceScene::destroyDndIconItem);
98 }
99}
100
104
105void WorkspaceScene::createDndIconItem()
106{
107 DragAndDropIcon *dragIcon = waylandServer()->seat()->dragIcon();
108 if (!dragIcon) {
109 return;
110 }
111 m_dndIcon = std::make_unique<DragAndDropIconItem>(dragIcon, this);
112 if (waylandServer()->seat()->isDragPointer()) {
113 auto updatePosition = [this]() {
114 const auto pointerPos = waylandServer()->seat()->pointerPos();
115 m_dndIcon->setPosition(pointerPos);
116 m_dndIcon->setOutput(workspace()->outputAt(pointerPos));
117 };
118
119 updatePosition();
120 connect(waylandServer()->seat(), &SeatInterface::pointerPosChanged, m_dndIcon.get(), updatePosition);
121 } else if (waylandServer()->seat()->isDragTouch()) {
122 auto updatePosition = [this]() {
123 const auto touchPos = waylandServer()->seat()->firstTouchPointPosition();
124 m_dndIcon->setPosition(touchPos);
125 m_dndIcon->setOutput(workspace()->outputAt(touchPos));
126 };
127
128 updatePosition();
129 connect(waylandServer()->seat(), &SeatInterface::touchMoved, m_dndIcon.get(), updatePosition);
130 }
131}
132
133void WorkspaceScene::destroyDndIconItem()
134{
135 m_dndIcon.reset();
136}
137
139{
140 return m_containerItem.get();
141}
142
143static SurfaceItem *findTopMostSurface(SurfaceItem *item)
144{
145 const QList<Item *> children = item->childItems();
146 if (children.isEmpty()) {
147 return item;
148 } else {
149 return findTopMostSurface(static_cast<SurfaceItem *>(children.constLast()));
150 }
151}
152
154{
155 if (!waylandServer()) {
156 return nullptr;
157 }
158 SurfaceItem *candidate = nullptr;
160 for (int i = stacking_order.count() - 1; i >= 0; i--) {
161 WindowItem *windowItem = stacking_order[i];
162 Window *window = windowItem->window();
163 if (window->isOnOutput(painted_screen) && window->opacity() > 0) {
164 if (!window->isClient() || !window->isFullScreen() || window->opacity() != 1.0) {
165 break;
166 }
167 if (!windowItem->surfaceItem()) {
168 break;
169 }
170 SurfaceItem *topMost = findTopMostSurface(windowItem->surfaceItem());
171 // the subsurface has to be able to cover the whole window
172 if (topMost->position() != QPoint(0, 0)) {
173 break;
174 }
175 // and it has to be completely opaque
176 if (!topMost->opaque().contains(QRect(0, 0, window->width(), window->height()))) {
177 break;
178 }
179 candidate = topMost;
180 break;
181 }
182 }
183 }
184 return candidate;
185}
186
188{
189 if (waylandServer()) {
190 Output *output = delegate->output();
191 const std::chrono::milliseconds frameTime =
192 std::chrono::duration_cast<std::chrono::milliseconds>(output->renderLoop()->lastPresentationTimestamp());
193
194 const QList<Item *> items = m_containerItem->sortedChildItems();
195 for (Item *item : items) {
196 if (!item->isVisible()) {
197 continue;
198 }
199 Window *window = static_cast<WindowItem *>(item)->window();
200 if (!window->isOnOutput(output)) {
201 continue;
202 }
203 if (auto surface = window->surface()) {
204 surface->traverseTree([&frameTime, &frame, &output](SurfaceInterface *surface) {
205 surface->frameRendered(frameTime.count());
206 if (auto feedback = surface->takePresentationFeedback(output)) {
207 frame->addFeedback(std::move(feedback));
208 }
209 });
210 }
211 }
212
213 if (m_dndIcon) {
214 if (auto surface = m_dndIcon->surface()) {
215 surface->traverseTree([&frameTime, &frame, &output](SurfaceInterface *surface) {
216 surface->frameRendered(frameTime.count());
217 if (auto feedback = surface->takePresentationFeedback(output)) {
218 frame->addFeedback(std::move(feedback));
219 }
220 });
221 }
222 }
223 }
224}
225
227{
229
230 painted_delegate = delegate;
231 if (kwinApp()->operationMode() == Application::OperationModeX11) {
232 painted_screen = workspace()->outputs().constFirst();
233 } else {
235 }
236
237 const RenderLoop *renderLoop = painted_screen->renderLoop();
238 const std::chrono::milliseconds presentTime =
239 std::chrono::duration_cast<std::chrono::milliseconds>(renderLoop->nextPresentationTimestamp());
240
241 if (presentTime > m_expectedPresentTimestamp) {
242 m_expectedPresentTimestamp = presentTime;
243 }
244
245 // preparation step
247
248 ScreenPrePaintData prePaintData;
249 prePaintData.mask = 0;
250 prePaintData.screen = painted_screen;
251
253 Q_EMIT preFrameRender();
254
255 effects->prePaintScreen(prePaintData, m_expectedPresentTimestamp);
256 m_paintContext.damage = prePaintData.paint;
257 m_paintContext.mask = prePaintData.mask;
258 m_paintContext.phase2Data.clear();
259
262 } else {
264 }
265
266 return m_paintContext.damage.translated(-delegate->viewport().topLeft());
267}
268
269static void resetRepaintsHelper(Item *item, SceneDelegate *delegate)
270{
271 item->resetRepaints(delegate);
272
273 const auto childItems = item->childItems();
274 for (Item *childItem : childItems) {
275 resetRepaintsHelper(childItem, delegate);
276 }
277}
278
279static void accumulateRepaints(Item *item, SceneDelegate *delegate, QRegion *repaints)
280{
281 *repaints += item->repaints(delegate);
282 item->resetRepaints(delegate);
283
284 const auto childItems = item->childItems();
285 for (Item *childItem : childItems) {
286 accumulateRepaints(childItem, delegate, repaints);
287 }
288}
289
291{
292 for (WindowItem *windowItem : std::as_const(stacking_order)) {
293 resetRepaintsHelper(windowItem, painted_delegate);
294
296 data.mask = m_paintContext.mask;
297 data.paint = infiniteRegion(); // no clipping, so doesn't really matter
298
299 effects->prePaintWindow(windowItem->effectWindow(), data, m_expectedPresentTimestamp);
300 m_paintContext.phase2Data.append(Phase2Data{
301 .item = windowItem,
302 .region = infiniteRegion(),
303 .opaque = data.opaque,
304 .mask = data.mask,
305 });
306 }
307
308 m_paintContext.damage = infiniteRegion();
309}
310
312{
313 for (WindowItem *windowItem : std::as_const(stacking_order)) {
314 Window *window = windowItem->window();
316 data.mask = m_paintContext.mask;
317 accumulateRepaints(windowItem, painted_delegate, &data.paint);
318
319 // Clip out the decoration for opaque windows; the decoration is drawn in the second pass.
320 if (window->opacity() == 1.0) {
321 const SurfaceItem *surfaceItem = windowItem->surfaceItem();
322 if (Q_LIKELY(surfaceItem)) {
323 data.opaque = surfaceItem->mapToGlobal(surfaceItem->opaque());
324 }
325
326 const DecorationItem *decorationItem = windowItem->decorationItem();
327 if (decorationItem) {
328 data.opaque += decorationItem->mapToGlobal(decorationItem->opaque());
329 }
330 }
331
332 effects->prePaintWindow(windowItem->effectWindow(), data, m_expectedPresentTimestamp);
333 m_paintContext.phase2Data.append(Phase2Data{
334 .item = windowItem,
335 .region = data.paint,
336 .opaque = data.opaque,
337 .mask = data.mask,
338 });
339 }
340
341 // Perform an occlusion cull pass, remove surface damage occluded by opaque windows.
342 QRegion opaque;
343 for (int i = m_paintContext.phase2Data.size() - 1; i >= 0; --i) {
344 const auto &paintData = m_paintContext.phase2Data.at(i);
345 m_paintContext.damage += paintData.region - opaque;
346 if (!(paintData.mask & (PAINT_WINDOW_TRANSLUCENT | PAINT_WINDOW_TRANSFORMED))) {
347 opaque += paintData.opaque;
348 }
349 }
350
351 if (m_dndIcon) {
352 accumulateRepaints(m_dndIcon.get(), painted_delegate, &m_paintContext.damage);
353 }
354}
355
357{
358 for (WindowItem *w : std::as_const(stacking_order)) {
359 effects->postPaintWindow(w->effectWindow());
360 }
361
363
365}
366
367void WorkspaceScene::paint(const RenderTarget &renderTarget, const QRegion &region)
368{
369 Output *output = kwinApp()->operationMode() == Application::OperationMode::OperationModeX11 ? nullptr : painted_screen;
370 RenderViewport viewport(output ? output->geometryF() : workspace()->geometry(), output ? output->scale() : 1, renderTarget);
371
372 m_renderer->beginFrame(renderTarget, viewport);
373
374 effects->paintScreen(renderTarget, viewport, m_paintContext.mask, region, painted_screen);
375 m_paintScreenCount = 0;
376 Q_EMIT frameRendered();
377
378 m_renderer->endFrame();
379}
380
381// the function that'll be eventually called by paintScreen() above
382void WorkspaceScene::finalPaintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion &region, Output *screen)
383{
384 m_paintScreenCount++;
386 paintGenericScreen(renderTarget, viewport, mask, screen);
387 } else {
388 paintSimpleScreen(renderTarget, viewport, mask, region);
389 }
390}
391
392// The generic painting code that can handle even transformations.
393// It simply paints bottom-to-top.
394void WorkspaceScene::paintGenericScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int, Output *screen)
395{
396 if (m_paintContext.mask & PAINT_SCREEN_BACKGROUND_FIRST) {
397 if (m_paintScreenCount == 1) {
398 m_renderer->renderBackground(renderTarget, viewport, infiniteRegion());
399 }
400 } else {
401 m_renderer->renderBackground(renderTarget, viewport, infiniteRegion());
402 }
403
404 for (const Phase2Data &paintData : std::as_const(m_paintContext.phase2Data)) {
405 paintWindow(renderTarget, viewport, paintData.item, paintData.mask, paintData.region);
406 }
407}
408
409// The optimized case without any transformations at all.
410// It can paint only the requested region and can use clipping
411// to reduce painting and improve performance.
412void WorkspaceScene::paintSimpleScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int, const QRegion &region)
413{
414 // This is the occlusion culling pass
415 QRegion visible = region;
416 for (int i = m_paintContext.phase2Data.size() - 1; i >= 0; --i) {
417 Phase2Data *data = &m_paintContext.phase2Data[i];
418 data->region = visible;
419
420 if (!(data->mask & PAINT_WINDOW_TRANSFORMED)) {
421 data->region &= data->item->mapToGlobal(data->item->boundingRect()).toAlignedRect();
422
423 if (!(data->mask & PAINT_WINDOW_TRANSLUCENT)) {
424 visible -= data->opaque;
425 }
426 }
427 }
428
429 m_renderer->renderBackground(renderTarget, viewport, visible);
430
431 for (const Phase2Data &paintData : std::as_const(m_paintContext.phase2Data)) {
432 paintWindow(renderTarget, viewport, paintData.item, paintData.mask, paintData.region);
433 }
434
435 if (m_dndIcon) {
436 const QRegion repaint = region & m_dndIcon->mapToGlobal(m_dndIcon->boundingRect()).toRect();
437 if (!repaint.isEmpty()) {
438 m_renderer->renderItem(renderTarget, viewport, m_dndIcon.get(), 0, repaint, WindowPaintData(viewport.projectionMatrix()));
439 }
440 }
441}
442
444{
445 QList<Item *> items = m_containerItem->sortedChildItems();
446 for (Item *item : std::as_const(items)) {
447 WindowItem *windowItem = static_cast<WindowItem *>(item);
448 if (windowItem->isVisible()) {
449 stacking_order.append(windowItem);
450 }
451 }
452}
453
458
459void WorkspaceScene::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, WindowItem *item, int mask, const QRegion &region)
460{
461 if (region.isEmpty()) { // completely clipped
462 return;
463 }
464
465 WindowPaintData data(viewport.projectionMatrix());
466 effects->paintWindow(renderTarget, viewport, item->effectWindow(), mask, region, data);
467}
468
469// the function that'll be eventually called by paintWindow() above
470void WorkspaceScene::finalPaintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion &region, WindowPaintData &data)
471{
472 effects->drawWindow(renderTarget, viewport, w, mask, region, data);
473}
474
475// will be eventually called from drawWindow()
476void WorkspaceScene::finalDrawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion &region, WindowPaintData &data)
477{
478 m_renderer->renderItem(renderTarget, viewport, w->windowItem(), mask, region, data);
479}
480
482{
483 return false;
484}
485
489
491{
492 return false;
493}
494
495} // namespace
496
497#include "moc_workspacescene.cpp"
@ OperationModeX11
KWin uses only X11 for managing windows and compositing.
Definition main.h:87
QRegion opaque() const override final
Representation of a window used by/for Effect classes.
WindowItem * windowItem() const
void drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion &region, WindowPaintData &data)
void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion &region, Output *screen)
bool blocksDirectScanout() const
bool makeOpenGLContextCurrent()
Makes the OpenGL compositing context current.
void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion &region, WindowPaintData &data)
void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
void prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime)
void postPaintWindow(EffectWindow *w)
QPointF position() const
Definition item.cpp:117
virtual QRegion opaque() const
Definition item.cpp:182
QRectF boundingRect() const
Definition item.cpp:157
bool isVisible() const
Definition item.cpp:383
QRegion mapToGlobal(const QRegion &region) const
Definition item.cpp:210
void resetRepaints(SceneDelegate *delegate)
Definition item.cpp:368
QList< Item * > childItems() const
Definition item.cpp:112
qreal scale() const
Definition output.cpp:455
QRectF geometryF() const
Definition output.cpp:465
virtual RenderLoop * renderLoop() const =0
std::chrono::nanoseconds lastPresentationTimestamp() const
std::chrono::nanoseconds nextPresentationTimestamp() const
QMatrix4x4 projectionMatrix() const
Output * output() const
Definition scene.cpp:52
QRect viewport() const
Definition scene.cpp:62
void setGeometry(const QRect &rect)
Definition scene.cpp:113
@ PAINT_WINDOW_TRANSLUCENT
Definition scene.h:53
@ PAINT_SCREEN_TRANSFORMED
Definition scene.h:60
@ PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS
Definition scene.h:62
@ PAINT_SCREEN_BACKGROUND_FIRST
Definition scene.h:64
@ PAINT_WINDOW_TRANSFORMED
Definition scene.h:55
QRect geometry() const
Definition scene.cpp:108
std::unique_ptr< ItemRenderer > m_renderer
Definition scene.h:94
QPointF pointerPos() const
Definition seat.cpp:437
void touchMoved(qint32 id, quint32 serial, const QPointF &globalPosition)
QPointF firstTouchPointPosition() const
Definition seat.cpp:1018
void pointerPosChanged(const QPointF &pos)
DragAndDropIcon * dragIcon() const
Definition seat.cpp:1387
Resource representing a wl_surface.
Definition surface.h:80
std::unique_ptr< PresentationFeedback > takePresentationFeedback(Output *output)
Definition surface.cpp:471
void frameRendered(quint32 msec)
Definition surface.cpp:459
SeatInterface * seat() const
virtual bool isClient() const
Definition window.cpp:302
qreal width
Definition window.h:99
SurfaceInterface * surface() const
Definition window.cpp:342
virtual bool isFullScreen() const
Definition window.cpp:3935
qreal height
Definition window.h:104
qreal opacity
Definition window.h:106
bool isOnOutput(Output *output) const
Definition window.cpp:254
SurfaceItem * surfaceItem() const
Window * window() const
EffectWindow * effectWindow() const
QList< Output * > outputs() const
Definition workspace.h:762
QRect geometry() const
void geometryChanged()
void frame(SceneDelegate *delegate, OutputFrame *frame) override
QRegion prePaint(SceneDelegate *delegate) override
SurfaceItem * scanoutCandidate() const override
QList< WindowItem * > stacking_order
virtual bool makeOpenGLContextCurrent()
void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, WindowItem *w, int mask, const QRegion &region)
virtual bool supportsNativeFence() const
Item * containerItem() const
void finalPaintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion &region, Output *screen)
void paintGenericScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, Output *screen)
void paintSimpleScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion &region)
void finalDrawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion &region, WindowPaintData &data)
virtual void doneOpenGLContextCurrent()
void finalPaintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion &region, WindowPaintData &data)
WorkspaceScene(std::unique_ptr< ItemRenderer > renderer)
SceneDelegate * painted_delegate
void paint(const RenderTarget &renderTarget, const QRegion &region) override
void postPaint() override
KWayland::Client::Seat * seat
KWIN_EXPORT QRect infiniteRegion()
Definition globals.h:234
WaylandServer * waylandServer()
Workspace * workspace()
Definition workspace.h:830
EffectsHandler * effects