From dc2ec0a8f95baa21aa460d49ed1ac36d398f62f4 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 8 Nov 2012 17:50:01 +0000 Subject: [PATCH] Keep similar theme nodes so we don't have to recompute CSS so often Because we calculate and cache CSS properties once per StThemeNode, and only a certain set of attributes can affect the CSS properties, it's advantageous for as many widgets as possible to share a single StThemeNode. Similarly, if a widget changes state and then changes back (e.g. gaining and losing the :hover pseudo-class), it should ideally get its original StThemeNode back again when it returns to the old state. Here, I'm using the StThemeContext as the location for a cache. StThemeNodes are currently never freed: this seems OK for Shell's usage (a finite number of IDs, classes, pseudo-classes and types). Bug: https://bugzilla.gnome.org/show_bug.cgi?id=687465 Reviewed-by: Jasper St. Pierre --- src/st/st-theme-context.c | 34 ++++++++++++++++++++++++++++++++++ src/st/st-theme-context.h | 3 +++ src/st/st-theme-node.c | 34 ++++++++++++++++++++++++++++++++++ src/st/st-theme-node.h | 1 + src/st/st-widget.c | 20 +++++++++++++------- 5 files changed, 85 insertions(+), 7 deletions(-) diff --git a/src/st/st-theme-context.c b/src/st/st-theme-context.c index 7b2a304f6..5891cf79d 100644 --- a/src/st/st-theme-context.c +++ b/src/st/st-theme-context.c @@ -31,6 +31,9 @@ struct _StThemeContext { PangoFontDescription *font; StThemeNode *root_node; StTheme *theme; + + /* set of StThemeNode */ + GHashTable *nodes; }; struct _StThemeContextClass { @@ -66,6 +69,8 @@ st_theme_context_finalize (GObject *object) (gpointer) st_theme_context_changed, context); + if (context->nodes) + g_hash_table_unref (context->nodes); if (context->root_node) g_object_unref (context->root_node); if (context->theme) @@ -105,6 +110,10 @@ st_theme_context_init (StThemeContext *context) "resolution-changed", G_CALLBACK (st_theme_context_changed), context); + + context->nodes = g_hash_table_new_full ((GHashFunc) st_theme_node_hash, + (GEqualFunc) st_theme_node_equal, + g_object_unref, NULL); } /** @@ -146,6 +155,7 @@ st_theme_context_changed (StThemeContext *context) { StThemeNode *old_root = context->root_node; context->root_node = NULL; + g_hash_table_remove_all (context->nodes); emit_changed (context); @@ -299,3 +309,27 @@ st_theme_context_get_root_node (StThemeContext *context) return context->root_node; } + +/** + * st_theme_context_intern_node: + * @context: a #StThemeContext + * @node: a #StThemeNode + * + * Return an existing node matching @node, or if that isn't possible, + * @node itself. + * + * Return value: (transfer none): a node with the same properties as @node + */ +StThemeNode * +st_theme_context_intern_node (StThemeContext *context, + StThemeNode *node) +{ + StThemeNode *mine = g_hash_table_lookup (context->nodes, node); + + /* this might be node or not - it doesn't actually matter */ + if (mine != NULL) + return mine; + + g_hash_table_add (context->nodes, g_object_ref (node)); + return node; +} diff --git a/src/st/st-theme-context.h b/src/st/st-theme-context.h index a0b73d9ff..9fa7afb70 100644 --- a/src/st/st-theme-context.h +++ b/src/st/st-theme-context.h @@ -62,6 +62,9 @@ const PangoFontDescription *st_theme_context_get_font (StThemeContext StThemeNode * st_theme_context_get_root_node (StThemeContext *context); +StThemeNode * st_theme_context_intern_node (StThemeContext *context, + StThemeNode *node); + G_END_DECLS #endif /* __ST_THEME_CONTEXT_H__ */ diff --git a/src/st/st-theme-node.c b/src/st/st-theme-node.c index fb7e9fb22..880029982 100644 --- a/src/st/st-theme-node.c +++ b/src/st/st-theme-node.c @@ -390,6 +390,40 @@ st_theme_node_equal (StThemeNode *node_a, StThemeNode *node_b) return TRUE; } +guint +st_theme_node_hash (StThemeNode *node) +{ + guint hash = GPOINTER_TO_UINT (node->parent_node); + + hash = hash * 33 + GPOINTER_TO_UINT (node->context); + hash = hash * 33 + GPOINTER_TO_UINT (node->theme); + hash = hash * 33 + ((guint) node->element_type); + + if (node->element_id != NULL) + hash = hash * 33 + g_str_hash (node->element_id); + + if (node->inline_style != NULL) + hash = hash * 33 + g_str_hash (node->inline_style); + + if (node->element_classes != NULL) + { + gchar **it; + + for (it = node->element_classes; *it != NULL; it++) + hash = hash * 33 + g_str_hash (*it) + 1; + } + + if (node->pseudo_classes != NULL) + { + gchar **it; + + for (it = node->pseudo_classes; *it != NULL; it++) + hash = hash * 33 + g_str_hash (*it) + 1; + } + + return hash; +} + static void ensure_properties (StThemeNode *node) { diff --git a/src/st/st-theme-node.h b/src/st/st-theme-node.h index 085fa6aa4..120cef898 100644 --- a/src/st/st-theme-node.h +++ b/src/st/st-theme-node.h @@ -110,6 +110,7 @@ StThemeNode *st_theme_node_get_parent (StThemeNode *node); StTheme *st_theme_node_get_theme (StThemeNode *node); gboolean st_theme_node_equal (StThemeNode *node_a, StThemeNode *node_b); +guint st_theme_node_hash (StThemeNode *node); GType st_theme_node_get_element_type (StThemeNode *node); const char *st_theme_node_get_element_id (StThemeNode *node); diff --git a/src/st/st-widget.c b/src/st/st-widget.c index 25b9ad87a..2a9b8da26 100644 --- a/src/st/st-widget.c +++ b/src/st/st-widget.c @@ -589,6 +589,8 @@ st_widget_get_theme_node (StWidget *widget) if (priv->theme_node == NULL) { + StThemeContext *context; + StThemeNode *tmp_node; StThemeNode *parent_node = NULL; ClutterStage *stage = NULL; ClutterActor *parent; @@ -629,16 +631,20 @@ st_widget_get_theme_node (StWidget *widget) else pseudo_class = direction_pseudo_class; - priv->theme_node = st_theme_node_new (st_theme_context_get_for_stage (stage), - parent_node, priv->theme, - G_OBJECT_TYPE (widget), - clutter_actor_get_name (CLUTTER_ACTOR (widget)), - priv->style_class, - pseudo_class, - priv->inline_style); + context = st_theme_context_get_for_stage (stage); + tmp_node = st_theme_node_new (context, parent_node, priv->theme, + G_OBJECT_TYPE (widget), + clutter_actor_get_name (CLUTTER_ACTOR (widget)), + priv->style_class, + pseudo_class, + priv->inline_style); if (pseudo_class != direction_pseudo_class) g_free (pseudo_class); + + priv->theme_node = g_object_ref (st_theme_context_intern_node (context, + tmp_node)); + g_object_unref (tmp_node); } return priv->theme_node;