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 @@
/*
* Avoid Nux damaging Nux as recommended by smspillaz. Though I don't
* believe it would be harmful or significantly expensive right now.
*/
cScreen->damageRegionSetEnabled(this, false);
- cScreen->damageRegion(nux_damage);
+ cScreen->damageRegion(nux_damage_);
cScreen->damageRegionSetEnabled(this, true);
+ CompRegion nux_damage_;
+
friend class UnityWindow;
};
(Note: diff not directly applicable, I had it merged with other stuff, but the behaviour is the same).
A small thing to note with this possible approach is that it will require some changes to nux. Specifically, since we call wt->RenderInterfaceFromForeignCmd in glPaintOutput, this is done N output times per paint pass, so the draw list will be cleared completely on the first output, and the second output won't be drawn at all. So nux needs to only release things from the draw list if they intersect the output geometry provided in RenderInterfaceFromForeignCmd.
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' unityshell/ src/unityshell. cpp 2012-06-17 14:17:45 +0000 unityshell/ src/unityshell. cpp 2012-06-17 14:18:38 +0000 :Default( )->IsExpoActive () || PaintMasks () & ShellPaintReque storInterface: :WindowPaintReq uestor) || :Default ()->IsExpoActive() || SCREEN_ TRANSFORMED_ MASK |
PAINT_ SCREEN_ WITH_TRANSFORME D_WINDOWS_ MASK))) (region) ; *output) ) (region) ; ().size( ) > 0 || tHelper: :HasDirtyHelper s(); t().size( ) > 0 || tHelper: :HasDirtyHelper s());
--- plugins/
+++ plugins/
@@ -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:
+ if ((GetProhibited
+ PluginAdapter:
(mask & (PAINT_
{
- compizDamageNux
- doShellRepaint = true;
+ requiring_repaint = true;
}
else if (shellIsHidden(
{
// Don't ever waste GPU and CPU rendering the shell in games/benchmarks!
- doShellRepaint = false;
+ requiring_repaint = false;
}
else
{
- compizDamageNux
- doShellRepaint = wt->GetDrawList
- BackgroundEffec
+ bool isDamaged = RegionIsNuxDamaged (CompRegion (*output));
+ requiring_repaint = isDamaged &&
+ (wt->GetDrawLis
+ BackgroundEffec
}
/* glPaintOutput is part of the opengl plugin, so we need the GLScreen base class. */
@@ -1419,6 +1416,19 @@
}
+bool UnityScreen: :RegionIsNuxDam aged (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( ) dlerWindowInter face *> remove_windows; :nuxDamageCompi z()
{
std::list <ShowdesktopHan
@@ -1491,8 +1501,6 @@
/* Grab changed nux regions and add damage rects for them */
void UnityScreen:
{
- 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 >GetAbsoluteGeo metry() ;
@@ -1518,22 +1526,22 @@
if (!tooltip.IsNull())
{
const nux::Geometry &g = tooltip-
- nux_damage += CompRegion(g.x, g.y, g.width, g.height);
+ nux_damage_ += CompRegion(g.x, g.y, g.width, g.height);
}
}
}
}
nux::Geometry geo = lastTooltipArea; lastTooltipArea .x, lastTooltipArea.y, .width, lastTooltipArea .height) ; lastTooltipArea .x, lastTooltipArea.y, .width, lastTooltipArea .height) ;
- nux_damage += CompRegion(
- lastTooltipArea
+ nux_damage_ += CompRegion(
+ lastTooltipArea
/* >damageRegionSe tEnabled( this, false); >damageRegion( nux_damage) ; >damageRegion( nux_damage_ ); >damageRegionSe tEnabled( this, true);
* Avoid Nux damaging Nux as recommended by smspillaz. Though I don't
* believe it would be harmful or significantly expensive right now.
*/
cScreen-
- cScreen-
+ cScreen-
cScreen-
lastTooltipArea = geo; EffectHelper: :ProcessDamage( geo);
@@ -1703,6 +1711,7 @@
Background
}
+ compizDamageNux (region) ; >damageRegion( region) ;
cScreen-
}
^ Buffer up nux damage, mark the nux bits as dirty if need be.
=== modified file 'plugins/ unityshell/ src/unityshell. h' unityshell/ src/unityshell. h 2012-06-17 14:17:45 +0000 unityshell/ src/unityshell. h 2012-06-17 14:18:38 +0000
--- plugins/
+++ plugins/
@@ -246,6 +246,9 @@
void RequireShellRepaint ();
bool RepaintPending ();
+ /* Clears the nux damage region */ spatch paint_dispatch_;
+ bool RegionIsNuxDamaged (const CompRegion ®);
+
ShellPaintDi
Settings dash_settings_; :SourceManager sources_; allowed_ mask_;
@@ -327,6 +330,8 @@
glib:
unsigned int no_paint_
+ CompRegion nux_damage_;
+
friend class UnityWindow;
};
(Note: diff not directly applicable, I had it merged with other stuff, but the behaviour is the same).
A small thing to note with this possible approach is that it will require some changes to nux. Specifically, since we call wt->RenderInter faceFromForeign Cmd in glPaintOutput, this is done N output times per paint pass, so the draw list will be cleared completely on the first output, and the second output won't be drawn at all. So nux needs to only release things from the draw list if they intersect the output geometry provided in RenderInterface FromForeignCmd.
Just a thought I guess.