From 9e97c067585e014649a33b073b1ab95636ff4802 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Fri, 31 Dec 2021 13:51:42 +0200 Subject: [PATCH] Move graphics reset handling to RenderBackend This makes the Scene less overloaded and it's needed for things such as render layers. In hindsight, it would be great to merge checkGraphicsReset() and beginFrame(), e.g. make beginFrame() return the status like in QRhi or VkSwapchain. If it's OUT_OF_DATE or something, reinitialize the compositor. --- src/composite.cpp | 9 +- .../scenes/opengl/openglbackend.cpp | 46 +++++ .../scenes/opengl/openglbackend.h | 1 + src/renderbackend.cpp | 5 + src/renderbackend.h | 2 + src/scene.h | 1 - src/scenes/opengl/scene_opengl.cpp | 159 ++++++------------ src/scenes/opengl/scene_opengl.h | 2 - 8 files changed, 111 insertions(+), 114 deletions(-) diff --git a/src/composite.cpp b/src/composite.cpp index 26bbce65e5..31d08e3e61 100644 --- a/src/composite.cpp +++ b/src/composite.cpp @@ -307,7 +307,6 @@ bool Compositor::setupStart() QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software); } - connect(m_scene, &Scene::resetCompositing, this, &Compositor::reinitialize); Q_EMIT sceneCreated(); return true; @@ -619,8 +618,14 @@ QList Compositor::windowsToRender() const void Compositor::composite(RenderLoop *renderLoop) { - const auto &output = m_renderLoops[renderLoop]; + if (m_backend->checkGraphicsReset()) { + qCDebug(KWIN_CORE) << "Graphics reset occurred"; + KNotification::event(QStringLiteral("graphicsreset"), i18n("Desktop effects were restarted due to a graphics reset")); + reinitialize(); + return; + } + const auto &output = m_renderLoops[renderLoop]; fTraceDuration("Paint (", output ? output->name() : QStringLiteral("screens"), ")"); const auto windows = windowsToRender(); diff --git a/src/platformsupport/scenes/opengl/openglbackend.cpp b/src/platformsupport/scenes/opengl/openglbackend.cpp index d78154e6ef..15190dd7ce 100644 --- a/src/platformsupport/scenes/opengl/openglbackend.cpp +++ b/src/platformsupport/scenes/opengl/openglbackend.cpp @@ -13,7 +13,21 @@ #include "screens.h" #include "utils.h" +#include + #include +#include + +// HACK: workaround for libepoxy < 1.3 +#ifndef GL_GUILTY_CONTEXT_RESET +#define GL_GUILTY_CONTEXT_RESET 0x8253 +#endif +#ifndef GL_INNOCENT_CONTEXT_RESET +#define GL_INNOCENT_CONTEXT_RESET 0x8254 +#endif +#ifndef GL_UNKNOWN_CONTEXT_RESET +#define GL_UNKNOWN_CONTEXT_RESET 0x8255 +#endif namespace KWin { @@ -97,4 +111,36 @@ SurfaceTexture *OpenGLBackend::createSurfaceTextureWayland(SurfacePixmapWayland return nullptr; } +bool OpenGLBackend::checkGraphicsReset() +{ + const GLenum status = glGetGraphicsResetStatus(); + if (Q_LIKELY(status == GL_NO_ERROR)) { + return false; + } + + switch (status) { + case GL_GUILTY_CONTEXT_RESET: + qCDebug(KWIN_OPENGL) << "A graphics reset attributable to the current GL context occurred."; + break; + case GL_INNOCENT_CONTEXT_RESET: + qCDebug(KWIN_OPENGL) << "A graphics reset not attributable to the current GL context occurred."; + break; + case GL_UNKNOWN_CONTEXT_RESET: + qCDebug(KWIN_OPENGL) << "A graphics reset of an unknown cause occurred."; + break; + default: + break; + } + + QElapsedTimer timer; + timer.start(); + + // Wait until the reset is completed or max 10 seconds + while (timer.elapsed() < 10000 && glGetGraphicsResetStatus() != GL_NO_ERROR) { + usleep(50); + } + + return true; +} + } diff --git a/src/platformsupport/scenes/opengl/openglbackend.h b/src/platformsupport/scenes/opengl/openglbackend.h index 03251658a4..00388f9681 100644 --- a/src/platformsupport/scenes/opengl/openglbackend.h +++ b/src/platformsupport/scenes/opengl/openglbackend.h @@ -49,6 +49,7 @@ public: virtual void init() = 0; CompositingType compositingType() const override final; + bool checkGraphicsReset() override final; virtual SurfaceTexture *createSurfaceTextureInternal(SurfacePixmapInternal *pixmap); virtual SurfaceTexture *createSurfaceTextureX11(SurfacePixmapX11 *pixmap); diff --git a/src/renderbackend.cpp b/src/renderbackend.cpp index f20d460b02..54c0ceb8bd 100644 --- a/src/renderbackend.cpp +++ b/src/renderbackend.cpp @@ -19,4 +19,9 @@ OverlayWindow *RenderBackend::overlayWindow() const return nullptr; } +bool RenderBackend::checkGraphicsReset() +{ + return false; +} + } // namespace KWin diff --git a/src/renderbackend.h b/src/renderbackend.h index d8bd0f46cb..096a91c286 100644 --- a/src/renderbackend.h +++ b/src/renderbackend.h @@ -29,6 +29,8 @@ public: virtual CompositingType compositingType() const = 0; virtual OverlayWindow *overlayWindow() const; + virtual bool checkGraphicsReset(); + virtual QRegion beginFrame(AbstractOutput *output) = 0; virtual void endFrame(AbstractOutput *output, const QRegion &renderedRegion, const QRegion &damagedRegion) = 0; }; diff --git a/src/scene.h b/src/scene.h index af07982bb0..8306f51b78 100644 --- a/src/scene.h +++ b/src/scene.h @@ -193,7 +193,6 @@ public: Q_SIGNALS: void frameRendered(); - void resetCompositing(); public Q_SLOTS: // a window has been closed diff --git a/src/scenes/opengl/scene_opengl.cpp b/src/scenes/opengl/scene_opengl.cpp index cc476e87eb..0dd766cac2 100644 --- a/src/scenes/opengl/scene_opengl.cpp +++ b/src/scenes/opengl/scene_opengl.cpp @@ -37,7 +37,6 @@ #include #include -#include #include #include @@ -45,23 +44,8 @@ #include #include #include - -#include -#include -#include #include -// HACK: workaround for libepoxy < 1.3 -#ifndef GL_GUILTY_CONTEXT_RESET -#define GL_GUILTY_CONTEXT_RESET 0x8253 -#endif -#ifndef GL_INNOCENT_CONTEXT_RESET -#define GL_INNOCENT_CONTEXT_RESET 0x8254 -#endif -#ifndef GL_UNKNOWN_CONTEXT_RESET -#define GL_UNKNOWN_CONTEXT_RESET 0x8255 -#endif - namespace KWin { @@ -113,40 +97,6 @@ bool SceneOpenGL::initFailed() const return !init_ok; } -void SceneOpenGL::handleGraphicsReset(GLenum status) -{ - switch (status) { - case GL_GUILTY_CONTEXT_RESET: - qCDebug(KWIN_OPENGL) << "A graphics reset attributable to the current GL context occurred."; - break; - - case GL_INNOCENT_CONTEXT_RESET: - qCDebug(KWIN_OPENGL) << "A graphics reset not attributable to the current GL context occurred."; - break; - - case GL_UNKNOWN_CONTEXT_RESET: - qCDebug(KWIN_OPENGL) << "A graphics reset of an unknown cause occurred."; - break; - - default: - break; - } - - QElapsedTimer timer; - timer.start(); - - // Wait until the reset is completed or max 10 seconds - while (timer.elapsed() < 10000 && glGetGraphicsResetStatus() != GL_NO_ERROR) - usleep(50); - - qCDebug(KWIN_OPENGL) << "Attempting to reset compositing."; - QMetaObject::invokeMethod(this, "resetCompositing", Qt::QueuedConnection); - - KNotification::event(QStringLiteral("graphicsreset"), i18n("Desktop effects were restarted due to a graphics reset")); - - m_resetOccurred = true; -} - /** * Render cursor texture in case hardware cursor is disabled. * Useful for screen recording apps or backends that can't do planes. @@ -237,10 +187,6 @@ static SurfaceItem *findTopMostSurface(SurfaceItem *item) void SceneOpenGL::paint(AbstractOutput *output, const QRegion &damage, const QList &toplevels, RenderLoop *renderLoop) { - if (m_resetOccurred) { - return; // A graphics reset has occurred, do nothing. - } - painted_screen = output; // actually paint the frame, flushed with the NEXT frame createStackingOrder(toplevels); @@ -258,71 +204,66 @@ void SceneOpenGL::paint(AbstractOutput *output, const QRegion &damage, const QLi scaling = 1; } - const GLenum status = glGetGraphicsResetStatus(); - if (status != GL_NO_ERROR) { - handleGraphicsReset(status); - } else { - renderLoop->beginFrame(); + renderLoop->beginFrame(); - SurfaceItem *fullscreenSurface = nullptr; - for (int i = stacking_order.count() - 1; i >=0; i--) { - Window *window = stacking_order[i]; - Toplevel *toplevel = window->window(); - if (output && toplevel->isOnOutput(output) && window->isVisible() && toplevel->opacity() > 0) { - AbstractClient *c = dynamic_cast(toplevel); - if (!c || !c->isFullScreen()) { - break; - } - if (!window->surfaceItem()) { - break; - } - SurfaceItem *topMost = findTopMostSurface(window->surfaceItem()); - auto pixmap = topMost->pixmap(); - if (!pixmap) { - break; - } - pixmap->update(); - // the subsurface has to be able to cover the whole window - if (topMost->position() != QPoint(0, 0)) { - break; - } - // and it has to be completely opaque - if (!window->isOpaque() && !topMost->opaque().contains(QRect(0, 0, window->width(), window->height()))) { - break; - } - fullscreenSurface = topMost; + SurfaceItem *fullscreenSurface = nullptr; + for (int i = stacking_order.count() - 1; i >=0; i--) { + Window *window = stacking_order[i]; + Toplevel *toplevel = window->window(); + if (output && toplevel->isOnOutput(output) && window->isVisible() && toplevel->opacity() > 0) { + AbstractClient *c = dynamic_cast(toplevel); + if (!c || !c->isFullScreen()) { break; } + if (!window->surfaceItem()) { + break; + } + SurfaceItem *topMost = findTopMostSurface(window->surfaceItem()); + auto pixmap = topMost->pixmap(); + if (!pixmap) { + break; + } + pixmap->update(); + // the subsurface has to be able to cover the whole window + if (topMost->position() != QPoint(0, 0)) { + break; + } + // and it has to be completely opaque + if (!window->isOpaque() && !topMost->opaque().contains(QRect(0, 0, window->width(), window->height()))) { + break; + } + fullscreenSurface = topMost; + break; } - renderLoop->setFullscreenSurface(fullscreenSurface); + } + renderLoop->setFullscreenSurface(fullscreenSurface); - bool directScanout = false; - if (m_backend->directScanoutAllowed(output) && !static_cast(effects)->blocksDirectScanout()) { - directScanout = m_backend->scanout(output, fullscreenSurface); - } - if (directScanout) { - renderLoop->endFrame(); - } else { - // prepare rendering makescontext current on the output - repaint = m_backend->beginFrame(output); - GLVertexBuffer::streamingBuffer()->beginFrame(); + bool directScanout = false; + if (m_backend->directScanoutAllowed(output) && !static_cast(effects)->blocksDirectScanout()) { + directScanout = m_backend->scanout(output, fullscreenSurface); + } + if (directScanout) { + renderLoop->endFrame(); + } else { + // prepare rendering makescontext current on the output + repaint = m_backend->beginFrame(output); + GLVertexBuffer::streamingBuffer()->beginFrame(); - GLVertexBuffer::setVirtualScreenGeometry(geo); - GLRenderTarget::setVirtualScreenGeometry(geo); - GLVertexBuffer::setVirtualScreenScale(scaling); - GLRenderTarget::setVirtualScreenScale(scaling); + GLVertexBuffer::setVirtualScreenGeometry(geo); + GLRenderTarget::setVirtualScreenGeometry(geo); + GLVertexBuffer::setVirtualScreenScale(scaling); + GLRenderTarget::setVirtualScreenScale(scaling); - updateProjectionMatrix(geo); + updateProjectionMatrix(geo); - paintScreen(damage.intersected(geo), repaint, &update, &valid, - renderLoop, projectionMatrix()); // call generic implementation - paintCursor(output, valid); + paintScreen(damage.intersected(geo), repaint, &update, &valid, + renderLoop, projectionMatrix()); // call generic implementation + paintCursor(output, valid); - renderLoop->endFrame(); + renderLoop->endFrame(); - GLVertexBuffer::streamingBuffer()->endOfFrame(); - m_backend->endFrame(output, valid, update); - } + GLVertexBuffer::streamingBuffer()->endOfFrame(); + m_backend->endFrame(output, valid, update); } // do cleanup diff --git a/src/scenes/opengl/scene_opengl.h b/src/scenes/opengl/scene_opengl.h index 79418cc528..a43cdf39f1 100644 --- a/src/scenes/opengl/scene_opengl.h +++ b/src/scenes/opengl/scene_opengl.h @@ -77,10 +77,8 @@ private: void doPaintBackground(const QVector< float >& vertices); void updateProjectionMatrix(const QRect &geometry); void performPaintWindow(EffectWindowImpl* w, int mask, const QRegion ®ion, WindowPaintData& data); - void handleGraphicsReset(GLenum status); bool init_ok = true; - bool m_resetOccurred = false; OpenGLBackend *m_backend; LanczosFilter *m_lanczosFilter = nullptr; QScopedPointer m_cursorTexture;