Compare commits

...

7 Commits

Author SHA1 Message Date
Matthias Clasen
64ed9d0502 Add profiler support for css validation
Push numbers about css validation and style creation
to sysprof.
2020-01-20 20:08:41 -05:00
Matthias Clasen
f58ea809fa css: Redo selector tree matching
Prototype a different approach that splits selectors into 3
groups in each node: names, classes, other.
2020-01-20 20:08:30 -05:00
Matthias Clasen
beae47edb1 css: Add a root to the selector tree
Add a virtual root to the selector tree, and give
it an ANY selector in order to not affect matches.

This will make some of the following changes easier.
2020-01-20 20:07:59 -05:00
Matthias Clasen
8782edbaf3 Revert "selector stats"
This reverts commit bf371e94dfafa72620040277ae7c77bc0907590f.
2020-01-20 20:07:59 -05:00
Matthias Clasen
2e74d1cbf4 selector stats 2020-01-20 20:07:59 -05:00
Matthias Clasen
f4e4e4030e css: Add getters for a matchers name and classes
These will be used in the selector tree in the future.

The classes getter annoyingly has to allow returning
allocated memory, due to widget paths. This can be
removed when widget paths go away.
2020-01-20 20:07:59 -05:00
Matthias Clasen
6716442c37 wip: quick per-frame css node stats
Just print out the time that a toplevel css node takes
to validate, as well as the number of invalidated nodes,
created and cached styles.
2020-01-20 17:39:28 -05:00
4 changed files with 484 additions and 11 deletions

View File

@@ -72,6 +72,80 @@ gtk_css_matcher_widget_path_get_previous (GtkCssMatcher *matcher,
return TRUE;
}
static const char *
gtk_css_matcher_widget_path_get_name (const GtkCssMatcher *matcher)
{
const GtkWidgetPath *siblings;
siblings = gtk_widget_path_iter_get_siblings (matcher->path.path, matcher->path.index);
if (siblings && matcher->path.sibling_index != gtk_widget_path_iter_get_sibling_index (matcher->path.path, matcher->path.index))
{
const char *path_name = gtk_widget_path_iter_get_object_name (siblings, matcher->path.sibling_index);
if (path_name == NULL)
path_name = g_type_name (gtk_widget_path_iter_get_object_type (siblings, matcher->path.sibling_index));
return path_name;
}
else
{
const char *path_name = gtk_widget_path_iter_get_object_name (matcher->path.path, matcher->path.index);
if (path_name == NULL)
path_name = g_type_name (gtk_widget_path_iter_get_object_type (matcher->path.path, matcher->path.index));
return path_name;
}
return NULL;
}
static GQuark *
gtk_css_matcher_widget_path_get_classes (const GtkCssMatcher *matcher,
guint *n_classes,
gboolean *allocated)
{
int num;
const GtkWidgetPath *siblings;
GSList *list, *l;
GQuark *classes;
const GQuark *decl_classes = NULL;
guint n_decl_classes = 0;
int i;
if (matcher->path.decl)
decl_classes = gtk_css_node_declaration_get_classes (matcher->path.decl, &n_decl_classes);
siblings = gtk_widget_path_iter_get_siblings (matcher->path.path, matcher->path.index);
if (siblings && matcher->path.sibling_index != gtk_widget_path_iter_get_sibling_index (matcher->path.path, matcher->path.index))
list = gtk_widget_path_iter_list_classes (siblings, matcher->path.sibling_index);
else
list = gtk_widget_path_iter_list_classes (matcher->path.path, matcher->path.index);
num = n_decl_classes + g_slist_length (list);
classes = g_new (GQuark, num);
i = 0;
if (matcher->path.decl)
{
for (; i < n_decl_classes; i++)
classes[i] = decl_classes[i];
}
for (l = list; l; l = l->next)
{
const char *name = l->data;
classes[i++] = g_quark_from_string (name);
}
*n_classes = num;
*allocated = TRUE;
return classes;
}
static gboolean
gtk_css_matcher_widget_path_has_state (const GtkCssMatcher *matcher,
GtkStateFlags state)
@@ -193,6 +267,8 @@ static const GtkCssMatcherClass GTK_CSS_MATCHER_WIDGET_PATH = {
GTK_CSS_MATCHER_TYPE_WIDGET_PATH,
gtk_css_matcher_widget_path_get_parent,
gtk_css_matcher_widget_path_get_previous,
gtk_css_matcher_widget_path_get_name,
gtk_css_matcher_widget_path_get_classes,
gtk_css_matcher_widget_path_has_state,
gtk_css_matcher_widget_path_has_name,
gtk_css_matcher_widget_path_has_class,
@@ -266,6 +342,22 @@ gtk_css_matcher_node_get_previous (GtkCssMatcher *matcher,
return gtk_css_node_init_matcher (node, matcher);
}
static const char *
gtk_css_matcher_node_get_name (const GtkCssMatcher *matcher)
{
return matcher->node.node_name;
}
static GQuark *
gtk_css_matcher_node_get_classes (const GtkCssMatcher *matcher,
guint *n_classes,
gboolean *allocated)
{
*n_classes = matcher->node.n_classes;
*allocated = FALSE;
return (GQuark *)matcher->node.classes;
}
static gboolean
gtk_css_matcher_node_has_state (const GtkCssMatcher *matcher,
GtkStateFlags state)
@@ -378,6 +470,8 @@ static const GtkCssMatcherClass GTK_CSS_MATCHER_NODE = {
GTK_CSS_MATCHER_TYPE_NODE,
gtk_css_matcher_node_get_parent,
gtk_css_matcher_node_get_previous,
gtk_css_matcher_node_get_name,
gtk_css_matcher_node_get_classes,
gtk_css_matcher_node_has_state,
gtk_css_matcher_node_has_name,
gtk_css_matcher_node_has_class,

View File

@@ -41,6 +41,11 @@ struct _GtkCssMatcherClass {
gboolean (* get_previous) (GtkCssMatcher *matcher,
const GtkCssMatcher *next);
const char * (* get_name) (const GtkCssMatcher *matcher);
GQuark * (* get_classes) (const GtkCssMatcher *matcher,
guint *n_classes,
gboolean *allocated);
gboolean (* has_state) (const GtkCssMatcher *matcher,
GtkStateFlags state);
gboolean (* has_name) (const GtkCssMatcher *matcher,
@@ -106,6 +111,20 @@ _gtk_css_matcher_get_previous (GtkCssMatcher *matcher,
return next->klass->get_previous (matcher, next);
}
static inline const char *
_gtk_css_matcher_get_name (const GtkCssMatcher *matcher)
{
return matcher->klass->get_name (matcher);
}
static inline GQuark *
_gtk_css_matcher_get_classes (const GtkCssMatcher *matcher,
guint *n_classes,
gboolean *allocated)
{
return matcher->klass->get_classes (matcher, n_classes, allocated);
}
static inline gboolean
_gtk_css_matcher_has_state (const GtkCssMatcher *matcher,
GtkStateFlags state)

View File

@@ -27,6 +27,7 @@
#include "gtksettingsprivate.h"
#include "gtktypebuiltins.h"
#include "gtkprivate.h"
#include "gdkprofilerprivate.h"
/*
* CSS nodes are the backbone of the GtkStyleContext implementation and
@@ -122,6 +123,13 @@ gtk_css_node_get_style_provider_or_null (GtkCssNode *cssnode)
return GTK_CSS_NODE_GET_CLASS (cssnode)->get_style_provider (cssnode);
}
#ifdef G_ENABLE_DEBUG
static int invalidated_nodes;
static int created_styles;
static guint invalidated_nodes_counter;
static guint created_styles_counter;
#endif
static void
gtk_css_node_set_invalid (GtkCssNode *node,
gboolean invalid)
@@ -131,6 +139,11 @@ gtk_css_node_set_invalid (GtkCssNode *node,
node->invalid = invalid;
#ifdef G_ENABLE_DEBUG
if (invalid)
invalidated_nodes++;
#endif
if (node->visible)
{
if (node->parent)
@@ -369,6 +382,10 @@ gtk_css_node_create_style (GtkCssNode *cssnode,
if (style)
return g_object_ref (style);
#ifdef G_ENABLE_DEBUG
created_styles++;
#endif
parent = cssnode->parent ? cssnode->parent->style : NULL;
if (change & GTK_CSS_CHANGE_NEEDS_RECOMPUTE)
@@ -665,6 +682,14 @@ gtk_css_node_class_init (GtkCssNodeClass *klass)
| G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, NUM_PROPERTIES, cssnode_properties);
#ifdef G_ENABLE_DEBUG
if (invalidated_nodes_counter == 0)
{
invalidated_nodes_counter = gdk_profiler_define_int_counter ("invalidated-nodes", "CSS Node Invalidations");
created_styles_counter = gdk_profiler_define_int_counter ("created-styles", "CSS Style Creations");
}
#endif
}
static void
@@ -1379,10 +1404,28 @@ void
gtk_css_node_validate (GtkCssNode *cssnode)
{
gint64 timestamp;
#ifdef G_ENABLE_DEBUG
gint64 before = g_get_monotonic_time ();
#endif
timestamp = gtk_css_node_get_timestamp (cssnode);
gtk_css_node_validate_internal (cssnode, timestamp);
#ifdef G_ENABLE_DEBUG
if (cssnode->parent == NULL)
{
if (gdk_profiler_is_running ())
{
gint64 after = g_get_monotonic_time ();
gdk_profiler_add_mark (before * 1000, (after - before) * 1000, "style", "");
gdk_profiler_set_int_counter (invalidated_nodes_counter, after * 1000, invalidated_nodes);
gdk_profiler_set_int_counter (created_styles_counter, after * 1000, created_styles);
invalidated_nodes = 0;
created_styles = 0;
}
}
#endif
}
gboolean

View File

@@ -101,6 +101,20 @@ union _GtkCssSelector
} position;
};
typedef struct {
const char *name;
gint32 offset;
} TreeNameMatch;
typedef struct {
GQuark class;
gint32 offset;
} TreeClassMatch;
typedef struct {
gint32 offset;
} TreeOtherMatch;
#define GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET G_MAXINT32
struct _GtkCssSelectorTree
{
@@ -109,6 +123,12 @@ struct _GtkCssSelectorTree
gint32 previous_offset;
gint32 sibling_offset;
gint32 matches_offset; /* pointers that we return as matches if selector matches */
gint32 name_offset;
gint32 class_offset;
gint32 other_offset;
guint32 n_names;
guint32 n_classes;
guint32 n_other;
};
static gboolean
@@ -135,6 +155,33 @@ gtk_css_selector_tree_get_matches (const GtkCssSelectorTree *tree)
return (gpointer *) ((guint8 *)tree + tree->matches_offset);
}
static inline TreeNameMatch *
gtk_css_selector_tree_get_names (const GtkCssSelectorTree *tree)
{
if (tree->name_offset == GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
return NULL;
return (TreeNameMatch *) ((guint8 *)tree + tree->name_offset);
}
static inline TreeClassMatch *
gtk_css_selector_tree_get_classes (const GtkCssSelectorTree *tree)
{
if (tree->class_offset == GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
return NULL;
return (TreeClassMatch *) ((guint8 *)tree + tree->class_offset);
}
static inline TreeOtherMatch *
gtk_css_selector_tree_get_others (const GtkCssSelectorTree *tree)
{
if (tree->other_offset == GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
return NULL;
return (TreeOtherMatch *) ((guint8 *)tree + tree->other_offset);
}
static void
g_ptr_array_insert_sorted (GPtrArray *array,
gpointer data)
@@ -1823,23 +1870,127 @@ gtk_css_selectors_skip_initial_selector (GtkCssSelector *selector, const GtkCssS
return (GtkCssSelector *)gtk_css_selector_previous (selector);
}
typedef struct {
TreeNameMatch *names;
int n_names;
TreeClassMatch *classes;
int n_classes;
TreeOtherMatch *other;
int n_other;
} TreeMatchData;
static int
name_sort_func (gconstpointer a, gconstpointer b)
{
const TreeNameMatch *na = a;
const TreeNameMatch *nb = b;
if (na->name < nb->name)
return -1;
else if (na->name > nb->name)
return 1;
else
return 0;
}
static int
class_sort_func (gconstpointer a, gconstpointer b)
{
const TreeClassMatch *ca = a;
const TreeClassMatch *cb = b;
if (ca->class < cb->class)
return -1;
else if (ca->class > cb->class)
return 1;
else
return 0;
}
static gboolean
gtk_css_selector_tree_match_foreach_no_check (const GtkCssSelector *selector,
const GtkCssMatcher *matcher,
gpointer res);
static gboolean
gtk_css_selector_tree_match_foreach (const GtkCssSelector *selector,
const GtkCssMatcher *matcher,
gpointer res)
{
const GtkCssSelectorTree *tree = (const GtkCssSelectorTree *) selector;
const GtkCssSelectorTree *prev;
if (!gtk_css_selector_match (selector, matcher))
return FALSE;
return gtk_css_selector_tree_match_foreach_no_check (selector, matcher, res);
}
static gboolean
gtk_css_selector_tree_match_foreach_no_check (const GtkCssSelector *selector,
const GtkCssMatcher *matcher,
gpointer res)
{
const GtkCssSelectorTree *tree = (const GtkCssSelectorTree *) selector;
const GtkCssSelectorTree *sub;
gtk_css_selector_tree_found_match (tree, res);
for (prev = gtk_css_selector_tree_get_previous (tree);
prev != NULL;
prev = gtk_css_selector_tree_get_sibling (prev))
gtk_css_selector_foreach (&prev->selector, matcher, gtk_css_selector_tree_match_foreach, res);
if (tree->n_names > 0)
{
TreeNameMatch *names;
TreeNameMatch *match;
TreeNameMatch key;
key.name = _gtk_css_matcher_get_name (matcher);
names = gtk_css_selector_tree_get_names (tree);
match = bsearch (&key, names, tree->n_names, sizeof (TreeNameMatch), name_sort_func);
if (match)
{
sub = gtk_css_selector_tree_at_offset (tree, match->offset);
gtk_css_selector_tree_match_foreach_no_check (&sub->selector, matcher, res);
}
}
if (tree->n_classes > 0)
{
GQuark *classes;
guint n_classes;
gboolean allocated;
TreeClassMatch *tree_classes;
int i, j;
classes = _gtk_css_matcher_get_classes (matcher, &n_classes, &allocated);
tree_classes = gtk_css_selector_tree_get_classes (tree);
i = j = 0;
while (i < n_classes && j < tree->n_classes)
{
if (classes[i] < tree_classes[j].class)
i++;
else if (classes[i] > tree_classes[j].class)
j++;
else
{
sub = gtk_css_selector_tree_at_offset (tree, tree_classes[j].offset);
gtk_css_selector_tree_match_foreach_no_check (&sub->selector, matcher, res);
i++;
j++;
}
}
if (allocated)
g_free (classes);
}
if (tree->n_other > 0)
{
TreeOtherMatch *other;
int i;
other = gtk_css_selector_tree_get_others (tree);
for (i = 0; i < tree->n_other; i++)
{
sub = gtk_css_selector_tree_at_offset (tree, other[i].offset);
gtk_css_selector_foreach (&sub->selector, matcher, gtk_css_selector_tree_match_foreach, res);
}
}
return FALSE;
}
@@ -1850,9 +2001,7 @@ _gtk_css_selector_tree_match_all (const GtkCssSelectorTree *tree,
{
GPtrArray *array = NULL;
for (; tree != NULL;
tree = gtk_css_selector_tree_get_sibling (tree))
gtk_css_selector_foreach (&tree->selector, matcher, gtk_css_selector_tree_match_foreach, &array);
gtk_css_selector_tree_match_foreach (&tree->selector, matcher, &array);
return array;
}
@@ -1986,6 +2135,8 @@ _gtk_css_selector_tree_print (const GtkCssSelectorTree *tree, GString *str, char
else
g_string_append_printf (str, " (%d matches)", n);
}
if (tree->n_names || tree->n_classes || tree->n_other)
g_string_append_printf (str, "(%d/%d/%d)", tree->n_names, tree->n_classes, tree->n_other);
len = str->len - len;
if (gtk_css_selector_tree_get_previous (tree))
@@ -2133,6 +2284,12 @@ subdivide_infos (GByteArray *array, GList *infos, gint32 parent_offset)
tree = alloc_tree (array, &tree_offset);
tree->parent_offset = parent_offset;
tree->selector = max_selector;
tree->name_offset = GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
tree->class_offset = GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
tree->other_offset = GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
tree->n_names = 0;
tree->n_classes = 0;
tree->n_other = 0;
exact_matches = NULL;
for (l = infos; l != NULL; l = l->next)
@@ -2220,6 +2377,11 @@ _gtk_css_selector_tree_builder_add (GtkCssSelectorTreeBuilder *builder,
static void
fixup_offsets (GtkCssSelectorTree *tree, guint8 *data)
{
TreeNameMatch *names;
TreeClassMatch *classes;
TreeOtherMatch *others;
int i;
while (tree != NULL)
{
if (tree->parent_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
@@ -2234,12 +2396,149 @@ fixup_offsets (GtkCssSelectorTree *tree, guint8 *data)
if (tree->matches_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
tree->matches_offset -= ((guint8 *)tree - data);
if (tree->name_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
tree->name_offset -= ((guint8 *)tree - data);
if (tree->class_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
tree->class_offset -= ((guint8 *)tree - data);
if (tree->other_offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET)
tree->other_offset -= ((guint8 *)tree - data);
names = gtk_css_selector_tree_get_names (tree);
for (i = 0; i < tree->n_names; i++)
names[i].offset -= ((guint8 *)tree - data);
classes = gtk_css_selector_tree_get_classes (tree);
for (i = 0; i < tree->n_classes; i++)
classes[i].offset -= ((guint8 *)tree - data);
others = gtk_css_selector_tree_get_others (tree);
for (i = 0; i < tree->n_other; i++)
others[i].offset -= ((guint8 *)tree - data);
fixup_offsets ((GtkCssSelectorTree *)gtk_css_selector_tree_get_previous (tree), data);
tree = (GtkCssSelectorTree *)gtk_css_selector_tree_get_sibling (tree);
}
}
static TreeMatchData *
create_match_data (GByteArray *array, gint32 tree_offset)
{
TreeMatchData *data;
const GtkCssSelectorTree *prev;
int n, c, o;
gint32 offset;
data = g_new0 (TreeMatchData, 1);
for (offset = get_tree (array, tree_offset)->previous_offset;
offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
offset = get_tree (array, offset)->sibling_offset)
{
prev = get_tree (array, offset);
if (prev->selector.class == &GTK_CSS_SELECTOR_NAME)
data->n_names++;
else if (prev->selector.class == &GTK_CSS_SELECTOR_CLASS)
data->n_classes++;
else
data->n_other++;
}
data->names = g_new (TreeNameMatch, data->n_names);
data->classes = g_new (TreeClassMatch, data->n_classes);
data->other = g_new (TreeOtherMatch, data->n_other);
n = c = o = 0;
for (offset = get_tree (array, tree_offset)->previous_offset;
offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
offset = get_tree (array, offset)->sibling_offset)
{
prev = get_tree (array, offset);
if (prev->selector.class == &GTK_CSS_SELECTOR_NAME)
{
data->names[n].name = prev->selector.name.name;
data->names[n].offset = offset;
n++;
}
else if (prev->selector.class == &GTK_CSS_SELECTOR_CLASS)
{
data->classes[c].class = prev->selector.style_class.style_class;
data->classes[c].offset = offset;
c++;
}
else
{
data->other[o].offset = offset;
o++;
}
}
g_assert (n == data->n_names);
g_assert (c == data->n_classes);
g_assert (o == data->n_other);
if (data->n_names)
qsort ((void *)data->names, (unsigned)data->n_names, sizeof (TreeNameMatch), name_sort_func);
if (data->n_classes)
qsort ((void *)data->classes, (unsigned)data->n_classes, sizeof (TreeClassMatch), class_sort_func);
return data;
}
static void
free_match_data (TreeMatchData *data)
{
g_free (data->names);
g_free (data->classes);
g_free (data->other);
g_free (data);
}
static void
create_tree_match_data (GByteArray *array, gint32 tree_offset)
{
GtkCssSelectorTree *tree;
TreeMatchData *data;
gint32 offset;
data = create_match_data (array, tree_offset);
tree = get_tree (array, tree_offset);
if (data->n_names > 0)
{
tree->name_offset = array->len;
tree->n_names = data->n_names;
g_byte_array_append (array, (guint8 *)data->names, data->n_names * sizeof (TreeNameMatch));
tree = get_tree (array, tree_offset);
}
if (data->n_classes > 0)
{
tree->class_offset = array->len;
tree->n_classes = data->n_classes;
g_byte_array_append (array, (guint8 *)data->classes, data->n_classes * sizeof (TreeClassMatch));
tree = get_tree (array, tree_offset);
}
if (data->n_other > 0)
{
tree->other_offset = array->len;
tree->n_other = data->n_other;
g_byte_array_append (array, (guint8 *)data->other, data->n_other * sizeof (TreeOtherMatch));
tree = get_tree (array, tree_offset);
}
free_match_data (data);
for (offset = tree->previous_offset;
offset != GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
offset = get_tree (array, offset)->sibling_offset)
{
create_tree_match_data (array, offset);
}
}
GtkCssSelectorTree *
_gtk_css_selector_tree_builder_build (GtkCssSelectorTreeBuilder *builder)
{
@@ -2249,9 +2548,27 @@ _gtk_css_selector_tree_builder_build (GtkCssSelectorTreeBuilder *builder)
guint len;
GList *l;
GtkCssSelectorRuleSetInfo *info;
gint32 tree_offset;
gint32 offset;
array = g_byte_array_new ();
subdivide_infos (array, builder->infos, GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET);
tree = alloc_tree (array, &tree_offset);
g_assert (tree_offset == 0);
tree->parent_offset = GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
tree->sibling_offset = GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
tree->matches_offset = GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
tree->selector.class = &GTK_CSS_SELECTOR_ANY;
tree->name_offset = GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
tree->class_offset = GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
tree->other_offset = GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
tree->n_names = 0;
tree->n_classes = 0;
tree->n_other = 0;
offset = subdivide_infos (array, builder->infos, tree_offset);
get_tree (array, tree_offset)->previous_offset = offset;
create_tree_match_data (array, 0);
len = array->len;
data = g_byte_array_free (array, FALSE);