From: Benjamin Otte Date: Tue, 20 Mar 2018 17:55:33 +0000 (+0100) Subject: widget: Cache the render node X-Git-Tag: archive/raspbian/4.4.1+ds1-2+rpi1^2~18^2~22^2~681 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=29111a16d4e3ef459b7974fa7d795b567fadb5e1;p=gtk4.git widget: Cache the render node This requires a bunch of refactorings: 1. Don't pass the current clip region to gtk_widget_snapshot() so we don't create full widget contents 3. Have a widget->priv->draw_needed that we invalidate on every queue_draw() call and set on every snapshot() 2. In queue_draw(), walk the widget chain to invalidate the render nodes of all parents --- diff --git a/gtk/gtksnapshot.c b/gtk/gtksnapshot.c index 828f828799..05b07aa5f7 100644 --- a/gtk/gtksnapshot.c +++ b/gtk/gtksnapshot.c @@ -81,12 +81,28 @@ gtk_snapshot_init (GtkSnapshot *self) { } +static cairo_region_t * +create_offset_region (const cairo_region_t *region, + int dx, + int dy) +{ + cairo_region_t *result; + + if (region == NULL) + return NULL; + + result = cairo_region_copy (region); + cairo_region_translate (result, -dx, -dy); + + return result; +} + static GskRenderNode * -gtk_snapshot_collect_default (GtkSnapshot *snapshot, - GtkSnapshotState *state, - GskRenderNode **nodes, - guint n_nodes, - const char *name) +gtk_snapshot_collect_default (GtkSnapshot *snapshot, + GtkSnapshotState *state, + GskRenderNode **nodes, + guint n_nodes, + const char *name) { GskRenderNode *node; @@ -156,6 +172,29 @@ gtk_snapshot_state_clear (GtkSnapshotState *state) g_clear_pointer (&state->name, g_free); } +static GtkSnapshot * +gtk_snapshot_new_internal (gboolean record_names, + cairo_region_t *clip, + char *name) +{ + GtkSnapshot *snapshot; + + snapshot = g_object_new (GTK_TYPE_SNAPSHOT, NULL); + + snapshot->record_names = record_names; + 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); + + gtk_snapshot_push_state (snapshot, + name, + clip, + 0, 0, + gtk_snapshot_collect_default); + + return snapshot; +} + /** * gtk_snapshot_new: * @record_names: whether to keep node names (for debugging purposes) @@ -173,7 +212,6 @@ gtk_snapshot_new (gboolean record_names, const char *name, ...) { - GtkSnapshot *snapshot; char *str; if (name && record_names) @@ -187,18 +225,43 @@ gtk_snapshot_new (gboolean record_names, else str = NULL; - snapshot = g_object_new (GTK_TYPE_SNAPSHOT, NULL); + return gtk_snapshot_new_internal (record_names, + (cairo_region_t *) clip, + str); +} - snapshot->record_names = record_names; - 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); +GtkSnapshot * +gtk_snapshot_new_child (GtkSnapshot *parent, + const char *name, + ...) +{ + GtkSnapshotState *parent_state; + GtkSnapshot *snapshot; + cairo_region_t *clip; + char *str; - gtk_snapshot_push_state (snapshot, - str, - (cairo_region_t *) clip, - 0, 0, - gtk_snapshot_collect_default); + if (name && parent->record_names) + { + va_list args; + + va_start (args, name); + str = g_strdup_vprintf (name, args); + va_end (args); + } + else + str = NULL; + + parent_state = gtk_snapshot_get_current_state (parent); + clip = create_offset_region (parent_state->clip_region, + parent_state->translate_x, + parent_state->translate_y); + + snapshot = gtk_snapshot_new_internal (parent->record_names, + clip, + str); + + if (clip) + cairo_region_destroy (clip); return snapshot; } @@ -401,35 +464,10 @@ gtk_snapshot_push_offset (GtkSnapshot *snapshot) else str = NULL; - if (state->clip_region) - { - offset_clip = cairo_region_copy (state->clip_region); - if (state->translate_x != floor (state->translate_x)) - { - cairo_region_translate (offset_clip, -1, 0); - cairo_region_union (offset_clip, state->clip_region); - if (state->translate_y != floor (state->translate_y)) - { - cairo_region_t *tmp = cairo_region_copy (offset_clip); - cairo_region_translate (offset_clip, 0, -1); - cairo_region_union (offset_clip, tmp); - cairo_region_destroy (tmp); - } - } - else if (state->translate_y != floor (state->translate_y)) - { - cairo_region_translate (offset_clip, 0, -1); - cairo_region_union (offset_clip, state->clip_region); - } - cairo_region_translate (offset_clip, - - floor (state->translate_x), - - floor (state->translate_y)); + offset_clip = create_offset_region (state->clip_region, + state->translate_x, + state->translate_y); - } - else - { - offset_clip = NULL; - } state = gtk_snapshot_push_state (snapshot, str, offset_clip, diff --git a/gtk/gtksnapshotprivate.h b/gtk/gtksnapshotprivate.h index b1501f6307..3b1a145ae4 100644 --- a/gtk/gtksnapshotprivate.h +++ b/gtk/gtksnapshotprivate.h @@ -97,6 +97,9 @@ struct _GtkSnapshotClass { GObjectClass parent_class; /* it's really GdkSnapshotClass, but don't tell anyone! */ }; +GtkSnapshot * gtk_snapshot_new_child (GtkSnapshot *parent, + const char *name, + ...) G_GNUC_PRINTF (2, 3); void gtk_snapshot_append_node_internal (GtkSnapshot *snapshot, GskRenderNode *node); diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index fec5fd8c48..c6f895c64c 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -60,7 +60,7 @@ #include "gtkselection.h" #include "gtksettingsprivate.h" #include "gtksizegroup-private.h" -#include "gtksnapshot.h" +#include "gtksnapshotprivate.h" #include "gtkstylecontextprivate.h" #include "gtktooltipprivate.h" #include "gtktypebuiltins.h" @@ -3011,6 +3011,7 @@ gtk_widget_init (GTypeInstance *instance, gpointer g_class) priv->sensitive = TRUE; priv->alloc_needed = TRUE; priv->alloc_needed_on_child = TRUE; + priv->draw_needed = TRUE; priv->focus_on_click = TRUE; #ifdef G_ENABLE_DEBUG priv->highlight_resize = FALSE; @@ -3570,8 +3571,7 @@ gtk_widget_unmap (GtkWidget *widget) g_object_ref (widget); gtk_widget_push_verify_invariants (widget); - if (!_gtk_widget_get_has_surface (widget)) - gtk_widget_queue_draw (widget); + gtk_widget_queue_draw (widget); _gtk_tooltip_hide (widget); g_signal_emit (widget, widget_signals[UNMAP], 0); @@ -4020,6 +4020,21 @@ gtk_widget_queue_draw_area (GtkWidget *widget, cairo_region_destroy (region); } +static void +gtk_widget_real_queue_draw (GtkWidget *widget) +{ + for (; widget; widget = _gtk_widget_get_parent (widget)) + { + GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); + + if (priv->draw_needed) + break; + + priv->draw_needed = TRUE; + g_clear_pointer (&priv->render_node, gsk_render_node_unref); + } +} + /** * gtk_widget_queue_draw: * @widget: a #GtkWidget @@ -4035,6 +4050,8 @@ gtk_widget_queue_draw (GtkWidget *widget) g_return_if_fail (GTK_IS_WIDGET (widget)); + gtk_widget_real_queue_draw (widget); + parent = _gtk_widget_get_parent (widget); rect = &widget->priv->clip; @@ -4288,6 +4305,8 @@ gtk_widget_queue_draw_region (GtkWidget *widget, if (!_gtk_widget_get_mapped (widget)) return; + gtk_widget_real_queue_draw (widget); + if (!_gtk_widget_get_parent (widget)) { g_assert (_gtk_widget_get_has_surface (widget)); @@ -4627,6 +4646,7 @@ check_clip: * are relative to */ gtk_widget_queue_draw_region (parent ? parent : widget, invalidate); cairo_region_destroy (invalidate); + gtk_widget_real_queue_draw (widget); } } @@ -13811,49 +13831,27 @@ gtk_widget_maybe_add_debug_render_nodes (GtkWidget *widget, #endif } -void -gtk_widget_snapshot (GtkWidget *widget, - GtkSnapshot *snapshot) +static GskRenderNode * +gtk_widget_create_render_node (GtkWidget *widget, + GtkSnapshot *parent_snapshot) { GtkWidgetClass *klass = GTK_WIDGET_GET_CLASS (widget); - GtkWidgetPrivate *priv; + GtkWidgetPrivate *priv = widget->priv; GtkCssValue *filter_value; RenderMode mode; double opacity; - cairo_rectangle_int_t offset_clip; GtkCssStyle *style; GtkAllocation allocation; GtkBorder margin, border, padding; + GtkSnapshot *snapshot; - if (!_gtk_widget_is_drawable (widget)) - return; - - if (_gtk_widget_get_alloc_needed (widget)) - { - g_warning ("Trying to snapshot %s %p without a current allocation", G_OBJECT_TYPE_NAME (widget), widget); - return; - } - - priv = widget->priv; - offset_clip = priv->clip; - offset_clip.x -= priv->allocation.x; - offset_clip.y -= priv->allocation.y; - - if (gtk_snapshot_clips_rect (snapshot, &offset_clip)) - return; - - opacity = widget->priv->alpha / 255.0; - if (opacity <= 0.0) - return; + snapshot = gtk_snapshot_new_child (parent_snapshot, "%s<%p>", gtk_widget_get_name (widget), widget); /* Compatibility mode: if the widget does not have a render node, we draw * using gtk_widget_draw() on a temporary node */ mode = get_render_mode (klass); - if (GTK_DEBUG_CHECK (SNAPSHOT)) - gtk_snapshot_push (snapshot, TRUE, "%s<%p>", gtk_widget_get_name (widget), widget); - filter_value = _gtk_style_context_peek_property (_gtk_widget_get_style_context (widget), GTK_CSS_PROPERTY_FILTER); gtk_css_filter_value_push_snapshot (filter_value, snapshot); @@ -13861,6 +13859,7 @@ gtk_widget_snapshot (GtkWidget *widget, get_box_margin (style, &margin); get_box_border (style, &border); get_box_padding (style, &padding); + opacity = widget->priv->alpha / 255.0; _gtk_widget_get_allocation (widget, &allocation); @@ -13868,6 +13867,11 @@ gtk_widget_snapshot (GtkWidget *widget, { cairo_t *cr; graphene_rect_t bounds; + cairo_rectangle_int_t offset_clip; + + offset_clip = priv->clip; + offset_clip.x -= priv->allocation.x; + offset_clip.y -= priv->allocation.y; graphene_rect_init (&bounds, offset_clip.x, @@ -13914,6 +13918,11 @@ gtk_widget_snapshot (GtkWidget *widget, gboolean result; cairo_t *cr; graphene_rect_t bounds; + cairo_rectangle_int_t offset_clip; + + offset_clip = priv->clip; + offset_clip.x -= priv->allocation.x; + offset_clip.y -= priv->allocation.y; graphene_rect_init (&bounds, offset_clip.x, @@ -13939,16 +13948,53 @@ gtk_widget_snapshot (GtkWidget *widget, gtk_snapshot_pop (snapshot); } - gtk_css_filter_value_pop_snapshot (filter_value, snapshot); #ifdef G_ENABLE_DEBUG gtk_widget_maybe_add_debug_render_nodes (widget, snapshot); #endif + return gtk_snapshot_free_to_node (snapshot); +} + +void +gtk_widget_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) +{ + GtkWidgetPrivate *priv = widget->priv; + cairo_rectangle_int_t offset_clip; + double opacity; + + if (!_gtk_widget_is_drawable (widget)) + return; + + if (_gtk_widget_get_alloc_needed (widget)) + { + g_warning ("Trying to snapshot %s %p without a current allocation", G_OBJECT_TYPE_NAME (widget), widget); + return; + } + + priv = widget->priv; + offset_clip = priv->clip; + offset_clip.x -= priv->allocation.x; + offset_clip.y -= priv->allocation.y; + + if (gtk_snapshot_clips_rect (snapshot, &offset_clip)) + return; + + opacity = widget->priv->alpha / 255.0; + if (opacity <= 0.0) + return; + + if (priv->draw_needed) + { + g_assert (priv->render_node == NULL); + priv->render_node = gtk_widget_create_render_node (widget, snapshot); + priv->draw_needed = FALSE; + } - if (GTK_DEBUG_CHECK (SNAPSHOT)) - gtk_snapshot_pop (snapshot); + if (priv->render_node) + gtk_snapshot_append_node (snapshot, priv->render_node); } static gboolean @@ -13968,7 +14014,6 @@ gtk_widget_render (GtkWidget *widget, GtkSnapshot *snapshot; GskRenderer *renderer; GskRenderNode *root; - cairo_region_t *clip; /* We only render double buffered on native windows */ if (!gdk_surface_has_native (surface)) @@ -13979,12 +14024,10 @@ gtk_widget_render (GtkWidget *widget, return; context = gsk_renderer_begin_draw_frame (renderer, region); - clip = gdk_drawing_context_get_clip (context); snapshot = gtk_snapshot_new (should_record_names (widget, renderer), - clip, + NULL, "Render<%s>", G_OBJECT_TYPE_NAME (widget)); - cairo_region_destroy (clip); gtk_widget_snapshot (widget, snapshot); root = gtk_snapshot_free_to_node (snapshot); if (root != NULL) diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h index 0c50bb2ca8..2c9286b12c 100644 --- a/gtk/gtkwidgetprivate.h +++ b/gtk/gtkwidgetprivate.h @@ -76,6 +76,9 @@ struct _GtkWidgetPrivate guint alloc_needed : 1; /* this widget needs a size_allocate() call */ guint alloc_needed_on_child : 1; /* 0 or more children - or this widget - need a size_allocate() call */ + /* Queue-draw related flags */ + guint draw_needed : 1; + /* Expand-related flags */ guint need_compute_expand : 1; /* Need to recompute computed_[hv]_expand */ guint computed_hexpand : 1; /* computed results (composite of child flags) */ @@ -142,7 +145,10 @@ struct _GtkWidgetPrivate /* The widget's requested sizes */ SizeRequestCache requests; - /* The widget's surface or its surface window if it does + /* The render node we draw or %NULL if not yet created. */ + GskRenderNode *render_node; + + /* The widget's surface or its parent surface if it does * not have a surface. (Which will be indicated by the * no_surface field being set). */