From 506214a63211416ca22cbc737c6d8f8a6a7f9c01 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Fri, 30 Apr 2021 11:42:54 +0300 Subject: [PATCH] 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. --- src/item.cpp | 78 ++++++++++++++++++++++++++++++++++- src/item.h | 4 ++ src/scene.cpp | 105 +++++++++++------------------------------------ src/scene.h | 6 --- src/toplevel.cpp | 10 ++--- 5 files changed, 106 insertions(+), 97 deletions(-) diff --git a/src/item.cpp b/src/item.cpp index e842ba982e..cffce2044a 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -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 &children) void Item::scheduleRepaint(const QRegion ®ion) { - window()->addLayerRepaint(mapToGlobal(region)); + const QRegion globalRegion = mapToGlobal(region); + if (kwinApp()->platform()->isPerScreenRenderingEnabled()) { + const QVector 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 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 diff --git a/src/item.h b/src/item.h index 05177678de..0196f4a58e 100644 --- a/src/item.h +++ b/src/item.h @@ -93,6 +93,8 @@ public: void scheduleRepaint(const QRegion ®ion); 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 m_parentItem; @@ -135,6 +138,7 @@ private: int m_y = 0; int m_width = 0; int m_height = 0; + QVector m_repaints; friend class Scene::Window; }; diff --git a/src/scene.cpp b/src/scene.cpp index 58eb1380b4..6841a73920 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -229,6 +229,16 @@ void Scene::finalPaintScreen(int mask, const QRegion ®ion, 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 ®ion) 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(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(client)) { m_windowItem.reset(new WindowItemWayland(this)); } else if (qobject_cast(client) || qobject_cast(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 ®ion) -{ - if (kwinApp()->platform()->isPerScreenRenderingEnabled()) { - const QVector 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 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()); diff --git a/src/scene.h b/src/scene.h index f28346c8e7..7954e7672e 100644 --- a/src/scene.h +++ b/src/scene.h @@ -360,13 +360,9 @@ public: void unreferencePreviousPixmap(); void discardQuads(); void preprocess(Item *item); - void addLayerRepaint(const QRegion ®ion); - QRegion repaints(int screen) const; - void resetRepaints(int screen); WindowItem *windowItem() const; SurfaceItem *surfaceItem() const; ShadowItem *shadowItem() const; - void scheduleRepaint(); virtual QSharedPointer windowTexture() { return {}; @@ -385,9 +381,7 @@ private: void unreferencePreviousPixmap_helper(SurfaceItem *item); void updateWindowPosition(); - void reallocRepaints(); - QVector m_repaints; int disable_painting; mutable QScopedPointer cached_quad_list; QScopedPointer m_windowItem; diff --git a/src/toplevel.cpp b/src/toplevel.cpp index 6026aa5a47..22460ce126 100644 --- a/src/toplevel.cpp +++ b/src/toplevel.cpp @@ -308,10 +308,9 @@ void Toplevel::addRepaint(int x, int y, int width, int height) void Toplevel::addRepaint(const QRegion ®ion) { - 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 ®ion) { - if (!effectWindow() || !effectWindow()->sceneWindow()) { - return; - } - effectWindow()->sceneWindow()->addLayerRepaint(region); + addRepaint(region.translated(-pos())); } void Toplevel::addRepaintFull()