From 85c5181a81b42a02cb70f97c3fef9273372f207e Mon Sep 17 00:00:00 2001 From: Philipp Knechtges Date: Wed, 6 Jul 2011 10:01:23 +0200 Subject: [PATCH] kwin: Avoid painting damaged areas which are occluded by another opaque window. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So far we have not used the information that damage events are window-specific, resulting in the behavior that we repainted the damaged area although it might be hidden behind another window. E.g. the CPU-Monitor plasmoid is almost all day occluded by a browser etc. and before this patch we have been repainting the appropiate area every time the plasmoid has been updated. Thx to Thomas Lübking for optimizing the patch. REVIEW: 101846 --- composite.cpp | 10 ---- scene.cpp | 131 ++++++++++++++++++++++++++++++++++++++------------ scene.h | 1 + 3 files changed, 100 insertions(+), 42 deletions(-) diff --git a/composite.cpp b/composite.cpp index f594d1b2c7..a8151634f6 100644 --- a/composite.cpp +++ b/composite.cpp @@ -404,16 +404,6 @@ void Workspace::performCompositing() if (c->readyForPainting()) windows.append(c); #endif - foreach (Toplevel * c, windows) { - // This could be possibly optimized WRT obscuring, but that'd need being already - // past prePaint() phase - probably not worth it. - repaints_region |= c->repaints().translated(c->pos()); - repaints_region |= c->decorationPendingRegion(); - c->resetRepaints(c->decorationRect()); - if (c->hasShadow()) { - c->resetRepaints(c->shadow()->shadowRegion().boundingRect()); - } - } QRegion repaints = repaints_region; // clear all repaints, so that post-pass can add repaints for the next repaint repaints_region = QRegion(); diff --git a/scene.cpp b/scene.cpp index 6512302d10..c007924990 100644 --- a/scene.cpp +++ b/scene.cpp @@ -114,12 +114,26 @@ void Scene::paintScreen(int* mask, QRegion* region) updateTimeDiff(); // preparation step static_cast(effects)->startPaint(); + ScreenPrePaintData pdata; pdata.mask = *mask; pdata.paint = *region; + + // region only includes all workspace-specific repaints but some effect (e.g. blur) + // rely on the full damaged area + QRegion dirtyArea; + foreach (Window * w, stacking_order) { // bottom to top + Toplevel* topw = w->window(); + dirtyArea |= topw->repaints().translated(topw->pos()); + dirtyArea |= topw->decorationPendingRegion(); + } + pdata.paint |= dirtyArea; + effects->prePaintScreen(pdata, time_diff); *mask = pdata.mask; - *region = pdata.paint; + // Subtract the dirty region and let finalPaintScreen decide which areas have to be drawn + *region |= pdata.paint - dirtyArea; + 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. @@ -188,6 +202,18 @@ void Scene::paintGenericScreen(int orig_mask, ScreenPaintData) paintBackground(infiniteRegion()); QList< Phase2Data > phase2; foreach (Window * w, stacking_order) { // bottom to top + Toplevel* topw = w->window(); + painted_region |= topw->repaints().translated(topw->pos()); + painted_region |= topw->decorationPendingRegion(); + + // Reset the repaint_region. + // This has to be done here because many effects schedule a repaint for + // the next frame within Effects::prePaintWindow. + topw->resetRepaints(topw->decorationRect()); + if (topw->hasShadow()) { + topw->resetRepaints(topw->shadow()->shadowRegion().boundingRect()); + } + WindowPrePaintData data; data.mask = orig_mask | (w->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT); w->resetPaintingEnabled(); @@ -225,16 +251,28 @@ void Scene::paintSimpleScreen(int orig_mask, QRegion region) | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_WITHOUT_FULL_REPAINTS | PAINT_WINDOW_TRANSLUCENT | PAINT_WINDOW_OPAQUE)) == 0); QHash< Window*, Phase2Data > phase2data; - // Draw each opaque window top to bottom, subtracting the bounding rect of - // each window from the clip region after it's been drawn. - for (int i = stacking_order.count() - 1; // top to bottom - i >= 0; - --i) { + + QRegion dirtyArea = region; + for (int i = 0; // do prePaintWindow bottom to top + i < stacking_order.count(); + ++i) { Window* w = stacking_order[ i ]; + Toplevel* topw = w->window(); WindowPrePaintData data; data.mask = orig_mask | (w->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT); w->resetPaintingEnabled(); data.paint = region; + data.paint |= topw->repaints().translated(topw->pos()); + data.paint |= topw->decorationPendingRegion(); + dirtyArea |= data.paint; + + // Reset the repaint_region. + // This has to be done here because many effects schedule a repaint for + // the next frame within Effects::prePaintWindow. + topw->resetRepaints(topw->decorationRect()); + if (topw->hasShadow()) { + topw->resetRepaints(topw->shadow()->shadowRegion().boundingRect()); + } // Clip out the decoration for opaque windows; the decoration is drawn in the second pass data.clip = w->isOpaque() ? w->clientShape().translated(w->x(), w->y()) : QRegion(); data.quads = w->buildQuads(); @@ -251,8 +289,6 @@ void Scene::paintSimpleScreen(int orig_mask, QRegion region) w->suspendUnredirect(true); continue; } - if (data.paint != region) // prepaint added area to draw - painted_region |= data.paint; // make sure it makes it to the screen // Schedule the window for painting phase2data[w] = Phase2Data(w, data.paint, data.clip, data.mask, data.quads); // no transformations, but translucency requires window pixmap @@ -262,39 +298,70 @@ void Scene::paintSimpleScreen(int orig_mask, QRegion region) // First opaque windows, top to bottom // This also calculates correct paint regions for windows, also taking // care of clipping - QRegion allclips; + QRegion allclips, upperTranslucentDamage; for (int i = stacking_order.count() - 1; i >= 0; --i) { - Window* w = stacking_order[ i ]; - if (!phase2data.contains(w)) + QHash< Window*, Phase2Data >::iterator data = phase2data.find(stacking_order[ i ]); + if (data == phase2data.end()) continue; - Phase2Data d = phase2data[w]; - // Calculate correct paint region and take the clip region into account - d.region = painted_region - allclips; - allclips |= d.clip; - if (d.mask & PAINT_WINDOW_TRANSLUCENT) { - // For translucent windows, the paint region must contain the - // entire painted area, except areas clipped by opaque windows - // above the translucent window - phase2data[w].region = d.region; - } else { + + Toplevel *tlw = data.key()->window(); + // In case there is a window with a higher stackposition which has translucent regions + // (e.g. decorations) that still have to be drawn, we also have to repaint the current window + // in these particular regions + data->region |= (upperTranslucentDamage & tlw->decorationRect().translated(tlw->pos())); + + // subtract the parts which have possibly been drawn already as part of + // a higher opaque window + data->region -= allclips; + + if (!(data->mask & PAINT_WINDOW_TRANSLUCENT)) { + // clip away this region for all windows below this one + allclips |= data->clip; // Paint the opaque window - paintWindow(d.window, d.mask, d.region, d.quads); - // Clip out the client area, so we only draw the decoration in the next pass - phase2data[w].region = d.region - d.clip; - phase2data[w].mask |= PAINT_DECORATION_ONLY; + data->painted_1stpass = data->clip & data->region; + paintWindow(data->window, data->mask, data->painted_1stpass, data->quads); + painted_region |= data->painted_1stpass; + + // Clip out the client area, so we only draw the rest in the next pass + data->region -= data->clip; + // if prePaintWindow didnt change the clipping area we only have to paint + // the decoration + if (data-> clip == data.key()->clientShape().translated(data.key()->x(), data.key()->y())) { + data->mask |= PAINT_DECORATION_ONLY; + } } + // extend the translucent damage for windows below this by remaining (translucent) regions + upperTranslucentDamage |= data->region; } - // Fill any areas of the root window not covered by windows - if (!(orig_mask & PAINT_SCREEN_BACKGROUND_FIRST)) - paintBackground(painted_region - allclips); + + QRegion painted_2ndpass; + // Fill any areas of the root window not covered by opaque windows + if (!(orig_mask & PAINT_SCREEN_BACKGROUND_FIRST)) { + painted_2ndpass = dirtyArea - allclips; + paintBackground(painted_2ndpass); + } + // Now walk the list bottom to top, drawing translucent windows. + QRegion lowerOpaqueDamage; for (int i = 0; i < stacking_order.count(); i++) { - Window* w = stacking_order[ i ]; - if (!phase2data.contains(w)) + QHash< Window*, Phase2Data >::iterator data = phase2data.find(stacking_order[ i ]); + if (data == phase2data.end()) continue; - Phase2Data d = phase2data[w]; - paintWindow(d.window, d.mask, d.region, d.quads); + + Toplevel *tlw = data.key()->window(); + + // add all regions of the lower windows which have already be drawn in the 1st pass + data->region |= (lowerOpaqueDamage & tlw->decorationRect().translated(tlw->pos())); + // and extend that region by opaque parts of ourself + lowerOpaqueDamage |= data->painted_1stpass; + + // add all regions which have been drawn so far in the 2nd pass + painted_2ndpass |= data->region; + data->region = painted_2ndpass; + + paintWindow(data->window, data->mask, data->region, data->quads); } + painted_region |= painted_2ndpass; } void Scene::paintWindow(Window* w, int mask, QRegion region, WindowQuadList quads) diff --git a/scene.h b/scene.h index 7ebb2e5675..6ce39c0f4a 100644 --- a/scene.h +++ b/scene.h @@ -136,6 +136,7 @@ protected: Window* window; QRegion region; QRegion clip; + QRegion painted_1stpass; int mask; WindowQuadList quads; };