Move repaint scheduling logic to Item

Currently, items depend on scene windows for creating pixmaps, repaint
scheduling, and caching quads.

This change moves repaint scheduling from scene windows to items to
make the scene items depend less on scene windows.

In hindsight, we may clean up the repaint scheduling machinery further
by introducing view objects.
This commit is contained in:
Vlad Zahorodnii 2021-04-30 11:42:54 +03:00
parent 386814176b
commit 506214a632
5 changed files with 106 additions and 97 deletions

View file

@ -5,6 +5,12 @@
*/
#include "item.h"
#include "abstract_output.h"
#include "composite.h"
#include "main.h"
#include "platform.h"
#include "renderloop.h"
#include "screens.h"
namespace KWin
{
@ -13,11 +19,24 @@ Item::Item(Scene::Window *window, Item *parent)
: m_window(window)
{
setParentItem(parent);
if (kwinApp()->platform()->isPerScreenRenderingEnabled()) {
connect(kwinApp()->platform(), &Platform::outputEnabled, this, &Item::reallocRepaints);
connect(kwinApp()->platform(), &Platform::outputDisabled, this, &Item::reallocRepaints);
}
reallocRepaints();
}
Item::~Item()
{
setParentItem(nullptr);
for (int i = 0; i < m_repaints.count(); ++i) {
const QRegion dirty = repaints(i);
if (!dirty.isEmpty()) {
Compositor::self()->addRepaint(dirty);
}
}
}
int Item::x() const
@ -325,12 +344,39 @@ void Item::stackChildren(const QList<Item *> &children)
void Item::scheduleRepaint(const QRegion &region)
{
window()->addLayerRepaint(mapToGlobal(region));
const QRegion globalRegion = mapToGlobal(region);
if (kwinApp()->platform()->isPerScreenRenderingEnabled()) {
const QVector<AbstractOutput *> outputs = kwinApp()->platform()->enabledOutputs();
if (m_repaints.count() != outputs.count()) {
return; // Repaints haven't been reallocated yet, do nothing.
}
for (int screenId = 0; screenId < m_repaints.count(); ++screenId) {
AbstractOutput *output = outputs[screenId];
const QRegion dirtyRegion = globalRegion & output->geometry();
if (!dirtyRegion.isEmpty()) {
m_repaints[screenId] += dirtyRegion;
output->renderLoop()->scheduleRepaint();
}
}
} else {
m_repaints[0] += globalRegion;
kwinApp()->platform()->renderLoop()->scheduleRepaint();
}
}
void Item::scheduleRepaint()
{
window()->scheduleRepaint();
if (kwinApp()->platform()->isPerScreenRenderingEnabled()) {
const QRect geometry = mapToGlobal(rect());
const QVector<AbstractOutput *> outputs = kwinApp()->platform()->enabledOutputs();
for (const AbstractOutput *output : outputs) {
if (output->geometry().intersects(geometry)) {
output->renderLoop()->scheduleRepaint();
}
}
} else {
kwinApp()->platform()->renderLoop()->scheduleRepaint();
}
}
void Item::preprocess()
@ -342,4 +388,32 @@ void Item::discardQuads()
window()->discardQuads();
}
QRegion Item::repaints(int screen) const
{
Q_ASSERT(!m_repaints.isEmpty());
const int index = screen != -1 ? screen : 0;
if (m_repaints[index] == infiniteRegion()) {
return QRect(QPoint(0, 0), screens()->size());
}
return m_repaints[index];
}
void Item::resetRepaints(int screen)
{
Q_ASSERT(!m_repaints.isEmpty());
const int index = screen != -1 ? screen : 0;
m_repaints[index] = QRegion();
}
void Item::reallocRepaints()
{
if (kwinApp()->platform()->isPerScreenRenderingEnabled()) {
m_repaints.resize(kwinApp()->platform()->enabledOutputs().count());
} else {
m_repaints.resize(1);
}
m_repaints.fill(infiniteRegion());
}
} // namespace KWin

View file

@ -93,6 +93,8 @@ public:
void scheduleRepaint(const QRegion &region);
void scheduleRepaint();
QRegion repaints(int screen) const;
void resetRepaints(int screen);
Q_SIGNALS:
/**
@ -126,6 +128,7 @@ private:
void addChild(Item *item);
void removeChild(Item *item);
void updateBoundingRect();
void reallocRepaints();
Scene::Window *m_window;
QPointer<Item> m_parentItem;
@ -135,6 +138,7 @@ private:
int m_y = 0;
int m_width = 0;
int m_height = 0;
QVector<QRegion> m_repaints;
friend class Scene::Window;
};

View file

@ -229,6 +229,16 @@ void Scene::finalPaintScreen(int mask, const QRegion &region, ScreenPaintData& d
Q_EMIT frameRendered();
}
static void resetRepaintsHelper(Item *item, int screen)
{
item->resetRepaints(screen);
const auto childItems = item->childItems();
for (Item *childItem : childItems) {
resetRepaintsHelper(childItem, screen);
}
}
// The generic painting code that can handle even transformations.
// It simply paints bottom-to-top.
void Scene::paintGenericScreen(int orig_mask, const ScreenPaintData &)
@ -242,7 +252,7 @@ void Scene::paintGenericScreen(int orig_mask, const ScreenPaintData &)
// Reset the repaint_region.
// This has to be done here because many effects schedule a repaint for
// the next frame within Effects::prePaintWindow.
w->resetRepaints(painted_screen);
resetRepaintsHelper(w->windowItem(), painted_screen);
WindowPrePaintData data;
data.mask = orig_mask | (w->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT);
@ -280,6 +290,17 @@ void Scene::paintGenericScreen(int orig_mask, const ScreenPaintData &)
}
}
static void accumulateRepaints(Item *item, int screen, QRegion *repaints)
{
*repaints += item->repaints(screen);
item->resetRepaints(screen);
const auto childItems = item->childItems();
for (Item *childItem : childItems) {
accumulateRepaints(childItem, screen, repaints);
}
}
// The optimized case without any transformations at all.
// It can paint only the requested region and can use clipping
// to reduce painting and improve performance.
@ -301,16 +322,11 @@ void Scene::paintSimpleScreen(int orig_mask, const QRegion &region)
data.mask = orig_mask | (window->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT);
window->resetPaintingEnabled();
data.paint = region;
data.paint |= window->repaints(painted_screen);
accumulateRepaints(window->windowItem(), painted_screen, &data.paint);
// Let the scene window update the window pixmap tree.
window->preprocess(window->windowItem());
// Reset the repaint_region.
// This has to be done here because many effects schedule a repaint for
// the next frame within Effects::prePaintWindow.
window->resetRepaints(painted_screen);
// Clip out the decoration for opaque windows; the decoration is drawn in the second pass
opaqueFullscreen = false; // TODO: do we care about unmanged windows here (maybe input windows?)
AbstractClient *client = dynamic_cast<AbstractClient *>(toplevel);
@ -751,12 +767,6 @@ Scene::Window::Window(Toplevel *client, QObject *parent)
, disable_painting(0)
, cached_quad_list(nullptr)
{
if (kwinApp()->platform()->isPerScreenRenderingEnabled()) {
connect(kwinApp()->platform(), &Platform::outputEnabled, this, &Window::reallocRepaints);
connect(kwinApp()->platform(), &Platform::outputDisabled, this, &Window::reallocRepaints);
}
reallocRepaints();
if (qobject_cast<WaylandClient *>(client)) {
m_windowItem.reset(new WindowItemWayland(this));
} else if (qobject_cast<X11Client *>(client) || qobject_cast<Unmanaged *>(client)) {
@ -773,12 +783,6 @@ Scene::Window::Window(Toplevel *client, QObject *parent)
Scene::Window::~Window()
{
for (int i = 0; i < m_repaints.count(); ++i) {
const QRegion dirty = repaints(i);
if (!dirty.isEmpty()) {
Compositor::self()->addRepaint(dirty);
}
}
}
void Scene::Window::updateToplevel(Deleted *deleted)
@ -1117,55 +1121,6 @@ void Scene::Window::preprocess(Item *item)
}
}
void Scene::Window::addLayerRepaint(const QRegion &region)
{
if (kwinApp()->platform()->isPerScreenRenderingEnabled()) {
const QVector<AbstractOutput *> outputs = kwinApp()->platform()->enabledOutputs();
if (m_repaints.count() != outputs.count()) {
return; // Repaints haven't been reallocated yet, do nothing.
}
for (int screenId = 0; screenId < m_repaints.count(); ++screenId) {
AbstractOutput *output = outputs[screenId];
const QRegion dirtyRegion = region & output->geometry();
if (!dirtyRegion.isEmpty()) {
m_repaints[screenId] += dirtyRegion;
output->renderLoop()->scheduleRepaint();
}
}
} else {
m_repaints[0] += region;
kwinApp()->platform()->renderLoop()->scheduleRepaint();
}
}
QRegion Scene::Window::repaints(int screen) const
{
Q_ASSERT(!m_repaints.isEmpty());
const int index = screen != -1 ? screen : 0;
if (m_repaints[index] == infiniteRegion()) {
return QRect(QPoint(0, 0), screens()->size());
}
return m_repaints[index];
}
void Scene::Window::resetRepaints(int screen)
{
Q_ASSERT(!m_repaints.isEmpty());
const int index = screen != -1 ? screen : 0;
m_repaints[index] = QRegion();
}
void Scene::Window::reallocRepaints()
{
if (kwinApp()->platform()->isPerScreenRenderingEnabled()) {
m_repaints.resize(kwinApp()->platform()->enabledOutputs().count());
} else {
m_repaints.resize(1);
}
m_repaints.fill(infiniteRegion());
}
WindowItem *Scene::Window::windowItem() const
{
return m_windowItem.data();
@ -1181,20 +1136,6 @@ ShadowItem *Scene::Window::shadowItem() const
return m_windowItem->shadowItem();
}
void Scene::Window::scheduleRepaint()
{
if (kwinApp()->platform()->isPerScreenRenderingEnabled()) {
const QVector<AbstractOutput *> outputs = kwinApp()->platform()->enabledOutputs();
for (AbstractOutput *output : outputs) {
if (window()->isOnOutput(output)) {
output->renderLoop()->scheduleRepaint();
}
}
} else {
kwinApp()->platform()->renderLoop()->scheduleRepaint();
}
}
void Scene::Window::updateWindowPosition()
{
m_windowItem->setPosition(pos());

View file

@ -360,13 +360,9 @@ public:
void unreferencePreviousPixmap();
void discardQuads();
void preprocess(Item *item);
void addLayerRepaint(const QRegion &region);
QRegion repaints(int screen) const;
void resetRepaints(int screen);
WindowItem *windowItem() const;
SurfaceItem *surfaceItem() const;
ShadowItem *shadowItem() const;
void scheduleRepaint();
virtual QSharedPointer<GLTexture> windowTexture() {
return {};
@ -385,9 +381,7 @@ private:
void unreferencePreviousPixmap_helper(SurfaceItem *item);
void updateWindowPosition();
void reallocRepaints();
QVector<QRegion> m_repaints;
int disable_painting;
mutable QScopedPointer<WindowQuadList> cached_quad_list;
QScopedPointer<WindowItem> m_windowItem;

View file

@ -308,10 +308,9 @@ void Toplevel::addRepaint(int x, int y, int width, int height)
void Toplevel::addRepaint(const QRegion &region)
{
if (!effectWindow() || !effectWindow()->sceneWindow()) {
return;
if (auto item = windowItem()) {
item->scheduleRepaint(region);
}
effectWindow()->sceneWindow()->addLayerRepaint(region.translated(pos()));
}
void Toplevel::addLayerRepaint(const QRect &rect)
@ -326,10 +325,7 @@ void Toplevel::addLayerRepaint(int x, int y, int width, int height)
void Toplevel::addLayerRepaint(const QRegion &region)
{
if (!effectWindow() || !effectWindow()->sceneWindow()) {
return;
}
effectWindow()->sceneWindow()->addLayerRepaint(region);
addRepaint(region.translated(-pos()));
}
void Toplevel::addRepaintFull()