So, there's a branch innocently named wip/cssvalue in git master that implements what I teasered in http://blogs.gnome.org/otte/2012/04/02/gtk-3-6/ and IMO is ready for master (after 3.4 branched off of course).
What does this branch do? Here's the TL;DR version: - Speed up CSS by a factor of 10 or so - Improve correctness of our CSS handling - Implement proper support for sibling selectors - Reduce memory taken by the CSS machinery by a factor of 2 - Implement CSS transitions (see http://dev.w3.org/csswg/css3-transitions/ ) - Set groundwork for CSS animations (see http://dev.w3.org/csswg/css3-animations/ ) And here's the long version for the people who want to review this branch (it's 160 commits, yay...) or just want to understand what I've done (links go to current code in the wip/cssvalue branch, not to the commits where things were introduced). A big problem with proper CSS handling is that a random change somewhere can potentially introduce updates to all siblings and their children of a widget. Say, if you click on a button and the button becomes active, a selector like ":active ~ * .something" will suddenly change things in a not-really-related element. In GTK 3.4, whenever such a thing happens, we do a gtk_widget_reset_styles(), which emits style_updated(), which blows all caches and calls gtk_widget_queue_resize(). Of course, almost nobody ever writes a selector like that in practice. So the first thing I did was classify potential changes to CSS using GtkCssChange, see http://git.gnome.org/browse/gtk+/tree/gtk/gtkcsstypesprivate.h?h=781d2c11bdd435f37ab8e364d59b3465b61a8d85#n26 then check which changes actually are relevant for a given widget using _gtk_style_provider_get_change() and ignore changes that are not relevant: http://git.gnome.org/browse/gtk+/tree/gtk/gtkstylecontext.c?h=781d2c11bdd435f37ab8e364d59b3465b61a8d85#n3047 This provided quite a good speedup. Then I realized that with all those potential changes going on, a big problem is that there can be lots of changes at once. If you add 30 widgets into a GtkBox, every other widget in the box will get its style invalidated 30 times (because it got a new sibling and its position changed). And if we actually can't optimize this change away, we get to update the style of every widget in the box 30 times. This is rather slow. (Glade does this to update its properties view, you can notice how slow it is when you select a different widget). So the next thing I did was add _gtk_style_context_queue_invalidate(). What that means is that instead of instantly updating the style whenever a change happens, GTK now waits until it is actually going to repaint the widget and only then updates the style. Until then, it just keeps the old values. This was kind of tricky to get right, because of course we have lots of code that causes style invalidations from the style_updated signal, which would then cause a new style_updated signal... But I hope I got it working well enough. The interesting things here are http://git.gnome.org/browse/gtk+/tree/gtk/gtkstylecontext.c?h=781d2c11bdd435f37ab8e364d59b3465b61a8d85#n3129 or rather the callers of this function, and http://git.gnome.org/browse/gtk+/tree/gtk/gtkstylecontext.c?h=781d2c11bdd435f37ab8e364d59b3465b61a8d85#n3026 which is _gtk_style_context_validate(), the function that does the actual invalidation. It does a lot more, but I'll get to that later. All of this is called from the core update loop in GTK, see http://git.gnome.org/browse/gtk+/tree/gtk/gtkcontainer.c?h=781d2c11bdd435f37ab8e364d59b3465b61a8d85#n1624 which essentially makes updating the screen in GTK a 3-step process (the first step being new): (1) update styles (2) recalculate sizes (3) draw Each of those steps can trigger updates in the later steps. (As mentioned above, also the other way, but you'll only see this in the next update - your fault! :)) By now we do a lot less invalidations, but there's still one thing going on. Because we still have a lot of cases where we can't manage to optimize things away yet, but still nothing changes. After we've queried all the new values from CSS, they are all the same. In this case, we could still avoid blowing all the widget's caches if we actually knew that all the values were still the same. This is were Alex' work on GtkCssValue comes in. GtkCssValue so far was essentially a refcounted GValue. He added this to avoid lots of GValue copies that happened when you look up properties from CSS and then copy them into all the style contexts. We saved megabytes of memory on rather simple applications like gtk-demo or Nautilus with that. http://git.gnome.org/browse/gtk+/commit/gtk/gtkcssvalue.c?id=0ece7a5de3eae5f4d7e4d1623d191a0a0628e652 is the commit that originally introduced it. It's part of GTK 3.4 already. Now, if I made GtkCssValue not just a GValue, but more like an object by adding a vtable, I could move a lot of functionality there, like the equality check I mentioned. And save even more memory by tailoring those values directly to the values that matter. This took a lot of refactoring, but in the end I got this: http://git.gnome.org/browse/gtk+/tree/gtk/gtkcssvalueprivate.h?h=781d2c11bdd435f37ab8e364d59b3465b61a8d85#n36 which is a tiny API that is implemented by the actual different values. Some examples: http://git.gnome.org/browse/gtk+/tree/gtk/gtkcssnumbervalueprivate.h?h=781d2c11bdd435f37ab8e364d59b3465b61a8d85 http://git.gnome.org/browse/gtk+/tree/gtk/gtkcssrgbavalueprivate.h?h=781d2c11bdd435f37ab8e364d59b3465b61a8d85 http://git.gnome.org/browse/gtk+/tree/gtk/gtkcssshadowvalueprivate.h?h=781d2c11bdd435f37ab8e364d59b3465b61a8d85 or their implementations (which are rather short, too): http://git.gnome.org/browse/gtk+/tree/gtk/gtkcssnumbervalue.c?h=781d2c11bdd435f37ab8e364d59b3465b61a8d85 http://git.gnome.org/browse/gtk+/tree/gtk/gtkcssrgbavalue.c?h=781d2c11bdd435f37ab8e364d59b3465b61a8d85 http://git.gnome.org/browse/gtk+/tree/gtk/gtkcssshadowvalue.c?h=781d2c11bdd435f37ab8e364d59b3465b61a8d85 In essence, every CSS "type" got its own GtkCssValue class. An important interlude right here, should you ever need to debug CSS values: GtkCssValue is an opaque type that resolves to something different depending on what source file you are in (I know, that sucks). But, should you ever need to inspect a GtkCssValue, _gtk_css_value_to_string() will give you a CSS string representation instantly, and you can go from there. Another interesting value type is the array type: http://git.gnome.org/browse/gtk+/tree/gtk/gtkcssarrayvalueprivate.h?h=781d2c11bdd435f37ab8e364d59b3465b61a8d85 http://git.gnome.org/browse/gtk+/tree/gtk/gtkcssarrayvalue.c?h=781d2c11bdd435f37ab8e364d59b3465b61a8d85 It's used whenever a CSS property is a list of values. you then need to query the actual subvalues from that list. font-family or the transition properties use that already. The background properties aren't converted yet. Now if you actually want to use CSS values inside GTK, the way to do it is to call _gtk_style_context_peek_property() (or _gtk_theming_engine_peek_property(). Not only do these functions give you the real value from the CSS, they are also a lot faster than gtk_style_context_get() / gtk_theming_engine_get(). Unfortunately they usually result in long incantations of function calls, like _gtk_css_number_value_get ( _gtk_css_array_value_get_nth ( _gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_TRANSITION_DURATION), i), 100); but that's what the power of CSS gets you. And I hope those function calls describe well enough what's actually happening right there. Anyway, once that conversion was complete, I had a _gtk_css_value_equal() function and could implement the actual check for equality of CSS changes. This is what the "changes" bitmask variable in _gtk_style_context_validate() tracks. And as you can see in the code, we don't call style_updated (via do_invalidate()) if no value actually changed. After this work, I spent a bit of time running my favorite Glade benchmark. The results of which can be seen in the commit message of this commit: http://git.gnome.org/browse/gtk+/commit/?id=d9e574da9eccb7b1decddde2c01516c84085089c In short, the total runtime of that test went down by a factor of 4 to 5. Not only that, but I threw sysprof at it and the remaining time was mostly spent actually drawing or inside Glade. Only 10% of time was still spent updating CSS values. So style updates are no longer the bottleneck. So now that I had per-value classes and really fast CSS updates that even tracked what value was updated, it was time to make use of them. And this is were CSS animations come in. So I spent some more work adding them. I had to remove the old animations framework because the two were conflicting in interesting ways (including both claiming the 'transition' CSS property name). After I had done so, it turned out there were a bunch of restrictions that applied and I had to fix them, which makes this not quite as awesome as it could have been. Read about it here: http://git.gnome.org/browse/gtk+/tree/gtk/gtkstylecontext.c?h=781d2c11bdd435f37ab8e364d59b3465b61a8d85#n1653 In short: Once your drawing code calls gtk_style_context_save(), nothing will get animated. And we do that 100s of times even in GTK. Go us. We need to make the whole rendering process more stateful to make full use of animations. For now, the "simple" widgets work fine though. And that is buttons, menuitems, toolbars and labels. Which should be quite enough to make things exciting for a while. So that's all for now. If there's questions, you know how to ask. :) Benjamin _______________________________________________ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list