From afe8048afdbdff6120f153faf30c144c1a52e146 Mon Sep 17 00:00:00 2001 From: Philipp Knechtges Date: Sat, 9 Jul 2011 12:43:32 +0200 Subject: [PATCH] kwin: optimizing the blur effect This patch adds the capability to draw blurred region top to bottom using paintSimpleScreen. REVIEW: 101898 --- effects/blur/blur.cpp | 123 +++++++++++++++--------------------------- effects/blur/blur.h | 3 ++ 2 files changed, 46 insertions(+), 80 deletions(-) diff --git a/effects/blur/blur.cpp b/effects/blur/blur.cpp index 8a50e07e0c..83012b160d 100644 --- a/effects/blur/blur.cpp +++ b/effects/blur/blur.cpp @@ -34,7 +34,6 @@ KWIN_EFFECT(blur, BlurEffect) KWIN_EFFECT_SUPPORTED(blur, BlurEffect::supported()) KWIN_EFFECT_ENABLEDBYDEFAULT(blur, BlurEffect::enabledByDefault()) - BlurEffect::BlurEffect() { shader = BlurShader::create(); @@ -157,11 +156,9 @@ QRegion BlurEffect::expand(const QRegion ®ion) const { QRegion expanded; - if (region.rectCount() < 20) { - foreach (const QRect & rect, region.rects()) + foreach (const QRect & rect, region.rects()) { expanded += expand(rect); - } else - expanded += expand(region.boundingRect()); + } return expanded; } @@ -223,86 +220,58 @@ void BlurEffect::prePaintScreen(ScreenPrePaintData &data, int time) QLinkedList blurRegions; bool checkDecos = effects->decorationsHaveAlpha() && effects->decorationSupportsBlurBehind(); bool clipChanged = false; + m_damagedArea = QRegion(); + m_currentBlur = QRegion(); effects->prePaintScreen(data, time); +} - // If the whole screen will be repainted anyway, there is no point in - // adding to the paint region. - if (data.mask & (PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS | PAINT_SCREEN_TRANSFORMED)) +void BlurEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) +{ + // this effect relies on prePaintWindow being called in the bottom to top order + + effects->prePaintWindow(w, data, time); + + if (!w->isPaintingEnabled()) { return; - - if (effects->activeFullScreenEffect()) - return; - - // Walk the window list top->bottom and check if the paint region is fully - // contained within opaque windows and doesn't intersect any blurred region. - QRegion paint = data.paint; - for (int i = windows.count() - 1; i >= 0; --i) { - EffectWindow *window = windows.at(i); - if (!window->isPaintingEnabled()) - continue; - - if (!window->hasAlpha()) { - paint -= window->contentsRect().translated(window->pos()); - if (paint.isEmpty()) - break; - } - - // The window decoration is treated as an object below the window - // contents, so check it after the contents. - if (window->hasAlpha() || (checkDecos && window->hasDecoration())) { - QRegion r = blurRegion(window); - if (r.isEmpty()) - continue; - - r = expand(r.translated(window->pos())); - if (r.intersects(paint)) - break; - } } - if (paint.isEmpty()) - return; - - // Walk the list again bottom->top and expand the paint region when - // it intersects a blurred region. - foreach (EffectWindow *window, windows) { - if (!window->isPaintingEnabled()) - continue; - - if (!window->hasAlpha() && !(checkDecos && window->hasDecoration())) - continue; - - QRegion r = blurRegion(window); - if (r.isEmpty()) - continue; - - r = expand(r.translated(window->pos())); - - // We can't do a partial repaint of a blurred region - if (r.intersects(data.paint)) { - data.paint += r; - clipChanged = true; - } else - blurRegions.append(r); + // to blur an area partially we have to shrink the opaque area of a window + QRegion newClip; + const QRegion oldClip = data.clip; + const int radius = shader->radius(); + foreach (const QRect& rect, data.clip.rects()) { + newClip |= rect.adjusted(radius,radius,-radius,-radius); } + data.clip = newClip; - while (clipChanged) { - clipChanged = false; - QMutableLinkedListIterator i(blurRegions); - while (i.hasNext()) { - const QRegion r = i.next(); - if (!r.intersects(data.paint)) - continue; + // we dont have to blur a region we dont see + m_currentBlur -= newClip; + // if we have to paint a non-opaque part of this window that intersects with the + // currently blurred region we have to redraw the whole region + if ((data.paint-oldClip).intersects(m_currentBlur)) { + data.paint |= m_currentBlur; + } + // TODO: make m_currentBlur a list of connected regions - data.paint += r; - clipChanged = true; - i.remove(); + // in case this window has regions to be blurred + const QRegion blurArea = blurRegion(w).translated(w->pos()); + const QRegion expandedBlur = expand(blurArea); + // if this window or an window underneath the blurred area is damaged we have to + // blur everything + if (m_damagedArea.intersects(blurArea) || data.paint.intersects(blurArea)) { + data.paint |= expandedBlur; + // we have to check again whether we do not damage an already blurred area + if (expandedBlur.intersects(m_currentBlur)) { + data.paint |= m_currentBlur; } } + m_currentBlur |= expandedBlur; - // Force the scene to call paintGenericScreen() so the windows are painted bottom -> top - data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_WITHOUT_FULL_REPAINTS; + // we dont consider damaged areas which are occluded and are not + // explicitly damaged by this window + m_damagedArea -= data.clip; + m_damagedArea |= data.paint; } bool BlurEffect::shouldBlur(const EffectWindow *w, int mask, const WindowPaintData &data) const @@ -310,12 +279,6 @@ bool BlurEffect::shouldBlur(const EffectWindow *w, int mask, const WindowPaintDa if (!target->valid() || !shader->isValid()) return false; - // Don't blur anything if we're painting top-to-bottom - if (!(mask & (PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_WITHOUT_FULL_REPAINTS | - PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS | - PAINT_SCREEN_TRANSFORMED))) - return false; - if (effects->activeFullScreenEffect() && !w->data(WindowForceBlurRole).toBool()) return false; @@ -341,7 +304,7 @@ void BlurEffect::drawWindow(EffectWindow *w, int mask, QRegion region, WindowPai { if (shouldBlur(w, mask, data)) { const QRect screen(0, 0, displayWidth(), displayHeight()); - const QRegion shape = blurRegion(w).translated(w->pos()) & screen; + const QRegion shape = region & blurRegion(w).translated(w->pos()) & screen; if (!shape.isEmpty() && region.intersects(shape.boundingRect())) doBlur(shape, screen, data.opacity * data.contents_opacity); diff --git a/effects/blur/blur.h b/effects/blur/blur.h index daed4dbee5..3c0922b8fb 100644 --- a/effects/blur/blur.h +++ b/effects/blur/blur.h @@ -44,6 +44,7 @@ public: void reconfigure(ReconfigureFlags flags); void prePaintScreen(ScreenPrePaintData &data, int time); + void prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time); void drawWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data); void paintEffectFrame(EffectFrame *frame, QRegion region, double opacity, double frameOpacity); @@ -66,6 +67,8 @@ private: GLRenderTarget *target; GLTexture *tex; long net_wm_blur_region; + QRegion m_damagedArea; // keeps track of the area which has been damaged (from bottom to top) + QRegion m_currentBlur; // keeps track of the currently blured area (from bottom to top) }; } // namespace KWin