Merge lp:~vanvugt/compiz/fix-unredirect-flicker into lp:compiz/0.9.8

Proposed by Daniel van Vugt on 2012-09-17
Status: Rejected
Rejected by: Daniel van Vugt on 2012-09-21
Proposed branch: lp:~vanvugt/compiz/fix-unredirect-flicker
Merge into: lp:compiz/0.9.8
Diff against target: 118 lines (+40/-3)
5 files modified
plugins/opengl/include/opengl/doublebuffer.h (+1/-0)
plugins/opengl/src/doublebuffer/src/double-buffer.cpp (+2/-1)
plugins/opengl/src/doublebuffer/tests/test-opengl-double-buffer.cpp (+22/-0)
plugins/opengl/src/privates.h (+2/-0)
plugins/opengl/src/screen.cpp (+13/-2)
To merge this branch: bzr merge lp:~vanvugt/compiz/fix-unredirect-flicker
Reviewer Review Type Date Requested Status
Sam Spilsbury 2012-09-17 Needs Information on 2012-09-17
jenkins (community) continuous-integration Approve on 2012-09-17
Review via email: mp+124632@code.launchpad.net

Commit message

Fix flickering observed when an "Unredirect Fullscreen Window" gets
redirected.
(LP: #1046664)

Description of the change

See commit message.

To post a comment you must log in.
Daniel van Vugt (vanvugt) wrote :

Sadly, this fix only works with GL, not GLES.

It may be possible to fix bug 1046664 by other means** and provide a fix for GLES too, but I'm fairly sure the only way to fix bug 1050749 with GLES would be to disallow unredirection with multiple monitors.

** which I have tried to find and failed so far.

jenkins (martin-mrazik+qa) wrote :
review: Approve (continuous-integration)
Sam Spilsbury (smspillaz) wrote :
Download full text (6.8 KiB)

Hmm, I'm a little confused by this. My understanding of this bug is that if you have fullscreen window Y on monitor B and no fullscreen windows on monitor A that unredirecting Y causes monitor B to flicker. Yes? The bounding shape of the output window is clipped in that case, and the way in which we swap buffers should have nothing to do with it.

I had a look into this and it seems like our fullscreen region detection code doesn't quite cover this case. What happens is that the we are doing the detection in terms of each monitor's paint pass.

So on monitor B, we do a pass and detect that a window is covering the monitor region and unredirect it. When we pass monitor A a bit later, we check every single window including the previously unredirected window from monitor B against monitor A, and find that nothing occludes monitor A. So we unredirect B. As a result, B is in a state of constantly being redirected and unredirected, hence the flicker.

Bug 1046664 is a completely different issue. I suspect what's going on there is that the unredirected window + menu are still there in the backbuffer, and then we partial-repaint the menu on top of that backbuffer (so the old menu is still there).

Here's a patch that at least fixes the constant un/redirection

=== modified file 'plugins/opengl/src/fsregion/fsregion.cpp'
--- plugins/opengl/src/fsregion/fsregion.cpp 2012-09-06 08:01:54 +0000
+++ plugins/opengl/src/fsregion/fsregion.cpp 2012-09-17 11:50:53 +0000
@@ -30,7 +30,8 @@

 FullscreenRegion::FullscreenRegion (const CompRect &rect) :
     covered (false),
- untouched (rect)
+ untouched (rect),
+ orig (untouched)
 {
 }

@@ -38,8 +39,19 @@
 FullscreenRegion::isCoveredBy (const CompRegion &region, WinFlags flags)
 {
     bool fullscreen = false;
+ /* We don't want to unredirect desktop or alpha windows, or consider them
+ * as not-occluding other fullscreen windows */
+ const bool canBeConsideredAsCovering = !(flags & (Desktop | Alpha));
+ /* The region of this fullscreen window covers the existing untouched area
+ * of this monitor */
+ const bool coversUntouchedRegion = region == untouched;

- if (!covered && !(flags & (Desktop | Alpha)) && region == untouched)
+ /* This window covers the fullscreen area of this monitor, consider
+ * the monitor covered and don't allow any further windows on this
+ * monitor to be considered fullscreen */
+ if (!covered &&
+ canBeConsideredAsCovering &&
+ coversUntouchedRegion)
     {
  covered = true;
  fullscreen = true;
@@ -50,5 +62,14 @@
     return fullscreen;
 }

+bool
+FullscreenRegion::allowRedirection (const CompRegion &region)
+{
+ /* Don't allow existing unredirected windows that cover this
+ * region to be redirected again as they were probably unredirected
+ * on another monitor */
+ return region.intersects (orig);
+}
+
 } // namespace opengl
 } // namespace compiz

=== modified file 'plugins/opengl/src/fsregion/fsregion.h'
--- plugins/opengl/src/fsregion/fsregion.h 2012-09-06 08:01:54 +0000
+++ plugins/opengl/src/fsregion/fsregion.h 2012-09-17 11:51:14 +0000
@@ -36,8 +36,8 @@
 public:
     typedef enum
     {
- Desktop = 1,
- Alpha = ...

Read more...

review: Needs Information
Daniel van Vugt (vanvugt) wrote :

"The bounding shape of the output window is clipped in that case, and the way in which we swap buffers should have nothing to do with it."

No, that's incorrect. Buffer swapping is a hardware operation that always swaps the whole screen (all outputs), unconditionally. It does not do clipping of any sort. It's a GLX function, not GL.

Daniel van Vugt (vanvugt) wrote :

Unrelated... if your solution solves the single monitor case then I'm happy to ignore/disable the multi-monitor case for now.

Sam Spilsbury (smspillaz) wrote :

> "The bounding shape of the output window is clipped in that case, and the way
> in which we swap buffers should have nothing to do with it."
>
> No, that's incorrect. Buffer swapping is a hardware operation that always
> swaps the whole screen (all outputs), unconditionally. It does not do clipping
> of any sort. It's a GLX function, not GL.

This is correct, however it shouldn't have anything to do with the flickering that you see on top of the unredirected window. When a window is unredirected, the window that compiz displays to is clipped so that it does not cover the area of the unredirected window. It doesn't matter if any part of that buffer will be swapped, it is not visible in any case.

We could be thinking of entirely separate problems here, and I apologize if that's the case. Can you clarify why the unredirected window and compiz are competing for the same regions if buffer swapping is on?

Daniel van Vugt (vanvugt) wrote :

"the window that compiz displays to is clipped so that it does not cover the area of the unredirected window"

That is relevant to regional sub-suffer redraws but not relevant to buffer swapping. Buffer swapping will swap everything, including unredirected/uninitialized regions. I don't believe that the X server has a hook into GLX to tell the driver to clip the swapped region around unredirected windows.

So why don't we see flickering all the time?...
With a single monitor, it /appears/ to mostly work either by luck or by design of the X server to ensure that unredirected windows are painted immediately after the buffer swap, but before vertical retrace. Either way, I can't find a documented reason why it behaves that way, or why it should work without flickering, ever.

Daniel van Vugt (vanvugt) wrote :

But as I said, if your solution lets us use more buffer swapping and not have to revert to sub-buffers like mine, then I would prefer yours.

Sam Spilsbury (smspillaz) wrote :

> "the window that compiz displays to is clipped so that it does not cover the
> area of the unredirected window"
>
> That is relevant to regional sub-suffer redraws but not relevant to buffer
> swapping. Buffer swapping will swap everything, including
> unredirected/uninitialized regions. I don't believe that the X server has a
> hook into GLX to tell the driver to clip the swapped region around
> unredirected windows.

Right, it doesn't clipped the swapped region in the application buffer (eg, compiz' output window). My contention here is that this part of the application buffer is not even visible. The bounding shape of the output window is clipped away in CompositeWindow::updateOutputWindow. If we didn't unredirect the window for instance, but still clipped it, what you'd see is just a blank area of the screen where the unredirected window should be.

The only reason I'm asking is because requiring partial copies may still be relevant if we need to ensure that this part of the OpenGL buffer (eg, not the visible part of the X output window) must remain untouched even while it is invisible.

>
> So why don't we see flickering all the time?...
> With a single monitor, it /appears/ to mostly work either by luck or by design
> of the X server to ensure that unredirected windows are painted immediately
> after the buffer swap, but before vertical retrace. Either way, I can't find a
> documented reason why it behaves that way, or why it should work without
> flickering, ever.

These windows are painted separately, in a region where X does not draw our output window to the screen.

Daniel van Vugt (vanvugt) wrote :

Again, unredirection should not work at all with buffer swapping. And I am yet to see any documentation explaining how it does partially work, sometimes. Looking at CompositeWindow::updateOutputWindow, the answer may be buried in the X Shape extension, but I doubt it.

Buffer swapping is usually implemented by page flipping. That means all clipping you think you have is totally irrelevant. A page flip just changes the address of the video memory you see on your monitor to point to the alternate buffer. A page flip does not process, change or clip the contents of the video memory. Therefore, unredirected windows on top of swapbuffers can only possibly be working if the unredirect windows are drawn last (over the swapped buffer), but during vblank period so you (usually) only see a single consistent image.

Daniel van Vugt (vanvugt) wrote :

Completing the paper trail:
It appears "it just works" because GLX is designed to draw the (unre)directed windows on top of the front buffer (whichever that is), after the swap:
http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.47.4979

So that's why it mostly works already without flickering for a single monitor.

It seems the multi-monitor flickering is just a mistake in the compiz opengl plugin, which is redirecting unredirected windows repeatedly, prematurely.

Daniel van Vugt (vanvugt) wrote :

OK, I have approved Sam's patch here:
https://code.launchpad.net/~compiz-team/compiz/fix-multimonitor-unredirect/+merge/124855

This branch however is still the only working fix for bug 1046664. But I'll try to find a nicer solution instead of reproposing this one right now.

3380. By Daniel van Vugt on 2012-09-19

Merge latest lp:compiz

3381. By Daniel van Vugt on 2012-09-19

Allow swapping to continue when overlay windows exist. This means we no longer
need to compromise multi-monitor visual quality.

Swapping is only disallowed on the single frame where the number of
overlays changes.

Daniel van Vugt (vanvugt) wrote :

I reject this proposal as a much nicer fix/workaround for bug 1046664 has now landed.

Unmerged revisions

3381. By Daniel van Vugt on 2012-09-19

Allow swapping to continue when overlay windows exist. This means we no longer
need to compromise multi-monitor visual quality.

Swapping is only disallowed on the single frame where the number of
overlays changes.

3380. By Daniel van Vugt on 2012-09-19

Merge latest lp:compiz

3379. By Daniel van Vugt on 2012-09-17

Slightly clearer design: Replace FORCE_SINGLE_BUFFER with !SWAP_ALLOWED

3378. By Daniel van Vugt on 2012-09-17

Add tests.

3377. By Daniel van Vugt on 2012-09-17

Don't allow DoubleBuffer::FORCE_SINGLE_BUFFER to be true ifdef USE_GLES.

3376. By Daniel van Vugt on 2012-09-17

First working prototype for avoiding flicker when switching between
redirected and (unre)direct rendering.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'plugins/opengl/include/opengl/doublebuffer.h'
2--- plugins/opengl/include/opengl/doublebuffer.h 2012-09-12 03:55:21 +0000
3+++ plugins/opengl/include/opengl/doublebuffer.h 2012-09-19 04:02:23 +0000
4@@ -26,6 +26,7 @@
5 VSYNC,
6 HAVE_PERSISTENT_BACK_BUFFER,
7 NEED_PERSISTENT_BACK_BUFFER,
8+ SWAP_ALLOWED,
9 _NSETTINGS
10 } Setting;
11
12
13=== modified file 'plugins/opengl/src/doublebuffer/src/double-buffer.cpp'
14--- plugins/opengl/src/doublebuffer/src/double-buffer.cpp 2012-09-12 03:55:21 +0000
15+++ plugins/opengl/src/doublebuffer/src/double-buffer.cpp 2012-09-19 04:02:23 +0000
16@@ -43,6 +43,7 @@
17 setting[VSYNC] = true;
18 setting[HAVE_PERSISTENT_BACK_BUFFER] = false;
19 setting[NEED_PERSISTENT_BACK_BUFFER] = false;
20+ setting[SWAP_ALLOWED] = true;
21 }
22
23 DoubleBuffer::~DoubleBuffer ()
24@@ -59,7 +60,7 @@
25 DoubleBuffer::render (const CompRegion &region,
26 bool fullscreen)
27 {
28- if (fullscreen)
29+ if (fullscreen && setting[SWAP_ALLOWED])
30 {
31 swap ();
32 if (setting[NEED_PERSISTENT_BACK_BUFFER] &&
33
34=== modified file 'plugins/opengl/src/doublebuffer/tests/test-opengl-double-buffer.cpp'
35--- plugins/opengl/src/doublebuffer/tests/test-opengl-double-buffer.cpp 2012-09-12 10:45:58 +0000
36+++ plugins/opengl/src/doublebuffer/tests/test-opengl-double-buffer.cpp 2012-09-19 04:02:23 +0000
37@@ -44,6 +44,28 @@
38 db.render (blitRegion, true);
39 }
40
41+TEST_F(DoubleBufferTest, TestSingleFullscreenNeverSwaps)
42+{
43+ EXPECT_CALL (db, swap ()).Times (0);
44+ EXPECT_CALL (db, copyFrontToBack ()).Times (0);
45+ EXPECT_CALL (db, blitAvailable ()).WillRepeatedly (Return (true));
46+
47+ db.set (DoubleBuffer::SWAP_ALLOWED, false);
48+ db.render (blitRegion, true);
49+}
50+
51+TEST_F(DoubleBufferTest, TestSingleRegionNeverSwaps)
52+{
53+ CompRegion reg (12, 34, 56, 78);
54+
55+ EXPECT_CALL (db, swap ()).Times (0);
56+ EXPECT_CALL (db, copyFrontToBack ()).Times (0);
57+ EXPECT_CALL (db, blitAvailable ()).WillRepeatedly (Return (true));
58+
59+ db.set (DoubleBuffer::SWAP_ALLOWED, false);
60+ db.render (reg, false);
61+}
62+
63 TEST_F(DoubleBufferTest, TestNoPaintedFullscreenOrFBOAlwaysBlitsSubBuffer)
64 {
65 EXPECT_CALL (db, blitAvailable ()).WillOnce (Return (true));
66
67=== modified file 'plugins/opengl/src/privates.h'
68--- plugins/opengl/src/privates.h 2012-09-12 04:01:02 +0000
69+++ plugins/opengl/src/privates.h 2012-09-19 04:02:23 +0000
70@@ -211,6 +211,8 @@
71
72 Pixmap rootPixmapCopy;
73 CompSize rootPixmapSize;
74+
75+ int prevOverlayCount;
76 };
77
78 class PrivateGLWindow :
79
80=== modified file 'plugins/opengl/src/screen.cpp'
81--- plugins/opengl/src/screen.cpp 2012-09-13 08:35:17 +0000
82+++ plugins/opengl/src/screen.cpp 2012-09-19 04:02:23 +0000
83@@ -1134,7 +1134,8 @@
84 shaderCache (),
85 autoProgram (new GLScreenAutoProgram(gs)),
86 rootPixmapCopy (None),
87- rootPixmapSize ()
88+ rootPixmapSize (),
89+ prevOverlayCount (0)
90 {
91 ScreenInterface::setHandler (screen);
92 }
93@@ -2044,6 +2045,14 @@
94 doubleBuffer.set (DoubleBuffer::VSYNC, optionGetSyncToVblank ());
95 doubleBuffer.set (DoubleBuffer::HAVE_PERSISTENT_BACK_BUFFER, useFbo);
96 doubleBuffer.set (DoubleBuffer::NEED_PERSISTENT_BACK_BUFFER, alwaysSwap);
97+
98+#ifndef USE_GLES
99+ int overlayCount = cScreen->overlayWindowCount ();
100+ doubleBuffer.set (DoubleBuffer::SWAP_ALLOWED,
101+ overlayCount == prevOverlayCount);
102+ prevOverlayCount = overlayCount;
103+#endif
104+
105 doubleBuffer.render (tmpRegion, fullscreen);
106
107 lastMask = mask;
108@@ -2070,7 +2079,9 @@
109 PrivateGLScreen::updateRenderMode ()
110 {
111 #ifndef USE_GLES
112- GL::fboEnabled = GL::fboSupported && optionGetFramebufferObject ();
113+ GL::fboEnabled = GL::fboSupported &&
114+ optionGetFramebufferObject () &&
115+ cScreen->overlayWindowCount () == 0;
116 GL::vboEnabled = GL::vboSupported && optionGetVertexBufferObject ();
117 #endif
118 }

Subscribers

People subscribed via source and target branches