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.