[plasma/kwin] src: effects: stop compositing the frame when the context is no longer current

Xaver Hugl <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit f25a765c0b947cebdc1f3ac3e24865958a4da0a6 by Xaver Hugl.
Committed on 23/07/2026 at 11:06.
Pushed by zamundaaa into branch 'master'.

effects: stop compositing the frame when the context is no longer current

This is necessary to be able to do multi-gpu copies in the item renderer, as
switching the EGL context to a different GPU and then switching back can fail
if a GPU reset happens. If effects wouldn't handle this, they'd simply crash.

M  +3    -4    src/effect/animationeffect.cpp
M  +1    -1    src/effect/animationeffect.h
M  +6    -6    src/effect/effect.cpp
M  +8    -5    src/effect/effect.h
M  +12   -9    src/effect/effecthandler.cpp
M  +3    -3    src/effect/effecthandler.h
M  +21   -13   src/effect/offscreeneffect.cpp
M  +2    -2    src/effect/offscreeneffect.h
M  +5    -0    src/opengl/eglcontext.cpp
M  +5    -0    src/opengl/eglcontext.h
M  +2    -2    src/plugins/blendchanges/blendchanges.cpp
M  +1    -1    src/plugins/blendchanges/blendchanges.h
M  +2    -2    src/plugins/blur/blur.cpp
M  +1    -1    src/plugins/blur/blur.h
M  +2    -2    src/plugins/colorblindnesscorrection/colorblindnesscorrection.cpp
M  +1    -1    src/plugins/colorblindnesscorrection/colorblindnesscorrection.h
M  +4    -6    src/plugins/diminactive/diminactive.cpp
M  +1    -1    src/plugins/diminactive/diminactive.h
M  +2    -2    src/plugins/kscreen/kscreen.cpp
M  +1    -1    src/plugins/kscreen/kscreen.h
M  +5    -2    src/plugins/magnifier/magnifier.cpp
M  +1    -1    src/plugins/magnifier/magnifier.h
M  +7    -8    src/plugins/mouseclick/mouseclick.cpp
M  +1    -1    src/plugins/mouseclick/mouseclick.h
M  +6    -3    src/plugins/mousemark/mousemark.cpp
M  +1    -1    src/plugins/mousemark/mousemark.h
M  +7    -5    src/plugins/screentransform/screentransform.cpp
M  +1    -1    src/plugins/screentransform/screentransform.h
M  +5    -2    src/plugins/showfps/showfpseffect.cpp
M  +1    -1    src/plugins/showfps/showfpseffect.h
M  +5    -4    src/plugins/showpaint/showpaint.cpp
M  +1    -1    src/plugins/showpaint/showpaint.h
M  +16   -14   src/plugins/slide/slide.cpp
M  +3    -3    src/plugins/slide/slide.h
M  +2    -2    src/plugins/slideback/slideback.cpp
M  +1    -1    src/plugins/slideback/slideback.h
M  +2    -2    src/plugins/slidingnotifications/slidingnotifications.cpp
M  +1    -1    src/plugins/slidingnotifications/slidingnotifications.h
M  +3    -4    src/plugins/slidingpopups/slidingpopups.cpp
M  +1    -1    src/plugins/slidingpopups/slidingpopups.h
M  +11   -6    src/plugins/thumbnailaside/thumbnailaside.cpp
M  +2    -2    src/plugins/thumbnailaside/thumbnailaside.h
M  +7    -8    src/plugins/touchpoints/touchpoints.cpp
M  +1    -1    src/plugins/touchpoints/touchpoints.h
M  +7    -3    src/plugins/zoom/zoom.cpp
M  +1    -1    src/plugins/zoom/zoom.h
M  +1    -1    src/scene/itemrenderer.h
M  +23   -9    src/scene/itemrenderer_opengl.cpp
M  +2    -2    src/scene/itemrenderer_opengl.h
M  +25   -17   src/scene/workspacescene.cpp
M  +6    -6    src/scene/workspacescene.h

https://invent.kde.org/plasma/kwin/-/commit/f25a765c0b947cebdc1f3ac3e24865958a4da0a6

diff --git a/src/effect/animationeffect.cpp b/src/effect/animationeffect.cpp
index d2638d9c777..d2a9d37cee8 100644
--- a/src/effect/animationeffect.cpp
+++ b/src/effect/animationeffect.cpp
@@ -502,12 +502,11 @@ static inline float geometryCompensation(int flags, float v)
     return 0.5 * (1.0 - v); // half compensation
 }
 
-void AnimationEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
+bool AnimationEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
 {
     auto it = d->m_animations.find(w);
     if (it == d->m_animations.end()) {
-        effects->paintWindow(renderTarget, viewport, w, mask, deviceRegion, data);
-        return;
+        return effects->paintWindow(renderTarget, viewport, w, mask, deviceRegion, data);
     }
     Region effectiveDeviceRegion = deviceRegion;
     auto &[window, pair] = *it;
@@ -629,7 +628,7 @@ void AnimationEffect::paintWindow(const RenderTarget &renderTarget, const Render
             break;
         }
     }
-    effects->paintWindow(renderTarget, viewport, w, mask, effectiveDeviceRegion, data);
+    return effects->paintWindow(renderTarget, viewport, w, mask, effectiveDeviceRegion, data);
 }
 
 void AnimationEffect::postPaintScreen()
diff --git a/src/effect/animationeffect.h b/src/effect/animationeffect.h
index 448ccd6958f..8fec673c4a3 100644
--- a/src/effect/animationeffect.h
+++ b/src/effect/animationeffect.h
@@ -326,7 +326,7 @@ public:
     // Reimplemented from KWin::Effect.
     QString debug(const QString &parameter) const override;
     void prePaintWindow(RenderView *view, EffectWindow *w, WindowPrePaintData &data) override;
-    void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data) override;
+    bool paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data) override;
     void postPaintScreen() override;
 
     /**
diff --git a/src/effect/effect.cpp b/src/effect/effect.cpp
index 8ebd291e3c7..fe99884ce4a 100644
--- a/src/effect/effect.cpp
+++ b/src/effect/effect.cpp
@@ -373,9 +373,9 @@ void Effect::prePaintScreen(ScreenPrePaintData &data)
     effects->prePaintScreen(data);
 }
 
-void Effect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
+bool Effect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
 {
-    effects->paintScreen(renderTarget, viewport, mask, deviceRegion, screen);
+    return effects->paintScreen(renderTarget, viewport, mask, deviceRegion, screen);
 }
 
 void Effect::postPaintScreen()
@@ -388,9 +388,9 @@ void Effect::prePaintWindow(RenderView *view, EffectWindow *w, WindowPrePaintDat
     effects->prePaintWindow(view, w, data);
 }
 
-void Effect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
+bool Effect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
 {
-    effects->paintWindow(renderTarget, viewport, w, mask, deviceRegion, data);
+    return effects->paintWindow(renderTarget, viewport, w, mask, deviceRegion, data);
 }
 
 bool Effect::provides(Feature)
@@ -408,9 +408,9 @@ QString Effect::debug(const QString &) const
     return QString();
 }
 
-void Effect::drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
+bool Effect::drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
 {
-    effects->drawWindow(renderTarget, viewport, w, mask, deviceRegion, data);
+    return effects->drawWindow(renderTarget, viewport, w, mask, deviceRegion, data);
 }
 
 void Effect::setPositionTransformations(WindowPaintData &data, Rect &logicalRegion, EffectWindow *w,
diff --git a/src/effect/effect.h b/src/effect/effect.h
index 7bab42ad2da..587a41bc981 100644
--- a/src/effect/effect.h
+++ b/src/effect/effect.h
@@ -705,8 +705,11 @@ public:
      *
      * In OpenGL based compositing, the frameworks ensures that the context is current
      * when this method is invoked.
+     *
+     * @returns if painting was successful or failed (for example because of a GPU reset).
+     *          If painting failed, effects should stop rendering immediately.
      */
-    virtual void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen);
+    [[nodiscard]] virtual bool paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen);
     /*!
      * Called after all the painting has been finished.
      *
@@ -717,8 +720,8 @@ public:
      *
      * You shouldn't paint anything here.
      *
-     * In OpenGL based compositing, the frameworks ensures that the context is current
-     * when this method is invoked.
+     * NOTE In the case of a GPU reset, there may not be an OpenGL context
+     *      current when this method is called!
      */
     virtual void postPaintScreen();
 
@@ -750,7 +753,7 @@ public:
      * In OpenGL based compositing, the frameworks ensures that the context is current
      * when this method is invoked.
      */
-    virtual void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data);
+    [[nodiscard]] virtual bool paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data);
 
     /*!
      * Called on Transparent resizes.
@@ -781,7 +784,7 @@ public:
      * In OpenGL based compositing, the frameworks ensures that the context is current
      * when this method is invoked.
      */
-    virtual void drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data);
+    [[nodiscard]] virtual bool drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data);
 
     /*!
      * This function can be overriden to handle grabbed keyboard input.
diff --git a/src/effect/effecthandler.cpp b/src/effect/effecthandler.cpp
index 7d1fb020391..94af4ada9b9 100644
--- a/src/effect/effecthandler.cpp
+++ b/src/effect/effecthandler.cpp
@@ -348,13 +348,14 @@ void EffectsHandler::prePaintScreen(ScreenPrePaintData &data)
     // no special final code
 }
 
-void EffectsHandler::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
+bool EffectsHandler::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
 {
     if (m_currentPaintScreenIterator != m_activeEffects.constEnd()) {
-        (*m_currentPaintScreenIterator++)->paintScreen(renderTarget, viewport, mask, deviceRegion, screen);
+        const bool ret = (*m_currentPaintScreenIterator++)->paintScreen(renderTarget, viewport, mask, deviceRegion, screen);
         --m_currentPaintScreenIterator;
+        return ret;
     } else {
-        m_scene->finalPaintScreen(renderTarget, viewport, mask, deviceRegion, screen);
+        return m_scene->finalPaintScreen(renderTarget, viewport, mask, deviceRegion, screen);
     }
 }
 
@@ -376,13 +377,14 @@ void EffectsHandler::prePaintWindow(RenderView *view, EffectWindow *w, WindowPre
     // no special final code
 }
 
-void EffectsHandler::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
+bool EffectsHandler::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
 {
     if (m_currentPaintWindowIterator != m_activeEffects.constEnd()) {
-        (*m_currentPaintWindowIterator++)->paintWindow(renderTarget, viewport, w, mask, deviceRegion, data);
+        const bool ret = (*m_currentPaintWindowIterator++)->paintWindow(renderTarget, viewport, w, mask, deviceRegion, data);
         --m_currentPaintWindowIterator;
+        return ret;
     } else {
-        m_scene->finalPaintWindow(renderTarget, viewport, w, mask, deviceRegion, data);
+        return m_scene->finalPaintWindow(renderTarget, viewport, w, mask, deviceRegion, data);
     }
 }
 
@@ -396,13 +398,14 @@ Effect *EffectsHandler::provides(Effect::Feature ef)
     return nullptr;
 }
 
-void EffectsHandler::drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
+bool EffectsHandler::drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
 {
     if (m_currentDrawWindowIterator != m_activeEffects.constEnd()) {
-        (*m_currentDrawWindowIterator++)->drawWindow(renderTarget, viewport, w, mask, deviceRegion, data);
+        const bool ret = (*m_currentDrawWindowIterator++)->drawWindow(renderTarget, viewport, w, mask, deviceRegion, data);
         --m_currentDrawWindowIterator;
+        return ret;
     } else {
-        m_scene->finalDrawWindow(renderTarget, viewport, w, mask, deviceRegion, data);
+        return m_scene->finalDrawWindow(renderTarget, viewport, w, mask, deviceRegion, data);
     }
 }
 
diff --git a/src/effect/effecthandler.h b/src/effect/effecthandler.h
index 81a5d132f31..1f3e42aefea 100644
--- a/src/effect/effecthandler.h
+++ b/src/effect/effecthandler.h
@@ -176,11 +176,11 @@ public:
 
     // for use by effects
     void prePaintScreen(ScreenPrePaintData &data);
-    void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen);
+    [[nodiscard]] bool paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen);
     void postPaintScreen();
     void prePaintWindow(RenderView *view, EffectWindow *w, WindowPrePaintData &data);
-    void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data);
-    void drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data);
+    [[nodiscard]] bool paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data);
+    [[nodiscard]] bool drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data);
     void renderWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data);
     QVariant kwinOption(KWinOption kwopt);
     /**
diff --git a/src/effect/offscreeneffect.cpp b/src/effect/offscreeneffect.cpp
index c7ee1acb694..8d2f18d9a50 100644
--- a/src/effect/offscreeneffect.cpp
+++ b/src/effect/offscreeneffect.cpp
@@ -29,7 +29,7 @@ public:
     void paint(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *window, const Region &deviceRegion,
                const WindowPaintData &data, const WindowQuadList &quads);
 
-    void maybeRender(EffectWindow *window);
+    [[nodiscard]] bool maybeRender(EffectWindow *window);
 
     std::unique_ptr<GLTexture> m_texture;
     std::unique_ptr<GLFramebuffer> m_fbo;
@@ -106,7 +106,7 @@ void OffscreenEffect::apply(EffectWindow *window, int mask, WindowPaintData &dat
 {
 }
 
-void OffscreenData::maybeRender(EffectWindow *window)
+bool OffscreenData::maybeRender(EffectWindow *window)
 {
     const qreal scale = window->screen()->scale();
     const RectF logicalGeometry = snapToPixels(window->expandedGeometry(), scale);
@@ -115,12 +115,12 @@ void OffscreenData::maybeRender(EffectWindow *window)
     if (textureSize.isEmpty()) {
         m_fbo.reset();
         m_texture.reset();
-        return;
+        return true;
     }
     if (!m_texture || m_texture->size() != textureSize) {
         m_texture = GLTexture::allocate(GL_RGBA8, textureSize);
         if (!m_texture) {
-            return;
+            return true;
         }
         m_texture->setFilter(GL_LINEAR);
         m_texture->setWrapMode(GL_CLAMP_TO_EDGE);
@@ -139,11 +139,14 @@ void OffscreenData::maybeRender(EffectWindow *window)
         data.setOpacity(1.0);
 
         const int mask = Effect::PAINT_WINDOW_TRANSFORMED | Effect::PAINT_WINDOW_TRANSLUCENT;
-        effects->drawWindow(renderTarget, viewport, window, mask, Region::infinite(), data);
+        if (!effects->drawWindow(renderTarget, viewport, window, mask, Region::infinite(), data)) {
+            return false;
+        }
 
         GLFramebuffer::popFramebuffer();
         m_isDirty = false;
     }
+    return true;
 }
 
 OffscreenData::~OffscreenData()
@@ -233,12 +236,11 @@ void OffscreenData::paint(const RenderTarget &renderTarget, const RenderViewport
     vbo->unbindArrays();
 }
 
-void OffscreenEffect::drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *window, int mask, const Region &deviceRegion, WindowPaintData &data)
+bool OffscreenEffect::drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *window, int mask, const Region &deviceRegion, WindowPaintData &data)
 {
     const auto it = d->windows.find(window);
     if (it == d->windows.end()) {
-        effects->drawWindow(renderTarget, viewport, window, mask, deviceRegion, data);
-        return;
+        return effects->drawWindow(renderTarget, viewport, window, mask, deviceRegion, data);
     }
     OffscreenData *offscreenData = it->second.get();
 
@@ -257,8 +259,11 @@ void OffscreenEffect::drawWindow(const RenderTarget &renderTarget, const RenderV
     quads.append(quad);
     apply(window, mask, data, quads);
 
-    offscreenData->maybeRender(window);
+    if (!offscreenData->maybeRender(window)) {
+        return false;
+    }
     offscreenData->paint(renderTarget, viewport, window, deviceRegion, data, quads);
+    return true;
 }
 
 void OffscreenEffect::handleWindowDamaged(EffectWindow *window)
@@ -320,17 +325,19 @@ CrossFadeEffect::CrossFadeEffect(QObject *parent)
 
 CrossFadeEffect::~CrossFadeEffect() = default;
 
-void CrossFadeEffect::drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *window, int mask, const Region &deviceRegion, WindowPaintData &data)
+bool CrossFadeEffect::drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *window, int mask, const Region &deviceRegion, WindowPaintData &data)
 {
     const auto it = d->windows.find(window);
 
     // paint the new window (if applicable) underneath
     if (data.crossFadeProgress() > 0 || it == d->windows.end()) {
-        Effect::drawWindow(renderTarget, viewport, window, mask, deviceRegion, data);
+        if (!Effect::drawWindow(renderTarget, viewport, window, mask, deviceRegion, data)) {
+            return false;
+        }
     }
 
     if (it == d->windows.end()) {
-        return;
+        return true;
     }
     CrossFadeWindowData *offscreenData = it->second.get();
 
@@ -368,6 +375,7 @@ void CrossFadeEffect::drawWindow(const RenderTarget &renderTarget, const RenderV
     WindowQuadList quads;
     quads.append(quad);
     offscreenData->paint(renderTarget, viewport, window, deviceRegion, previousWindowData, quads);
+    return true;
 }
 
 void CrossFadeEffect::redirect(EffectWindow *window)
@@ -392,7 +400,7 @@ void CrossFadeEffect::redirect(EffectWindow *window)
     window->setData(WindowForceBackgroundContrastRole, QVariant());
 
     effects->makeOpenGLContextCurrent();
-    offscreenData->maybeRender(window);
+    (void)offscreenData->maybeRender(window);
     offscreenData->frameGeometryAtCapture = window->frameGeometry();
 
     window->setData(WindowForceBlurRole, blurRole);
diff --git a/src/effect/offscreeneffect.h b/src/effect/offscreeneffect.h
index 32cca60552a..404a92b5723 100644
--- a/src/effect/offscreeneffect.h
+++ b/src/effect/offscreeneffect.h
@@ -41,7 +41,7 @@ public:
     static bool supported();
 
 protected:
-    void drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *window, int mask, const Region &deviceRegion, WindowPaintData &data) override;
+    bool drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *window, int mask, const Region &deviceRegion, WindowPaintData &data) override;
 
     /**
      * This function must be called when the effect wants to animate the specified
@@ -104,7 +104,7 @@ public:
     explicit CrossFadeEffect(QObject *parent = nullptr);
     ~CrossFadeEffect() override;
 
-    void drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *window, int mask, const Region &deviceRegion, WindowPaintData &data) override;
+    bool drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *window, int mask, const Region &deviceRegion, WindowPaintData &data) override;
 
     /**
      * This function must be called when the effect wants to animate the specified
diff --git a/src/opengl/eglcontext.cpp b/src/opengl/eglcontext.cpp
index 02a15687bde..464da01ac23 100644
--- a/src/opengl/eglcontext.cpp
+++ b/src/opengl/eglcontext.cpp
@@ -572,6 +572,11 @@ bool EglContext::isFailed() const
     return m_failed;
 }
 
+void EglContext::setFailed()
+{
+    m_failed = true;
+}
+
 bool EglContext::isCompatibleWith(EglContext *other) const
 {
     // technically speaking, context sharing could be done
diff --git a/src/opengl/eglcontext.h b/src/opengl/eglcontext.h
index 16b05154f0d..60b7b3999b5 100644
--- a/src/opengl/eglcontext.h
+++ b/src/opengl/eglcontext.h
@@ -90,6 +90,11 @@ public:
      *          or the context was reset
      */
     bool isFailed() const;
+    /**
+     * Mark the context as needing to be re-created. This can be useful
+     * for non-reset errors that can't be recovered from.
+     */
+    void setFailed();
 
     GLenum checkGraphicsResetStatus();
     void glReadnPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, GLvoid *data);
diff --git a/src/plugins/blendchanges/blendchanges.cpp b/src/plugins/blendchanges/blendchanges.cpp
index bffad1ba43e..fc163a2f808 100644
--- a/src/plugins/blendchanges/blendchanges.cpp
+++ b/src/plugins/blendchanges/blendchanges.cpp
@@ -83,10 +83,10 @@ void BlendChanges::postPaintScreen()
     effects->addRepaintFull();
 }
 
-void BlendChanges::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
+bool BlendChanges::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
 {
     data.setCrossFadeProgress(m_timeline.value());
-    effects->paintWindow(renderTarget, viewport, w, mask, deviceRegion, data);
+    return effects->paintWindow(renderTarget, viewport, w, mask, deviceRegion, data);
 }
 
 void BlendChanges::prePaintScreen(ScreenPrePaintData &data)
diff --git a/src/plugins/blendchanges/blendchanges.h b/src/plugins/blendchanges/blendchanges.h
index 37fd21bd0d7..7616725200a 100644
--- a/src/plugins/blendchanges/blendchanges.h
+++ b/src/plugins/blendchanges/blendchanges.h
@@ -27,7 +27,7 @@ public:
     // Effect interface
     void prePaintScreen(ScreenPrePaintData &data) override;
     void postPaintScreen() override;
-    void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data) override;
+    bool paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data) override;
 
     bool isActive() const override;
 
diff --git a/src/plugins/blur/blur.cpp b/src/plugins/blur/blur.cpp
index 25a143c3059..ad495b47977 100644
--- a/src/plugins/blur/blur.cpp
+++ b/src/plugins/blur/blur.cpp
@@ -503,12 +503,12 @@ bool BlurEffect::shouldBlur(const EffectWindow *w, int mask, const WindowPaintDa
     return true;
 }
 
-void BlurEffect::drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
+bool BlurEffect::drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
 {
     blur(renderTarget, viewport, w, mask, deviceRegion, data);
 
     // Draw the window over the blurred area
-    effects->drawWindow(renderTarget, viewport, w, mask, deviceRegion, data);
+    return effects->drawWindow(renderTarget, viewport, w, mask, deviceRegion, data);
 }
 
 GLTexture *BlurEffect::ensureNoiseTexture()
diff --git a/src/plugins/blur/blur.h b/src/plugins/blur/blur.h
index 3692a2035ea..ee230160224 100644
--- a/src/plugins/blur/blur.h
+++ b/src/plugins/blur/blur.h
@@ -58,7 +58,7 @@ public:
 
     void reconfigure(ReconfigureFlags flags) override;
     void prePaintScreen(ScreenPrePaintData &data) override;
-    void drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data) override;
+    bool drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data) override;
 
     bool provides(Feature feature) override;
     bool isActive() const override;
diff --git a/src/plugins/colorblindnesscorrection/colorblindnesscorrection.cpp b/src/plugins/colorblindnesscorrection/colorblindnesscorrection.cpp
index c9fca5c0dff..60d185b055a 100644
--- a/src/plugins/colorblindnesscorrection/colorblindnesscorrection.cpp
+++ b/src/plugins/colorblindnesscorrection/colorblindnesscorrection.cpp
@@ -112,7 +112,7 @@ void ColorBlindnessCorrectionEffect::loadData()
     connect(effects, &EffectsHandler::windowAdded, this, &ColorBlindnessCorrectionEffect::correctColor);
 }
 
-void ColorBlindnessCorrectionEffect::drawWindow(const RenderTarget &renderTarget,
+bool ColorBlindnessCorrectionEffect::drawWindow(const RenderTarget &renderTarget,
                                                 const RenderViewport &viewport,
                                                 EffectWindow *w,
                                                 int mask,
@@ -124,7 +124,7 @@ void ColorBlindnessCorrectionEffect::drawWindow(const RenderTarget &renderTarget
         data.setSaturation(1.0f - m_intensity);
     }
 
-    OffscreenEffect::drawWindow(renderTarget, viewport, w, mask, logicalRegion, data);
+    return OffscreenEffect::drawWindow(renderTarget, viewport, w, mask, logicalRegion, data);
 }
 
 void ColorBlindnessCorrectionEffect::correctColor(KWin::EffectWindow *w)
diff --git a/src/plugins/colorblindnesscorrection/colorblindnesscorrection.h b/src/plugins/colorblindnesscorrection/colorblindnesscorrection.h
index cbc41972f43..d65238be7ed 100644
--- a/src/plugins/colorblindnesscorrection/colorblindnesscorrection.h
+++ b/src/plugins/colorblindnesscorrection/colorblindnesscorrection.h
@@ -36,7 +36,7 @@ public:
     bool provides(Feature) override;
     void reconfigure(ReconfigureFlags flags) override;
     int requestedEffectChainPosition() const override;
-    void drawWindow(const RenderTarget &renderTarget,
+    bool drawWindow(const RenderTarget &renderTarget,
                     const RenderViewport &viewport,
                     EffectWindow *w,
                     int mask,
diff --git a/src/plugins/diminactive/diminactive.cpp b/src/plugins/diminactive/diminactive.cpp
index cdbcccb0c08..d2307ab40db 100644
--- a/src/plugins/diminactive/diminactive.cpp
+++ b/src/plugins/diminactive/diminactive.cpp
@@ -100,29 +100,27 @@ void DimInactiveEffect::prePaintScreen(ScreenPrePaintData &data)
     effects->prePaintScreen(data);
 }
 
-void DimInactiveEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
+bool DimInactiveEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
 {
     auto transitionIt = m_transitions.constFind(w);
     if (transitionIt != m_transitions.constEnd()) {
         const qreal transitionProgress = (*transitionIt).value();
         dimWindow(data, m_dimStrength * transitionProgress);
-        effects->paintWindow(renderTarget, viewport, w, mask, deviceRegion, data);
-        return;
+        return effects->paintWindow(renderTarget, viewport, w, mask, deviceRegion, data);
     }
 
     auto forceIt = m_forceDim.constFind(w);
     if (forceIt != m_forceDim.constEnd()) {
         const qreal forcedStrength = *forceIt;
         dimWindow(data, forcedStrength);
-        effects->paintWindow(renderTarget, viewport, w, mask, deviceRegion, data);
-        return;
+        return effects->paintWindow(renderTarget, viewport, w, mask, deviceRegion, data);
     }
 
     if (canDimWindow(w)) {
         dimWindow(data, m_dimStrength);
     }
 
-    effects->paintWindow(renderTarget, viewport, w, mask, deviceRegion, data);
+    return effects->paintWindow(renderTarget, viewport, w, mask, deviceRegion, data);
 }
 
 void DimInactiveEffect::postPaintScreen()
diff --git a/src/plugins/diminactive/diminactive.h b/src/plugins/diminactive/diminactive.h
index 75c0847ce51..4f7ccfa6e95 100644
--- a/src/plugins/diminactive/diminactive.h
+++ b/src/plugins/diminactive/diminactive.h
@@ -37,7 +37,7 @@ public:
     void reconfigure(ReconfigureFlags flags) override;
 
     void prePaintScreen(ScreenPrePaintData &data) override;
-    void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data) override;
+    bool paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data) override;
     void postPaintScreen() override;
 
     int requestedEffectChainPosition() const override;
diff --git a/src/plugins/kscreen/kscreen.cpp b/src/plugins/kscreen/kscreen.cpp
index f1790733efe..a15a19664bc 100644
--- a/src/plugins/kscreen/kscreen.cpp
+++ b/src/plugins/kscreen/kscreen.cpp
@@ -107,7 +107,7 @@ void KscreenEffect::prePaintWindow(RenderView *view, EffectWindow *w, WindowPreP
     effects->prePaintWindow(view, w, data);
 }
 
-void KscreenEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
+bool KscreenEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
 {
     auto screen = w->screen();
     if (isScreenActive(screen)) {
@@ -131,7 +131,7 @@ void KscreenEffect::paintWindow(const RenderTarget &renderTarget, const RenderVi
             break;
         }
     }
-    effects->paintWindow(renderTarget, viewport, w, mask, deviceRegion, data);
+    return effects->paintWindow(renderTarget, viewport, w, mask, deviceRegion, data);
 }
 
 void KscreenEffect::setState(ScreenState &state, FadeOutState newState)
diff --git a/src/plugins/kscreen/kscreen.h b/src/plugins/kscreen/kscreen.h
index 6b5d59cdeaa..2416d819111 100644
--- a/src/plugins/kscreen/kscreen.h
+++ b/src/plugins/kscreen/kscreen.h
@@ -24,7 +24,7 @@ public:
     void prePaintScreen(ScreenPrePaintData &data) override;
     void postPaintScreen() override;
     void prePaintWindow(RenderView *view, EffectWindow *w, WindowPrePaintData &data) override;
-    void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data) override;
+    bool paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data) override;
 
     void reconfigure(ReconfigureFlags flags) override;
     bool isActive() const override;
diff --git a/src/plugins/magnifier/magnifier.cpp b/src/plugins/magnifier/magnifier.cpp
index 3ea02d6b1cb..31bb4062d08 100644
--- a/src/plugins/magnifier/magnifier.cpp
+++ b/src/plugins/magnifier/magnifier.cpp
@@ -176,9 +176,11 @@ void MagnifierEffect::prePaintScreen(ScreenPrePaintData &data)
     }
 }
 
-void MagnifierEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
+bool MagnifierEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
 {
-    effects->paintScreen(renderTarget, viewport, mask, deviceRegion, screen); // paint normal screen
+    if (!effects->paintScreen(renderTarget, viewport, mask, deviceRegion, screen)) { // paint normal screen)
+        return false;
+    }
     if (m_zoom != 1.0 && m_fbo) {
         // get the right area from the current rendered screen
         const Rect area = magnifierArea();
@@ -241,6 +243,7 @@ void MagnifierEffect::paintScreen(const RenderTarget &renderTarget, const Render
             vbo->render(GL_TRIANGLES);
         }
     }
+    return true;
 }
 
 void MagnifierEffect::postPaintScreen()
diff --git a/src/plugins/magnifier/magnifier.h b/src/plugins/magnifier/magnifier.h
index 68a43236c77..beeb20df007 100644
--- a/src/plugins/magnifier/magnifier.h
+++ b/src/plugins/magnifier/magnifier.h
@@ -34,7 +34,7 @@ public:
     ~MagnifierEffect() override;
     void reconfigure(ReconfigureFlags) override;
     void prePaintScreen(ScreenPrePaintData &data) override;
-    void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen) override;
+    bool paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen) override;
     void postPaintScreen() override;
     bool isActive() const override;
     static bool supported();
diff --git a/src/plugins/mouseclick/mouseclick.cpp b/src/plugins/mouseclick/mouseclick.cpp
index 2e02c1af7ba..824991c3447 100644
--- a/src/plugins/mouseclick/mouseclick.cpp
+++ b/src/plugins/mouseclick/mouseclick.cpp
@@ -94,13 +94,13 @@ void MouseClickEffect::prePaintScreen(ScreenPrePaintData &data)
     effects->prePaintScreen(data);
 }
 
-void MouseClickEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
+bool MouseClickEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
 {
-    effects->paintScreen(renderTarget, viewport, mask, deviceRegion, screen);
-
-    if (effects->isOpenGLCompositing()) {
-        paintScreenSetupGl(renderTarget, viewport.projectionMatrix());
+    if (!effects->paintScreen(renderTarget, viewport, mask, deviceRegion, screen)) {
+        return false;
     }
+
+    paintScreenSetupGl(renderTarget, viewport.projectionMatrix());
     for (const auto &click : m_clicks) {
         for (int i = 0; i < m_ringCount; ++i) {
             float alpha = computeAlpha(click.get(), i);
@@ -124,9 +124,8 @@ void MouseClickEffect::paintScreen(const RenderTarget &renderTarget, const Rende
             drawCircle(viewport, tool.m_color, tool.m_globalPosition.x(), tool.m_globalPosition.y(), size);
         }
     }
-    if (effects->isOpenGLCompositing()) {
-        paintScreenFinishGl();
-    }
+    paintScreenFinishGl();
+    return true;
 }
 
 void MouseClickEffect::postPaintScreen()
diff --git a/src/plugins/mouseclick/mouseclick.h b/src/plugins/mouseclick/mouseclick.h
index 3d33e33d6f3..e215e06803c 100644
--- a/src/plugins/mouseclick/mouseclick.h
+++ b/src/plugins/mouseclick/mouseclick.h
@@ -104,7 +104,7 @@ public:
     ~MouseClickEffect() override;
     void reconfigure(ReconfigureFlags) override;
     void prePaintScreen(ScreenPrePaintData &data) override;
-    void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen) override;
+    bool paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen) override;
     void postPaintScreen() override;
     bool isActive() const override;
 
diff --git a/src/plugins/mousemark/mousemark.cpp b/src/plugins/mousemark/mousemark.cpp
index 6fe9e67d46b..870109e314d 100644
--- a/src/plugins/mousemark/mousemark.cpp
+++ b/src/plugins/mousemark/mousemark.cpp
@@ -178,11 +178,13 @@ void MouseMarkEffect::endDrawings()
     effects->addRepaintFull();
 }
 
-void MouseMarkEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
+bool MouseMarkEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
 {
-    effects->paintScreen(renderTarget, viewport, mask, deviceRegion, screen); // paint normal screen
+    if (!effects->paintScreen(renderTarget, viewport, mask, deviceRegion, screen)) { // paint normal screen
+        return false;
+    }
     if (marks.isEmpty() && drawings.isEmpty()) {
-        return;
+        return true;
     }
     if (effects->openglContext()) {
         glLineWidth(width);
@@ -216,6 +218,7 @@ void MouseMarkEffect::paintScreen(const RenderTarget &renderTarget, const Render
         }
         glLineWidth(1.0);
     }
+    return true;
 }
 
 bool MouseMarkEffect::touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time)
diff --git a/src/plugins/mousemark/mousemark.h b/src/plugins/mousemark/mousemark.h
index 11757127be3..3888acd95c0 100644
--- a/src/plugins/mousemark/mousemark.h
+++ b/src/plugins/mousemark/mousemark.h
@@ -34,7 +34,7 @@ public:
     MouseMarkEffect();
     ~MouseMarkEffect() override;
     void reconfigure(ReconfigureFlags) override;
-    void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen) override;
+    bool paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen) override;
     bool isActive() const override;
     int requestedEffectChainPosition() const override;
     bool touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) override;
diff --git a/src/plugins/screentransform/screentransform.cpp b/src/plugins/screentransform/screentransform.cpp
index c456f3c39d2..86bdb37e7a4 100644
--- a/src/plugins/screentransform/screentransform.cpp
+++ b/src/plugins/screentransform/screentransform.cpp
@@ -199,12 +199,11 @@ static RectF lerp(const RectF &a, const RectF &b, qreal t)
     return ret;
 }
 
-void ScreenTransformEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
+bool ScreenTransformEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
 {
     auto it = m_states.find(screen);
     if (it == m_states.end() || m_currentView->backendOutput() != screen->backendOutput()) {
-        effects->paintScreen(renderTarget, viewport, mask, deviceRegion, screen);
-        return;
+        return effects->paintScreen(renderTarget, viewport, mask, deviceRegion, screen);
     }
 
     // Render the screen in an offscreen texture.
@@ -214,7 +213,8 @@ void ScreenTransformEffect::paintScreen(const RenderTarget &renderTarget, const
         it->m_current.texture = GLTexture::allocate(renderTarget.texture()->internalFormat(), nativeSize);
         if (!it->m_current.texture) {
             m_states.remove(screen);
-            return;
+            EglContext::currentContext()->setFailed();
+            return false;
         }
         it->m_current.framebuffer = std::make_unique<GLFramebuffer>(it->m_current.texture.get());
     }
@@ -250,7 +250,8 @@ void ScreenTransformEffect::paintScreen(const RenderTarget &renderTarget, const
 
     GLVertexBuffer *vbo = texturedRectVbo(lerp(it->m_oldGeometry, screenRect, blendFactor), scale);
     if (!vbo) {
-        return;
+        EglContext::currentContext()->setFailed();
+        return false;
     }
 
     ShaderManager *sm = ShaderManager::instance();
@@ -271,6 +272,7 @@ void ScreenTransformEffect::paintScreen(const RenderTarget &renderTarget, const
     it->m_current.texture->unbind();
 
     effects->addRepaintFull();
+    return true;
 }
 
 bool ScreenTransformEffect::isActive() const
diff --git a/src/plugins/screentransform/screentransform.h b/src/plugins/screentransform/screentransform.h
index 5fa520ef472..f9149e573c6 100644
--- a/src/plugins/screentransform/screentransform.h
+++ b/src/plugins/screentransform/screentransform.h
@@ -28,7 +28,7 @@ public:
     ~ScreenTransformEffect() override;
 
     void prePaintScreen(ScreenPrePaintData &data) override;
-    void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen) override;
+    bool paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen) override;
 
     bool isActive() const override;
 
diff --git a/src/plugins/showfps/showfpseffect.cpp b/src/plugins/showfps/showfpseffect.cpp
index 66187965c1f..413060fd6d6 100644
--- a/src/plugins/showfps/showfpseffect.cpp
+++ b/src/plugins/showfps/showfpseffect.cpp
@@ -100,9 +100,11 @@ void ShowFpsEffect::prePaintScreen(ScreenPrePaintData &data)
     screenData->m_scene->setGeometry(QRect(rect.x() + rect.width() - 300, rect.y(), 300, 150));
 }
 
-void ShowFpsEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
+bool ShowFpsEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
 {
-    effects->paintScreen(renderTarget, viewport, mask, deviceRegion, screen);
+    if (!effects->paintScreen(renderTarget, viewport, mask, deviceRegion, screen)) {
+        return false;
+    }
 
     auto &screenData = m_data[m_currentView];
     Region repaintRegion = deviceRegion & viewport.deviceRect();
@@ -111,6 +113,7 @@ void ShowFpsEffect::paintScreen(const RenderTarget &renderTarget, const RenderVi
     for (const Rect &rect : repaintRegion.rects()) {
         screenData->m_paintAmount += rect.width() * rect.height();
     }
+    return true;
 }
 
 void ShowFpsEffect::postPaintScreen()
diff --git a/src/plugins/showfps/showfpseffect.h b/src/plugins/showfps/showfpseffect.h
index 81a70fa61d0..429759ca83b 100644
--- a/src/plugins/showfps/showfpseffect.h
+++ b/src/plugins/showfps/showfpseffect.h
@@ -59,7 +59,7 @@ public:
     ~ShowFpsEffect() override;
 
     void prePaintScreen(ScreenPrePaintData &data) override;
-    void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen) override;
+    bool paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen) override;
     void postPaintScreen() override;
 
     static bool supported();
diff --git a/src/plugins/showpaint/showpaint.cpp b/src/plugins/showpaint/showpaint.cpp
index 1687bf47877..8de021882d0 100644
--- a/src/plugins/showpaint/showpaint.cpp
+++ b/src/plugins/showpaint/showpaint.cpp
@@ -33,15 +33,16 @@ static const QList<QColor> s_colors{
 
 ShowPaintEffect::ShowPaintEffect() = default;
 
-void ShowPaintEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
+bool ShowPaintEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
 {
-    effects->paintScreen(renderTarget, viewport, mask, deviceRegion, screen);
-    if (effects->isOpenGLCompositing()) {
-        paintGL(renderTarget, viewport, deviceRegion);
+    if (!effects->paintScreen(renderTarget, viewport, mask, deviceRegion, screen)) {
+        return false;
     }
+    paintGL(renderTarget, viewport, deviceRegion);
     if (++m_colorIndex == s_colors.count()) {
         m_colorIndex = 0;
     }
+    return true;
 }
 
 void ShowPaintEffect::paintGL(const RenderTarget &renderTarget, const RenderViewport &viewport, const Region &deviceRegion)
diff --git a/src/plugins/showpaint/showpaint.h b/src/plugins/showpaint/showpaint.h
index 0064db21df9..16ab714be17 100644
--- a/src/plugins/showpaint/showpaint.h
+++ b/src/plugins/showpaint/showpaint.h
@@ -21,7 +21,7 @@ class ShowPaintEffect : public Effect
 public:
     ShowPaintEffect();
 
-    void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen) override;
+    bool paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen) override;
 
 private:
     void paintGL(const RenderTarget &renderTarget, const RenderViewport &viewport, const Region &deviceRegion);
diff --git a/src/plugins/slide/slide.cpp b/src/plugins/slide/slide.cpp
index 7d0a71cba99..5daa5fd850d 100644
--- a/src/plugins/slide/slide.cpp
+++ b/src/plugins/slide/slide.cpp
@@ -183,12 +183,12 @@ void SlideEffectScreen::prePaintScreen(ScreenPrePaintData &data)
     data.mask |= Effect::PAINT_SCREEN_TRANSFORMED;
 }
 
-void SlideEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
+bool SlideEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
 {
     if (SlideEffectScreen *slideScreen = getSlideEffectScreen(screen)) {
         slideScreen->paintScreen();
     }
-    effects->paintScreen(renderTarget, viewport, mask, deviceRegion, screen);
+    return effects->paintScreen(renderTarget, viewport, mask, deviceRegion, screen);
 }
 
 void SlideEffectScreen::paintScreen()
@@ -252,24 +252,23 @@ void SlideEffect::prePaintWindow(RenderView *view, EffectWindow *w, WindowPrePai
     effects->prePaintWindow(view, w, data);
 }
 
-void SlideEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceGeometry, WindowPaintData &data)
+bool SlideEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceGeometry, WindowPaintData &data)
 {
     if (SlideEffectScreen *slideScreen = getSlideEffectScreen(w->screen())) {
-        slideScreen->paintWindow(renderTarget, viewport, w, mask, deviceGeometry, data);
+        return slideScreen->paintWindow(renderTarget, viewport, w, mask, deviceGeometry, data);
     } else {
-        effects->paintWindow(renderTarget, viewport, w, mask, deviceGeometry, data);
+        return effects->paintWindow(renderTarget, viewport, w, mask, deviceGeometry, data);
     }
 }
 
-void SlideEffectScreen::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceGeometry, WindowPaintData &data)
+bool SlideEffectScreen::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceGeometry, WindowPaintData &data)
 {
     if (!willBePainted(w)) {
-        return;
+        return true;
     }
 
     if (!isTranslated(w)) {
-        effects->paintWindow(renderTarget, viewport, w, mask, deviceGeometry, data);
-        return;
+        return effects->paintWindow(renderTarget, viewport, w, mask, deviceGeometry, data);
     }
 
     const int gridWidth = effects->desktopGridWidth();
@@ -304,16 +303,19 @@ void SlideEffectScreen::paintWindow(const RenderTarget &renderTarget, const Rend
             const Rect screenArea = screen->geometry();
             const Rect logicalDamage = screenArea.translated(drawTranslation).intersected(screenArea);
 
-            effects->paintWindow(
-                renderTarget, viewport, w, mask,
-                // Only paint the region that intersects the current screen and desktop.
-                deviceGeometry.intersected(viewport.mapToDeviceCoordinatesAligned(logicalDamage)),
-                data);
+            if (!effects->paintWindow(
+                    renderTarget, viewport, w, mask,
+                    // Only paint the region that intersects the current screen and desktop.
+                    deviceGeometry.intersected(viewport.mapToDeviceCoordinatesAligned(logicalDamage)),
+                    data)) {
+                return false;
+            }
 
             // Undo the translation for the next screen. I know, it hurts me too.
             data += QPoint(-drawTranslation.x(), -drawTranslation.y());
         }
     }
+    return true;
 }
 
 void SlideEffect::postPaintScreen()
diff --git a/src/plugins/slide/slide.h b/src/plugins/slide/slide.h
index 251335088d2..40c80945b23 100644
--- a/src/plugins/slide/slide.h
+++ b/src/plugins/slide/slide.h
@@ -57,7 +57,7 @@ public:
     void paintScreen();
     void postPaintScreen();
 
-    void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceGeometry, WindowPaintData &data);
+    bool paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceGeometry, WindowPaintData &data);
 
     bool isActive() const;
     void desktopChanged(VirtualDesktop *old, VirtualDesktop *current, EffectWindow *with);
@@ -133,11 +133,11 @@ public:
     void reconfigure(ReconfigureFlags) override;
 
     void prePaintScreen(ScreenPrePaintData &data) override;
-    void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen) override;
+    bool paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen) override;
     void postPaintScreen() override;
 
     void prePaintWindow(RenderView *view, EffectWindow *w, WindowPrePaintData &data) override;
-    void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceGeometry, WindowPaintData &data) override;
+    bool paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceGeometry, WindowPaintData &data) override;
 
     bool isActive() const override;
     int requestedEffectChainPosition() const override;
diff --git a/src/plugins/slideback/slideback.cpp b/src/plugins/slideback/slideback.cpp
index bd3ef19f986..11f2aea3225 100644
--- a/src/plugins/slideback/slideback.cpp
+++ b/src/plugins/slideback/slideback.cpp
@@ -241,7 +241,7 @@ void SlideBackEffect::prePaintWindow(RenderView *view, EffectWindow *w, WindowPr
     effects->prePaintWindow(view, w, data);
 }
 
-void SlideBackEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceGeometry, WindowPaintData &data)
+bool SlideBackEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceGeometry, WindowPaintData &data)
 {
     if (motionManager.isManaging(w)) {
         motionManager.apply(w, data);
@@ -250,8 +250,8 @@ void SlideBackEffect::paintWindow(const RenderTarget &renderTarget, const Render
     for (const Region &r : std::as_const(clippedRegions)) {
         effectiveRegion = effectiveRegion.intersected(viewport.mapToDeviceCoordinatesAligned(r));
     }
-    effects->paintWindow(renderTarget, viewport, w, mask, effectiveRegion, data);
     clippedRegions.clear();
+    return effects->paintWindow(renderTarget, viewport, w, mask, effectiveRegion, data);
 }
 
 void SlideBackEffect::slotWindowDeleted(EffectWindow *w)
diff --git a/src/plugins/slideback/slideback.h b/src/plugins/slideback/slideback.h
index 544ecc01a9b..df1f6a71811 100644
--- a/src/plugins/slideback/slideback.h
+++ b/src/plugins/slideback/slideback.h
@@ -26,7 +26,7 @@ public:
     SlideBackEffect();
 
     void prePaintWindow(RenderView *view, EffectWindow *w, WindowPrePaintData &data) override;
-    void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceGeometry, WindowPaintData &data) override;
+    bool paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceGeometry, WindowPaintData &data) override;
 
     void prePaintScreen(ScreenPrePaintData &data) override;
     void postPaintScreen() override;
diff --git a/src/plugins/slidingnotifications/slidingnotifications.cpp b/src/plugins/slidingnotifications/slidingnotifications.cpp
index be8576d419e..f4379abd219 100644
--- a/src/plugins/slidingnotifications/slidingnotifications.cpp
+++ b/src/plugins/slidingnotifications/slidingnotifications.cpp
@@ -449,7 +449,7 @@ void SlidingNotificationsEffect::prePaintWindow(RenderView *view, EffectWindow *
     effects->prePaintWindow(view, window, data);
 }
 
-void SlidingNotificationsEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *window, int mask, const Region &deviceGeometry, WindowPaintData &data)
+bool SlidingNotificationsEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *window, int mask, const Region &deviceGeometry, WindowPaintData &data)
 {
     const auto it = m_animations.find(window);
     if (it == m_animations.end()) {
@@ -468,7 +468,7 @@ void SlidingNotificationsEffect::paintWindow(const RenderTarget &renderTarget, c
         animation->displace->apply(data);
     }
 
-    effects->paintWindow(renderTarget, viewport, window, mask, clipped, data);
+    return effects->paintWindow(renderTarget, viewport, window, mask, clipped, data);
 }
 
 void SlidingNotificationsEffect::postPaintScreen()
diff --git a/src/plugins/slidingnotifications/slidingnotifications.h b/src/plugins/slidingnotifications/slidingnotifications.h
index 74ab01add81..e26c60a29d3 100644
--- a/src/plugins/slidingnotifications/slidingnotifications.h
+++ b/src/plugins/slidingnotifications/slidingnotifications.h
@@ -96,7 +96,7 @@ public:
     bool blocksDirectScanout() const override;
 
     void prePaintWindow(RenderView *view, EffectWindow *window, WindowPrePaintData &data) override;
-    void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceGeometry, WindowPaintData &data) override;
+    bool paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceGeometry, WindowPaintData &data) override;
     void postPaintScreen() override;
 
     static bool supported();
diff --git a/src/plugins/slidingpopups/slidingpopups.cpp b/src/plugins/slidingpopups/slidingpopups.cpp
index fc931f24d7d..f05aeb32dff 100644
--- a/src/plugins/slidingpopups/slidingpopups.cpp
+++ b/src/plugins/slidingpopups/slidingpopups.cpp
@@ -123,12 +123,11 @@ void SlidingPopupsEffect::prePaintWindow(RenderView *view, EffectWindow *w, Wind
     effects->prePaintWindow(view, w, data);
 }
 
-void SlidingPopupsEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceGeometry, WindowPaintData &data)
+bool SlidingPopupsEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceGeometry, WindowPaintData &data)
 {
     auto animationIt = m_animations.find(w);
     if (animationIt == m_animations.end()) {
-        effects->paintWindow(renderTarget, viewport, w, mask, deviceGeometry, data);
-        return;
+        return effects->paintWindow(renderTarget, viewport, w, mask, deviceGeometry, data);
     }
 
     const AnimationData &animData = m_animationsData[w];
@@ -167,7 +166,7 @@ void SlidingPopupsEffect::paintWindow(const RenderTarget &renderTarget, const Re
 
     effectiveRegion &= viewport.mapToDeviceCoordinatesAligned(damagedLogicalArea(w, animData));
 
-    effects->paintWindow(renderTarget, viewport, w, mask, effectiveRegion, data);
+    return effects->paintWindow(renderTarget, viewport, w, mask, effectiveRegion, data);
 }
 
 void SlidingPopupsEffect::postPaintScreen()
diff --git a/src/plugins/slidingpopups/slidingpopups.h b/src/plugins/slidingpopups/slidingpopups.h
index 1af0a73c195..ff11db03ee8 100644
--- a/src/plugins/slidingpopups/slidingpopups.h
+++ b/src/plugins/slidingpopups/slidingpopups.h
@@ -31,7 +31,7 @@ public:
     ~SlidingPopupsEffect() override;
 
     void prePaintWindow(RenderView *view, EffectWindow *w, WindowPrePaintData &data) override;
-    void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceGeometry, WindowPaintData &data) override;
+    bool paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceGeometry, WindowPaintData &data) override;
     void reconfigure(ReconfigureFlags flags) override;
     bool isActive() const override;
     void postPaintScreen() override;
diff --git a/src/plugins/thumbnailaside/thumbnailaside.cpp b/src/plugins/thumbnailaside/thumbnailaside.cpp
index 2a678a0f6e7..2b31b30c36d 100644
--- a/src/plugins/thumbnailaside/thumbnailaside.cpp
+++ b/src/plugins/thumbnailaside/thumbnailaside.cpp
@@ -54,10 +54,12 @@ void ThumbnailAsideEffect::reconfigure(ReconfigureFlags)
     arrange();
 }
 
-void ThumbnailAsideEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
+bool ThumbnailAsideEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
 {
     painted = Region();
-    effects->paintScreen(renderTarget, viewport, mask, deviceRegion, screen);
+    if (!effects->paintScreen(renderTarget, viewport, mask, deviceRegion, screen)) {
+        return false;
+    }
 
     for (const Data &d : std::as_const(windows)) {
         if (painted.intersects(viewport.mapToDeviceCoordinatesAligned(d.rect))) {
@@ -65,16 +67,19 @@ void ThumbnailAsideEffect::paintScreen(const RenderTarget &renderTarget, const R
             data.multiplyOpacity(opacity);
             Rect region;
             setPositionTransformations(data, region, d.window, d.rect, Qt::KeepAspectRatio);
-            effects->drawWindow(renderTarget, viewport, d.window, PAINT_WINDOW_OPAQUE | PAINT_WINDOW_TRANSLUCENT | PAINT_WINDOW_TRANSFORMED,
-                                viewport.mapToDeviceCoordinatesAligned(region), data);
+            if (!effects->drawWindow(renderTarget, viewport, d.window, PAINT_WINDOW_OPAQUE | PAINT_WINDOW_TRANSLUCENT | PAINT_WINDOW_TRANSFORMED,
+                                     viewport.mapToDeviceCoordinatesAligned(region), data)) {
+                return false;
+            }
         }
     }
+    return true;
 }
 
-void ThumbnailAsideEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceGeometry, WindowPaintData &data)
+bool ThumbnailAsideEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceGeometry, WindowPaintData &data)
 {
-    effects->paintWindow(renderTarget, viewport, w, mask, deviceGeometry, data);
     painted += deviceGeometry;
+    return effects->paintWindow(renderTarget, viewport, w, mask, deviceGeometry, data);
 }
 
 void ThumbnailAsideEffect::slotWindowDamaged(EffectWindow *w)
diff --git a/src/plugins/thumbnailaside/thumbnailaside.h b/src/plugins/thumbnailaside/thumbnailaside.h
index f7b1e0e0c7b..425e94a77d9 100644
--- a/src/plugins/thumbnailaside/thumbnailaside.h
+++ b/src/plugins/thumbnailaside/thumbnailaside.h
@@ -35,8 +35,8 @@ class ThumbnailAsideEffect
 public:
     ThumbnailAsideEffect();
     void reconfigure(ReconfigureFlags) override;
-    void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen) override;
-    void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data) override;
+    bool paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen) override;
+    bool paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data) override;
 
     // for properties
     int configuredMaxWidth() const
diff --git a/src/plugins/touchpoints/touchpoints.cpp b/src/plugins/touchpoints/touchpoints.cpp
index cf8d7ad0e3b..d4ef79d1b79 100644
--- a/src/plugins/touchpoints/touchpoints.cpp
+++ b/src/plugins/touchpoints/touchpoints.cpp
@@ -113,13 +113,13 @@ void TouchPointsEffect::prePaintScreen(ScreenPrePaintData &data)
     effects->prePaintScreen(data);
 }
 
-void TouchPointsEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
+bool TouchPointsEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
 {
-    effects->paintScreen(renderTarget, viewport, mask, deviceRegion, screen);
-
-    if (effects->isOpenGLCompositing()) {
-        paintScreenSetupGl(renderTarget, viewport.projectionMatrix());
+    if (!effects->paintScreen(renderTarget, viewport, mask, deviceRegion, screen)) {
+        return false;
     }
+
+    paintScreenSetupGl(renderTarget, viewport.projectionMatrix());
     for (auto it = m_points.constBegin(), end = m_points.constEnd(); it != end; ++it) {
         for (int i = 0; i < m_ringCount; ++i) {
             float alpha = computeAlpha(it->time, i);
@@ -131,9 +131,8 @@ void TouchPointsEffect::paintScreen(const RenderTarget &renderTarget, const Rend
             }
         }
     }
-    if (effects->isOpenGLCompositing()) {
-        paintScreenFinishGl();
-    }
+    paintScreenFinishGl();
+    return true;
 }
 
 void TouchPointsEffect::postPaintScreen()
diff --git a/src/plugins/touchpoints/touchpoints.h b/src/plugins/touchpoints/touchpoints.h
index c01ab7c87da..09e227974de 100644
--- a/src/plugins/touchpoints/touchpoints.h
+++ b/src/plugins/touchpoints/touchpoints.h
@@ -29,7 +29,7 @@ public:
     TouchPointsEffect();
     ~TouchPointsEffect() override;
     void prePaintScreen(ScreenPrePaintData &data) override;
-    void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen) override;
+    bool paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen) override;
     void postPaintScreen() override;
     bool isActive() const override;
     bool touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) override;
diff --git a/src/plugins/zoom/zoom.cpp b/src/plugins/zoom/zoom.cpp
index f4dc2f70464..0cbb7d05c39 100644
--- a/src/plugins/zoom/zoom.cpp
+++ b/src/plugins/zoom/zoom.cpp
@@ -358,18 +358,21 @@ GLShader *ZoomEffect::shaderForZoom(double zoom)
     return ShaderManager::instance()->shader(ShaderTrait::MapTexture | ShaderTrait::TransformColorspace);
 }
 
-void ZoomEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
+bool ZoomEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
 {
     OffscreenData *offscreenData = ensureOffscreenData(renderTarget, viewport, screen);
     if (!offscreenData) {
-        return;
+        EglContext::currentContext()->setFailed();
+        return false;
     }
 
     // Render the scene in an offscreen texture and then upscale it.
     RenderTarget offscreenRenderTarget(offscreenData->framebuffer.get(), renderTarget.colorDescription());
     RenderViewport offscreenViewport(viewport.renderRect(), viewport.scale(), offscreenRenderTarget, QPoint());
     GLFramebuffer::pushFramebuffer(offscreenData->framebuffer.get());
-    effects->paintScreen(offscreenRenderTarget, offscreenViewport, mask, deviceRegion, screen);
+    if (!effects->paintScreen(offscreenRenderTarget, offscreenViewport, mask, deviceRegion, screen)) {
+        return false;
+    }
     GLFramebuffer::popFramebuffer();
 
     const auto scale = viewport.scale();
@@ -395,6 +398,7 @@ void ZoomEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewp
         offscreen.texture->render(offscreen.viewport.size() * scale);
     }
     ShaderManager::instance()->popShader();
+    return true;
 }
 
 void ZoomEffect::postPaintScreen()
diff --git a/src/plugins/zoom/zoom.h b/src/plugins/zoom/zoom.h
index 77b2938b2e9..567d24c4312 100644
--- a/src/plugins/zoom/zoom.h
+++ b/src/plugins/zoom/zoom.h
@@ -44,7 +44,7 @@ public:
 
     void reconfigure(ReconfigureFlags flags) override;
     void prePaintScreen(ScreenPrePaintData &data) override;
-    void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen) override;
+    bool paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen) override;
     void postPaintScreen() override;
     bool isActive() const override;
     int requestedEffectChainPosition() const override;
diff --git a/src/scene/itemrenderer.h b/src/scene/itemrenderer.h
index a3b826fbce1..c941a391b8b 100644
--- a/src/scene/itemrenderer.h
+++ b/src/scene/itemrenderer.h
@@ -53,7 +53,7 @@ public:
     virtual void endFrame();
 
     virtual void renderBackground(const RenderTarget &renderTarget, const RenderViewport &viewport, const Region &deviceRegion) = 0;
-    virtual void renderItem(const RenderTarget &renderTarget, const RenderViewport &viewport, Item *item, int mask, const Region &deviceRegion, const WindowPaintData &data, const std::function<bool(Item *)> &filter, const std::function<bool(Item *)> &holeFilter) = 0;
+    [[nodiscard]] virtual bool renderItem(const RenderTarget &renderTarget, const RenderViewport &viewport, Item *item, int mask, const Region &deviceRegion, const WindowPaintData &data, const std::function<bool(Item *)> &filter, const std::function<bool(Item *)> &holeFilter) = 0;
 
     virtual void setLayerDebugging(bool enable);
 };
diff --git a/src/scene/itemrenderer_opengl.cpp b/src/scene/itemrenderer_opengl.cpp
index 532901435f5..cc5e741e58d 100644
--- a/src/scene/itemrenderer_opengl.cpp
+++ b/src/scene/itemrenderer_opengl.cpp
@@ -150,12 +150,12 @@ static RenderGeometry clipQuads(const Item *item, const ItemRendererOpenGL::Rend
     return geometry;
 }
 
-void ItemRendererOpenGL::createRenderNode(Item *item, RenderContext *context, const std::function<bool(Item *)> &filter, const std::function<bool(Item *)> &holeFilter)
+bool ItemRendererOpenGL::createRenderNode(Item *item, RenderContext *context, const std::function<bool(Item *)> &filter, const std::function<bool(Item *)> &holeFilter)
 {
     bool hole = false;
     if (filter && filter(item)) {
         if (!holeFilter || !holeFilter(item)) {
-            return;
+            return true;
         }
         hole = true;
     }
@@ -183,7 +183,9 @@ void ItemRendererOpenGL::createRenderNode(Item *item, RenderContext *context, co
             break;
         }
         if (childItem->explicitVisible()) {
-            createRenderNode(childItem, context, filter, holeFilter);
+            if (!createRenderNode(childItem, context, filter, holeFilter)) {
+                return false;
+            }
         }
     }
 
@@ -202,7 +204,12 @@ void ItemRendererOpenGL::createRenderNode(Item *item, RenderContext *context, co
         });
     }
 
+    // For multi-gpu copies, preprocess may change the active EGL context,
+    // and switching back can fail in the case of a GPU reset.
     item->preprocess();
+    if (!EglContext::currentContext()) {
+        return false;
+    }
 
     RenderGeometry geometry = clipQuads(item, context);
 
@@ -330,7 +337,9 @@ void ItemRendererOpenGL::createRenderNode(Item *item, RenderContext *context, co
             continue;
         }
         if (childItem->explicitVisible()) {
-            createRenderNode(childItem, context, filter, holeFilter);
+            if (!createRenderNode(childItem, context, filter, holeFilter)) {
+                return false;
+            }
         }
     }
 
@@ -339,6 +348,7 @@ void ItemRendererOpenGL::createRenderNode(Item *item, RenderContext *context, co
     if (!context->cornerStack.isEmpty()) {
         context->cornerStack.pop();
     }
+    return true;
 }
 
 void ItemRendererOpenGL::renderBackground(const RenderTarget &renderTarget, const RenderViewport &viewport, const Region &deviceRegion)
@@ -362,10 +372,10 @@ void ItemRendererOpenGL::renderBackground(const RenderTarget &renderTarget, cons
     }
 }
 
-void ItemRendererOpenGL::renderItem(const RenderTarget &renderTarget, const RenderViewport &viewport, Item *item, int mask, const Region &deviceRegion, const WindowPaintData &data, const std::function<bool(Item *)> &filter, const std::function<bool(Item *)> &holeFilter)
+bool ItemRendererOpenGL::renderItem(const RenderTarget &renderTarget, const RenderViewport &viewport, Item *item, int mask, const Region &deviceRegion, const WindowPaintData &data, const std::function<bool(Item *)> &filter, const std::function<bool(Item *)> &holeFilter)
 {
     if (deviceRegion.isEmpty()) {
-        return;
+        return true;
     }
 
     RenderContext renderContext{
@@ -381,14 +391,16 @@ void ItemRendererOpenGL::renderItem(const RenderTarget &renderTarget, const Rend
     renderContext.transformStack.push(QMatrix4x4());
     renderContext.opacityStack.push(data.opacity());
 
-    createRenderNode(item, &renderContext, filter, holeFilter);
+    if (!createRenderNode(item, &renderContext, filter, holeFilter)) {
+        return false;
+    }
 
     int totalVertexCount = 0;
     for (const RenderNode &node : std::as_const(renderContext.renderNodes)) {
         totalVertexCount += node.geometry.count();
     }
     if (totalVertexCount == 0) {
-        return;
+        return true;
     }
 
     GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
@@ -397,7 +409,8 @@ void ItemRendererOpenGL::renderItem(const RenderTarget &renderTarget, const Rend
 
     const auto map = vbo->map<GLVertex2D>(totalVertexCount);
     if (!map) {
-        return;
+        EglContext::currentContext()->isFailed();
+        return true;
     }
 
     for (int i = 0, v = 0; i < renderContext.renderNodes.count(); i++) {
@@ -557,6 +570,7 @@ void ItemRendererOpenGL::renderItem(const RenderTarget &renderTarget, const Rend
     if (renderContext.hardwareClipping) {
         glDisable(GL_SCISSOR_TEST);
     }
+    return true;
 }
 
 void ItemRendererOpenGL::visualizeFractional(const RenderViewport &viewport, const Region &logicalRegion, const RenderContext &renderContext)
diff --git a/src/scene/itemrenderer_opengl.h b/src/scene/itemrenderer_opengl.h
index 09015929e7b..6659a53b118 100644
--- a/src/scene/itemrenderer_opengl.h
+++ b/src/scene/itemrenderer_opengl.h
@@ -84,14 +84,14 @@ public:
     void endFrame() override;
 
     void renderBackground(const RenderTarget &renderTarget, const RenderViewport &viewport, const Region &deviceRegion) override;
-    void renderItem(const RenderTarget &renderTarget, const RenderViewport &viewport, Item *item, int mask, const Region &deviceRegion, const WindowPaintData &data, const std::function<bool(Item *)> &filter, const std::function<bool(Item *)> &holeFilter) override;
+    bool renderItem(const RenderTarget &renderTarget, const RenderViewport &viewport, Item *item, int mask, const Region &deviceRegion, const WindowPaintData &data, const std::function<bool(Item *)> &filter, const std::function<bool(Item *)> &holeFilter) override;
 
     void setLayerDebugging(bool enable) override;
 
 private:
     QVector4D modulate(float opacity, float brightness) const;
     void setBlendEnabled(bool enabled);
-    void createRenderNode(Item *item, RenderContext *context, const std::function<bool(Item *)> &filter, const std::function<bool(Item *)> &holeFilter);
+    bool createRenderNode(Item *item, RenderContext *context, const std::function<bool(Item *)> &filter, const std::function<bool(Item *)> &holeFilter);
     void visualizeFractional(const RenderViewport &viewport, const Region &logicalRegion, const RenderContext &renderContext);
 
     bool m_blendingEnabled = false;
diff --git a/src/scene/workspacescene.cpp b/src/scene/workspacescene.cpp
index b7475261f5d..c7b367d8be2 100644
--- a/src/scene/workspacescene.cpp
+++ b/src/scene/workspacescene.cpp
@@ -702,34 +702,38 @@ void WorkspaceScene::paint(const RenderTarget &renderTarget, const QPoint &devic
 
     m_renderer->beginFrame(renderTarget, viewport);
 
-    effects->paintScreen(renderTarget, viewport, m_paintContext.mask, deviceRegion, painted_screen);
+    if (!effects->paintScreen(renderTarget, viewport, m_paintContext.mask, deviceRegion, painted_screen)) {
+        return;
+    }
 
     Q_EMIT frameRendered();
     m_renderer->endFrame();
 }
 
 // the function that'll be eventually called by paintScreen() above
-void WorkspaceScene::finalPaintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
+bool WorkspaceScene::finalPaintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen)
 {
     if (mask & (PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS)) {
-        paintGenericScreen(renderTarget, viewport, mask, screen);
+        return paintGenericScreen(renderTarget, viewport, mask, screen);
     } else {
-        paintSimpleScreen(renderTarget, viewport, mask, deviceRegion);
+        return paintSimpleScreen(renderTarget, viewport, mask, deviceRegion);
     }
 }
 
 // The generic painting code that can handle even transformations.
 // It simply paints bottom-to-top.
-void WorkspaceScene::paintGenericScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int, LogicalOutput *screen)
+bool WorkspaceScene::paintGenericScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int, LogicalOutput *screen)
 {
     m_renderer->renderBackground(renderTarget, viewport, Region::infinite());
 
     for (const Phase2Data &paintData : std::as_const(m_paintContext.phase2Data)) {
-        paintWindow(renderTarget, viewport, paintData.item, paintData.mask, paintData.deviceRegion);
+        if (!paintWindow(renderTarget, viewport, paintData.item, paintData.mask, paintData.deviceRegion)) {
+            return false;
+        }
     }
 
     const Rect bounds = viewport.mapToDeviceCoordinates(m_overlayItem->mapToScene(m_overlayItem->boundingRect())).toRect();
-    m_renderer->renderItem(renderTarget, viewport, m_overlayItem.get(), PAINT_SCREEN_TRANSFORMED, bounds, WindowPaintData{}, [this](Item *item) {
+    return m_renderer->renderItem(renderTarget, viewport, m_overlayItem.get(), PAINT_SCREEN_TRANSFORMED, bounds, WindowPaintData{}, [this](Item *item) {
         return !painted_delegate->shouldRenderItem(item);
     }, [this](Item *item) {
         return painted_delegate->shouldRenderHole(item);
@@ -739,7 +743,7 @@ void WorkspaceScene::paintGenericScreen(const RenderTarget &renderTarget, const
 // The optimized case without any transformations at all.
 // It can paint only the requested region and can use clipping
 // to reduce painting and improve performance.
-void WorkspaceScene::paintSimpleScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int, const Region &deviceRegion)
+bool WorkspaceScene::paintSimpleScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int, const Region &deviceRegion)
 {
     // This is the occlusion culling pass
     Region visible = deviceRegion;
@@ -762,17 +766,21 @@ void WorkspaceScene::paintSimpleScreen(const RenderTarget &renderTarget, const R
     m_renderer->renderBackground(renderTarget, viewport, visible);
 
     for (const Phase2Data &paintData : std::as_const(m_paintContext.phase2Data)) {
-        paintWindow(renderTarget, viewport, paintData.item, paintData.mask, paintData.deviceRegion);
+        if (!paintWindow(renderTarget, viewport, paintData.item, paintData.mask, paintData.deviceRegion)) {
+            return false;
+        }
     }
 
     const Rect bounds = viewport.mapToDeviceCoordinates(m_overlayItem->mapToScene(m_overlayItem->boundingRect())).toRect();
     const Region deviceRepaint = deviceRegion & bounds;
     if (!deviceRepaint.isEmpty()) {
-        m_renderer->renderItem(renderTarget, viewport, m_overlayItem.get(), PAINT_SCREEN_TRANSFORMED, deviceRepaint, WindowPaintData{}, [this](Item *item) {
+        return m_renderer->renderItem(renderTarget, viewport, m_overlayItem.get(), PAINT_SCREEN_TRANSFORMED, deviceRepaint, WindowPaintData{}, [this](Item *item) {
             return !painted_delegate->shouldRenderItem(item);
         }, [this](Item *item) {
             return painted_delegate->shouldRenderHole(item);
         });
+    } else {
+        return true;
     }
 }
 
@@ -799,28 +807,28 @@ void WorkspaceScene::clearStackingOrder()
     stacking_order.clear();
 }
 
-void WorkspaceScene::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, WindowItem *item, int mask, const Region &deviceRegion)
+bool WorkspaceScene::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, WindowItem *item, int mask, const Region &deviceRegion)
 {
     if (deviceRegion.isEmpty()) { // completely clipped
-        return;
+        return true;
     }
 
     WindowPaintData data;
-    effects->paintWindow(renderTarget, viewport, item->effectWindow(), mask, deviceRegion, data);
+    return effects->paintWindow(renderTarget, viewport, item->effectWindow(), mask, deviceRegion, data);
 }
 
 // the function that'll be eventually called by paintWindow() above
-void WorkspaceScene::finalPaintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
+bool WorkspaceScene::finalPaintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
 {
-    effects->drawWindow(renderTarget, viewport, w, mask, deviceRegion, data);
+    return effects->drawWindow(renderTarget, viewport, w, mask, deviceRegion, data);
 }
 
 // will be eventually called from drawWindow()
-void WorkspaceScene::finalDrawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
+bool WorkspaceScene::finalDrawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data)
 {
     // TODO: Reconsider how the CrossFadeEffect captures the initial window contents to remove
     // null pointer delegate checks in "should render item" and "should render hole" checks.
-    m_renderer->renderItem(renderTarget, viewport, w->windowItem(), mask, deviceRegion, data, [this](Item *item) {
+    return m_renderer->renderItem(renderTarget, viewport, w->windowItem(), mask, deviceRegion, data, [this](Item *item) {
         return painted_delegate && !painted_delegate->shouldRenderItem(item);
     }, [this](Item *item) {
         return painted_delegate && painted_delegate->shouldRenderHole(item);
diff --git a/src/scene/workspacescene.h b/src/scene/workspacescene.h
index 0ad9d10fa5b..662ec64dbb0 100644
--- a/src/scene/workspacescene.h
+++ b/src/scene/workspacescene.h
@@ -76,20 +76,20 @@ protected:
     void clearStackingOrder();
     friend class EffectsHandler;
     // called after all effects had their paintScreen() called
-    void finalPaintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen);
+    [[nodiscard]] bool finalPaintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion, LogicalOutput *screen);
     // shared implementation of painting the screen in the generic
     // (unoptimized) way
     void preparePaintGenericScreen();
-    void paintGenericScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, LogicalOutput *screen);
+    [[nodiscard]] bool paintGenericScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, LogicalOutput *screen);
     // shared implementation of painting the screen in an optimized way
     void preparePaintSimpleScreen();
-    void paintSimpleScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion);
+    [[nodiscard]] bool paintSimpleScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const Region &deviceRegion);
     // called after all effects had their paintWindow() called
-    void finalPaintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data);
+    [[nodiscard]] bool finalPaintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data);
     // shared implementation, starts painting the window
-    void paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, WindowItem *w, int mask, const Region &deviceRegion);
+    [[nodiscard]] bool paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, WindowItem *w, int mask, const Region &deviceRegion);
     // called after all effects had their drawWindow() called
-    void finalDrawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data);
+    [[nodiscard]] bool finalDrawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const Region &deviceRegion, WindowPaintData &data);
 
     // saved data for 2nd pass of optimized screen painting
     struct Phase2Data
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.