How to remove flicker from Gtk+

In between spending time taking care of a sick kid, a sick wife and being sick myself I have slowly been working on the remaining issues in the client-side-windows branch of Gtk+. The initial and main interest in having client side windows is that it lets us emulate all thats needed for widgets to work without any server side windows, which lets us do things like put Gtk+ widgets inside clutter, etc. However another interesting, and not entierly obvious advantage of client side windows is that it allows us to remove flicker. This post will describe how this works and show the effects.

Gtk+ already does quite a lot of things to avoid flicker. For instance, all drawing in expose events is automatically double buffered so that you never see partially drawn results. The remaining flickering is related to the effect of moving or resizing server side subwindows. Although even these are minimized by Gtk+, since many widgets don’t use such windows or only use input-only windows which don’t cause any visual effects. However, there are still some areas where subwindows are used, mostly in cases where scrolling is involved.

Lets start with an example on how scrolling works:

This is a regular Evince window showing a pdf, and we want to scroll down. This happens in several steps. First we copy the bottom area of the window to the top of the window:

Then we mark the newly scrolled in area at the bottom as invalid:

As a result of this Gtk+ will call the application to redraw the invalid region as soon as it has finished handling the incomming events:

Voila! We have scrolled. (In reality more happened above, the scrollbar area was marked invalid and repainted also, but lets ignore that for now.)

This example also makes it easy to see where flicker comes from. The drawing of the newly exposed area is double buffered, so the newly drawn area is replaced atomically, however the initial copy is not done with the Gtk+ drawing system, instead its done with a XCopyArea directly on the window (not a subwindow move, but with similar effect). So, the xserver will display that immediately, while there might be some delay before the expose of the scrolled in area is drawn causing visual tearing.

Another common problem is widget resizing/move that can be seen in my previous blog entry. In this case what happens is that a widget with a subwindow is moved and/or resized and it ends up over another widget. The window move operation is done immediately in the server and results in a copy similar to the above, and then there is some delay before the widgets are redrawn to match that.

Now, client side windows don’t by itself fix this, but the copies above and all rendering is now under control of the client (i.e. the app) so we have the tools to do something about it. The solution is to delay the copying until we’re ready to draw everything that will be drawn, so we never show any partial results. Whenever some region of a window is copied we just record the area to be copied and by how much. When we’re handling the expose events for the invalid area we handle the expose up to the point of drawing everything in the double buffer. At this point we replay all the copies we recorded, except we don’t care about copying anything that will draw into the area which will be drawn by the expose. Then we blit out the final result of the expose event.

Furthermore, in practice it often happens that we do several moves/scrolls of the same region before it its drawn. This works with the above approach, but is a bit wasteful as some regions are copied twice. So, instead of just simply keeping track of all copies being made we try to combine such double copies into a single copy, thus minimizing the actual copies we make in the end.

So, how does this look in the end? Its kind of hard to capture this kind of flicker with a screengrabber, so here is a video I took with my phone:

The copy is done with XCopyArea at the end, so it is done on the server.

There really is no way to do graphics operations without involving the server. However, the difference here is that all graphical operations are directly under our control, nothing happen indirectly due to changes in subwindows, etc.

It may be a stupid question, but does it somehow improve initial flicker during creation of some complex dialogs like GtkFileChooserDialog or there is only one way of improving this situation, that is, hiding and showing the dialog?

Oleg:
Its kinda hard to tell exactly whats causing that flicker, but it seems better with the csw branch. Part of the flickering is the dialog is shown before all child widgets are shown (the lists and such), so there is some unnecessary high-level flashing when these are shown that should be fixed first.