From 74391e250e9d744c9b8a95bc2f3806bb64b51a7e Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Thu, 29 Oct 2020 20:25:39 +0200 Subject: [PATCH] Store repaint regions per individual screen AnimationEffect schedules repaints in postPaintWindow() and performs cleanup in preScreenPaint(). With the X11-style rendering, this doesn't have any issues, scheduled repaints will be reset during the next compositing cycle. But with per screen rendering, we might hit the following case - Paint screen 0 - Reset scheduled repaints - AnimationEffect::prePaintScreen(): update the timeline - AnimationEffect::postPaintScreen(): schedule a repaint - Paint screen 1 - Reset scheduled repaints - AnimationEffect::prePaintScreen(): destroy the animation - AnimationEffect::postPaintScreen(): no repaint is scheduled - Return to the event loop In this scenario, the repaint region scheduled by AnimationEffect will be lost when compositing is performed on screen 1. There is no any other way to fix this issue but maintain repaint regions per each individual screen if per screen rendering is enabled. BUG: 428439 --- composite.cpp | 10 +-- deleted.cpp | 5 -- plugins/scenes/opengl/scene_opengl.cpp | 4 + plugins/scenes/qpainter/scene_qpainter.cpp | 3 + plugins/scenes/xrender/scene_xrender.cpp | 1 + scene.cpp | 90 ++++++++++++++++++++-- scene.h | 14 ++++ toplevel.cpp | 79 ++++++++----------- toplevel.h | 10 +-- unmanaged.cpp | 2 +- x11client.cpp | 2 +- xdgshellclient.cpp | 2 +- 12 files changed, 149 insertions(+), 73 deletions(-) diff --git a/composite.cpp b/composite.cpp index 50bb3ee9b2..cb312dbb0b 100644 --- a/composite.cpp +++ b/composite.cpp @@ -729,7 +729,7 @@ template static bool repaintsPending(const QList &windows) { return std::any_of(windows.begin(), windows.end(), - [](T *t) { return !t->repaints().isEmpty(); }); + [](const T *t) { return t->wantsRepaint(); }); } bool Compositor::windowRepaintsPending() const @@ -745,16 +745,16 @@ bool Compositor::windowRepaintsPending() const } if (auto *server = waylandServer()) { const auto &clients = server->clients(); - auto test = [](AbstractClient *c) { - return c->readyForPainting() && !c->repaints().isEmpty(); + auto test = [](const AbstractClient *c) { + return c->readyForPainting() && c->wantsRepaint(); }; if (std::any_of(clients.begin(), clients.end(), test)) { return true; } } const auto &internalClients = workspace()->internalClients(); - auto internalTest = [] (InternalClient *client) { - return client->isShown(true) && !client->repaints().isEmpty(); + auto internalTest = [] (const InternalClient *client) { + return client->isShown(true) && client->wantsRepaint(); }; if (std::any_of(internalClients.begin(), internalClients.end(), internalTest)) { return true; diff --git a/deleted.cpp b/deleted.cpp index 20982de4c9..0d52293258 100644 --- a/deleted.cpp +++ b/deleted.cpp @@ -47,11 +47,6 @@ Deleted::Deleted() Deleted::~Deleted() { - const QRegion dirty = repaints(); - if (!dirty.isEmpty()) { - addWorkspaceRepaint(dirty); - } - if (delete_refcount != 0) qCCritical(KWIN_CORE) << "Deleted client has non-zero reference count (" << delete_refcount << ")"; Q_ASSERT(delete_refcount == 0); diff --git a/plugins/scenes/opengl/scene_opengl.cpp b/plugins/scenes/opengl/scene_opengl.cpp index 7417519c58..9ccc2a6220 100644 --- a/plugins/scenes/opengl/scene_opengl.cpp +++ b/plugins/scenes/opengl/scene_opengl.cpp @@ -350,6 +350,8 @@ SceneOpenGL::SceneOpenGL(OpenGLBackend *backend, QObject *parent) qCDebug(KWIN_OPENGL) << "Explicit synchronization with the X command stream disabled by environment variable"; } } + + setPerScreenRenderingEnabled(m_backend->perScreenRendering()); } SceneOpenGL::~SceneOpenGL() @@ -633,6 +635,7 @@ qint64 SceneOpenGL::paint(const QRegion &damage, const QList &toplev // trigger start render timer m_backend->prepareRenderingFrame(); for (int i = 0; i < screens()->count(); ++i) { + painted_screen = i; const QRect &geo = screens()->geometry(i); const qreal scaling = screens()->scale(i); QRegion update; @@ -663,6 +666,7 @@ qint64 SceneOpenGL::paint(const QRegion &damage, const QList &toplev GLVertexBuffer::streamingBuffer()->framePosted(); } } else { + painted_screen = -1; m_backend->makeCurrent(); QRegion repaint = m_backend->prepareRenderingFrame(); diff --git a/plugins/scenes/qpainter/scene_qpainter.cpp b/plugins/scenes/qpainter/scene_qpainter.cpp index 2750a594e7..3429615828 100644 --- a/plugins/scenes/qpainter/scene_qpainter.cpp +++ b/plugins/scenes/qpainter/scene_qpainter.cpp @@ -55,6 +55,7 @@ SceneQPainter::SceneQPainter(QPainterBackend *backend, QObject *parent) , m_backend(backend) , m_painter(new QPainter()) { + setPerScreenRenderingEnabled(m_backend->perScreenRendering()); } SceneQPainter::~SceneQPainter() @@ -98,6 +99,7 @@ qint64 SceneQPainter::paint(const QRegion &_damage, const QList &top } QRegion overallUpdate; for (int i = 0; i < screens()->count(); ++i) { + painted_screen = i; const QRect geometry = screens()->geometry(i); QImage *buffer = m_backend->bufferForScreen(i); if (!buffer || buffer->isNull()) { @@ -118,6 +120,7 @@ qint64 SceneQPainter::paint(const QRegion &_damage, const QList &top m_backend->showOverlay(); m_backend->present(mask, overallUpdate); } else { + painted_screen = -1; m_painter->begin(m_backend->buffer()); m_painter->setClipping(true); m_painter->setClipRegion(damage); diff --git a/plugins/scenes/xrender/scene_xrender.cpp b/plugins/scenes/xrender/scene_xrender.cpp index 1243caa22a..0dc0ab1d86 100644 --- a/plugins/scenes/xrender/scene_xrender.cpp +++ b/plugins/scenes/xrender/scene_xrender.cpp @@ -240,6 +240,7 @@ bool SceneXrender::initFailed() const // the entry point for painting qint64 SceneXrender::paint(const QRegion &damage, const QList &toplevels) { + painted_screen = -1; QElapsedTimer renderTimer; renderTimer.start(); diff --git a/scene.cpp b/scene.cpp index fe4105cfc3..5b4748f498 100644 --- a/scene.cpp +++ b/scene.cpp @@ -68,8 +68,8 @@ #include "shadow.h" #include "subsurfacemonitor.h" #include "wayland_server.h" - #include "thumbnailitem.h" +#include "composite.h" #include #include @@ -93,6 +93,16 @@ Scene::~Scene() Q_ASSERT(m_windows.isEmpty()); } +bool Scene::isPerScreenRenderingEnabled() const +{ + return m_isPerScreenRenderingEnabled; +} + +void Scene::setPerScreenRenderingEnabled(bool enabled) +{ + m_isPerScreenRenderingEnabled = enabled; +} + // returns mask and possibly modified region void Scene::paintScreen(int* mask, const QRegion &damage, const QRegion &repaint, QRegion *updateRegion, QRegion *validRegion, const QMatrix4x4 &projection, const QRect &outputGeometry, const qreal screenScale) @@ -197,15 +207,13 @@ void Scene::paintGenericScreen(int orig_mask, const ScreenPaintData &) QVector phase2; phase2.reserve(stacking_order.size()); foreach (Window * w, stacking_order) { // bottom to top - Toplevel* topw = w->window(); - // Let the scene window update the window pixmap tree. w->preprocess(); // Reset the repaint_region. // This has to be done here because many effects schedule a repaint for // the next frame within Effects::prePaintWindow. - topw->resetRepaints(); + w->resetRepaints(painted_screen); WindowPrePaintData data; data.mask = orig_mask | (w->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT); @@ -264,7 +272,7 @@ 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 |= toplevel->repaints(); + data.paint |= window->repaints(painted_screen); // Let the scene window update the window pixmap tree. window->preprocess(); @@ -272,7 +280,7 @@ void Scene::paintSimpleScreen(int orig_mask, const QRegion ®ion) // Reset the repaint_region. // This has to be done here because many effects schedule a repaint for // the next frame within Effects::prePaintWindow. - toplevel->resetRepaints(); + 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?) @@ -735,10 +743,22 @@ Scene::Window::Window(Toplevel *client, QObject *parent) , disable_painting(0) , cached_quad_list(nullptr) { + const Scene *scene = Compositor::self()->scene(); + if (scene->isPerScreenRenderingEnabled()) { + connect(screens(), &Screens::countChanged, this, &Window::reallocRepaints); + } + reallocRepaints(); } Scene::Window::~Window() { + for (int i = 0; i < m_repaints.count(); ++i) { + const QRegion dirty = repaints(i); + if (!dirty.isEmpty()) { + Compositor::self()->addRepaint(dirty); + } + } + delete m_shadow; } @@ -1109,6 +1129,64 @@ void Scene::Window::preprocess() } } +void Scene::Window::addRepaint(const QRegion ®ion) +{ + for (int screen = 0; screen < m_repaints.count(); ++screen) { + m_repaints[screen] += region; + } +} + +void Scene::Window::addLayerRepaint(const QRegion ®ion) +{ + for (int screen = 0; screen < m_layerRepaints.count(); ++screen) { + m_layerRepaints[screen] += region; + } +} + +QRegion Scene::Window::repaints(int screen) const +{ + Q_ASSERT(!m_repaints.isEmpty() && !m_layerRepaints.isEmpty()); + const int index = screen != -1 ? screen : 0; + if (m_repaints[index] == infiniteRegion() || m_layerRepaints[index] == infiniteRegion()) { + return QRect(QPoint(0, 0), screens()->size()); + } + return m_repaints[index].translated(pos()) + m_layerRepaints[index]; +} + +void Scene::Window::resetRepaints(int screen) +{ + Q_ASSERT(!m_repaints.isEmpty() && !m_layerRepaints.isEmpty()); + const int index = screen != -1 ? screen : 0; + m_repaints[index] = QRegion(); + m_layerRepaints[index] = QRegion(); +} + +void Scene::Window::reallocRepaints() +{ + const Scene *scene = Compositor::self()->scene(); + if (scene->isPerScreenRenderingEnabled()) { + m_repaints.resize(screens()->count()); + m_layerRepaints.resize(screens()->count()); + } else { + m_repaints.resize(1); + m_layerRepaints.resize(1); + } + + m_repaints.fill(infiniteRegion()); + m_layerRepaints.fill(infiniteRegion()); +} + +static bool wantsRepaint_test(const QRegion ®ion) +{ + return !region.isEmpty(); +} + +bool Scene::Window::wantsRepaint() const +{ + return std::any_of(m_repaints.begin(), m_repaints.end(), wantsRepaint_test) || + std::any_of(m_layerRepaints.begin(), m_layerRepaints.end(), wantsRepaint_test); +} + //**************************************** // WindowPixmap //**************************************** diff --git a/scene.h b/scene.h index f03fb2132e..a01c64f6de 100644 --- a/scene.h +++ b/scene.h @@ -136,6 +136,7 @@ public: virtual bool blocksForRetrace() const; virtual bool syncsToVBlank() const; virtual OverlayWindow* overlayWindow() const = 0; + bool isPerScreenRenderingEnabled() const; virtual bool makeOpenGLContextCurrent(); virtual void doneOpenGLContextCurrent(); @@ -202,6 +203,7 @@ public Q_SLOTS: void windowClosed(KWin::Toplevel* c, KWin::Deleted* deleted); protected: virtual Window *createWindow(Toplevel *toplevel) = 0; + void setPerScreenRenderingEnabled(bool enabled); void createStackingOrder(const QList &toplevels); void clearStackingOrder(); // shared implementation, starts painting the screen @@ -262,6 +264,8 @@ protected: // time since last repaint int time_diff; QElapsedTimer last_time; + // The screen that is being currently painted + int painted_screen = -1; private: void paintWindowThumbnails(Scene::Window *w, const QRegion ®ion, qreal opacity, qreal brightness, qreal saturation); void paintDesktopThumbnails(Scene::Window *w); @@ -270,6 +274,7 @@ private: QVector< Window* > stacking_order; // how many times finalPaintScreen() has been called int m_paintScreenCount = 0; + bool m_isPerScreenRenderingEnabled = false; }; /** @@ -354,6 +359,11 @@ public: void unreferencePreviousPixmap(); void discardQuads(); void preprocess(); + void addRepaint(const QRegion ®ion); + void addLayerRepaint(const QRegion ®ion); + QRegion repaints(int screen) const; + void resetRepaints(int screen); + bool wantsRepaint() const; virtual QSharedPointer windowTexture() { return {}; @@ -391,8 +401,12 @@ protected: ImageFilterType filter; Shadow *m_shadow; private: + void reallocRepaints(); + QScopedPointer m_currentPixmap; QScopedPointer m_previousPixmap; + QVector m_repaints; + QVector m_layerRepaints; int m_referencePixmapCounter; int disable_painting; mutable QRegion m_bufferShape; diff --git a/toplevel.cpp b/toplevel.cpp index c85a2b4835..bfd0098f09 100644 --- a/toplevel.cpp +++ b/toplevel.cpp @@ -113,8 +113,6 @@ void Toplevel::copyToDeleted(Toplevel* c) ready_for_painting = c->ready_for_painting; damage_handle = XCB_NONE; damage_region = c->damage_region; - repaints_region = c->repaints_region; - layer_repaints_region = c->layer_repaints_region; is_shape = c->is_shape; effect_window = c->effect_window; if (effect_window != nullptr) @@ -311,7 +309,6 @@ void Toplevel::finishCompositing(ReleaseReason releaseReason) damage_handle = XCB_NONE; damage_region = QRegion(); - repaints_region = QRegion(); effect_window = nullptr; } @@ -404,7 +401,7 @@ void Toplevel::getDamageRegionReply() const QRect frameRect = frameGeometry(); damage_region += region; - repaints_region += region.translated(bufferRect.topLeft() - frameRect.topLeft()); + addRepaint(region.translated(bufferRect.topLeft() - frameRect.topLeft())); free(reply); } @@ -423,7 +420,7 @@ void Toplevel::addDamageFull() const QRect damagedRect(0, 0, bufferRect.width(), bufferRect.height()); damage_region = damagedRect; - repaints_region |= damagedRect.translated(offsetX, offsetY); + addRepaint(damagedRect.translated(offsetX, offsetY)); emit damaged(this, damage_region); } @@ -433,63 +430,47 @@ void Toplevel::resetDamage() damage_region = QRegion(); } -void Toplevel::addRepaint(const QRect& r) +void Toplevel::addRepaint(const QRect &rect) { - if (!compositing()) { + addRepaint(QRegion(rect)); +} + +void Toplevel::addRepaint(int x, int y, int width, int height) +{ + addRepaint(QRegion(x, y, width, height)); +} + +void Toplevel::addRepaint(const QRegion ®ion) +{ + if (!effectWindow() || !effectWindow()->sceneWindow()) { return; } - repaints_region += r; + effectWindow()->sceneWindow()->addRepaint(region); emit needsRepaint(); } -void Toplevel::addRepaint(int x, int y, int w, int h) +void Toplevel::addLayerRepaint(const QRect &rect) { - QRect r(x, y, w, h); - addRepaint(r); + addLayerRepaint(QRegion(rect)); } -void Toplevel::addRepaint(const QRegion& r) +void Toplevel::addLayerRepaint(int x, int y, int width, int height) { - if (!compositing()) { + addLayerRepaint(QRegion(x, y, width, height)); +} + +void Toplevel::addLayerRepaint(const QRegion ®ion) +{ + if (!effectWindow() || !effectWindow()->sceneWindow()) { return; } - repaints_region += r; - emit needsRepaint(); -} - -void Toplevel::addLayerRepaint(const QRect& r) -{ - if (!compositing()) { - return; - } - layer_repaints_region += r; - emit needsRepaint(); -} - -void Toplevel::addLayerRepaint(int x, int y, int w, int h) -{ - QRect r(x, y, w, h); - addLayerRepaint(r); -} - -void Toplevel::addLayerRepaint(const QRegion& r) -{ - if (!compositing()) - return; - layer_repaints_region += r; + effectWindow()->sceneWindow()->addLayerRepaint(region); emit needsRepaint(); } void Toplevel::addRepaintFull() { - repaints_region = visibleRect().translated(-pos()); - emit needsRepaint(); -} - -void Toplevel::resetRepaints() -{ - repaints_region = QRegion(); - layer_repaints_region = QRegion(); + addRepaint(visibleRect().translated(-pos())); } void Toplevel::addWorkspaceRepaint(int x, int y, int w, int h) @@ -511,6 +492,14 @@ void Toplevel::addWorkspaceRepaint(const QRegion ®ion) } } +bool Toplevel::wantsRepaint() const +{ + if (!effectWindow() || !effectWindow()->sceneWindow()) { + return false; + } + return effectWindow()->sceneWindow()->wantsRepaint(); +} + void Toplevel::setReadyForPainting() { if (!ready_for_painting) { diff --git a/toplevel.h b/toplevel.h index 1108c598ec..bfd8e0ee32 100644 --- a/toplevel.h +++ b/toplevel.h @@ -467,8 +467,7 @@ public: void addWorkspaceRepaint(const QRect& r); void addWorkspaceRepaint(int x, int y, int w, int h); void addWorkspaceRepaint(const QRegion ®ion); - QRegion repaints() const; - void resetRepaints(); + bool wantsRepaint() const; QRegion damage() const; void resetDamage(); EffectWindowImpl* effectWindow(); @@ -734,8 +733,6 @@ protected: int bit_depth; NETWinInfo* info; bool ready_for_painting; - QRegion repaints_region; // updating, repaint just requires repaint of that area - QRegion layer_repaints_region; /** * An FBO object KWin internal windows might render to. */ @@ -939,11 +936,6 @@ inline QRegion Toplevel::damage() const return damage_region; } -inline QRegion Toplevel::repaints() const -{ - return repaints_region.translated(pos()) | layer_repaints_region; -} - inline bool Toplevel::shape() const { return is_shape; diff --git a/unmanaged.cpp b/unmanaged.cpp index c55caac2f4..72dad46261 100644 --- a/unmanaged.cpp +++ b/unmanaged.cpp @@ -165,7 +165,7 @@ bool Unmanaged::isOutline() const void Unmanaged::addDamage(const QRegion &damage) { - repaints_region += damage; + addRepaint(damage); Toplevel::addDamage(damage); } diff --git a/x11client.cpp b/x11client.cpp index 8df25b0114..fdb40bc39b 100644 --- a/x11client.cpp +++ b/x11client.cpp @@ -2792,7 +2792,7 @@ void X11Client::addDamage(const QRegion &damage) setupWindowManagementInterface(); } } - repaints_region += damage.translated(bufferGeometry().topLeft() - frameGeometry().topLeft()); + addRepaint(damage.translated(bufferGeometry().topLeft() - frameGeometry().topLeft())); Toplevel::addDamage(damage); } diff --git a/xdgshellclient.cpp b/xdgshellclient.cpp index e94fdea391..a9514a5930 100644 --- a/xdgshellclient.cpp +++ b/xdgshellclient.cpp @@ -322,7 +322,7 @@ void XdgSurfaceClient::addDamage(const QRegion &damage) { const int offsetX = bufferGeometry().x() - frameGeometry().x(); const int offsetY = bufferGeometry().y() - frameGeometry().y(); - repaints_region += damage.translated(offsetX, offsetY); + addRepaint(damage.translated(offsetX, offsetY)); Toplevel::addDamage(damage); }