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); }