diff --git a/clients/oxygen/oxygenclient.desktop b/clients/oxygen/oxygenclient.desktop index e58ac44169..0294d631de 100644 --- a/clients/oxygen/oxygenclient.desktop +++ b/clients/oxygen/oxygenclient.desktop @@ -172,7 +172,7 @@ Comment[zh_TW]=下一代桌面的樣式 X-KDE-Library=kwin3_oxygen X-KDE-PluginInfo-Author=Nuno Pinheiro, Casper Boemann, Riccardo Iaconelli, Huynh Huu Long, Thomas Luebking, Hugo Pereira Da Costa, Matthew Woehlke -X-KDE-PluginInfo-Email=nuno@oxygen-icons.org, cbr@boemann.dk, riccardo@kde.org, long.upcase@googlemail.com, thomas.luebking@web.de, hugo@oxygen-icons.org, mw_triad@users.sourceforge.net +X-KDE-PluginInfo-Email=nuno@oxygen-icons.org, cbr@boemann.dk, riccardo@kde.org, long.upcase@googlemail.com, thomas.luebking@web.de, hugo.pereira@free.fr, mw_triad@users.sourceforge.net X-KDE-PluginInfo-Name=Oxygen X-KDE-PluginInfo-Category= X-KDE-PluginInfo-Depends= diff --git a/egl_wayland_backend.h b/egl_wayland_backend.h index c2ee2f1554..99a5cd9cdd 100644 --- a/egl_wayland_backend.h +++ b/egl_wayland_backend.h @@ -264,8 +264,8 @@ public: virtual ~EglWaylandBackend(); virtual void screenGeometryChanged(const QSize &size); virtual SceneOpenGL::TexturePrivate *createBackendTexture(SceneOpenGL::Texture *texture); - virtual void prepareRenderingFrame(); - virtual void endRenderingFrame(const QRegion &damage); + virtual QRegion prepareRenderingFrame(); + virtual void endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion); Shm *shm(); protected: diff --git a/eglonxbackend.cpp b/eglonxbackend.cpp index 7893e8d079..53442e2a9c 100644 --- a/eglonxbackend.cpp +++ b/eglonxbackend.cpp @@ -37,6 +37,7 @@ EglOnXBackend::EglOnXBackend() : OpenGLBackend() , ctx(EGL_NO_CONTEXT) , surfaceHasSubPost(0) + , m_bufferAge(0) { init(); // Egl is always direct rendering @@ -99,6 +100,16 @@ void EglOnXBackend::init() } } } + + setSupportsBufferAge(false); + + if (hasGLExtension("EGL_EXT_buffer_age")) { + const QByteArray useBufferAge = qgetenv("KWIN_USE_BUFFER_AGE"); + + if (useBufferAge != "0") + setSupportsBufferAge(true); + } + setSyncsToVBlank(false); setBlocksForRetrace(false); gs_tripleBufferNeedsDetection = false; @@ -268,6 +279,13 @@ void EglOnXBackend::present() if (lastDamage().isEmpty()) return; + if (supportsBufferAge()) { + eglSwapBuffers(dpy, surface); + eglQuerySurface(dpy, surface, EGL_BUFFER_AGE_EXT, &m_bufferAge); + setLastDamage(QRegion()); + return; + } + const QRegion displayRegion(0, 0, displayWidth(), displayHeight()); const bool fullRepaint = (lastDamage() == displayRegion); @@ -312,8 +330,11 @@ void EglOnXBackend::present() void EglOnXBackend::screenGeometryChanged(const QSize &size) { Q_UNUSED(size) - // no backend specific code needed + // TODO: base implementation in OpenGLBackend + + // The back buffer contents are now undefined + m_bufferAge = 0; } SceneOpenGL::TexturePrivate *EglOnXBackend::createBackendTexture(SceneOpenGL::Texture *texture) @@ -321,8 +342,10 @@ SceneOpenGL::TexturePrivate *EglOnXBackend::createBackendTexture(SceneOpenGL::Te return new EglTexture(texture, this); } -void EglOnXBackend::prepareRenderingFrame() +QRegion EglOnXBackend::prepareRenderingFrame() { + QRegion repaint; + if (gs_tripleBufferNeedsDetection) { // the composite timer floors the repaint frequency. This can pollute our triple buffering // detection because the glXSwapBuffers call for the new frame has to wait until the pending @@ -331,21 +354,55 @@ void EglOnXBackend::prepareRenderingFrame() // fllush the buffer queue usleep(1000); } + present(); + + if (supportsBufferAge()) + repaint = accumulatedDamageHistory(m_bufferAge); + startRenderTimer(); eglWaitNative(EGL_CORE_NATIVE_ENGINE); + + return repaint; } -void EglOnXBackend::endRenderingFrame(const QRegion &damage) +void EglOnXBackend::endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { - setLastDamage(damage); - glFlush(); + if (damagedRegion.isEmpty()) { + setLastDamage(QRegion()); + + // If the damaged region of a window is fully occluded, the only + // rendering done, if any, will have been to repair a reused back + // buffer, making it identical to the front buffer. + // + // In this case we won't post the back buffer. Instead we'll just + // set the buffer age to 1, so the repaired regions won't be + // rendered again in the next frame. + if (!renderedRegion.isEmpty()) + glFlush(); + + m_bufferAge = 1; + return; + } + + setLastDamage(renderedRegion); + if (!blocksForRetrace()) { - present(); // this sets lastDamage emtpy and prevents execution from prepareRenderingFrame() + // This also sets lastDamage to empty which prevents the frame from + // being posted again when prepareRenderingFrame() is called. + present(); + } else { + // Make sure that the GPU begins processing the command stream + // now and not the next time prepareRenderingFrame() is called. + glFlush(); } if (overlayWindow()->window()) // show the window only after the first pass, overlayWindow()->show(); // since that pass may take long + + // Save the damaged region to history + if (supportsBufferAge()) + addToDamageHistory(damagedRegion); } bool EglOnXBackend::makeCurrent() diff --git a/eglonxbackend.h b/eglonxbackend.h index 1f66997976..36d47005f6 100644 --- a/eglonxbackend.h +++ b/eglonxbackend.h @@ -34,8 +34,8 @@ public: virtual ~EglOnXBackend(); virtual void screenGeometryChanged(const QSize &size); virtual SceneOpenGL::TexturePrivate *createBackendTexture(SceneOpenGL::Texture *texture); - virtual void prepareRenderingFrame(); - virtual void endRenderingFrame(const QRegion &damage); + virtual QRegion prepareRenderingFrame(); + virtual void endRenderingFrame(const QRegion &damage, const QRegion &damagedRegion); virtual bool makeCurrent() override; virtual void doneCurrent() override; @@ -51,6 +51,7 @@ private: EGLSurface surface; EGLContext ctx; int surfaceHasSubPost; + int m_bufferAge; friend class EglTexture; }; diff --git a/glxbackend.cpp b/glxbackend.cpp index 2fd56e9d19..73e9dfa186 100644 --- a/glxbackend.cpp +++ b/glxbackend.cpp @@ -46,6 +46,7 @@ GlxBackend::GlxBackend() , fbconfig(NULL) , glxWindow(None) , ctx(nullptr) + , m_bufferAge(0) , haveSwapInterval(false) { init(); @@ -104,8 +105,19 @@ void GlxBackend::init() options->setGlPreferBufferSwap('e'); // for unknown drivers - should not happen glPlatform->printResults(); initGL(GlxPlatformInterface); + // Check whether certain features are supported haveSwapInterval = glXSwapIntervalMESA || glXSwapIntervalEXT || glXSwapIntervalSGI; + + setSupportsBufferAge(false); + + if (hasGLExtension("GLX_EXT_buffer_age")) { + const QByteArray useBufferAge = qgetenv("KWIN_USE_BUFFER_AGE"); + + if (useBufferAge != "0") + setSupportsBufferAge(true); + } + setSyncsToVBlank(false); setBlocksForRetrace(false); haveWaitSync = false; @@ -426,6 +438,13 @@ void GlxBackend::present() if (lastDamage().isEmpty()) return; + if (supportsBufferAge()) { + glXSwapBuffers(display(), glxWindow); + glXQueryDrawable(display(), glxWindow, GLX_BACK_BUFFER_AGE_EXT, (GLuint *) &m_bufferAge); + setLastDamage(QRegion()); + return; + } + const QRegion displayRegion(0, 0, displayWidth(), displayHeight()); const bool fullRepaint = (lastDamage() == displayRegion); @@ -486,6 +505,9 @@ void GlxBackend::screenGeometryChanged(const QSize &size) makeCurrent(); glViewport(0, 0, size.width(), size.height()); + + // The back buffer contents are now undefined + m_bufferAge = 0; } SceneOpenGL::TexturePrivate *GlxBackend::createBackendTexture(SceneOpenGL::Texture *texture) @@ -493,8 +515,10 @@ SceneOpenGL::TexturePrivate *GlxBackend::createBackendTexture(SceneOpenGL::Textu return new GlxTexture(texture, this); } -void GlxBackend::prepareRenderingFrame() +QRegion GlxBackend::prepareRenderingFrame() { + QRegion repaint; + if (gs_tripleBufferNeedsDetection) { // the composite timer floors the repaint frequency. This can pollute our triple buffering // detection because the glXSwapBuffers call for the new frame has to wait until the pending @@ -503,21 +527,55 @@ void GlxBackend::prepareRenderingFrame() // fllush the buffer queue usleep(1000); } + present(); + + if (supportsBufferAge()) + repaint = accumulatedDamageHistory(m_bufferAge); + startRenderTimer(); glXWaitX(); + + return repaint; } -void GlxBackend::endRenderingFrame(const QRegion &damage) +void GlxBackend::endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { - setLastDamage(damage); - glFlush(); + if (damagedRegion.isEmpty()) { + setLastDamage(QRegion()); + + // If the damaged region of a window is fully occluded, the only + // rendering done, if any, will have been to repair a reused back + // buffer, making it identical to the front buffer. + // + // In this case we won't post the back buffer. Instead we'll just + // set the buffer age to 1, so the repaired regions won't be + // rendered again in the next frame. + if (!renderedRegion.isEmpty()) + glFlush(); + + m_bufferAge = 1; + return; + } + + setLastDamage(renderedRegion); + if (!blocksForRetrace()) { - present(); // this sets lastDamage emtpy and prevents execution from prepareRenderingFrame() + // This also sets lastDamage to empty which prevents the frame from + // being posted again when prepareRenderingFrame() is called. + present(); + } else { + // Make sure that the GPU begins processing the command stream + // now and not the next time prepareRenderingFrame() is called. + glFlush(); } if (overlayWindow()->window()) // show the window only after the first pass, overlayWindow()->show(); // since that pass may take long + + // Save the damaged region to history + if (supportsBufferAge()) + addToDamageHistory(damagedRegion); } bool GlxBackend::makeCurrent() diff --git a/glxbackend.h b/glxbackend.h index b6a9ce523b..26ca19c27c 100644 --- a/glxbackend.h +++ b/glxbackend.h @@ -44,8 +44,8 @@ public: virtual ~GlxBackend(); virtual void screenGeometryChanged(const QSize &size); virtual SceneOpenGL::TexturePrivate *createBackendTexture(SceneOpenGL::Texture *texture); - virtual void prepareRenderingFrame(); - virtual void endRenderingFrame(const QRegion &damage); + virtual QRegion prepareRenderingFrame(); + virtual void endRenderingFrame(const QRegion &damage, const QRegion &damagedRegion); virtual bool makeCurrent() override; virtual void doneCurrent() override; @@ -66,6 +66,7 @@ private: GLXFBConfig fbconfig; GLXWindow glxWindow; GLXContext ctx; + int m_bufferAge; bool haveSwapInterval, haveWaitSync; friend class GlxTexture; }; diff --git a/libkwineffects/kwinglutils_funcs.h b/libkwineffects/kwinglutils_funcs.h index 87384c95d1..6540a79998 100644 --- a/libkwineffects/kwinglutils_funcs.h +++ b/libkwineffects/kwinglutils_funcs.h @@ -75,6 +75,9 @@ void KWINGLUTILS_EXPORT glResolveFunctions(OpenGLPlatformInterface platformInter #define GL_READ_FRAMEBUFFER 0x8CA8 #endif +#ifndef GLX_BACK_BUFFER_AGE_EXT +#define GLX_BACK_BUFFER_AGE_EXT 0x20F4 +#endif #include @@ -501,6 +504,10 @@ extern KWINGLUTILS_EXPORT glCopyBufferSubData_func glCopyBufferSubData; #define EGL_POST_SUB_BUFFER_SUPPORTED_NV 0x30BE #endif +#ifndef EGL_BUFFER_AGE_EXT +#define EGL_BUFFER_AGE_EXT 0x313D +#endif + #ifndef GL_UNPACK_ROW_LENGTH #define GL_UNPACK_ROW_LENGTH 0x0CF2 #endif diff --git a/scene.cpp b/scene.cpp index 6b3503c9a8..424edff61f 100644 --- a/scene.cpp +++ b/scene.cpp @@ -101,48 +101,62 @@ Scene::~Scene() } // returns mask and possibly modified region -void Scene::paintScreen(int* mask, QRegion* region) +void Scene::paintScreen(int* mask, const QRegion &damage, const QRegion &repaint, + QRegion *updateRegion, QRegion *validRegion) { const QRegion displayRegion(0, 0, displayWidth(), displayHeight()); - *mask = (*region == displayRegion) ? 0 : PAINT_SCREEN_REGION; + *mask = (damage == displayRegion) ? 0 : PAINT_SCREEN_REGION; updateTimeDiff(); // preparation step static_cast(effects)->startPaint(); + QRegion region = damage; + ScreenPrePaintData pdata; pdata.mask = *mask; - pdata.paint = *region; + pdata.paint = region; effects->prePaintScreen(pdata, time_diff); *mask = pdata.mask; - *region = pdata.paint; + region = pdata.paint; if (*mask & (PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS)) { // Region painting is not possible with transformations, // because screen damage doesn't match transformed positions. *mask &= ~PAINT_SCREEN_REGION; - *region = infiniteRegion(); + region = infiniteRegion(); } else if (*mask & PAINT_SCREEN_REGION) { // make sure not to go outside visible screen - *region &= displayRegion; + region &= displayRegion; } else { // whole screen, not transformed, force region to be full - *region = displayRegion; + region = displayRegion; } - painted_region = *region; + + painted_region = region; + repaint_region = repaint; + if (*mask & PAINT_SCREEN_BACKGROUND_FIRST) { - paintBackground(*region); + paintBackground(region); } + ScreenPaintData data; - effects->paintScreen(*mask, *region, data); - foreach (Window * w, stacking_order) { + effects->paintScreen(*mask, region, data); + + foreach (Window *w, stacking_order) { effects->postPaintWindow(effectWindow(w)); } + effects->postPaintScreen(); - *region |= painted_region; + // make sure not to go outside of the screen area - *region &= displayRegion; + *updateRegion = damaged_region; + *validRegion = (region | painted_region) & displayRegion; + + repaint_region = QRegion(); + damaged_region = QRegion(); + // make sure all clipping is restored Q_ASSERT(!PaintClipper::clip()); } @@ -222,6 +236,8 @@ void Scene::paintGenericScreen(int orig_mask, ScreenPaintData) foreach (const Phase2Data & d, phase2) { paintWindow(d.window, d.mask, d.region, d.quads); } + + damaged_region = QRegion(0, 0, displayWidth(), displayHeight()); } // The optimized case without any transformations at all. @@ -298,8 +314,13 @@ void Scene::paintSimpleScreen(int orig_mask, QRegion region) w->suspendUnredirect(data.mask & PAINT_WINDOW_TRANSLUCENT); } - const QRegion displayRegion(0, 0, displayWidth(), displayHeight()); + // Save the part of the repaint region that's exclusively rendered to + // bring a reused back buffer up to date. Then union the dirty region + // with the repaint region. + const QRegion repaintClip = repaint_region - dirtyArea; + dirtyArea |= repaint_region; + const QRegion displayRegion(0, 0, displayWidth(), displayHeight()); bool fullRepaint(dirtyArea == displayRegion); // spare some expensive region operations if (!fullRepaint) { extendPaintRegion(dirtyArea, opaqueFullscreen); @@ -307,6 +328,8 @@ void Scene::paintSimpleScreen(int orig_mask, QRegion region) } QRegion allclips, upperTranslucentDamage; + upperTranslucentDamage = repaint_region; + // This is the occlusion culling pass for (int i = phase2data.count() - 1; i >= 0; --i) { QPair< Window*, Phase2Data > *entry = &phase2data[i]; @@ -351,10 +374,21 @@ void Scene::paintSimpleScreen(int orig_mask, QRegion region) paintWindow(data->window, data->mask, data->region, data->quads); } - if (fullRepaint) + + if (fullRepaint) { painted_region = displayRegion; - else + damaged_region = displayRegion; + } else { painted_region |= paintedArea; + + // Clip the repainted region from the damaged region. + // It's important that we don't add the union of the damaged region + // and the repainted region to the damage history. Otherwise the + // repaint region will grow with every frame until it eventually + // covers the whole back buffer, at which point we're always doing + // full repaints. + damaged_region = paintedArea - repaintClip; + } } static Scene::Window *s_recursionCheck = NULL; diff --git a/scene.h b/scene.h index 000e702524..1bbf93a834 100644 --- a/scene.h +++ b/scene.h @@ -117,7 +117,8 @@ public Q_SLOTS: virtual void windowClosed(KWin::Toplevel* c, KWin::Deleted* deleted) = 0; protected: // shared implementation, starts painting the screen - void paintScreen(int* mask, QRegion* region); + void paintScreen(int *mask, const QRegion &damage, const QRegion &repaint, + QRegion *updateRegion, QRegion *validRegion); friend class EffectsHandlerImpl; // called after all effects had their paintScreen() called void finalPaintScreen(int mask, QRegion region, ScreenPaintData& data); @@ -162,6 +163,10 @@ protected: // up all the way from paintSimpleScreen() up to paintScreen(), so save them here rather // than propagate them up in arguments. QRegion painted_region; + // Additional damage that needs to be repaired to bring a reused back buffer up to date + QRegion repaint_region; + // The dirty region before it was unioned with repaint_region + QRegion damaged_region; // time since last repaint int time_diff; QElapsedTimer last_time; diff --git a/scene_opengl.cpp b/scene_opengl.cpp index 5474325ff5..31a4abe738 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -83,6 +83,7 @@ OpenGLBackend::OpenGLBackend() , m_syncsToVBlank(false) , m_blocksForRetrace(false) , m_directRendering(false) + , m_haveBufferAge(false) , m_failed(false) { } @@ -109,6 +110,29 @@ void OpenGLBackend::idle() } } +void OpenGLBackend::addToDamageHistory(const QRegion ®ion) +{ + if (m_damageHistory.count() > 10) + m_damageHistory.removeLast(); + + m_damageHistory.prepend(region); +} + +QRegion OpenGLBackend::accumulatedDamageHistory(int bufferAge) const +{ + QRegion region; + + // Note: An age of zero means the buffer contents are undefined + if (bufferAge > 0 && bufferAge <= m_damageHistory.count()) { + for (int i = 0; i < bufferAge - 1; i++) + region |= m_damageHistory[i]; + } else { + region = QRegion(0, 0, displayWidth(), displayHeight()); + } + + return region; +} + /************************************************ * SceneOpenGL ***********************************************/ @@ -343,7 +367,7 @@ qint64 SceneOpenGL::paint(QRegion damage, ToplevelList toplevels) } m_backend->makeCurrent(); - m_backend->prepareRenderingFrame(); + QRegion repaint = m_backend->prepareRenderingFrame(); const GLenum status = glGetGraphicsResetStatus(); if (status != GL_NO_ERROR) { @@ -356,22 +380,33 @@ qint64 SceneOpenGL::paint(QRegion damage, ToplevelList toplevels) checkGLError("Paint1"); #endif - paintScreen(&mask, &damage); // call generic implementation + // After this call, updateRegion will contain the damaged region in the + // back buffer. This is the region that needs to be posted to repair + // the front buffer. It doesn't include the additional damage returned + // by prepareRenderingFrame(). validRegion is the region that has been + // repainted, and may be larger than updateRegion. + QRegion updateRegion, validRegion; + paintScreen(&mask, damage, repaint, &updateRegion, &validRegion); // call generic implementation + #ifndef KWIN_HAVE_OPENGLES const QRegion displayRegion(0, 0, displayWidth(), displayHeight()); + // copy dirty parts from front to backbuffer - if (options->glPreferBufferSwap() == Options::CopyFrontBuffer && damage != displayRegion) { + if (!m_backend->supportsBufferAge() && + options->glPreferBufferSwap() == Options::CopyFrontBuffer && + validRegion != displayRegion) { glReadBuffer(GL_FRONT); - copyPixels(displayRegion - damage); + copyPixels(displayRegion - validRegion); glReadBuffer(GL_BACK); damage = displayRegion; } #endif + #ifdef CHECK_GL_ERROR checkGLError("Paint2"); #endif - m_backend->endRenderingFrame(damage); + m_backend->endRenderingFrame(validRegion, updateRegion); // do cleanup stacking_order.clear(); @@ -427,6 +462,9 @@ void SceneOpenGL::paintBackground(QRegion region) void SceneOpenGL::extendPaintRegion(QRegion ®ion, bool opaqueFullscreen) { + if (m_backend->supportsBufferAge()) + return; + if (options->glPreferBufferSwap() == Options::ExtendDamage) { // only Extend "large" repaints const QRegion displayRegion(0, 0, displayWidth(), displayHeight()); uint damagedPixels = 0; diff --git a/scene_opengl.h b/scene_opengl.h index bfcfb3374b..bfd94b9a6d 100644 --- a/scene_opengl.h +++ b/scene_opengl.h @@ -463,23 +463,29 @@ public: } virtual void screenGeometryChanged(const QSize &size) = 0; virtual SceneOpenGL::TexturePrivate *createBackendTexture(SceneOpenGL::Texture *texture) = 0; + /** * @brief Backend specific code to prepare the rendering of a frame including flushing the * previously rendered frame to the screen if the backend works this way. + * + * @return A region that if not empty will be repainted in addition to the damaged region **/ - virtual void prepareRenderingFrame() = 0; + virtual QRegion prepareRenderingFrame() = 0; + /** * @brief Backend specific code to handle the end of rendering a frame. * - * @param damage The actual updated region in this frame + * @param renderedRegion The possibly larger region that has been rendered + * @param damagedRegion The damaged region that should be posted **/ - virtual void endRenderingFrame(const QRegion &damage) = 0; + virtual void endRenderingFrame(const QRegion &damage, const QRegion &damagedRegion) = 0; virtual bool makeCurrent() = 0; virtual void doneCurrent() = 0; /** * @brief Compositor is going into idle mode, flushes any pending paints. **/ void idle(); + /** * @return bool Whether the scene needs to flush a frame. **/ @@ -540,6 +546,21 @@ public: bool isDirectRendering() const { return m_directRendering; } + + bool supportsBufferAge() const { + return m_haveBufferAge; + } + + /** + * Returns the damage that has accumulated since a buffer of the given age was presented. + */ + QRegion accumulatedDamageHistory(int bufferAge) const; + + /** + * Saves the given region to damage history. + */ + void addToDamageHistory(const QRegion ®ion); + protected: /** * @brief Backend specific flushing of frame to screen. @@ -586,6 +607,11 @@ protected: void setIsDirectRendering(bool direct) { m_directRendering = direct; } + + void setSupportsBufferAge(bool value) { + m_haveBufferAge = value; + } + /** * @return const QRegion& Damage of previously rendered frame **/ @@ -623,6 +649,10 @@ private: * @brief Whether direct rendering is used, defaults to @c false. **/ bool m_directRendering; + /** + * @brief Whether the backend supports GLX_EXT_buffer_age / EGL_EXT_buffer_age. + */ + bool m_haveBufferAge; /** * @brief Whether the initialization failed, of course default to @c false. **/ @@ -631,6 +661,10 @@ private: * @brief Damaged region of previously rendered frame. **/ QRegion m_lastDamage; + /** + * @brief The damage history for the past 10 frames. + */ + QList m_damageHistory; /** * @brief Timer to measure how long a frame renders. **/ diff --git a/scene_xrender.cpp b/scene_xrender.cpp index d7df048aa3..2a699021c4 100644 --- a/scene_xrender.cpp +++ b/scene_xrender.cpp @@ -191,12 +191,13 @@ qint64 SceneXrender::paint(QRegion damage, ToplevelList toplevels) } int mask = 0; - paintScreen(&mask, &damage); + QRegion updateRegion, validRegion; + paintScreen(&mask, damage, QRegion(), &updateRegion, &validRegion); if (m_overlayWindow->window()) // show the window only after the first pass, since m_overlayWindow->show(); // that pass may take long - present(mask, damage); + present(mask, updateRegion); // do cleanup stacking_order.clear();