New drawing/scrolling model

I've just pushed the wip/simple-draw4 branch which is the latest
version of my work trying to simplify and modernize the Gtk drawing
machinery. Its now in a state where I think its time to discuss the
merging of this.

The very first commit in the branch makes gdk_window_move() and
gdk_window_scroll() very dumb. Previously it tried to copy as much as
possible of the existing pixels and only invalidate the areas that
were previously not visible. Now it just invalidates the entire
scrolled area and redraws it.

This may seem a bit crazy and slow, but in fact its just the first
step in making the gdk drawing model more modern. Doing scrolling by
same-surface copying is an old technology has some problems in the
modern world. First of all, on modern hardware a same-surface copy
doesn't work very well (for technical reasons). In fact, the operation
is a no-op in the wayland backend atm. Secondly, in a more "modern"
scene graph like in recent gtk3 most windows have alpha pixels and
render over the window background rather than each window rendering
its own opaque background, so scrolling via copy doesn't work.

So, the branch continues with deleting lots of very tricky code inside
gdk where does things like figuring out the cases where we can apply
the copy optimization, and handling async exposes from the xserver
racing with copy-area of the same region from the app.

A more modern way to do scrolling is to keep an offscreen buffer for
the content inside the scrolling region, covering what is currently
visible in the viewport plus a bit more. Then when we draw the window
we just draw the buffer in the right place, making scrolling very fast.
Although if you scroll to far we have to render a new piece of the
offscreen buffer.

To do this kind of scrolling a container needs two things, a way to get
told when a child needs redrawing so that we can make our cache dirty,
and a way to retarget rendering the children to the offscreen buffer.

We add some gdk API (gdk_window_set_invalidate_handler) for the first,
but the second is a bit more complex. Right now rendering is done in
many separate phases, one per GdkWindow where for each GdkWindow there
may be several non-window widgets (and each widget may have several
windows too). The branch completely redoes this so that Gtk+ only ever
handles expose events on native windows (i.e. generally toplevels
only) and then renders everything inside the ::draw signal handler of
the toplevel widget.

In order to maximize backwards compatibility we still call draw()
multiple times on multi-window widgets, with the right clipping set up
and the right window backgrounds rendered, however, there are still
some minor differences. For instance, we expose primarily in widget
order whereas we previously exposed in window order (although for
windows in the same widget we respect the window order), but this
is rarely a problem because intra-widget window order in gdk is hard
to control anyway due to realize being called in unexpected places.

With this in place the branch then does some further simplifications
and then adds fast scrolling support to GtkViewport and GtkTreeView
via the new GtkPixelCache helper object.

I've tried a bunch of apps and most things seem to work. Currently I
know of two problems:

The control-center background panel calls gdk_cairo_create() inside
the draw() handler, which draws directly to the window rather than the
passed in cairo_t. This "works" in a sense, but the rendering is then
unaffected by the cairo_push()/pop() that the stack uses for
crossfading, so the crossfade effect breaks. This could possibly
happen in other cases, but it seems like it is pretty uncommon (I
didn't find any other problematic use in all of Gnome).

The control-center display panel does this weird thing where it
"draws" areas using cairo, and then reads it back to use as input
regions. This code assumes that the coordinates on the cairo_t when
converted via cairo_user_to_device() is in the coordinate space of the
widgets windows, whereas it is now in the coordinate space of the
toplevel. This makes the input regions offset by the position of
the widget making it hard to click/drag them. This is a pretty
weird thing to do, and i don't expect we'll run into other apps
doing the same thing.

IMHO we can just land the branch and fix these issues inside the
control-center. Its a minor incompatibility change, but I think its
still worth it.

There is still some further work we may want to work on after the
branch lands, in case someone wants to help out:

* Port GtkIconView to GtkPixelCache
* Port GtkTextView to GtkPixelCache
* Have some way to hint a GtkPixelCache so that we e.g. always
  render an entire treeview row or textview paragraph when
  painting, to avoid multiple setups for drawing it when scrolling.
* Try a tile-based approach for GtkPixelCache to avoid having
  to do a same-surface copy (usign an intermediate surface) when
  scrolling the cache.

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