Merge lp:~vanvugt/unity/regionalDamage into lp:unity

Proposed by Daniel van Vugt
Status: Superseded
Proposed branch: lp:~vanvugt/unity/regionalDamage
Merge into: lp:unity
Diff against target: 542 lines (+202/-74)
8 files modified
launcher/AbstractLauncherIcon.h (+1/-0)
launcher/Launcher.cpp (+11/-0)
launcher/Launcher.h (+4/-0)
launcher/LauncherIcon.cpp (+3/-0)
panel/PanelController.cpp (+15/-0)
panel/PanelController.h (+1/-0)
plugins/unityshell/src/unityshell.cpp (+159/-68)
plugins/unityshell/src/unityshell.h (+8/-6)
To merge this branch: bzr merge lp:~vanvugt/unity/regionalDamage
Reviewer Review Type Date Requested Status
Sam Spilsbury (community) Needs Fixing
Unity Team Pending
Daniel van Vugt Pending
Review via email: mp+111551@code.launchpad.net

This proposal supersedes a proposal from 2012-06-19.

This proposal has been superseded by a proposal from 2012-06-26.

Commit message

Stop Unity from redrawing the shell on every frame (ie. when it doesn't need
to). It had a severe impact on graphics performance. (LP: #988079)

This especially improves OpenGL application performance and multi-monitor
desktop performance. Because unity was previously slowing down compiz
rendering by 20-40% for each monitor added to the system. This slowdown no
longer occurs as only damaged areas of the unity shell are repainted. Now
unity will not have any impact on compiz rendering performance for most
frames.

Coincidentally, this also fixes LP: #967112 and LP: #992516. Maybe more...

Description of the change

For me, this branch eliminates the 25-30% slowdown (compared to regular compiz) when unity is running with a single monitor. With 2 monitors, this branch eliminates the 75%(!) slowdown experienced when unity is running. Extrapolate and hypothesize as you like.

In multi-monitor tests, unity was slowing down my system to the point of only rendering 30 FPS with two monitors. That's half what it should be. With this branch, I get 60 FPS like I should.

WARNING: This fix can only work if no window is redrawing _under_ a panel or launcher. If you have something drawing under the panel, launcher, tooltip or quicklist, then the shell will need to redraw and you won't get any performance benefit. Fixing that is a much bigger job and should be considered separate.

To post a comment you must log in.
Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

227 + /*
228 + * The shell is hidden if there exists any window that fully covers
229 + * the output and is in front of all Nux windows on that output.
230 + * We could also check CompositeWindow::opacity() but that would be slower
231 + * and almost always pointless.
232 + */
233 + if (w->isMapped() &&
234 + w->isViewable() &&
235 + !w->inShowDesktopMode() && // Why must this != isViewable?
236 + w->geometry().contains(output))

What might be faster here is using the occlusion detection passes from core. That will take into account whether or not the window is truly occluded. Usually you can get this by checking gw->clip () in GLWindow.

This code won't work for windows that are semitransparent or have an alpha channel, which will work a little weird.

Revision history for this message
Andrea Azzarone (azzar1) wrote : Posted in a previous version of this proposal

Does it fix bug 977984 too?

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote : Posted in a previous version of this proposal

I guess you can even remove this now

const std::string REDRAW_IDLE = "redraw-idle";

in: if (launcher_controller_.get()) no need for .get().

Overall looks good...

PS: pay attention to style, i.e.:
 bool UnityScreen::shellIsHidden(const CompOutput &output) -> bool UnityScreen::ShellIsHidden(CompOutput const& output)
 const std::vector<Window> &nuxwins(nux::XInputWindow::NativeHandleList()); -> std::vector<Window> const& nuxwins(nux::XInputWindow::NativeHandleList());

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Marco, I think your style is wrong :)
In C and C++, & and * are part of the variable, not part of the type:
    A b, *c, d, &e(b);
That is why they usually go next to the variable and not the type.

Also, the Google C++ style guide that Unity is meant to use agrees:
    http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Reference_Arguments
but then it allows both styles :)
    http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Pointer_and_Reference_Expressions

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Andrea, no I don't think this will fix bug 977984.

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Sam, gw->priv->clip only seems to get updated after glPaintOutputs which looks too late. How does one use clip() normally?

shellIsHidden is designed to be fast. And it is AFAIK. Yes, I did already add a comment about transparent windows. But we're only talking about fullscreen windows here. And I don't think the case of transparent or alpha fullscreen windows is one that we should need to waste time handling. If you make a fullscreen window transparent then you won't see the shell behind it. Does that matter?

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Actually, it does matter. The whole point of this proposal is to avoid rendering the shell because it hogs so much GPU. So I think we should not be rendering the shell if it is stacked below a transparent fullscreen window.

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Just noticed a bug with NVIDIA:
Un-fullscreening a window does not damage the screen (!?) so the shell doesn't repaint until something changes or you use it. Not sure about the most elegant fix. It looks like a compiz bug, but I haven't yet proven it.

Revision history for this message
Daniel van Vugt (vanvugt) : Posted in a previous version of this proposal
review: Needs Fixing
Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote : Posted in a previous version of this proposal

> Marco, I think your style is wrong :)
> In C and C++, & and * are part of the variable, not part of the type:
> A b, *c, d, &e(b);
> That is why they usually go next to the variable and not the type.

I know, and that's how I used to do before, but the Unity style guide is different, see:
http://bazaar.launchpad.net/~unity-team/unity/trunk/view/head:/guides/cppguide.xml
(recently updated by Tim) http://bazaar.launchpad.net/~unity-team/unity/trunk/revision/2378

So here we use:
PointerType* ptr;
Object const& obj = ...;

Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal
Download full text (4.2 KiB)

> Sam, gw->priv->clip only seems to get updated after glPaintOutputs which looks
> too late. How does one use clip() normally?

The clip region is updated after the occlusion pass. You are right, this happens within paintOutputRegion and not paintOutputs like I thought.

I feel like there are two options here:

1. Framebuffer object rebinds are not condition on whether or not the shell is repainting, but just as it is now with BackgroundEffectHelper::HasDirtyHelpers (). That leaves the problem of windows on top of the shell. At the moment, the damage collection code examines all damage events, but we don't care about stuff underneath the shell. As such, you could probably ignore any damage events coming in between PaintDisplay and glPaintOutput (damageRegionSetEnabled (false)). I don't think this will handle the case of fullscreen windows on top of the shell very well however. In compiz at least, we'd need a concept of layered damage, which is not trivial as it requires all damage to be tied to a particular object.

At this point, you can determine whether or not to paint the shell when you actually hit a nux window. This again, depends on being able to see nux windows in the paint stack (see lp:~smspillaz/unity/unity.less-paint-insanity). I guess at the moment, you could use the heuristic, and then Window -> CompWindow the nux window and check the clip region there to see if it is empty.

2. Second option is a bit trickier, but might also work. You'll need to effectively make unity do the occlusion pass. What you would do here is loop all the windows and do something like:

class UnityWindow :
....
{
....
private:

    CompRegion clip_;
};

void DetectOcclusionsOnUnityWindows (const GLMatrix &matrix,
                                     CompOutput *output)
{
GLWindowPaintAttrib a;
CompRegion tmpRegion (*output);

/* Top to bottom */
for (CompWindow *w : screen->reverseWindows ())
{
    UnityWindow *uw = UnityWindow::get (w);

    /* We only care about windows we actually clipped */
    clip = infiniteRegion;

    if (window->alpha ())
        continue;

    if (!window->isViewable ())
        continue;

    /* GLWindow::glPaint will return true if this window should be regarded
     * as occluding other windows. PAINT_WINDOW_OCCLUSION_DETECTION_MASK will
     * not actually paint the window, but will allow plugins to modify the state
     * of the window as if it was being painted, so we will know whether it should
     * occlude other windows */
    if (gw->glPaint (a, matrix, tmpRegion, PAINT_WINDOW_OCCLUSION_DETECTION_MASK))
        tmpRegion -= w->region (); // Not inputRect () or outputRect (): Decorations are ARGB windows

    uw->clip_ = tmpRegion;
}
}

Now, you can loop the list of nux windows (probably safe to store that internally, a nux window is a nux window for life, just detect it on the UnityWindow ctor) and see if they all have empty clip regions. If so, don't paint the shell.

You can also use the region detection to determine what parts of the shell to "damage" as well.

iirc, this is something similar to what the blur plugin does. Have a look at that for other ideas. This will also fix problems to do with ...

Read more...

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

I am keeping shellIsHidden because it is fast and simple. It doesn't re-enter compiz and doesn't require any CompRegion logic. It just uses rectangles.

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

> I am keeping shellIsHidden because it is fast and simple. It doesn't re-enter
> compiz and doesn't require any CompRegion logic. It just uses rectangles.

My gripe with shellIsHidden is that it is inaccurate and will run into lots of edge cases. It doesn't make visual sense when you have windows with alpha regions, transparency or transformations preventing the shell from painting if the bounding area occludes the shell. Its pretty much defeats the purpose of having alpha regions, shape regions and transformations in the first place. You can easily run into cases where the shell won't paint even when it should, and I think that's bad. Unless you have a way to handle those cases, or a very strong argument as to why we shouldn't handle those cases, I honestly think its a blocking factor here.

The OpenGL plugin already uses similar looking logic to do its occlusion detection, and there is very little (if any) performance penalty for it. There is hardly any CompRegion allocation at all, because the variables are stored locally. In fact, every time I've callgrinded compiz, paintOutputRegion has not appeared as a hotspot anywhere. The re-entrancy into compiz is also very inexpensive because most plugins will have glPaint disabled if they are not running any animations.

I would suggest at least trying to make it work.

Other bits of review:

121 +std::vector<nux::View*> Controller::Impl::GetPanelViews() const
122 +{
123 + std::vector<nux::View*> views;
124 + views.reserve(windows_.size());
125 + for (auto window: windows_)
126 + views.push_back(ViewForWindow(window));
127 + return views;
128 +}
129 +

I think it probably makes more sense to have the panel controller keep track of the views in its own vector so that you can just return a const ref to the vector. Since we're going to be calling this function every time we call compizDamageNux, the cost of doing the initial tracking for outweighs the cost of the query here.

270 + w->isViewable() &&
271 + !w->inShowDesktopMode() && // Why must this != isViewable?

You probably wanted

w->state () & CompWindowStateHiddenMask

260 + // Loop through windows from back to front
261 + for (CompWindow* w : screen->windows ())
262 + {

If you still want to use this, it would be easier to go front to back, I had to read the code like 8 times to figure out how it worked.

// front to back
for (CompWindow *w : screen->reverseWindows ())
{
 if (w->isMapped() &&
  w->isViewable() &&
  !w->inShowDesktopMode() && // Why must this != isViewable?
  w->geometry().contains(output))
    return true; // a window that covers the whole output discovered at top first
 else
   for (Window id : nuxwins)
     if (w->id () == id && output->intersects (w->geometry ())
       return false; // a nux windows discovered first
}

review: Needs Fixing
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Sam, if you want people to pay attention, use fewer words. I'll get to this later.

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

> Sam, if you want people to pay attention, use fewer words. I'll get to this
> later.

I apologize for my verbosity. Some things are complicated. This is.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

ALPHA AND TRANSPARENCY
I can only find one edge case so far. Using the obs plugin make a fullscreen window translucent. I would like to be able to detect this using CompositeWindow opacity() and alpha(), but there are four problems with that:
  1. opacity() returns OPAQUE even after updateOpacity and even when the window is translucent. Bug?
  2. updateOpacity() triggers window damage. Not a good idea. Bug?
  3. updateOpacity() requires a round trip.
  4. alpha() is not implemented. Does it have a use?
Actually, obs doesn't use composite opacity at all. It stores opacity privately and I don't want to create a dependency of unityshell on obs. I know you've said the occlusion code is designed for all this, but I don't yet understand it. And I'm not going to use it until I understand what it's meant to do.

PANEL CONTROLLER
I would prefer to not modify the panel code at all. Also, I think the current design does the best at information hiding. I think it's only creating a vector that is typically size 1 or 2 anyway. So performance should not be an issue.

HIDDEN WINDOW DETECTION
Fixed as smspillaz suggested.

SHELLISHIDDEN
Fixed (simplified) as smspillaz suggested.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'launcher/AbstractLauncherIcon.h'
2--- launcher/AbstractLauncherIcon.h 2012-06-06 15:40:19 +0000
3+++ launcher/AbstractLauncherIcon.h 2012-06-26 09:51:19 +0000
4@@ -218,6 +218,7 @@
5
6 sigc::signal<void, AbstractLauncherIcon::Ptr> needs_redraw;
7 sigc::signal<void, AbstractLauncherIcon::Ptr> remove;
8+ sigc::signal<void, nux::ObjectPtr<nux::View>> tooltip_visible;
9 sigc::signal<void> visibility_changed;
10
11 sigc::connection needs_redraw_connection;
12
13=== modified file 'launcher/Launcher.cpp'
14--- launcher/Launcher.cpp 2012-06-20 18:51:16 +0000
15+++ launcher/Launcher.cpp 2012-06-26 09:51:19 +0000
16@@ -1520,6 +1520,11 @@
17 }
18 }
19
20+nux::ObjectPtr<nux::View> Launcher::GetActiveTooltip() const
21+{
22+ return _active_tooltip;
23+}
24+
25 void Launcher::SetActionState(LauncherActionState actionstate)
26 {
27 if (_launcher_action_state == actionstate)
28@@ -1681,6 +1686,7 @@
29 EnsureAnimation();
30
31 icon->needs_redraw.connect(sigc::mem_fun(this, &Launcher::OnIconNeedsRedraw));
32+ icon->tooltip_visible.connect(sigc::mem_fun(this, &Launcher::OnTooltipVisible));
33 }
34
35 void Launcher::OnIconRemoved(AbstractLauncherIcon::Ptr icon)
36@@ -1757,6 +1763,11 @@
37 EnsureAnimation();
38 }
39
40+void Launcher::OnTooltipVisible(nux::ObjectPtr<nux::View> view)
41+{
42+ _active_tooltip = view;
43+}
44+
45 void Launcher::Draw(nux::GraphicsEngine& GfxContext, bool force_draw)
46 {
47
48
49=== modified file 'launcher/Launcher.h'
50--- launcher/Launcher.h 2012-06-15 02:03:31 +0000
51+++ launcher/Launcher.h 2012-06-26 09:51:19 +0000
52@@ -96,6 +96,8 @@
53 return _parent;
54 };
55
56+ nux::ObjectPtr<nux::View> GetActiveTooltip() const; // nullptr = no tooltip
57+
58 virtual void RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags);
59 virtual void RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags);
60 virtual void RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags);
61@@ -269,6 +271,7 @@
62 void OnOrderChanged();
63
64 void OnIconNeedsRedraw(AbstractLauncherIcon::Ptr icon);
65+ void OnTooltipVisible(nux::ObjectPtr<nux::View> view);
66
67 void OnOverlayHidden(GVariant* data);
68 void OnOverlayShown(GVariant* data);
69@@ -311,6 +314,7 @@
70
71 LauncherModel::Ptr _model;
72 nux::BaseWindow* _parent;
73+ nux::ObjectPtr<nux::View> _active_tooltip;
74 QuicklistView* _active_quicklist;
75
76 nux::HLayout* m_Layout;
77
78=== modified file 'launcher/LauncherIcon.cpp'
79--- launcher/LauncherIcon.cpp 2012-06-19 01:00:36 +0000
80+++ launcher/LauncherIcon.cpp 2012-06-26 09:51:19 +0000
81@@ -513,6 +513,7 @@
82 LoadTooltip();
83 _tooltip->ShowTooltipWithTipAt(tip_x, tip_y);
84 _tooltip->ShowWindow(!tooltip_text().empty());
85+ tooltip_visible.emit(_tooltip);
86 }
87
88 void
89@@ -534,6 +535,7 @@
90
91 if (_tooltip)
92 _tooltip->ShowWindow(false);
93+ tooltip_visible.emit(nux::ObjectPtr<nux::View>(nullptr));
94 }
95
96 bool LauncherIcon::OpenQuicklist(bool select_first_item, int monitor)
97@@ -653,6 +655,7 @@
98 {
99 if (_tooltip)
100 _tooltip->ShowWindow(false);
101+ tooltip_visible.emit(nux::ObjectPtr<nux::View>(nullptr));
102 }
103
104 bool
105
106=== modified file 'panel/PanelController.cpp'
107--- panel/PanelController.cpp 2012-06-15 02:03:31 +0000
108+++ panel/PanelController.cpp 2012-06-26 09:51:19 +0000
109@@ -49,6 +49,7 @@
110 void QueueRedraw();
111
112 std::vector<Window> GetTrayXids() const;
113+ std::vector<nux::View*> GetPanelViews() const;
114 std::vector<nux::Geometry> GetGeometries() const;
115
116 // NOTE: nux::Property maybe?
117@@ -106,6 +107,15 @@
118 return xids;
119 }
120
121+std::vector<nux::View*> Controller::Impl::GetPanelViews() const
122+{
123+ std::vector<nux::View*> views;
124+ views.reserve(windows_.size());
125+ for (auto window: windows_)
126+ views.push_back(ViewForWindow(window));
127+ return views;
128+}
129+
130 std::vector<nux::Geometry> Controller::Impl::GetGeometries() const
131 {
132 std::vector<nux::Geometry> geometries;
133@@ -325,6 +335,11 @@
134 return pimpl->GetTrayXids();
135 }
136
137+std::vector<nux::View*> Controller::GetPanelViews() const
138+{
139+ return pimpl->GetPanelViews();
140+}
141+
142 std::vector<nux::Geometry> Controller::GetGeometries() const
143 {
144 return pimpl->GetGeometries();
145
146=== modified file 'panel/PanelController.h'
147--- panel/PanelController.h 2012-05-06 23:48:38 +0000
148+++ panel/PanelController.h 2012-06-26 09:51:19 +0000
149@@ -41,6 +41,7 @@
150 void QueueRedraw();
151
152 std::vector<Window> GetTrayXids() const;
153+ std::vector<nux::View*> GetPanelViews() const;
154 std::vector<nux::Geometry> GetGeometries() const;
155
156 // NOTE: nux::Property maybe?
157
158=== modified file 'plugins/unityshell/src/unityshell.cpp'
159--- plugins/unityshell/src/unityshell.cpp 2012-06-25 21:37:57 +0000
160+++ plugins/unityshell/src/unityshell.cpp 2012-06-26 09:51:19 +0000
161@@ -100,7 +100,6 @@
162 const unsigned int SCROLL_UP_BUTTON = 7;
163
164 const std::string RELAYOUT_TIMEOUT = "relayout-timeout";
165-const std::string REDRAW_IDLE = "redraw-idle";
166 } // namespace local
167 } // anon namespace
168
169@@ -118,6 +117,7 @@
170 , super_keypressed_(false)
171 , newFocusedWindow(nullptr)
172 , doShellRepaint(false)
173+ , didShellRepaint(false)
174 , allowWindowPaint(false)
175 , damaged(false)
176 , _key_nav_mode_requested(false)
177@@ -908,6 +908,7 @@
178 }
179
180 doShellRepaint = false;
181+ didShellRepaint = true;
182 damaged = false;
183 }
184
185@@ -1210,7 +1211,20 @@
186 {
187 bool ret;
188
189- doShellRepaint = true;
190+ /*
191+ * Very important!
192+ * Don't waste GPU and CPU rendering the shell on every frame if you don't
193+ * need to. Doing so on every frame causes Nux to hog the GPU and slow down
194+ * ALL rendering. (LP: #988079)
195+ */
196+ bool force = forcePaintOnTop() || PluginAdapter::Default()->IsExpoActive();
197+ if (force)
198+ doShellRepaint = true;
199+ else if (region.isEmpty())
200+ doShellRepaint = false;
201+ else
202+ doShellRepaint = wt->GetDrawList().size() > 0;
203+
204 allowWindowPaint = true;
205 _last_output = output;
206 paint_panel_ = false;
207@@ -1226,13 +1240,20 @@
208 * attempts to bind it will only increment
209 * its bind reference so make sure that
210 * you always unbind as much as you bind */
211- _fbo->bind (nux::Geometry (output->x (), output->y (), output->width (), output->height ()));
212+ if (doShellRepaint)
213+ _fbo->bind (nux::Geometry (output->x (), output->y (), output->width (), output->height ()));
214 #endif
215
216+ overShell = CompRegion();
217+ nuxRegion = CompRegion();
218+
219 /* glPaintOutput is part of the opengl plugin, so we need the GLScreen base class. */
220 ret = gScreen->glPaintOutput(attrib, transform, region, output, mask);
221
222 #ifndef USE_MODERN_COMPIZ_GL
223+ if (doShellRepaint && !force && overShell.contains(*output))
224+ doShellRepaint = false;
225+
226 if (doShellRepaint)
227 paintDisplay(region, transform, mask);
228 #endif
229@@ -1285,16 +1306,32 @@
230 for (ShowdesktopHandlerWindowInterface *wi : ShowdesktopHandler::animating_windows)
231 wi->HandleAnimations (ms);
232
233+ // Workaround Nux bug LP: #1014610:
234 if (damaged)
235 {
236 damaged = false;
237- damageNuxRegions();
238+ nuxDamageCompiz();
239 }
240
241+ compizDamageNux(cScreen->currentDamage());
242+
243+ didShellRepaint = false;
244 }
245
246 void UnityScreen::donePaint()
247 {
248+ /*
249+ * It's only safe to clear the draw list if drawing actually occurred
250+ * (i.e. the shell was not obscured behind a fullscreen window).
251+ * If you clear the draw list and drawing has not occured then you'd be
252+ * left with all your views thinking they're queued for drawing still and
253+ * would refuse to redraw when you return from fullscreen.
254+ * I think this is a Nux bug. ClearDrawList should ideally also mark all
255+ * the queued views as draw_cmd_queued_=false.
256+ */
257+ if (didShellRepaint)
258+ wt->ClearDrawList();
259+
260 std::list <ShowdesktopHandlerWindowInterface *> remove_windows;
261
262 for (ShowdesktopHandlerWindowInterface *wi : ShowdesktopHandler::animating_windows)
263@@ -1315,42 +1352,100 @@
264 cScreen->donePaint ();
265 }
266
267+void UnityScreen::compizDamageNux(CompRegion const& damage)
268+{
269+ if (!launcher_controller_)
270+ return;
271+
272+ CompRect::vector const& rects(damage.rects());
273+ for (const CompRect &r : rects)
274+ {
275+ nux::Geometry geo(r.x(), r.y(), r.width(), r.height());
276+ BackgroundEffectHelper::ProcessDamage(geo);
277+ }
278+
279+ auto launchers = launcher_controller_->launchers();
280+ for (auto launcher : launchers)
281+ {
282+ if (!launcher->Hidden())
283+ {
284+ nux::Geometry geo = launcher->GetAbsoluteGeometry();
285+ CompRegion launcher_region(geo.x, geo.y, geo.width, geo.height);
286+ if (damage.intersects(launcher_region))
287+ launcher->QueueDraw();
288+ nux::ObjectPtr<nux::View> tooltip = launcher->GetActiveTooltip();
289+ if (!tooltip.IsNull())
290+ {
291+ nux::Geometry tip = tooltip->GetAbsoluteGeometry();
292+ CompRegion tip_region(tip.x, tip.y, tip.width, tip.height);
293+ if (damage.intersects(tip_region))
294+ tooltip->QueueDraw();
295+ }
296+ }
297+ }
298+
299+ std::vector<nux::View*> const& panels(panel_controller_->GetPanelViews());
300+ for (nux::View* view : panels)
301+ {
302+ nux::Geometry geo = view->GetAbsoluteGeometry();
303+ CompRegion panel_region(geo.x, geo.y, geo.width, geo.height);
304+ if (damage.intersects(panel_region))
305+ view->QueueDraw();
306+ }
307+
308+ QuicklistManager* qm = QuicklistManager::Default();
309+ if (qm)
310+ {
311+ QuicklistView* view = qm->Current();
312+ if (view)
313+ {
314+ nux::Geometry geo = view->GetAbsoluteGeometry();
315+ CompRegion quicklist_region(geo.x, geo.y, geo.width, geo.height);
316+ if (damage.intersects(quicklist_region))
317+ view->QueueDraw();
318+ }
319+ }
320+}
321+
322 /* Grab changed nux regions and add damage rects for them */
323-void UnityScreen::damageNuxRegions()
324+void UnityScreen::nuxDamageCompiz()
325 {
326+ // Workaround Nux bug LP: #1014610 (unbounded DrawList growth)
327+ // Also, ensure we don't dereference null *controller_ on startup.
328+ if (damaged || !launcher_controller_ || !dash_controller_)
329+ return;
330+ damaged = true;
331+
332 CompRegion nux_damage;
333
334- if (damaged)
335- return;
336-
337- std::vector<nux::Geometry> dirty = wt->GetDrawList();
338- damaged = true;
339-
340- for (std::vector<nux::Geometry>::iterator it = dirty.begin(), end = dirty.end();
341- it != end; ++it)
342- {
343- nux::Geometry const& geo = *it;
344- nux_damage += CompRegion(geo.x, geo.y, geo.width, geo.height);
345- }
346-
347- nux::Geometry geo = wt->GetWindowCompositor().GetTooltipMainWindowGeometry();
348- nux_damage += CompRegion(geo.x, geo.y, geo.width, geo.height);
349-
350- geo = lastTooltipArea;
351- nux_damage += CompRegion(lastTooltipArea.x, lastTooltipArea.y,
352- lastTooltipArea.width, lastTooltipArea.height);
353-
354- /*
355- * Avoid Nux damaging Nux as recommended by smspillaz. Though I don't
356- * believe it would be harmful or significantly expensive right now.
357- */
358+ std::vector<nux::Geometry> const& dirty = wt->GetDrawList();
359+ for (auto geo : dirty)
360+ nux_damage += CompRegion(geo.x, geo.y, geo.width, geo.height);
361+
362+ if (launcher_controller_->IsOverlayOpen())
363+ {
364+ nux::BaseWindow* dash_window = dash_controller_->window();
365+ nux::Geometry const& geo = dash_window->GetAbsoluteGeometry();
366+ nux_damage += CompRegion(geo.x, geo.y, geo.width, geo.height);
367+ }
368+
369+ auto launchers = launcher_controller_->launchers();
370+ for (auto launcher : launchers)
371+ {
372+ if (!launcher->Hidden())
373+ {
374+ nux::ObjectPtr<nux::View> tooltip = launcher->GetActiveTooltip();
375+ if (!tooltip.IsNull())
376+ {
377+ nux::Geometry const& g = tooltip->GetAbsoluteGeometry();
378+ nux_damage += CompRegion(g.x, g.y, g.width, g.height);
379+ }
380+ }
381+ }
382+
383 cScreen->damageRegionSetEnabled(this, false);
384 cScreen->damageRegion(nux_damage);
385 cScreen->damageRegionSetEnabled(this, true);
386-
387- wt->ClearDrawList();
388-
389- lastTooltipArea = geo;
390 }
391
392 /* handle X Events */
393@@ -1510,13 +1605,7 @@
394
395 void UnityScreen::damageRegion(const CompRegion &region)
396 {
397- const CompRect::vector &rects(region.rects());
398- for (const CompRect &r : rects)
399- {
400- nux::Geometry geo(r.x(), r.y(), r.width(), r.height());
401- BackgroundEffectHelper::ProcessDamage(geo);
402- }
403-
404+ compizDamageNux(region);
405 cScreen->damageRegion(region);
406 }
407
408@@ -2149,14 +2238,6 @@
409 return false;
410 }
411
412-const CompWindowList& UnityScreen::getWindowPaintList()
413-{
414- CompWindowList& pl = _withRemovedNuxWindows = cScreen->getWindowPaintList();
415- pl.remove_if(isNuxWindow);
416-
417- return pl;
418-}
419-
420 void UnityScreen::RaiseInputWindows()
421 {
422 std::vector<Window> const& xwns = nux::XInputWindow::NativeHandleList();
423@@ -2181,6 +2262,24 @@
424 const CompRegion& region,
425 unsigned int mask)
426 {
427+ if (isNuxWindow(window))
428+ {
429+ if (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK)
430+ {
431+ uScreen->nuxRegion += window->geometry();
432+ uScreen->nuxRegion -= uScreen->overShell;
433+ }
434+ return false;
435+ }
436+ else if (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK &&
437+ !(mask & PAINT_WINDOW_TRANSLUCENT_MASK) &&
438+ window->state() & CompWindowStateFullscreenMask)
439+ // && !window->alpha() <-- doesn't work. False positives.
440+ {
441+ uScreen->overShell += window->geometry();
442+ uScreen->overShell -= uScreen->nuxRegion;
443+ }
444+
445 GLWindowPaintAttrib wAttrib = attrib;
446
447 if (mMinimizeHandler)
448@@ -2239,7 +2338,17 @@
449 }
450 }
451
452- if (uScreen->doShellRepaint && !uScreen->forcePaintOnTop ())
453+ /*
454+ * Paint the shell in *roughly* the compiz stacking order. This is only
455+ * approximate because we're painting all the nux windows as soon as we find
456+ * the bottom-most nux window (from bottom to top).
457+ * But remember to avoid painting the shell if it's within the overShell
458+ * region.
459+ */
460+ if (uScreen->doShellRepaint &&
461+ !uScreen->forcePaintOnTop () &&
462+ !uScreen->overShell.contains(window->geometry())
463+ )
464 {
465 std::vector<Window> const& xwns = nux::XInputWindow::NativeHandleList();
466 unsigned int size = xwns.size();
467@@ -2554,25 +2663,7 @@
468
469 void UnityScreen::onRedrawRequested()
470 {
471- // disable blur updates so we dont waste perf. This can stall the blur during animations
472- // but ensures a smooth animation.
473- if (_in_paint)
474- {
475- if (!sources_.GetSource(local::REDRAW_IDLE))
476- {
477- auto redraw_idle(std::make_shared<glib::Idle>(glib::Source::Priority::DEFAULT));
478- sources_.Add(redraw_idle, local::REDRAW_IDLE);
479-
480- redraw_idle->Run([&]() {
481- onRedrawRequested();
482- return false;
483- });
484- }
485- }
486- else
487- {
488- damageNuxRegions();
489- }
490+ nuxDamageCompiz();
491 }
492
493 /* Handle option changes and plug that into nux windows */
494
495=== modified file 'plugins/unityshell/src/unityshell.h'
496--- plugins/unityshell/src/unityshell.h 2012-06-23 01:54:48 +0000
497+++ plugins/unityshell/src/unityshell.h 2012-06-26 09:51:19 +0000
498@@ -133,9 +133,6 @@
499 CompOutput*,
500 unsigned int);
501
502- /* Pop our InputOutput windows from the paint list */
503- const CompWindowList& getWindowPaintList();
504-
505 /* handle X11 events */
506 void handleEvent(XEvent*);
507
508@@ -212,7 +209,10 @@
509
510 bool initPluginActions();
511 void initLauncher();
512- void damageNuxRegions();
513+
514+ void compizDamageNux(CompRegion const& region);
515+ void nuxDamageCompiz();
516+
517 void onRedrawRequested();
518 void Relayout();
519
520@@ -260,7 +260,6 @@
521 bool enable_shortcut_overlay_;
522
523 GestureEngine gesture_engine_;
524- nux::Geometry lastTooltipArea;
525 bool needsRelayout;
526 bool _in_paint;
527 bool super_keypressed_;
528@@ -277,11 +276,14 @@
529
530 /* handle paint order */
531 bool doShellRepaint;
532+ bool didShellRepaint;
533 bool allowWindowPaint;
534 bool damaged;
535 bool _key_nav_mode_requested;
536 CompOutput* _last_output;
537- CompWindowList _withRemovedNuxWindows;
538+
539+ CompRegion nuxRegion;
540+ CompRegion overShell;
541
542 nux::Property<nux::Geometry> primary_monitor_;
543