Re: Freezing updates for compositing managers



Hi Owen + Denis,
	Me and some of the people at Collabora have been looking at the
feasibility of implementing some kind of freeze of updates with Gtk too,
in order to try and reduce tearing artefacts that we are getting in
the compositor.
	I guess in order to eradicate artefacts, you really need two-way
communication between Gtk and the window manager as you say - as even if the compositor draws after a complete frame, there is no guarantee that the window hasn't started rendering the next frame and damaged the contents
of the window.
	Even if there is a 'WAIT_FOR_COMPOSITOR' signalling mechanism,
it could prove problematic to implement in Gtk when functions like gdk_window_scroll are expected not to block and yet to scroll the window
contents immediately (however perhaps this could be postponed?).
	If we don't wait in Gtk, most likely drawing operations on the
window will be locked while the window is composited (and the compositor
will have to wait for the current drawing operation on the window to finish). At least this seems to be the case for us. With multiple windows this can end up stalling the window manager quite seriously,
as it waits for each window in turn.
	One possible solution which would allow good performance
without blocking would be to create extra pixmaps to perform triple buffering using the windowless Gtk branch.

It is probably too much work, but it might be worth considering, given
the advantages?

It could work something like this:

* The window manager adds two window properties, PIXMAP_A and PIXMAP_B,
 containing the IDs of two pixmaps.
* Gtk detects this and writes a property, PIXMAP_WRITING (which is either
 A,B or W(for the window). This tells the window manager where the window
 is currently writing to.
* After it has finished a frame, the window sets PIXMAP_READY to whatever PIXMAP_WRITING was (to signal a frame is drawn to the compositor)
 and then chooses a new destination for PIXMAP_WRITING.
* The window manager detects the change of PIXMAP_READY, then sets PIXMAP_READING=PIXMAP_READY and composits using this pixmap. Damage
 events would need to be created specially when rendering to one
 of the two pixmaps.
* In the mean time, the next time the window finishes rendering a frame,
 it sets PIXMAP_READY=PIXMAP_WRITING, and then chooses whichever of A,B
 or W is not specified in PIXMAP_READY or PIXMAP_READING.

I believe this would work without any race conditions, however there are definitely quite a few issues here.

Bad:
* Memory usage - however there would be no need for the current double buffering in Gtk, so I suppose at least only one extra buffer is used,
 and we do save the overhead of allocating new pixmaps each frame.
* Because there are 3 windows to render to, Gtk's exposed area would have to
 be the sum of the previous 2 exposed areas (or the previous two exposed
areas would have to be copied from PIXMAP_READY). In a lot of cases (scrolling) this would not be any worse than doing the panning operation
 I guess, but lots of small updates in different areas could be slower.
* Overly ambitious? I have no idea of the internals of the windowless branch. It could be relatively easy, or it could be extremely painful.

Good:
* Performance would be a lot better due to the non-blocking I imagine.
* There would be no need for double buffering in Gtk and hence no blitting of
 the final image, and no allocation of damage-sized pixmaps for every frame
- which would be a decent performance boost.
* Hopefully no flicker whatsoever

Any thoughts? This is probably a fair chunk of work, but I don't see many
other ways of getting rid of tearing completely. Proper double buffering (flipping buffers, not Gtk's blitting) could be possible if memory usage
were a problem, but that might cause more problems with synchronisation?

- Gordon
	

-----------------------------

On Mon, 2009-06-29 at 10:10 +0200, Denis Dzyubenko wrote:
Hi Owen,

Owen Taylor wrote:
> Fixing this really requires application intervention - when the
> compositor receives the MapNotify for the window the window might
> have drawn yet, or might not, and there's no way that the
> compositor can know.
> > This is actually a symptom of a more general situation - an
> application wants to make multiple X requests (here a map and
> then the redraw) and not have the compositor draw until it is done.
> > Thinking of it that way - that gives a pretty straightforward
> solution - the app can set a property that says "don't update me" -
> say _NET_WM_FREEZE_UPDATES - and then remove it when it is ready
> to be painted again.

Sounds like an excellent idea, however it also sounds similar to something that the wm-spec already have - can we use the existing _NET_WM_SYNC_REQUEST protocol for that? Since compositing manager might already support it, it could be pretty straightforward to implement the same protocol for the case when a client window is initially mapped. For example, when the compositor receives the MapNotify, it might send the _NET_WM_SYNC_REQUEST client message and delay showing the window until the client responds by setting a _NET_WM_SYNC_REQUEST_COUNTER.

I was originally going to respond and say "it sounds related, but it
isn't related."

But then I had an idea. You can't really use _NET_WM_SYNC_REQUEST
unmodified - it's inherently initiated from the window manager and not
from the client. And this needs to be initiated from the client. (*)

But what if we turned _NET_WM_SYNC_REQUEST_COUNTER into a more
general frame counter. With the following handling:

- When the client starts drawing a frame, it increments the counter to
  an odd value to freeze updates.
- When the client finishes drawing, it increments the counter to an
  even value to thaw updates.
- When the window manager wants to do a sync, it picks an even value
  at least a 100 [arbitrary] values ahead of the last value it
  received from the client and sends _NET_WM_SYNC_REQUEST to the
  client with that value. The client then uses that value on the
  frame completion after processing the resize.

Advantages of this:

- It has the same race-condition-free-startup and no-round-trip on
  updates as my proposal.

- It integrates with the sync request system in an aesthetically
  pleasing fashion.

- It has a very useful extension:

   After redrawing a frame, the window manager sends a
_NET_WM_FRAME_REDRAWN client message to all clients updating the frame counter since the last redraw. The message contains
   the frame value (and maybe some timing information.)

  Voila: properly throttled application updates in a composited
  environment.

Disadvantages of this:

- Compatibility is tricky; if the window manager is expecting the
  old _NET_WM_SYNC_REQUEST_COUNTER behavior, spontaneous client
  updates will break the WM's use of _NET_WM_SYNC_REQUEST as far
  as I can tell.

  Having the client adapt its behavior is tricky because:

   - Switching between compositing managers and window managers
     in the fly.
   - The possibility of a standalone compositor that wants the
     frame counter with an old window manager that expects the
     old sync counter behavior.

  So, I don't really see how to avoid having the application expose
  two separate counters. [ Applications could, of course, choose to
  only support the new method ... we could deprecate the old way]

- The Sync extension is not fun to understand and use; it's much
harder to implement the above as compared to my _NET_WM_FREEZE_UPDATES. Toolkits and window managers have however,
  already figured out much of this to handle _NET_WM_SYNC_REQUEST.

- Owen

(*) Even if you accept the extra WM <=> client round trip, I don't
think it works out without toolkit modifications to have the window
manager to spontaneously ask for a sync - it's specified currently
to be tied to a ConfigureNotify. Plus, it doesn't extend to the
other uses cases like atomic frame updates.





[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]