From 755dd81e49b042e0e49366172a9a7dc74b4b9b5a Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Mon, 9 Nov 2020 16:10:03 +0200 Subject: [PATCH] Refactor how per screen rendering is handled In order to allow per screen rendering, we need the Compositor to be able to drive rendering on each screen. Currently, it's not possible because Scene::paint() paints all screen. With this change, the Compositor will be able to ask the Scene to paint only a screen with the specific id. --- composite.cpp | 10 +- composite.h | 1 + platformsupport/scenes/opengl/backend.h | 22 +--- plugins/platforms/drm/egl_gbm_backend.cpp | 1 - plugins/platforms/drm/egl_stream_backend.cpp | 1 - .../hwcomposer/egl_hwcomposer_backend.cpp | 1 - plugins/platforms/virtual/egl_gbm_backend.cpp | 1 - .../platforms/wayland/egl_wayland_backend.cpp | 1 - .../platforms/x11/common/eglonxbackend.cpp | 1 - .../platforms/x11/standalone/glxbackend.cpp | 1 - .../x11/windowed/egl_x11_backend.cpp | 1 - plugins/scenes/opengl/scene_opengl.cpp | 123 ++++++++---------- plugins/scenes/opengl/scene_opengl.h | 3 +- plugins/scenes/qpainter/scene_qpainter.cpp | 33 ++--- plugins/scenes/qpainter/scene_qpainter.h | 2 +- plugins/scenes/xrender/scene_xrender.cpp | 8 +- plugins/scenes/xrender/scene_xrender.h | 2 +- scene.h | 2 +- 18 files changed, 83 insertions(+), 131 deletions(-) diff --git a/composite.cpp b/composite.cpp index cb312dbb0b..6ad4fe76e8 100644 --- a/composite.cpp +++ b/composite.cpp @@ -686,7 +686,15 @@ void Compositor::performCompositing() if (m_framesToTestForSafety > 0 && (m_scene->compositingType() & OpenGLCompositing)) { kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PreFrame); } - m_timeSinceLastVBlank = m_scene->paint(repaints, windows); + m_renderTimer.start(); + if (m_scene->isPerScreenRenderingEnabled()) { + for (int screenId = 0; screenId < screens()->count(); ++screenId) { + m_scene->paint(screenId, repaints, windows); + } + } else { + m_scene->paint(-1, repaints, windows); + } + m_timeSinceLastVBlank = m_renderTimer.elapsed(); if (m_framesToTestForSafety > 0) { if (m_scene->compositingType() & OpenGLCompositing) { kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PostFrame); diff --git a/composite.h b/composite.h index 61aadb919a..114cd35cf7 100644 --- a/composite.h +++ b/composite.h @@ -157,6 +157,7 @@ private: bool m_composeAtSwapCompletion; int m_framesToTestForSafety = 3; + QElapsedTimer m_renderTimer; QElapsedTimer m_monotonicClock; }; diff --git a/platformsupport/scenes/opengl/backend.h b/platformsupport/scenes/opengl/backend.h index 137a86fae1..5f3991fce7 100644 --- a/platformsupport/scenes/opengl/backend.h +++ b/platformsupport/scenes/opengl/backend.h @@ -10,7 +10,6 @@ #ifndef KWIN_SCENE_OPENGL_BACKEND_H #define KWIN_SCENE_OPENGL_BACKEND_H -#include #include #include @@ -47,13 +46,7 @@ public: virtual ~OpenGLBackend(); virtual void init() = 0; - /** - * @return Time passes since start of rendering current frame. - * @see startRenderTimer - */ - qint64 renderTime() { - return m_renderTimer.nsecsElapsed(); - } + virtual void screenGeometryChanged(const QSize &size) = 0; virtual SceneOpenGLTexturePrivate *createBackendTexture(SceneOpenGLTexture *texture) = 0; @@ -288,14 +281,6 @@ protected: void setLastDamage(const QRegion &damage) { m_lastDamage = damage; } - /** - * @brief Starts the timer for how long it takes to render the frame. - * - * @see renderTime - */ - void startRenderTimer() { - m_renderTimer.start(); - } /** * Sets the platform-specific @p extensions. @@ -348,11 +333,6 @@ private: * @brief The damage history for the past 10 frames. */ QList m_damageHistory; - /** - * @brief Timer to measure how long a frame renders. - */ - QElapsedTimer m_renderTimer; - QList m_extensions; }; diff --git a/plugins/platforms/drm/egl_gbm_backend.cpp b/plugins/platforms/drm/egl_gbm_backend.cpp index 7451a34085..58bc1aee45 100644 --- a/plugins/platforms/drm/egl_gbm_backend.cpp +++ b/plugins/platforms/drm/egl_gbm_backend.cpp @@ -513,7 +513,6 @@ SceneOpenGLTexturePrivate *EglGbmBackend::createBackendTexture(SceneOpenGLTextur QRegion EglGbmBackend::prepareRenderingFrame() { - startRenderTimer(); return QRegion(); } diff --git a/plugins/platforms/drm/egl_stream_backend.cpp b/plugins/platforms/drm/egl_stream_backend.cpp index debd3eb8eb..29205f6277 100644 --- a/plugins/platforms/drm/egl_stream_backend.cpp +++ b/plugins/platforms/drm/egl_stream_backend.cpp @@ -477,7 +477,6 @@ SceneOpenGLTexturePrivate *EglStreamBackend::createBackendTexture(SceneOpenGLTex QRegion EglStreamBackend::prepareRenderingFrame() { - startRenderTimer(); return QRegion(); } diff --git a/plugins/platforms/hwcomposer/egl_hwcomposer_backend.cpp b/plugins/platforms/hwcomposer/egl_hwcomposer_backend.cpp index 06255bd9aa..74dc036317 100644 --- a/plugins/platforms/hwcomposer/egl_hwcomposer_backend.cpp +++ b/plugins/platforms/hwcomposer/egl_hwcomposer_backend.cpp @@ -145,7 +145,6 @@ QRegion EglHwcomposerBackend::prepareRenderingFrame() present(); // TODO: buffer age? - startRenderTimer(); // triggers always a full repaint return QRegion(QRect(QPoint(0, 0), m_backend->size())); } diff --git a/plugins/platforms/virtual/egl_gbm_backend.cpp b/plugins/platforms/virtual/egl_gbm_backend.cpp index e0e12723e8..c3b08956ff 100644 --- a/plugins/platforms/virtual/egl_gbm_backend.cpp +++ b/plugins/platforms/virtual/egl_gbm_backend.cpp @@ -167,7 +167,6 @@ QRegion EglGbmBackend::prepareRenderingFrame() if (!lastDamage().isEmpty()) { present(); } - startRenderTimer(); if (!GLRenderTarget::isRenderTargetBound()) { GLRenderTarget::pushRenderTarget(m_fbo); } diff --git a/plugins/platforms/wayland/egl_wayland_backend.cpp b/plugins/platforms/wayland/egl_wayland_backend.cpp index 8a175bf1ba..ef7001a9cd 100644 --- a/plugins/platforms/wayland/egl_wayland_backend.cpp +++ b/plugins/platforms/wayland/egl_wayland_backend.cpp @@ -366,7 +366,6 @@ SceneOpenGLTexturePrivate *EglWaylandBackend::createBackendTexture(SceneOpenGLTe QRegion EglWaylandBackend::prepareRenderingFrame() { eglWaitNative(EGL_CORE_NATIVE_ENGINE); - startRenderTimer(); m_swapping = false; return QRegion(); } diff --git a/plugins/platforms/x11/common/eglonxbackend.cpp b/plugins/platforms/x11/common/eglonxbackend.cpp index c727bc5b5f..aecf064f95 100644 --- a/plugins/platforms/x11/common/eglonxbackend.cpp +++ b/plugins/platforms/x11/common/eglonxbackend.cpp @@ -402,7 +402,6 @@ QRegion EglOnXBackend::prepareRenderingFrame() if (supportsBufferAge()) repaint = accumulatedDamageHistory(m_bufferAge); - startRenderTimer(); eglWaitNative(EGL_CORE_NATIVE_ENGINE); return repaint; diff --git a/plugins/platforms/x11/standalone/glxbackend.cpp b/plugins/platforms/x11/standalone/glxbackend.cpp index d0d454c6df..e6b09a61b4 100644 --- a/plugins/platforms/x11/standalone/glxbackend.cpp +++ b/plugins/platforms/x11/standalone/glxbackend.cpp @@ -799,7 +799,6 @@ QRegion GlxBackend::prepareRenderingFrame() if (supportsBufferAge()) repaint = accumulatedDamageHistory(m_bufferAge); - startRenderTimer(); glXWaitX(); return repaint; diff --git a/plugins/platforms/x11/windowed/egl_x11_backend.cpp b/plugins/platforms/x11/windowed/egl_x11_backend.cpp index 1ca9071eaa..c7237c4209 100644 --- a/plugins/platforms/x11/windowed/egl_x11_backend.cpp +++ b/plugins/platforms/x11/windowed/egl_x11_backend.cpp @@ -62,7 +62,6 @@ void EglX11Backend::present() QRegion EglX11Backend::prepareRenderingFrame() { - startRenderTimer(); return QRegion(); } diff --git a/plugins/scenes/opengl/scene_opengl.cpp b/plugins/scenes/opengl/scene_opengl.cpp index 170773fd61..2694b432de 100644 --- a/plugins/scenes/opengl/scene_opengl.cpp +++ b/plugins/scenes/opengl/scene_opengl.cpp @@ -535,6 +535,8 @@ void SceneOpenGL::handleGraphicsReset(GLenum status) QMetaObject::invokeMethod(this, "resetCompositing", Qt::QueuedConnection); KNotification::event(QStringLiteral("graphicsreset"), i18n("Desktop effects were restarted due to a graphics reset")); + + m_resetOccurred = true; } @@ -620,105 +622,84 @@ void SceneOpenGL::aboutToStartPainting(int screenId, const QRegion &damage) m_backend->aboutToStartPainting(screenId, damage); } -qint64 SceneOpenGL::paint(const QRegion &damage, const QList &toplevels) +void SceneOpenGL::paint(int screenId, const QRegion &damage, const QList &toplevels) { + if (m_resetOccurred) { + return; // A graphics reset has occurred, do nothing. + } + + painted_screen = screenId; // actually paint the frame, flushed with the NEXT frame createStackingOrder(toplevels); - // After this call, updateRegion will contain the damaged region in the - // back buffer. This is the region that needs to be posted to repair - // the front buffer. It doesn't include the additional damage returned - // by prepareRenderingFrame(). validRegion is the region that has been - // repainted, and may be larger than updateRegion. - QRegion updateRegion, validRegion; - if (m_backend->perScreenRendering()) { - // 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; - QRegion valid; - // prepare rendering makes context current on the output - QRegion repaint = m_backend->prepareRenderingForScreen(i); - GLVertexBuffer::setVirtualScreenGeometry(geo); - GLRenderTarget::setVirtualScreenGeometry(geo); - GLVertexBuffer::setVirtualScreenScale(scaling); - GLRenderTarget::setVirtualScreenScale(scaling); + QRegion update; + QRegion valid; + QRegion repaint; + QRect geo; + qreal scaling; - const GLenum status = glGetGraphicsResetStatus(); - if (status != GL_NO_ERROR) { - handleGraphicsReset(status); - return 0; - } - - int mask = 0; - updateProjectionMatrix(); - - paintScreen(&mask, damage.intersected(geo), repaint, &update, &valid, projectionMatrix(), geo, scaling); // call generic implementation - paintCursor(valid); - - GLVertexBuffer::streamingBuffer()->endOfFrame(); - - m_backend->endRenderingFrameForScreen(i, valid, update); - - GLVertexBuffer::streamingBuffer()->framePosted(); - } + // prepare rendering makes context current on the output + if (screenId != -1) { + repaint = m_backend->prepareRenderingForScreen(screenId); + geo = screens()->geometry(screenId); + scaling = screens()->scale(screenId); } else { - painted_screen = -1; - m_backend->makeCurrent(); - QRegion repaint = m_backend->prepareRenderingFrame(); + repaint = m_backend->prepareRenderingFrame(); + geo = screens()->geometry(); + scaling = 1; + } - const GLenum status = glGetGraphicsResetStatus(); - if (status != GL_NO_ERROR) { - handleGraphicsReset(status); - return 0; - } - GLVertexBuffer::setVirtualScreenGeometry(screens()->geometry()); - GLRenderTarget::setVirtualScreenGeometry(screens()->geometry()); - GLVertexBuffer::setVirtualScreenScale(1); - GLRenderTarget::setVirtualScreenScale(1); + GLVertexBuffer::setVirtualScreenGeometry(geo); + GLRenderTarget::setVirtualScreenGeometry(geo); + GLVertexBuffer::setVirtualScreenScale(scaling); + GLRenderTarget::setVirtualScreenScale(scaling); + const GLenum status = glGetGraphicsResetStatus(); + if (status != GL_NO_ERROR) { + handleGraphicsReset(status); + } else { int mask = 0; updateProjectionMatrix(); - paintScreen(&mask, damage, repaint, &updateRegion, &validRegion, projectionMatrix()); // call generic implementation - if (!GLPlatform::instance()->isGLES()) { + paintScreen(&mask, damage.intersected(geo), repaint, &update, &valid, projectionMatrix(), geo, scaling); // call generic implementation + paintCursor(valid); + + if (!GLPlatform::instance()->isGLES() && screenId == -1) { const QSize &screenSize = screens()->size(); const QRegion displayRegion(0, 0, screenSize.width(), screenSize.height()); // copy dirty parts from front to backbuffer if (!m_backend->supportsBufferAge() && options->glPreferBufferSwap() == Options::CopyFrontBuffer && - validRegion != displayRegion) { + valid != displayRegion) { glReadBuffer(GL_FRONT); - m_backend->copyPixels(displayRegion - validRegion); + m_backend->copyPixels(displayRegion - valid); glReadBuffer(GL_BACK); - validRegion = displayRegion; + valid = displayRegion; } } GLVertexBuffer::streamingBuffer()->endOfFrame(); - - m_backend->endRenderingFrame(validRegion, updateRegion); - - GLVertexBuffer::streamingBuffer()->framePosted(); - } - - if (m_currentFence) { - if (!m_syncManager->updateFences()) { - qCDebug(KWIN_OPENGL) << "Aborting explicit synchronization with the X command stream."; - qCDebug(KWIN_OPENGL) << "Future frames will be rendered unsynchronized."; - delete m_syncManager; - m_syncManager = nullptr; + if (screenId != -1) { + m_backend->endRenderingFrameForScreen(screenId, valid, update); + } else { + m_backend->endRenderingFrame(valid, update); + } + GLVertexBuffer::streamingBuffer()->framePosted(); + + if (m_currentFence) { + if (!m_syncManager->updateFences()) { + qCDebug(KWIN_OPENGL) << "Aborting explicit synchronization with the X command stream."; + qCDebug(KWIN_OPENGL) << "Future frames will be rendered unsynchronized."; + delete m_syncManager; + m_syncManager = nullptr; + } + m_currentFence = nullptr; } - m_currentFence = nullptr; } // do cleanup clearStackingOrder(); - return m_backend->renderTime(); } QMatrix4x4 SceneOpenGL::transformation(int mask, const ScreenPaintData &data) const diff --git a/plugins/scenes/opengl/scene_opengl.h b/plugins/scenes/opengl/scene_opengl.h index 6805b66694..0e6c60c63e 100644 --- a/plugins/scenes/opengl/scene_opengl.h +++ b/plugins/scenes/opengl/scene_opengl.h @@ -35,7 +35,7 @@ public: ~SceneOpenGL() override; bool initFailed() const override; bool hasPendingFlush() const override; - qint64 paint(const QRegion &damage, const QList &windows) override; + void paint(int screenId, const QRegion &damage, const QList &windows) override; Scene::EffectFrame *createEffectFrame(EffectFrameImpl *frame) override; Shadow *createShadow(Toplevel *toplevel) override; void screenGeometryChanged(const QSize &size) override; @@ -95,6 +95,7 @@ private: bool viewportLimitsMatched(const QSize &size) const; private: + bool m_resetOccurred = false; bool m_debug; OpenGLBackend *m_backend; SyncManager *m_syncManager; diff --git a/plugins/scenes/qpainter/scene_qpainter.cpp b/plugins/scenes/qpainter/scene_qpainter.cpp index b8a0aa513c..cc4d1c3633 100644 --- a/plugins/scenes/qpainter/scene_qpainter.cpp +++ b/plugins/scenes/qpainter/scene_qpainter.cpp @@ -81,30 +81,25 @@ void SceneQPainter::paintGenericScreen(int mask, const ScreenPaintData &data) m_painter->restore(); } -qint64 SceneQPainter::paint(const QRegion &_damage, const QList &toplevels) +void SceneQPainter::paint(int screenId, const QRegion &_damage, const QList &toplevels) { Q_ASSERT(m_backend->perScreenRendering()); - QElapsedTimer renderTimer; - renderTimer.start(); + painted_screen = screenId; createStackingOrder(toplevels); QRegion damage = _damage; - for (int i = 0; i < screens()->count(); ++i) { - int mask = 0; + int mask = 0; - painted_screen = i; - m_backend->prepareRenderingFrame(i); - const bool needsFullRepaint = m_backend->needsFullRepaint(i); - if (needsFullRepaint) { - mask |= Scene::PAINT_SCREEN_BACKGROUND_FIRST; - damage = screens()->geometry(); - } - const QRect geometry = screens()->geometry(i); - QImage *buffer = m_backend->bufferForScreen(i); - if (!buffer || buffer->isNull()) { - continue; - } + m_backend->prepareRenderingFrame(screenId); + const bool needsFullRepaint = m_backend->needsFullRepaint(screenId); + if (needsFullRepaint) { + mask |= Scene::PAINT_SCREEN_BACKGROUND_FIRST; + damage = screens()->geometry(screenId); + } + const QRect geometry = screens()->geometry(screenId); + QImage *buffer = m_backend->bufferForScreen(screenId); + if (buffer && !buffer->isNull()) { m_painter->begin(buffer); m_painter->setWindow(geometry); @@ -113,13 +108,11 @@ qint64 SceneQPainter::paint(const QRegion &_damage, const QList &top paintCursor(updateRegion); m_painter->end(); - m_backend->present(i, mask, updateRegion); + m_backend->present(screenId, mask, updateRegion); } // do cleanup clearStackingOrder(); - - return renderTimer.nsecsElapsed(); } void SceneQPainter::paintBackground(const QRegion ®ion) diff --git a/plugins/scenes/qpainter/scene_qpainter.h b/plugins/scenes/qpainter/scene_qpainter.h index dffe29d2b4..b47b1c18be 100644 --- a/plugins/scenes/qpainter/scene_qpainter.h +++ b/plugins/scenes/qpainter/scene_qpainter.h @@ -25,7 +25,7 @@ public: ~SceneQPainter() override; bool usesOverlayWindow() const override; OverlayWindow* overlayWindow() const override; - qint64 paint(const QRegion &damage, const QList &windows) override; + void paint(int screenId, const QRegion &damage, const QList &windows) override; void paintGenericScreen(int mask, const ScreenPaintData &data) override; CompositingType compositingType() const override; bool initFailed() const override; diff --git a/plugins/scenes/xrender/scene_xrender.cpp b/plugins/scenes/xrender/scene_xrender.cpp index 0dc0ab1d86..9e29398659 100644 --- a/plugins/scenes/xrender/scene_xrender.cpp +++ b/plugins/scenes/xrender/scene_xrender.cpp @@ -238,11 +238,9 @@ bool SceneXrender::initFailed() const } // the entry point for painting -qint64 SceneXrender::paint(const QRegion &damage, const QList &toplevels) +void SceneXrender::paint(int screenId, const QRegion &damage, const QList &toplevels) { - painted_screen = -1; - QElapsedTimer renderTimer; - renderTimer.start(); + painted_screen = screenId; createStackingOrder(toplevels); @@ -255,8 +253,6 @@ qint64 SceneXrender::paint(const QRegion &damage, const QList &tople m_backend->present(mask, updateRegion); // do cleanup clearStackingOrder(); - - return renderTimer.nsecsElapsed(); } void SceneXrender::paintGenericScreen(int mask, const ScreenPaintData &data) diff --git a/plugins/scenes/xrender/scene_xrender.h b/plugins/scenes/xrender/scene_xrender.h index 22f7cea0b1..d90f487e73 100644 --- a/plugins/scenes/xrender/scene_xrender.h +++ b/plugins/scenes/xrender/scene_xrender.h @@ -143,7 +143,7 @@ public: CompositingType compositingType() const override { return XRenderCompositing; } - qint64 paint(const QRegion &damage, const QList &windows) override; + void paint(int screenId, const QRegion &damage, const QList &windows) override; Scene::EffectFrame *createEffectFrame(EffectFrameImpl *frame) override; Shadow *createShadow(Toplevel *toplevel) override; void screenGeometryChanged(const QSize &size) override; diff --git a/scene.h b/scene.h index 5e79fcb0c8..00f3a32264 100644 --- a/scene.h +++ b/scene.h @@ -65,7 +65,7 @@ public: // The entry point for the main part of the painting pass. // returns the time since the last vblank signal - if there's one // ie. "what of this frame is lost to painting" - virtual qint64 paint(const QRegion &damage, const QList &windows) = 0; + virtual void paint(int screenId, const QRegion &damage, const QList &windows) = 0; /** * Adds the Toplevel to the Scene.