kwin/src/scene.h

421 lines
12 KiB
C
Raw Normal View History

2020-08-02 22:22:19 +00:00
/*
KWin - the KDE window manager
This file is part of the KDE project.
2020-08-02 22:22:19 +00:00
SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
2020-08-02 22:22:19 +00:00
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef KWIN_SCENE_H
#define KWIN_SCENE_H
#include "kwineffects.h"
#include "renderlayerdelegate.h"
2022-01-18 08:35:52 +00:00
#include "utils/common.h"
2022-04-22 17:39:12 +00:00
#include "window.h"
#include <optional>
#include <QElapsedTimer>
#include <QMatrix4x4>
namespace KWin
{
namespace Decoration
{
class DecoratedClientImpl;
}
class Output;
class DecorationRenderer;
class Deleted;
class EffectFrameImpl;
class EffectWindowImpl;
class GLTexture;
class Item;
class RenderLoop;
class Scene;
2022-04-22 17:39:12 +00:00
class SceneWindow;
class Shadow;
class ShadowItem;
class SurfaceItem;
class SurfacePixmapInternal;
class SurfacePixmapWayland;
class SurfacePixmapX11;
class SurfaceTexture;
class WindowItem;
class ScreenLockerFilter
{
public:
ScreenLockerFilter(Scene *scene);
~ScreenLockerFilter();
2022-04-22 17:39:12 +00:00
bool filterAcceptsWindow(Window *w) const;
};
class SceneDelegate : public RenderLayerDelegate
{
Q_OBJECT
public:
explicit SceneDelegate(Scene *scene, QObject *parent = nullptr);
explicit SceneDelegate(Scene *scene, Output *output, QObject *parent = nullptr);
~SceneDelegate() override;
QRect viewport() const;
scene: Rework surface damage tracking It's not possible to get the surface damage before calling Scene::paint(), which is a big problem because it blocks proper surface damage and buffer damage calculation when walking render layer tree. This change reworks the scene compositing stages to allow getting the next surface damage before calling Scene::paint(). The main challenge is that the effects can expand the surface damage. We have to call prePaintWindow() and prePaintScreen() before actually starting painting. However, prePaintWindow() is called after starting rendering. This change makes Scene call prePaintWindow() and prePaintScreen() so it's possible to know the surface damage beforehand. Unfortunately, it's also a breaking change. Some fullscreen effects will have to adapt to the new Scene paint order. Paint hooks will be invoked in the following order: * prePaintScreen() once per frame * prePaintWindow() once per frame * paintScreen() can be called multiple times * paintWindow() can be called as many times as paintScreen() * postPaintWindow() once per frame * postPaintScreen() once per frame After walking the render layer tree, the Compositor will poke the render backend for the back buffer repair region and combine it with the surface damage to get the buffer damage, which can be passed to the render backend (in order to optimize performance with tiled gpus) and Scene::paint(), which will determine what parts of the scene have to repainted based on the buffer damage.
2022-02-16 17:13:57 +00:00
QRegion repaints() const override;
SurfaceItem *scanoutCandidate() const override;
void prePaint() override;
void postPaint() override;
void paint(RenderTarget *renderTarget, const QRegion &region) override;
private:
Scene *m_scene;
Output *m_output = nullptr;
};
class KWIN_EXPORT Scene : public QObject
2011-01-30 14:34:42 +00:00
{
Q_OBJECT
2011-01-30 14:34:42 +00:00
public:
explicit Scene(QObject *parent = nullptr);
~Scene() override;
2011-01-30 14:34:42 +00:00
class EffectFrame;
void initialize();
/**
* Schedules a repaint for the specified @a region.
*/
void addRepaint(const QRegion &region);
void addRepaint(int x, int y, int width, int height);
void addRepaintFull();
scene: Rework surface damage tracking It's not possible to get the surface damage before calling Scene::paint(), which is a big problem because it blocks proper surface damage and buffer damage calculation when walking render layer tree. This change reworks the scene compositing stages to allow getting the next surface damage before calling Scene::paint(). The main challenge is that the effects can expand the surface damage. We have to call prePaintWindow() and prePaintScreen() before actually starting painting. However, prePaintWindow() is called after starting rendering. This change makes Scene call prePaintWindow() and prePaintScreen() so it's possible to know the surface damage beforehand. Unfortunately, it's also a breaking change. Some fullscreen effects will have to adapt to the new Scene paint order. Paint hooks will be invoked in the following order: * prePaintScreen() once per frame * prePaintWindow() once per frame * paintScreen() can be called multiple times * paintWindow() can be called as many times as paintScreen() * postPaintWindow() once per frame * postPaintScreen() once per frame After walking the render layer tree, the Compositor will poke the render backend for the back buffer repair region and combine it with the surface damage to get the buffer damage, which can be passed to the render backend (in order to optimize performance with tiled gpus) and Scene::paint(), which will determine what parts of the scene have to repainted based on the buffer damage.
2022-02-16 17:13:57 +00:00
QRegion damage() const;
QRect geometry() const;
void setGeometry(const QRect &rect);
QList<SceneDelegate *> delegates() const;
void addDelegate(SceneDelegate *delegate);
void removeDelegate(SceneDelegate *delegate);
2011-01-30 14:34:42 +00:00
// Returns true if the ctor failed to properly initialize.
virtual bool initFailed() const = 0;
SurfaceItem *scanoutCandidate() const;
void prePaint(Output *output);
void postPaint();
virtual void paint(RenderTarget *renderTarget, const QRegion &region) = 0;
/**
2022-04-22 17:39:12 +00:00
* Adds the Window to the Scene.
*
* If the toplevel gets deleted, then the scene will try automatically
* to re-bind an underlying scene window to the corresponding Deleted.
*
* @param toplevel The window to be added.
* @note You can add a toplevel to scene only once.
*/
2022-04-22 17:39:12 +00:00
void addToplevel(Window *toplevel);
/**
2022-04-22 17:39:12 +00:00
* Removes the Window from the Scene.
*
* @param toplevel The window to be removed.
* @note You can remove a toplevel from the scene only once.
*/
2022-04-22 17:39:12 +00:00
void removeToplevel(Window *toplevel);
2011-01-30 14:34:42 +00:00
/**
* @brief Creates the Scene backend of an EffectFrame.
*
* @param frame The EffectFrame this Scene::EffectFrame belongs to.
*/
virtual Scene::EffectFrame *createEffectFrame(EffectFrameImpl *frame) = 0;
/**
* @brief Creates the Scene specific Shadow subclass.
*
* An implementing class has to create a proper instance. It is not allowed to
* return @c null.
*
2022-04-22 17:39:12 +00:00
* @param toplevel The Window for which the Shadow needs to be created.
*/
2022-04-22 17:39:12 +00:00
virtual Shadow *createShadow(Window *toplevel) = 0;
2011-01-30 14:34:42 +00:00
// Flags controlling how painting is done.
enum {
2022-04-22 17:39:12 +00:00
// SceneWindow (or at least part of it) will be painted opaque.
PAINT_WINDOW_OPAQUE = 1 << 0,
2022-04-22 17:39:12 +00:00
// SceneWindow (or at least part of it) will be painted translucent.
PAINT_WINDOW_TRANSLUCENT = 1 << 1,
2022-04-22 17:39:12 +00:00
// SceneWindow will be painted with transformed geometry.
PAINT_WINDOW_TRANSFORMED = 1 << 2,
2011-01-30 14:34:42 +00:00
// Paint only a region of the screen (can be optimized, cannot
// be used together with TRANSFORMED flags).
PAINT_SCREEN_REGION = 1 << 3,
2011-01-30 14:34:42 +00:00
// Whole screen will be painted with transformed geometry.
PAINT_SCREEN_TRANSFORMED = 1 << 4,
2011-01-30 14:34:42 +00:00
// At least one window will be painted with transformed geometry.
PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS = 1 << 5,
// Clear whole background as the very first step, without optimizing it
PAINT_SCREEN_BACKGROUND_FIRST = 1 << 6,
// PAINT_DECORATION_ONLY = 1 << 7 has been removed
2022-04-22 17:39:12 +00:00
// SceneWindow will be painted with a lanczos filter.
2012-03-02 13:03:05 +00:00
PAINT_WINDOW_LANCZOS = 1 << 8
// PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_WITHOUT_FULL_REPAINTS = 1 << 9 has been removed
};
Better handling for making the compositing OpenGL context current With QtQuick2 it's possible that the scene graph rendering context either lives in an own thread or uses the main GUI thread. In the latter case it's the same thread as our compositing OpenGL context lives in. This means our basic assumption that between two rendering passes the context stays current does not hold. The code already ensured that before we start a rendering pass the context is made current, but there are many more possible cases. If we use OpenGL in areas not triggered by the rendering loop but in response to other events the context needs to be made current. This includes the loading and unloading of effects (some effects use OpenGL in the static effect check, in the ctor and dtor), background loading of texture data, lazy loading after first usage invoked by shortcut, etc. etc. To properly handle these cases new methods are added to EffectsHandler to make the compositing OpenGL context current. These calls delegate down into the scene. On non-OpenGL scenes they are noop, but on OpenGL they go into the backend and make the context current. In addition they ensure that Qt doesn't think that it's QOpenGLContext is current by calling doneCurrent() on the QOpenGLContext::currentContext(). This unfortunately causes an additional call to makeCurrent with a null context, but there is no other way to tell Qt - it doesn't notice when a different context is made current with low level API calls. In the multi-threaded architecture this doesn't matter as ::currentContext() returns null. A short evaluation showed that a transition to QOpenGLContext doesn't seem feasible. Qt only supports either GLX or EGL while KWin supports both and when entering the transition phase for Wayland, it would become extremely tricky if our native platform is X11, but we want a Wayland EGL context. A future solution might be to have a "KWin-QPA plugin" which uses either xcb or Wayland and hides everything from Qt. The API documentation is extended to describe when the effects-framework ensures that an OpenGL context is current. The effects are changed to make the context current in cases where it's not guaranteed. This has been done by looking for creation or deletion of GLTextures and Shaders. If there are other OpenGL usages outside the rendering loop, ctor/dtor this needs to be changed, too.
2013-11-22 14:05:36 +00:00
virtual bool makeOpenGLContextCurrent();
virtual void doneOpenGLContextCurrent();
virtual bool supportsNativeFence() const;
Better handling for making the compositing OpenGL context current With QtQuick2 it's possible that the scene graph rendering context either lives in an own thread or uses the main GUI thread. In the latter case it's the same thread as our compositing OpenGL context lives in. This means our basic assumption that between two rendering passes the context stays current does not hold. The code already ensured that before we start a rendering pass the context is made current, but there are many more possible cases. If we use OpenGL in areas not triggered by the rendering loop but in response to other events the context needs to be made current. This includes the loading and unloading of effects (some effects use OpenGL in the static effect check, in the ctor and dtor), background loading of texture data, lazy loading after first usage invoked by shortcut, etc. etc. To properly handle these cases new methods are added to EffectsHandler to make the compositing OpenGL context current. These calls delegate down into the scene. On non-OpenGL scenes they are noop, but on OpenGL they go into the backend and make the context current. In addition they ensure that Qt doesn't think that it's QOpenGLContext is current by calling doneCurrent() on the QOpenGLContext::currentContext(). This unfortunately causes an additional call to makeCurrent with a null context, but there is no other way to tell Qt - it doesn't notice when a different context is made current with low level API calls. In the multi-threaded architecture this doesn't matter as ::currentContext() returns null. A short evaluation showed that a transition to QOpenGLContext doesn't seem feasible. Qt only supports either GLX or EGL while KWin supports both and when entering the transition phase for Wayland, it would become extremely tricky if our native platform is X11, but we want a Wayland EGL context. A future solution might be to have a "KWin-QPA plugin" which uses either xcb or Wayland and hides everything from Qt. The API documentation is extended to describe when the effects-framework ensures that an OpenGL context is current. The effects are changed to make the context current in cases where it's not guaranteed. This has been done by looking for creation or deletion of GLTextures and Shaders. If there are other OpenGL usages outside the rendering loop, ctor/dtor this needs to be changed, too.
2013-11-22 14:05:36 +00:00
virtual QMatrix4x4 screenProjectionMatrix() const;
virtual DecorationRenderer *createDecorationRenderer(Decoration::DecoratedClientImpl *) = 0;
/**
* Whether the Scene is able to drive animations.
* This is used as a hint to the effects system which effects can be supported.
* If the Scene performs software rendering it is supposed to return @c false,
* if rendering is hardware accelerated it should return @c true.
*/
virtual bool animationsSupported() const = 0;
/**
* The QPainter used by a QPainter based compositor scene.
* Default implementation returns @c nullptr;
*/
virtual QPainter *scenePainter() const;
/**
* The backend specific extensions (e.g. EGL/GLX extensions).
*
* Not the OpenGL (ES) extension!
*
* Default implementation returns empty list
*/
virtual QVector<QByteArray> openGLPlatformInterfaceExtensions() const;
virtual QSharedPointer<GLTexture> textureForOutput(Output *output) const
{
Q_UNUSED(output);
return {};
}
virtual SurfaceTexture *createSurfaceTextureInternal(SurfacePixmapInternal *pixmap);
virtual SurfaceTexture *createSurfaceTextureX11(SurfacePixmapX11 *pixmap);
virtual SurfaceTexture *createSurfaceTextureWayland(SurfacePixmapWayland *pixmap);
virtual void paintDesktop(int desktop, int mask, const QRegion &region, ScreenPaintData &data);
QMatrix4x4 renderTargetProjectionMatrix() const;
QRect renderTargetRect() const;
void setRenderTargetRect(const QRect &rect);
qreal renderTargetScale() const;
void setRenderTargetScale(qreal scale);
QRegion mapToRenderTarget(const QRegion &region) const;
Q_SIGNALS:
void frameRendered();
public Q_SLOTS:
// a window has been closed
2022-04-22 17:39:12 +00:00
void windowClosed(Window *c, Deleted *deleted);
2011-01-30 14:34:42 +00:00
protected:
2022-04-22 17:39:12 +00:00
virtual SceneWindow *createWindow(Window *toplevel) = 0;
void createStackingOrder();
void clearStackingOrder();
2011-01-30 14:34:42 +00:00
// shared implementation, starts painting the screen
scene: Rework surface damage tracking It's not possible to get the surface damage before calling Scene::paint(), which is a big problem because it blocks proper surface damage and buffer damage calculation when walking render layer tree. This change reworks the scene compositing stages to allow getting the next surface damage before calling Scene::paint(). The main challenge is that the effects can expand the surface damage. We have to call prePaintWindow() and prePaintScreen() before actually starting painting. However, prePaintWindow() is called after starting rendering. This change makes Scene call prePaintWindow() and prePaintScreen() so it's possible to know the surface damage beforehand. Unfortunately, it's also a breaking change. Some fullscreen effects will have to adapt to the new Scene paint order. Paint hooks will be invoked in the following order: * prePaintScreen() once per frame * prePaintWindow() once per frame * paintScreen() can be called multiple times * paintWindow() can be called as many times as paintScreen() * postPaintWindow() once per frame * postPaintScreen() once per frame After walking the render layer tree, the Compositor will poke the render backend for the back buffer repair region and combine it with the surface damage to get the buffer damage, which can be passed to the render backend (in order to optimize performance with tiled gpus) and Scene::paint(), which will determine what parts of the scene have to repainted based on the buffer damage.
2022-02-16 17:13:57 +00:00
void paintScreen(const QRegion &region);
2011-01-30 14:34:42 +00:00
friend class EffectsHandlerImpl;
// called after all effects had their paintScreen() called
void finalPaintScreen(int mask, const QRegion &region, ScreenPaintData &data);
2011-01-30 14:34:42 +00:00
// shared implementation of painting the screen in the generic
// (unoptimized) way
scene: Rework surface damage tracking It's not possible to get the surface damage before calling Scene::paint(), which is a big problem because it blocks proper surface damage and buffer damage calculation when walking render layer tree. This change reworks the scene compositing stages to allow getting the next surface damage before calling Scene::paint(). The main challenge is that the effects can expand the surface damage. We have to call prePaintWindow() and prePaintScreen() before actually starting painting. However, prePaintWindow() is called after starting rendering. This change makes Scene call prePaintWindow() and prePaintScreen() so it's possible to know the surface damage beforehand. Unfortunately, it's also a breaking change. Some fullscreen effects will have to adapt to the new Scene paint order. Paint hooks will be invoked in the following order: * prePaintScreen() once per frame * prePaintWindow() once per frame * paintScreen() can be called multiple times * paintWindow() can be called as many times as paintScreen() * postPaintWindow() once per frame * postPaintScreen() once per frame After walking the render layer tree, the Compositor will poke the render backend for the back buffer repair region and combine it with the surface damage to get the buffer damage, which can be passed to the render backend (in order to optimize performance with tiled gpus) and Scene::paint(), which will determine what parts of the scene have to repainted based on the buffer damage.
2022-02-16 17:13:57 +00:00
void preparePaintGenericScreen();
virtual void paintGenericScreen(int mask, const ScreenPaintData &data);
2011-01-30 14:34:42 +00:00
// shared implementation of painting the screen in an optimized way
scene: Rework surface damage tracking It's not possible to get the surface damage before calling Scene::paint(), which is a big problem because it blocks proper surface damage and buffer damage calculation when walking render layer tree. This change reworks the scene compositing stages to allow getting the next surface damage before calling Scene::paint(). The main challenge is that the effects can expand the surface damage. We have to call prePaintWindow() and prePaintScreen() before actually starting painting. However, prePaintWindow() is called after starting rendering. This change makes Scene call prePaintWindow() and prePaintScreen() so it's possible to know the surface damage beforehand. Unfortunately, it's also a breaking change. Some fullscreen effects will have to adapt to the new Scene paint order. Paint hooks will be invoked in the following order: * prePaintScreen() once per frame * prePaintWindow() once per frame * paintScreen() can be called multiple times * paintWindow() can be called as many times as paintScreen() * postPaintWindow() once per frame * postPaintScreen() once per frame After walking the render layer tree, the Compositor will poke the render backend for the back buffer repair region and combine it with the surface damage to get the buffer damage, which can be passed to the render backend (in order to optimize performance with tiled gpus) and Scene::paint(), which will determine what parts of the scene have to repainted based on the buffer damage.
2022-02-16 17:13:57 +00:00
void preparePaintSimpleScreen();
virtual void paintSimpleScreen(int mask, const QRegion &region);
2011-01-30 14:34:42 +00:00
// paint the background (not the desktop background - the whole background)
virtual void paintBackground(const QRegion &region) = 0;
2011-01-30 14:34:42 +00:00
// called after all effects had their paintWindow() called
void finalPaintWindow(EffectWindowImpl *w, int mask, const QRegion &region, WindowPaintData &data);
2011-01-30 14:34:42 +00:00
// shared implementation, starts painting the window
2022-04-22 17:39:12 +00:00
virtual void paintWindow(SceneWindow *w, int mask, const QRegion &region);
2011-01-30 14:34:42 +00:00
// called after all effects had their drawWindow() called
virtual void finalDrawWindow(EffectWindowImpl *w, int mask, const QRegion &region, WindowPaintData &data);
[libkwineffects] Introduce API to easily show a QtQuick scene in an effect Summary: EffectQuickView/Scene is a convenient class to render a QtQuick scenegraph into an effect. Current methods (such as present windows) involve creating an underlying platform window which is expensive, causes a headache to filter out again in the rest of the code, and only works as an overlay. The new class exposes things more natively to an effect where we don't mess with real windows, we can perform the painting anywhere in the view and we don't have issues with hiding/closing. QtQuick has both software and hardware accelerated modes, and kwin also has 3 render backends. Every combination is supported. * When used in OpenGL mode for both, we render into an FBO export the texture ID then it's up to the effect to render that into a scene. * When using software QtQuick rendering we blit into an image, upload that into a KWinGLTexture which serves as an abstraction layer and render that into the scene. * When using GL for QtQuick and XRender/QPainter in kwin everything is rendered into the internal FBO, blit and exported as an image. * When using software rendering for both an image gets passed directly. Mouse and keyboard events can be forwarded, only if the effect intercepts them. The class is meant to be generic enough that we can remove all the QtQuick code from Aurorae. The intention is also to replace EffectFrameImpl using this backend and we can kill all of the EffectFrame code throughout the scenes. The close button in present windows will also be ported to this, simplifiying that code base. Classes that handle the rendering and handling QML are intentionally split so that in the future we can have a declarative effects API create overlays from within the same context. Similar to how one can instantiate windows from a typical QML scene. Notes: I don't like how I pass the kwin GL context from the backends into the effect, but I need something that works with the library separation. It also currently has wayland problem if I create a QOpenGLContext before the QPA is set up with a scene - but I don't have anything better? I know for the EffectFrame we need an API to push things through the effects stack to handle blur/invert etc. Will deal with that when we port the EffectFrame. Test Plan: Used in an effect Reviewers: #kwin, zzag Reviewed By: #kwin, zzag Subscribers: zzag, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D24215
2019-09-27 15:06:37 +00:00
virtual void paintOffscreenQuickView(OffscreenQuickView *w) = 0;
[libkwineffects] Introduce API to easily show a QtQuick scene in an effect Summary: EffectQuickView/Scene is a convenient class to render a QtQuick scenegraph into an effect. Current methods (such as present windows) involve creating an underlying platform window which is expensive, causes a headache to filter out again in the rest of the code, and only works as an overlay. The new class exposes things more natively to an effect where we don't mess with real windows, we can perform the painting anywhere in the view and we don't have issues with hiding/closing. QtQuick has both software and hardware accelerated modes, and kwin also has 3 render backends. Every combination is supported. * When used in OpenGL mode for both, we render into an FBO export the texture ID then it's up to the effect to render that into a scene. * When using software QtQuick rendering we blit into an image, upload that into a KWinGLTexture which serves as an abstraction layer and render that into the scene. * When using GL for QtQuick and XRender/QPainter in kwin everything is rendered into the internal FBO, blit and exported as an image. * When using software rendering for both an image gets passed directly. Mouse and keyboard events can be forwarded, only if the effect intercepts them. The class is meant to be generic enough that we can remove all the QtQuick code from Aurorae. The intention is also to replace EffectFrameImpl using this backend and we can kill all of the EffectFrame code throughout the scenes. The close button in present windows will also be ported to this, simplifiying that code base. Classes that handle the rendering and handling QML are intentionally split so that in the future we can have a declarative effects API create overlays from within the same context. Similar to how one can instantiate windows from a typical QML scene. Notes: I don't like how I pass the kwin GL context from the backends into the effect, but I need something that works with the library separation. It also currently has wayland problem if I create a QOpenGLContext before the QPA is set up with a scene - but I don't have anything better? I know for the EffectFrame we need an API to push things through the effects stack to handle blur/invert etc. Will deal with that when we port the EffectFrame. Test Plan: Used in an effect Reviewers: #kwin, zzag Reviewed By: #kwin, zzag Subscribers: zzag, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D24215
2019-09-27 15:06:37 +00:00
2011-01-30 14:34:42 +00:00
// saved data for 2nd pass of optimized screen painting
struct Phase2Data
{
2022-04-22 17:39:12 +00:00
SceneWindow *window = nullptr;
2011-01-30 14:34:42 +00:00
QRegion region;
QRegion opaque;
int mask = 0;
2011-01-30 14:34:42 +00:00
};
scene: Rework surface damage tracking It's not possible to get the surface damage before calling Scene::paint(), which is a big problem because it blocks proper surface damage and buffer damage calculation when walking render layer tree. This change reworks the scene compositing stages to allow getting the next surface damage before calling Scene::paint(). The main challenge is that the effects can expand the surface damage. We have to call prePaintWindow() and prePaintScreen() before actually starting painting. However, prePaintWindow() is called after starting rendering. This change makes Scene call prePaintWindow() and prePaintScreen() so it's possible to know the surface damage beforehand. Unfortunately, it's also a breaking change. Some fullscreen effects will have to adapt to the new Scene paint order. Paint hooks will be invoked in the following order: * prePaintScreen() once per frame * prePaintWindow() once per frame * paintScreen() can be called multiple times * paintWindow() can be called as many times as paintScreen() * postPaintWindow() once per frame * postPaintScreen() once per frame After walking the render layer tree, the Compositor will poke the render backend for the back buffer repair region and combine it with the surface damage to get the buffer damage, which can be passed to the render backend (in order to optimize performance with tiled gpus) and Scene::paint(), which will determine what parts of the scene have to repainted based on the buffer damage.
2022-02-16 17:13:57 +00:00
struct PaintContext
{
scene: Rework surface damage tracking It's not possible to get the surface damage before calling Scene::paint(), which is a big problem because it blocks proper surface damage and buffer damage calculation when walking render layer tree. This change reworks the scene compositing stages to allow getting the next surface damage before calling Scene::paint(). The main challenge is that the effects can expand the surface damage. We have to call prePaintWindow() and prePaintScreen() before actually starting painting. However, prePaintWindow() is called after starting rendering. This change makes Scene call prePaintWindow() and prePaintScreen() so it's possible to know the surface damage beforehand. Unfortunately, it's also a breaking change. Some fullscreen effects will have to adapt to the new Scene paint order. Paint hooks will be invoked in the following order: * prePaintScreen() once per frame * prePaintWindow() once per frame * paintScreen() can be called multiple times * paintWindow() can be called as many times as paintScreen() * postPaintWindow() once per frame * postPaintScreen() once per frame After walking the render layer tree, the Compositor will poke the render backend for the back buffer repair region and combine it with the surface damage to get the buffer damage, which can be passed to the render backend (in order to optimize performance with tiled gpus) and Scene::paint(), which will determine what parts of the scene have to repainted based on the buffer damage.
2022-02-16 17:13:57 +00:00
QRegion damage;
int mask = 0;
QVector<Phase2Data> phase2Data;
};
// The screen that is being currently painted
Output *painted_screen = nullptr;
// windows in their stacking order
2022-04-22 17:39:12 +00:00
QVector<SceneWindow *> stacking_order;
ScreenLockerFilter m_filter;
private:
Provide expected presentation time to effects Effects are given the interval between two consecutive frames. The main flaw of this approach is that if the Compositor transitions from the idle state to "active" state, i.e. when there is something to repaint, effects may see a very large interval between the last painted frame and the current. In order to address this issue, the Scene invalidates the timer that is used to measure time between consecutive frames before the Compositor is about to become idle. While this works perfectly fine with Xinerama-style rendering, with per screen rendering, determining whether the compositor is about to idle is rather a tedious task mostly because a single output can't be used for the test. Furthermore, since the Compositor schedules pointless repaints just to ensure that it's idle, it might take several attempts to figure out whether the scene timer must be invalidated if you use (true) per screen rendering. Ideally, all effects should use a timeline helper that is aware of the underlying render loop and its timings. However, this option is off the table because it will involve a lot of work to implement it. Alternative and much simpler option is to pass the expected presentation time to effects rather than time between consecutive frames. This means that effects are responsible for determining how much animation timelines have to be advanced. Typically, an effect would have to store the presentation timestamp provided in either prePaint{Screen,Window} and use it in the subsequent prePaint{Screen,Window} call to estimate the amount of time passed between the next and the last frames. Unfortunately, this is an API incompatible change. However, it shouldn't take a lot of work to port third-party binary effects, which don't use the AnimationEffect class, to the new API. On the bright side, we no longer need to be concerned about the Compositor getting idle. We do still try to determine whether the Compositor is about to idle, primarily, because the OpenGL render backend swaps buffers on present, but that will change with the ongoing compositing timing rework.
2020-11-20 15:44:04 +00:00
std::chrono::milliseconds m_expectedPresentTimestamp = std::chrono::milliseconds::zero();
QList<SceneDelegate *> m_delegates;
2022-04-22 17:39:12 +00:00
QHash<Window *, SceneWindow *> m_windows;
QRect m_geometry;
QMatrix4x4 m_renderTargetProjectionMatrix;
QRect m_renderTargetRect;
qreal m_renderTargetScale = 1;
// how many times finalPaintScreen() has been called
int m_paintScreenCount = 0;
scene: Rework surface damage tracking It's not possible to get the surface damage before calling Scene::paint(), which is a big problem because it blocks proper surface damage and buffer damage calculation when walking render layer tree. This change reworks the scene compositing stages to allow getting the next surface damage before calling Scene::paint(). The main challenge is that the effects can expand the surface damage. We have to call prePaintWindow() and prePaintScreen() before actually starting painting. However, prePaintWindow() is called after starting rendering. This change makes Scene call prePaintWindow() and prePaintScreen() so it's possible to know the surface damage beforehand. Unfortunately, it's also a breaking change. Some fullscreen effects will have to adapt to the new Scene paint order. Paint hooks will be invoked in the following order: * prePaintScreen() once per frame * prePaintWindow() once per frame * paintScreen() can be called multiple times * paintWindow() can be called as many times as paintScreen() * postPaintWindow() once per frame * postPaintScreen() once per frame After walking the render layer tree, the Compositor will poke the render backend for the back buffer repair region and combine it with the surface damage to get the buffer damage, which can be passed to the render backend (in order to optimize performance with tiled gpus) and Scene::paint(), which will determine what parts of the scene have to repainted based on the buffer damage.
2022-02-16 17:13:57 +00:00
PaintContext m_paintContext;
2011-01-30 14:34:42 +00:00
};
// The base class for windows representations in composite backends
2022-04-22 17:39:12 +00:00
class SceneWindow : public QObject
2011-01-30 14:34:42 +00:00
{
Q_OBJECT
2011-01-30 14:34:42 +00:00
public:
2022-04-22 17:39:12 +00:00
explicit SceneWindow(Window *client, QObject *parent = nullptr);
~SceneWindow() override;
2011-01-30 14:34:42 +00:00
// perform the actual painting of the window
virtual void performPaint(int mask, const QRegion &region, const WindowPaintData &data) = 0;
2011-01-30 14:34:42 +00:00
int x() const;
int y() const;
int width() const;
int height() const;
QRect geometry() const;
QPoint pos() const;
QSize size() const;
QRect rect() const;
// access to the internal window class
// TODO eventually get rid of this
2022-04-22 17:39:12 +00:00
Window *window() const;
2011-01-30 14:34:42 +00:00
// should the window be painted
bool isPaintingEnabled() const;
void resetPaintingEnabled();
// Flags explaining why painting should be disabled
enum {
2022-04-22 17:39:12 +00:00
// SceneWindow will not be painted
PAINT_DISABLED = 1 << 0,
2022-04-22 17:39:12 +00:00
// SceneWindow will not be painted because it is deleted
PAINT_DISABLED_BY_DELETE = 1 << 1,
2022-04-22 17:39:12 +00:00
// SceneWindow will not be painted because of which desktop it's on
PAINT_DISABLED_BY_DESKTOP = 1 << 2,
2022-04-22 17:39:12 +00:00
// SceneWindow will not be painted because it is minimized
PAINT_DISABLED_BY_MINIMIZE = 1 << 3,
2022-04-22 17:39:12 +00:00
// SceneWindow will not be painted because it's not on the current activity
PAINT_DISABLED_BY_ACTIVITY = 1 << 5
};
2011-01-30 14:34:42 +00:00
void enablePainting(int reason);
void disablePainting(int reason);
// is the window visible at all
bool isVisible() const;
QRegion decorationShape() const;
void updateToplevel(Deleted *deleted);
void referencePreviousPixmap();
void unreferencePreviousPixmap();
WindowItem *windowItem() const;
SurfaceItem *surfaceItem() const;
ShadowItem *shadowItem() const;
protected:
2022-04-22 17:39:12 +00:00
Window *toplevel;
2011-01-30 14:34:42 +00:00
private:
void referencePreviousPixmap_helper(SurfaceItem *item);
void unreferencePreviousPixmap_helper(SurfaceItem *item);
void updateWindowPosition();
2011-01-30 14:34:42 +00:00
int disable_painting;
QScopedPointer<WindowItem> m_windowItem;
2022-04-22 17:39:12 +00:00
Q_DISABLE_COPY(SceneWindow)
2011-01-30 14:34:42 +00:00
};
class Scene::EffectFrame
2011-01-30 14:34:42 +00:00
{
public:
EffectFrame(EffectFrameImpl *frame);
2011-01-30 14:34:42 +00:00
virtual ~EffectFrame();
virtual void render(const QRegion &region, double opacity, double frameOpacity) = 0;
2011-01-30 14:34:42 +00:00
virtual void free() = 0;
virtual void freeIconFrame() = 0;
virtual void freeTextFrame() = 0;
virtual void freeSelection() = 0;
virtual void crossFadeIcon() = 0;
virtual void crossFadeText() = 0;
protected:
EffectFrameImpl *m_effectFrame;
2011-01-30 14:34:42 +00:00
};
2022-04-22 17:39:12 +00:00
inline int SceneWindow::x() const
2011-01-30 14:34:42 +00:00
{
return toplevel->x();
2011-01-30 14:34:42 +00:00
}
2022-04-22 17:39:12 +00:00
inline int SceneWindow::y() const
2011-01-30 14:34:42 +00:00
{
return toplevel->y();
2011-01-30 14:34:42 +00:00
}
2022-04-22 17:39:12 +00:00
inline int SceneWindow::width() const
2011-01-30 14:34:42 +00:00
{
return toplevel->width();
2011-01-30 14:34:42 +00:00
}
2022-04-22 17:39:12 +00:00
inline int SceneWindow::height() const
2011-01-30 14:34:42 +00:00
{
return toplevel->height();
2011-01-30 14:34:42 +00:00
}
2022-04-22 17:39:12 +00:00
inline QRect SceneWindow::geometry() const
2011-01-30 14:34:42 +00:00
{
return toplevel->frameGeometry();
2011-01-30 14:34:42 +00:00
}
2022-04-22 17:39:12 +00:00
inline QSize SceneWindow::size() const
2011-01-30 14:34:42 +00:00
{
return toplevel->size();
2011-01-30 14:34:42 +00:00
}
2022-04-22 17:39:12 +00:00
inline QPoint SceneWindow::pos() const
2011-01-30 14:34:42 +00:00
{
return toplevel->pos();
2011-01-30 14:34:42 +00:00
}
2022-04-22 17:39:12 +00:00
inline QRect SceneWindow::rect() const
2011-01-30 14:34:42 +00:00
{
return toplevel->rect();
2011-01-30 14:34:42 +00:00
}
2022-04-22 17:39:12 +00:00
inline Window *SceneWindow::window() const
2011-01-30 14:34:42 +00:00
{
return toplevel;
2011-01-30 14:34:42 +00:00
}
} // namespace
#endif