codethink sent me to the gtk+ hackfest this week, hosted in the igalia offices. some really nice things are happening there so far.
first of all, i landed GApplication on glib master (and in gtk too). sorry for any breakage that may have caused. actions support should be coming soon…
second, after some discussion with emmanuele, i sent out to write a paint clock that could be shared by both gtk and clutter. i stayed up until 4am last night dumping my brain into vim and the result of that work is in glib (gio) in the form of GPeriodic. there is also a branch in gtk (wip/paint-clock) that uses GPeriodic to schedule repainting of windows and (for example and proof of concept) to animate GtkSpinner. presently cody is working on a somewhat less-trivial case: making GtkExpander animate smoothly. emmanuele is currently attempting to rebase clutter’s master clock on top of it, so the end result is that clutter and gtk should be on the same clock by the end of the week.
there is not currently any support for merging of input events. that’s mostly due to the fact that it’s a really hard problem and we probably won’t get a chance to deal with it at all before 3.0.
the other thing that we’re working on this week is reducing the GdkWindow abuse that occurs in gtk. samsung sent a few hackers to the hackfest, and one of them (boram) is trying to replace the 2+n input/output GdkWindows in GtkEntry with input-only windows. we believe that this is a reasonable intermediate step to improve use of the new gtk_widget_draw() api without having to think about the input issues (again, because they are quite hard).
i’m personally about to set out on a GtkViewport reimplementation that, first, behaves very naively by always redrawing everything and later caches the content of the scrolled area in an offscreen window (including some of the non-visible areas as a sort of pre-fetching).
there is some talk that gtk4 may be coming in the nearer future than any of us anticipated……
I’m sure that it was based on useful discussion going on at the hackfest, but the fact that GPeriodic just ended up in the master glib trunk without any design proposal on gtk-devel-list, or any form of deliberation appals me. (Yes, I’ll try to look at it this week….)
In terms of GtkEntry, it was left with a window for a very specific reason, which was that XIM input methods, which was what we were using back then, sometimes created windows that were children of the input area’s X window. XIM is no longer something we care about, so the windows can easily be removed.
Someone should definitely fix GtkMenuBar as well, since the window there was left for another very specific reason – it got forgotten about.
GtkViewport – I don’t think caching is a good idea. Current GtkViewport doesn’t cache, it performs fine, and, unlike caching doesn’t require fancy heuristics to keep it from falling over horribly when someone is trying to scroll a non-trivial sized area. What you *do* need to do if you are going to a clipped-redraw model is implement scrolling with gdk_window_scroll_area().
Re the caching in the scrolling, let me give you a small warning. During the csw work i once made the scrolling code do:
1) allocate a new pixmap for the whole target size
2) copy the source of the scroll to the dest area, in the new pixmap
3) draw the newly exposed region in the new pixmap
4) draw the new pixmap over the window
The idea would be that this will be 100% tear free scrolling. The result was 100% tear-free slow-as-hell scrolling, resulting in an immediate change where we now do the scrolling on the window and then paint the pixmap over the new region (but we delay the scrolling until the new region has been rendered to the pixmap). Result: fast, mostly tear-free scrolling.
The problem with pixmap and caches like this is that there is always a risk that the pixmap ends up in system ram (especially for short lived pixmaps i believe), which causes extremely slow performance like this.
So, whatever you do, when emulating scrolling without windows, the end result should always be gdk_window_move_region() when scrolling widgets that are axis-aligned.
hi Alex,
were you allocating a new pixmap each time? i’m planning to keep the pixmap around.
Yes, this was just using the begin/end_paint pixmap (making it larger when needed for scrolling). Just making a pixmap long-lived is no guarantee that it will be in gfx memory though, it depends on how you use it when drawing to it and how the driver is implemented.
Anyway, I don’t think using a permanent offscreen pixmap is the right thing here. In a “modern” desktop with a compositing manager every (toplevel) window will already have an offscreen pixmap, using another one seems both a waste of memory and a potential to make things slower.
Alex, Owen: The problem we’ve been discussing here is getting rid of GdkWindow completely. So no widget has a GdkWindow. Also, we want widgets to be completely driven by their parents. So a connection to something else is “wrong” for that design.
The biggest obstacle to getting this right for the output is of course making sure that scrolling works well enough (as windows are only used for scrolling pretty much). And we’re debating how to do that.
But we definitely will not be able to use gdk_window_scroll() or anything for that.
Also, do we have any benchmarks that show the slowness of just ignoring all these window move optimizations? Because so far I haven’t seen any – just people claiming “don’t do that”.
Alex: we also want to implement functionality that is complete unavailable using anything that would turn into a XCopyArea on the ‘visible’ window — for example, scrolling semi-transparent content over a non-solid background. In that case it’s fairly clear that we need the scrolled content to be in an off-screen window with an alpha channel.
We also have some interest in caching content that is not on screen.
1) Absolutely instant scrolling to nearby areas (because they were already painted before we needed them).
2) Far less calling of draw() on the contained widget since we can update the cache in ‘chunks’ instead of one row at a time.
2) (as a special case of both of the above) workable “scroll past the end” functionality where you have animations that have the scrolled content go past its end and ‘snap back’ — without having to repaint the part that temporarily fell off the screen when we went too far.
Benjamin:
There will always be a GdkWindow somewhere, for the toplevel window if nowhere else. Of course, access to it may very well be gotten via a call to the parent GtkWidget which eventually reaches the point where the gdk_window_move_region() call is made, so it might not be accessed “directly”. You just have to design your stuff right so that the common case is efficient.
Also, I obviously did not say gdk_window_scroll, but gdk_window_move_region.
As for the benchmarking, I obviously did not benchmark anything that doesn’t exist yet, but simple logic dictates that doing a lot more work is always slower. For scrolling, a hw assist bitblit is the fastest thing possible, and anything beyound that is a performance regression. Now, how bad is it? The only way to know is to try.
desrt:
Obviously various complications can arise in more complex cases. I’m talking about what should happen in the bog-standard case, as this case is very important. The design should be such that everything is doable with a nice API, but the right thing automatically falls out at the bottom in the “normal” case.
Keeping a cache of out-of-sight image data doesn’t mean you have to also keep a cache of visible data.
Also, remember that scrolling is not only about performance, but also correctness. gdk_window_move_region tracks scrolling for the translation queue such that async expose events are properly translated as per the scrolling that has happened in the client since the expose event was generated in the xserver.
Modern hardware does not have a bit blitter. And OpenGL does not allow self-copies at all. So all the advantages we might get by using XCopyArea will be undone by the X server or the drivers later on. In fact, from looking at all the cairo backends, XCopyArea seems to be the only operation in existence that supports source == dest operations.
Fwiw, keeping track of a pixmap is obviously correct as it’s managed completely client-side, so no synchronization issues ever arise.
> gdk_window_move_region tracks scrolling for the translation queue such
> that async expose events are properly translated as per the scrolling
> that has happened in the client since the expose event was generated in
> the xserver.
This is what happens on X, on win32 it does something else (modifies the per-window os-global dirty region) that is equivalent and also needed for correct handling of scrolling.
Also, i hope that we’ll also have a rendering fallback that only uses clipping, no offscreen pixmap for the case of gtk_widget_draw() for non-display purposes (i.e. for printing).
In practice I expect the X drivers to extend the opengl operation set with something like a xcopyarea, just because scrolling (be it in-window or on a pixmap) is such a common, important operation. About the hardware not having a blitter. Yes, there might be no dedicated hw part for blitting, but thats just because the rest of the hw is generic enough to allow blits.
I can’t think of any other operation where src == dest is useful, so I’m not surprised there are none.
> Fwiw, keeping track of a pixmap is obviously correct as it’s managed completely client-side,
> so no synchronization issues ever arise.
Yeah, as long as you never read from the window or do window-to-window copies i guess you’ll never get into the problematic situation.
Actually, isn’t the easiest way to support this to just add a (virtual) method to GtkWidget which is much like gdk_window_move_region, like:
gtk_widget_move_region(widget, region, dx, dy)
The default implementation would be like
if (is_window(widget)) {
gdk_window_move_region(widget->window, region, dx, dy);
} else {
region_offset(region, widget->x, widget->y); // Maybe clip too?
gtk_widget_move_region(widget->parent, region, dx, dy);
}
Then each widget can override this for custom stuff. I.e. the viewport
using offscreen pixmaps can implement scrolling by copying an area on the
pixmap.
A transformed (non-axis-aligned) widget can implement the whole function just as
gtk_widget_queue_draw_area(widget, bounding_box(transformed region)
Hmmm.. While its true that there is no real synchronization issues there is still some complexities. The way we track damage right now is as regions on GdkWindow, and the gdk_window scrolling (_scroll and _move_region) operations also update this. For instance, if you first invalidate a button and then scroll the area the button is in the dirty region is moved in the window. If this doesn’t happen then things won’t repaint correctly.
So, unless gdk_window_move_region is used we need to move damage handling to the widget side…