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: 413 lines (+166/-35)
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 (+125/-33)
plugins/unityshell/src/unityshell.h (+6/-2)
To merge this branch: bzr merge lp:~vanvugt/unity/regionalDamage
Reviewer Review Type Date Requested Status
Unity Team Pending
Review via email: mp+109809@code.launchpad.net

Description of the change

*PROTOTYPE*

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

For me, this branch eliminates the 25-30% slowdown experienced 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.

This also fixes bug 967112 and bug 992516. Probably more...

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

Approve the concept, been wanting to do this for some time ;-)

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

FYI, this is going to have pretty giant conflict with:

https://code.launchpad.net/~smspillaz/unity/unity.less-paint-insanity/+merge/110267

Since the "should we repaint" logic was moved into a separate file.

You should be able to get away with:

12 + /*
13 + * TODO: Figure out if we can ask compiz when:
14 + * output->containsFullscreenWindows();
15 + * and if true, then force doShellRepaint=false here.
16 + */
           bool doShellRepaint = wt->GetDrawList().size() > 0 ||
18 + BackgroundEffectHelper::HasDirtyHelpers() ||
19 + switcher_controller_->Visible() ||
20 + launcher_controller_->IsOverlayOpen() ||
21 + (mask & (PAINT_SCREEN_TRANSFORMED_MASK |
22 + PAINT_SCREEN_FULL_MASK |
23 + PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK));
24 +

if (doShellRepaint)
  ShellRepaintRequired ();

25 + /* Warning: ^ checking for PAINT_SCREEN_FULL_MASK is necessary right now
26 + * to avoid flickering. However it will nullify our performance
27 + * optimizations for people who have enabled "Force full screen
28 + * redraws" in the Workarounds plugin.
29 + */

A small note - it would certainly be nice if we had a method to get a collection of all the nux base windows.

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

Rev 2405 is way out of date. Ignore it. Still in progress.

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

I will fix conflicts as they arise :)

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

Of course, just a heads up :)

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

Tip on detecting if the shell is hidden (I did this once myself).

It might make more sense to use core's built in occlusion detection to figure this out. I think in that case when you get a redraw on a nux window, region will be empty in glPaint. That's worth double checking. (Also worth noting is that this only works if you disable removing the nux windows from the paint list, which should hopefully be done in the future)

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

Well, the current method of detecting if the shell is occluded is working perfectly. It's also simple and fast. But there's still more testing to go and I'm open to any reasonable changes.

Revision history for this message
Andrea Azzarone (azzar1) wrote :

Fix bug 992516 for me too.
Regression: when I try to expand/collapse a dash category Unity freezes (not a crash).

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

Sure, just using cores occlusion detection will be faster and less breakable. this will only work wehnwe arent removing nux windows from the paint list.

Revision history for this message
Andrea Azzarone (azzar1) wrote :

> Fix bug 992516 for me too.
> Regression: when I try to expand/collapse a dash category Unity freezes (not a
> crash).

Nvm. The freeze no longer happens.

Revision history for this message
Sam Spilsbury (smspillaz) wrote :
Download full text (5.3 KiB)

I've noticed that on multiple monitors there is definitely a feedback effect, where compizDamageNux causes something on the second screen to redraw, and then that redraw region is fed right back to us in glPaintOutput which causes that thing to be queued for redraw again.

I'll let you come to your own ideas on this, but one "solution" I've found (and I say "solution" because it has known problems which I think are fixable, more on that on a bit) is to use damageRegion to compute the dirty regions that need redrawing so that we can use the wrap handlers to avoid feedback. For example:

=== modified file 'plugins/unityshell/src/unityshell.cpp'
--- plugins/unityshell/src/unityshell.cpp 2012-06-17 14:17:45 +0000
+++ plugins/unityshell/src/unityshell.cpp 2012-06-17 14:18:38 +0000
@@ -1306,34 +1306,35 @@
 {
   bool ret;
+
+ bool requiring_repaint = false;
+
   // A few cases where we want to force the shell to be painted
- if (forcePaintOnTop() ||
- PluginAdapter::Default()->IsExpoActive() ||
+ if ((GetProhibitedPaintMasks () & ShellPaintRequestorInterface::WindowPaintRequestor) ||
+ PluginAdapter::Default ()->IsExpoActive() ||
       (mask & (PAINT_SCREEN_TRANSFORMED_MASK |
                PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK)))
   {
- compizDamageNux(region);
- doShellRepaint = true;
+ requiring_repaint = true;
   }
   else if (shellIsHidden(*output))
   {
     // Don't ever waste GPU and CPU rendering the shell in games/benchmarks!
- doShellRepaint = false;
+ requiring_repaint = false;
   }
   else
   {
- compizDamageNux(region);
- doShellRepaint = wt->GetDrawList().size() > 0 ||
- BackgroundEffectHelper::HasDirtyHelpers();
+ bool isDamaged = RegionIsNuxDamaged (CompRegion (*output));
+ requiring_repaint = isDamaged &&
+ (wt->GetDrawList().size() > 0 ||
+ BackgroundEffectHelper::HasDirtyHelpers());
   }

   /* glPaintOutput is part of the opengl plugin, so we need the GLScreen base class. */
@@ -1419,6 +1416,19 @@

 }

+bool UnityScreen::RegionIsNuxDamaged (const CompRegion &r)
+{
+ static CompRegion tmp = emptyRegion;
+
+ tmp &= emptyRegion;
+ tmp |= r;
+ tmp &= nux_damage_;
+
+ nux_damage_ -= tmp;
+
+ return !tmp.isEmpty ();
+}
+

^ Checks if a particular output region has damaged nux regions intersecting the output region that came from damageRegion

 void UnityScreen::donePaint()
 {
   std::list <ShowdesktopHandlerWindowInterface *> remove_windows;
@@ -1491,8 +1501,6 @@
 /* Grab changed nux regions and add damage rects for them */
 void UnityScreen::nuxDamageCompiz()
 {
- CompRegion nux_damage;
-
   if (damaged)
     return;

@@ -1503,7 +1511,7 @@
        it != end; ++it)
   {
     nux::Geometry const& geo = *it;
- nux_damage += CompRegion(geo.x, geo.y, geo.width, geo.height);
+ nux_damage_ += CompRegion(geo.x, geo.y, geo.width, geo.height);
   }

   // launcher_controller_ will still be null on startup
@@ -1518,22 +1526,22 @@
         if (!tooltip.IsNull())
         {
           const nux::Geometry &g = tooltip->GetAbsoluteGeometry();
- nux_damage += CompRegion(g.x, g.y, g.width, g.height)...

Read more...

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

(These feedback problems make it good to think about testing too...)

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

I haven't even begun multi-monitor testing. Will do this week. Please wait...

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

On Mon, 18 Jun 2012, Daniel van Vugt wrote:

> I haven't even begun multi-monitor testing. Will do this week. Please wait...

No problem, I've just been playing around with the branch because I want
faster rendering :P

> --
> https://code.launchpad.net/~vanvugt/unity/regionalDamage/+merge/109809
> Your team DX Unity Bugs is subscribed to branch lp:unity.
>

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

(Small update, moving wt->ClearDrawList () to donePaint instead of glPaintOutput fixes most of the annoyances to do with flicking. No nux patching needed)

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

Looks like this fix now depends on bug 1014610 being resolved for Nux :(

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-19 09:18: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-15 02:03:31 +0000
15+++ launcher/Launcher.cpp 2012-06-19 09:18: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-19 09:18: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-19 09:18: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-19 09:18: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-19 09:18: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-15 02:03:31 +0000
160+++ plugins/unityshell/src/unityshell.cpp 2012-06-19 09:18:19 +0000
161@@ -1204,7 +1204,20 @@
162 {
163 bool ret;
164
165- doShellRepaint = true;
166+ /*
167+ * Very important!
168+ * Don't waste GPU and CPU rendering the shell on every frame if you don't
169+ * need to. Doing so on every frame causes Nux to hog the GPU and slow down
170+ * all other OpenGL apps (LP: #988079)
171+ */
172+ if (forcePaintOnTop() || PluginAdapter::Default()->IsExpoActive())
173+ doShellRepaint = true;
174+ else if (region.isEmpty() || shellIsHidden(*output))
175+ doShellRepaint = false;
176+ else
177+ doShellRepaint = wt->GetDrawList().size() > 0 ||
178+ BackgroundEffectHelper::HasDirtyHelpers();
179+
180 allowWindowPaint = true;
181 _last_output = output;
182 paint_panel_ = false;
183@@ -1220,7 +1233,8 @@
184 * attempts to bind it will only increment
185 * its bind reference so make sure that
186 * you always unbind as much as you bind */
187- _fbo->bind (nux::Geometry (output->x (), output->y (), output->width (), output->height ()));
188+ if (doShellRepaint)
189+ _fbo->bind (nux::Geometry (output->x (), output->y (), output->width (), output->height ()));
190 #endif
191
192 /* glPaintOutput is part of the opengl plugin, so we need the GLScreen base class. */
193@@ -1279,16 +1293,20 @@
194 for (ShowdesktopHandlerWindowInterface *wi : ShowdesktopHandler::animating_windows)
195 wi->HandleAnimations (ms);
196
197+ // Workaround Nux bug LP: #1014610:
198 if (damaged)
199 {
200 damaged = false;
201- damageNuxRegions();
202+ nuxDamageCompiz();
203 }
204
205+ compizDamageNux(cScreen->currentDamage());
206 }
207
208 void UnityScreen::donePaint()
209 {
210+ wt->ClearDrawList();
211+
212 std::list <ShowdesktopHandlerWindowInterface *> remove_windows;
213
214 for (ShowdesktopHandlerWindowInterface *wi : ShowdesktopHandler::animating_windows)
215@@ -1309,11 +1327,94 @@
216 cScreen->donePaint ();
217 }
218
219+bool UnityScreen::shellIsHidden(const CompOutput &output)
220+{
221+ bool hidden = false;
222+ const std::vector<Window> &nuxwins(nux::XInputWindow::NativeHandleList());
223+
224+ // Loop through windows from back to front
225+ for (CompWindow *w : screen->windows ())
226+ {
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))
237+ {
238+ hidden = true;
239+ }
240+ else if (hidden)
241+ {
242+ for (Window n : nuxwins)
243+ {
244+ if (w->id() == n && output.intersects(w->geometry()))
245+ {
246+ hidden = false;
247+ break;
248+ }
249+ }
250+ }
251+ }
252+
253+ return hidden;
254+}
255+
256+void UnityScreen::compizDamageNux(const CompRegion &damage)
257+{
258+ auto launchers = launcher_controller_->launchers();
259+ for (auto launcher : launchers)
260+ {
261+ if (!launcher->Hidden())
262+ {
263+ nux::Geometry geo = launcher->GetAbsoluteGeometry();
264+ CompRegion launcher_region(geo.x, geo.y, geo.width, geo.height);
265+ if (damage.intersects(launcher_region))
266+ launcher->QueueDraw();
267+ nux::ObjectPtr<nux::View> tooltip = launcher->GetActiveTooltip();
268+ if (!tooltip.IsNull())
269+ {
270+ nux::Geometry tip = tooltip->GetAbsoluteGeometry();
271+ CompRegion tip_region(tip.x, tip.y, tip.width, tip.height);
272+ if (damage.intersects(tip_region))
273+ tooltip->QueueDraw();
274+ }
275+ }
276+ }
277+
278+ const std::vector<nux::View*> &panels(panel_controller_->GetPanelViews());
279+ for (nux::View *view : panels)
280+ {
281+ nux::Geometry geo = view->GetAbsoluteGeometry();
282+ CompRegion panel_region(geo.x, geo.y, geo.width, geo.height);
283+ if (damage.intersects(panel_region))
284+ view->QueueDraw();
285+ }
286+
287+ QuicklistManager *qm = QuicklistManager::Default();
288+ if (qm)
289+ {
290+ QuicklistView *view = qm->Current();
291+ if (view)
292+ {
293+ nux::Geometry geo = view->GetAbsoluteGeometry();
294+ CompRegion quicklist_region(geo.x, geo.y, geo.width, geo.height);
295+ if (damage.intersects(quicklist_region))
296+ view->QueueDraw();
297+ }
298+ }
299+}
300+
301 /* Grab changed nux regions and add damage rects for them */
302-void UnityScreen::damageNuxRegions()
303+void UnityScreen::nuxDamageCompiz()
304 {
305 CompRegion nux_damage;
306
307+ // Workaround Nux bug LP: #1014610 (unbounded DrawList growth)
308 if (damaged)
309 return;
310
311@@ -1327,12 +1428,23 @@
312 nux_damage += CompRegion(geo.x, geo.y, geo.width, geo.height);
313 }
314
315- nux::Geometry geo = wt->GetWindowCompositor().GetTooltipMainWindowGeometry();
316- nux_damage += CompRegion(geo.x, geo.y, geo.width, geo.height);
317-
318- geo = lastTooltipArea;
319- nux_damage += CompRegion(lastTooltipArea.x, lastTooltipArea.y,
320- lastTooltipArea.width, lastTooltipArea.height);
321+ // launcher_controller_ will still be null on startup
322+ if (launcher_controller_.get())
323+ {
324+ auto launchers = launcher_controller_->launchers();
325+ for (auto launcher : launchers)
326+ {
327+ if (!launcher->Hidden())
328+ {
329+ nux::ObjectPtr<nux::View> tooltip = launcher->GetActiveTooltip();
330+ if (!tooltip.IsNull())
331+ {
332+ const nux::Geometry &g = tooltip->GetAbsoluteGeometry();
333+ nux_damage += CompRegion(g.x, g.y, g.width, g.height);
334+ }
335+ }
336+ }
337+ }
338
339 /*
340 * Avoid Nux damaging Nux as recommended by smspillaz. Though I don't
341@@ -1341,10 +1453,6 @@
342 cScreen->damageRegionSetEnabled(this, false);
343 cScreen->damageRegion(nux_damage);
344 cScreen->damageRegionSetEnabled(this, true);
345-
346- wt->ClearDrawList();
347-
348- lastTooltipArea = geo;
349 }
350
351 /* handle X Events */
352@@ -1511,6 +1619,8 @@
353 BackgroundEffectHelper::ProcessDamage(geo);
354 }
355
356+ compizDamageNux(region);
357+
358 cScreen->damageRegion(region);
359 }
360
361@@ -2527,25 +2637,7 @@
362
363 void UnityScreen::onRedrawRequested()
364 {
365- // disable blur updates so we dont waste perf. This can stall the blur during animations
366- // but ensures a smooth animation.
367- if (_in_paint)
368- {
369- if (!sources_.GetSource(local::REDRAW_IDLE))
370- {
371- auto redraw_idle(std::make_shared<glib::Idle>(glib::Source::Priority::DEFAULT));
372- sources_.Add(redraw_idle, local::REDRAW_IDLE);
373-
374- redraw_idle->Run([&]() {
375- onRedrawRequested();
376- return false;
377- });
378- }
379- }
380- else
381- {
382- damageNuxRegions();
383- }
384+ nuxDamageCompiz();
385 }
386
387 /* Handle option changes and plug that into nux windows */
388
389=== modified file 'plugins/unityshell/src/unityshell.h'
390--- plugins/unityshell/src/unityshell.h 2012-06-15 02:03:31 +0000
391+++ plugins/unityshell/src/unityshell.h 2012-06-19 09:18:19 +0000
392@@ -212,7 +212,12 @@
393
394 bool initPluginActions();
395 void initLauncher();
396- void damageNuxRegions();
397+
398+ void compizDamageNux(const CompRegion &region);
399+ void nuxDamageCompiz();
400+
401+ bool shellIsHidden(const CompOutput &output);
402+
403 void onRedrawRequested();
404 void Relayout();
405
406@@ -255,7 +260,6 @@
407 bool enable_shortcut_overlay_;
408
409 GestureEngine gesture_engine_;
410- nux::Geometry lastTooltipArea;
411 bool needsRelayout;
412 bool _in_paint;
413 bool super_keypressed_;