From a2fcbb7e652fd2be95beacf180c01fe128e55cee Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Thu, 4 Apr 2013 23:06:29 -0400 Subject: [PATCH] st-widget: Cache two paint states In most cases, we'll transition between two states on hover / focus. Instead of recalculating and repainting our resources on state change, simply cache the last state when we transition. https://bugzilla.gnome.org/show_bug.cgi?id=697274 --- src/st/st-theme-node-drawing.c | 8 +++++- src/st/st-theme-node.c | 10 ++++---- src/st/st-theme-node.h | 2 ++ src/st/st-widget.c | 45 +++++++++++++++++++++++++++------- 4 files changed, 50 insertions(+), 15 deletions(-) diff --git a/src/st/st-theme-node-drawing.c b/src/st/st-theme-node-drawing.c index b2f88c2fe..0f942f1f8 100644 --- a/src/st/st-theme-node-drawing.c +++ b/src/st/st-theme-node-drawing.c @@ -1965,7 +1965,10 @@ st_theme_node_paint (StThemeNode *node, return; if (state->alloc_width != width || state->alloc_height != height) - st_theme_node_render_resources (node, state, width, height); + { + state->node = node; + st_theme_node_render_resources (node, state, width, height); + } /* Rough notes about the relationship of borders and backgrounds in CSS3; * see http://www.w3.org/TR/css3-background/ for more accurate details. @@ -2097,6 +2100,7 @@ st_theme_node_paint_state_init (StThemeNodePaintState *state) { int corner_id; + state->node = NULL; state->box_shadow_material = COGL_INVALID_HANDLE; state->prerendered_texture = COGL_INVALID_HANDLE; state->prerendered_material = COGL_INVALID_HANDLE; @@ -2116,6 +2120,8 @@ st_theme_node_paint_state_copy (StThemeNodePaintState *state, st_theme_node_paint_state_free (state); + state->node = other->node; + state->alloc_width = other->alloc_width; state->alloc_height = other->alloc_height; diff --git a/src/st/st-theme-node.c b/src/st/st-theme-node.c index 62980b1df..a9a1fb58c 100644 --- a/src/st/st-theme-node.c +++ b/src/st/st-theme-node.c @@ -3801,8 +3801,8 @@ st_theme_node_geometry_equal (StThemeNode *node, /** * st_theme_node_paint_equal: - * @node: a #StThemeNode - * @other: a different #StThemeNode + * @node: (allow-none): a #StThemeNode + * @other: (allow-none): a different #StThemeNode * * Check if st_theme_node_paint() will paint identically for @node as it does * for @other. Note that in some cases this function may return %TRUE even @@ -3819,13 +3819,13 @@ st_theme_node_paint_equal (StThemeNode *node, StShadow *shadow, *other_shadow; int i; - g_return_val_if_fail (ST_IS_THEME_NODE (node), FALSE); + /* Make sure NULL != NULL */ + if (node == NULL || other == NULL) + return TRUE; if (node == other) return TRUE; - g_return_val_if_fail (ST_IS_THEME_NODE (other), FALSE); - _st_theme_node_ensure_background (node); _st_theme_node_ensure_background (other); diff --git a/src/st/st-theme-node.h b/src/st/st-theme-node.h index 8f5da57e5..fc6caa130 100644 --- a/src/st/st-theme-node.h +++ b/src/st/st-theme-node.h @@ -97,6 +97,8 @@ typedef enum { typedef struct _StThemeNodePaintState StThemeNodePaintState; struct _StThemeNodePaintState { + StThemeNode *node; + float alloc_width; float alloc_height; diff --git a/src/st/st-widget.c b/src/st/st-widget.c index 01c7b4c63..c07dd7b4f 100644 --- a/src/st/st-widget.c +++ b/src/st/st-widget.c @@ -80,7 +80,8 @@ struct _StWidgetPrivate StWidget *prev_last_child; StWidget *prev_first_child; - StThemeNodePaintState paint_state; + StThemeNodePaintState paint_states[2]; + int current_paint_state : 2; }; /** @@ -271,6 +272,18 @@ st_widget_remove_transition (StWidget *widget) } } +static void +next_paint_state (StWidget *widget) +{ + widget->priv->current_paint_state = (widget->priv->current_paint_state + 1) % G_N_ELEMENTS (widget->priv->paint_states); +} + +static StThemeNodePaintState * +current_paint_state (StWidget *widget) +{ + return &widget->priv->paint_states[widget->priv->current_paint_state]; +} + static void st_widget_texture_cache_changed (StTextureCache *cache, const char *uri, @@ -304,8 +317,11 @@ st_widget_texture_cache_changed (StTextureCache *cache, * the paint state. We should probably implement a method to * the theme node to determine this, but for now, just wipe * the entire paint state. + * + * Use the existing state instead of a new one because it's + * assumed the rest of the state will stay the same. */ - st_theme_node_paint_state_invalidate (&actor->priv->paint_state); + st_theme_node_paint_state_invalidate (current_paint_state (actor)); if (CLUTTER_ACTOR_IS_MAPPED (CLUTTER_ACTOR (actor))) clutter_actor_queue_redraw (CLUTTER_ACTOR (actor)); @@ -360,6 +376,7 @@ static void st_widget_finalize (GObject *gobject) { StWidgetPrivate *priv = ST_WIDGET (gobject)->priv; + int i; g_free (priv->style_class); g_free (priv->pseudo_class); @@ -367,7 +384,8 @@ st_widget_finalize (GObject *gobject) g_free (priv->accessible_name); g_free (priv->inline_style); - st_theme_node_paint_state_free (&priv->paint_state); + for (i = 0; i < G_N_ELEMENTS (priv->paint_states); i++) + st_theme_node_paint_state_init (&priv->paint_states[i]); G_OBJECT_CLASS (st_widget_parent_class)->finalize (gobject); } @@ -456,7 +474,7 @@ st_widget_paint_background (StWidget *widget) opacity); else st_theme_node_paint (theme_node, - &widget->priv->paint_state, + current_paint_state (widget), &allocation, opacity); } @@ -1520,6 +1538,7 @@ static void st_widget_init (StWidget *actor) { StWidgetPrivate *priv; + int i; actor->priv = priv = ST_WIDGET_GET_PRIVATE (actor); priv->is_stylable = TRUE; @@ -1535,14 +1554,17 @@ st_widget_init (StWidget *actor) g_signal_connect (st_texture_cache_get_default (), "texture-file-changed", G_CALLBACK (st_widget_texture_cache_changed), actor); - st_theme_node_paint_state_init (&priv->paint_state); + for (i = 0; i < G_N_ELEMENTS (priv->paint_states); i++) + st_theme_node_paint_state_init (&priv->paint_states[i]); } static void on_transition_completed (StThemeNodeTransition *transition, StWidget *widget) { - st_theme_node_paint_state_copy (&widget->priv->paint_state, + next_paint_state (widget); + + st_theme_node_paint_state_copy (current_paint_state (widget), st_theme_node_transition_get_new_paint_state (transition)); st_widget_remove_transition (widget); @@ -1569,7 +1591,7 @@ st_widget_recompute_style (StWidget *widget, transition_duration = st_theme_node_get_transition_duration (new_theme_node); - paint_equal = old_theme_node && st_theme_node_paint_equal (old_theme_node, new_theme_node); + paint_equal = st_theme_node_paint_equal (old_theme_node, new_theme_node); g_object_get (gtk_settings_get_default (), "gtk-enable-animations", &animations_enabled, @@ -1593,7 +1615,7 @@ st_widget_recompute_style (StWidget *widget, widget->priv->transition_animation = st_theme_node_transition_new (old_theme_node, new_theme_node, - &widget->priv->paint_state, + current_paint_state (widget), transition_duration); g_signal_connect (widget->priv->transition_animation, "completed", @@ -1610,7 +1632,12 @@ st_widget_recompute_style (StWidget *widget, } if (!paint_equal) - st_theme_node_paint_state_invalidate (&widget->priv->paint_state); + { + next_paint_state (widget); + + if (!st_theme_node_paint_equal (new_theme_node, current_paint_state (widget)->node)) + st_theme_node_paint_state_invalidate (current_paint_state (widget)); + } g_signal_emit (widget, signals[STYLE_CHANGED], 0); widget->priv->is_style_dirty = FALSE;