snapshot: Add gtk_snapshot_from_parent

Most of the time, the GtkSnapshot objects we create while snapshotting
widgets don't end up containing all that many nodes or states in their
respective node or state stack. This undermines the amortized allocation
behavior of the G(Ptr)Array we use for the stacks. So instead, use the
(until now unused) parent_snapshot GtkSnapshot* passed to
gtk_widget_create_render_node and reuse its node and state stack.

We do not avoid allocating a new GtkSnapshot object, but we do avoid
allocating a ton of G(Ptr)Array objects and we also avoid realloc'ing
their storage.
This commit is contained in:
Timm Bäder 2019-01-16 08:30:42 +01:00 committed by Timm Bäder
parent c1c764255f
commit cf1526cca6
3 changed files with 35 additions and 7 deletions

View File

@ -162,6 +162,7 @@ gtk_snapshot_new (void)
snapshot = g_object_new (GTK_TYPE_SNAPSHOT, NULL);
snapshot->from_parent = FALSE;
snapshot->state_stack = g_array_new (FALSE, TRUE, sizeof (GtkSnapshotState));
g_array_set_clear_func (snapshot->state_stack, (GDestroyNotify)gtk_snapshot_state_clear);
snapshot->nodes = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_render_node_unref);
@ -173,6 +174,24 @@ gtk_snapshot_new (void)
return snapshot;
}
/* Private. Does the same as _new but does not allocate a NEW
* state/node stack. */
GtkSnapshot *
gtk_snapshot_new_with_parent (GtkSnapshot *parent_snapshot)
{
GtkSnapshot *snapshot = g_object_new (GTK_TYPE_SNAPSHOT, NULL);
snapshot->state_stack = parent_snapshot->state_stack;
snapshot->nodes = parent_snapshot->nodes;
snapshot->from_parent = TRUE;
gtk_snapshot_push_state (snapshot,
0, 0,
gtk_snapshot_collect_default);
return snapshot;
}
/**
* gtk_snapshot_free_to_node: (skip)
* @snapshot: (transfer full): a #GtkSnapshot
@ -904,7 +923,8 @@ gtk_snapshot_pop_internal (GtkSnapshot *snapshot)
guint state_index;
GskRenderNode *node;
if (snapshot->state_stack->len == 0)
if (snapshot->state_stack->len == 0 &&
!snapshot->from_parent)
{
g_warning ("Too many gtk_snapshot_pop() calls.");
return NULL;
@ -950,17 +970,21 @@ gtk_snapshot_to_node (GtkSnapshot *snapshot)
GskRenderNode *result;
/* We should have exactly our initial state */
if (snapshot->state_stack->len > 1)
if (snapshot->state_stack->len > 1 &&
!snapshot->from_parent)
{
g_warning ("Too many gtk_snapshot_push() calls. %u states remaining.", snapshot->state_stack->len);
}
result = gtk_snapshot_pop_internal (snapshot);
g_array_free (snapshot->state_stack, TRUE);
snapshot->state_stack = NULL;
if (!snapshot->from_parent)
{
g_array_free (snapshot->state_stack, TRUE);
g_ptr_array_free (snapshot->nodes, TRUE);
}
g_ptr_array_free (snapshot->nodes, TRUE);
snapshot->state_stack = NULL;
snapshot->nodes = NULL;
return result;

View File

@ -90,6 +90,8 @@ struct _GdkSnapshot {
GArray *state_stack;
GPtrArray *nodes;
guint from_parent : 1;
};
struct _GtkSnapshotClass {
@ -99,6 +101,8 @@ struct _GtkSnapshotClass {
void gtk_snapshot_append_node_internal (GtkSnapshot *snapshot,
GskRenderNode *node);
GtkSnapshot * gtk_snapshot_new_with_parent (GtkSnapshot *parent_snapshot);
G_END_DECLS
#endif /* __GTK_SNAPSHOT_PRIVATE_H__ */

View File

@ -12961,7 +12961,7 @@ gtk_widget_create_render_node (GtkWidget *widget,
if (opacity <= 0.0)
return NULL;
snapshot = gtk_snapshot_new ();
snapshot = gtk_snapshot_new_with_parent (parent_snapshot);
_gtk_widget_get_allocation (widget, &allocation);
gtk_snapshot_push_debug (snapshot,