From d3e0a1f68cd80019bdd39e566851371f8b03d1c1 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 4 Jun 2020 10:15:03 +0200 Subject: [PATCH] Avoid quadratic slowdown in gtk_widget_add() If you add a widget to a parent, this will invalidate the css nodes for parent/siblings. Afterwards, if the parent is mapped, we will realize the new child. This calls gtk_widget_update_alpha() which needs the css opacity, so it revalidates the css. Thus, for each widget_add (while visible) will trigger a full revalidation of each sibling. If you add N children to a parent that leads to O(N^2) revalidations. To demo this I changed gtk-demo to always double the count (independent of the fps) and print the time it took. Here is the results (after a bit): Setting fishbowl count=256 took 3,4 msec Setting fishbowl count=512 took 10,1 msec Setting fishbowl count=1024 took 34,1 msec Setting fishbowl count=2048 took 126,3 msec Setting fishbowl count=4096 took 480,3 msec Setting fishbowl count=8192 took 1892,7 msec Setting fishbowl count=16384 took 7751,0 msec Setting fishbowl count=32768 took 38097,7 msec Setting fishbowl count=65536 took 191987,7 msec To fix this we drop gtk_widget_update_alpha() and just calculate it when needed (which is only in a single place). It was really only necessary because we previously set the alpha on the surface. With this fix the above becomes: Setting fishbowl count=256 took 1,0 msec Setting fishbowl count=512 took 1,9 msec Setting fishbowl count=1024 took 3,7 msec Setting fishbowl count=2048 took 7,4 msec Setting fishbowl count=4096 took 18,1 msec Setting fishbowl count=8192 took 31,0 msec Setting fishbowl count=16384 took 66,3 msec Setting fishbowl count=32768 took 126,7 msec Setting fishbowl count=65536 took 244,6 msec Setting fishbowl count=131072 took 492,2 msec Setting fishbowl count=262144 took 984,3 msec --- gtk/gtkwidget.c | 47 +++++++----------------------------------- gtk/gtkwidgetprivate.h | 1 - 2 files changed, 8 insertions(+), 40 deletions(-) diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 73f223ecd4..41c6b4fa5a 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -613,8 +613,6 @@ static PangoContext* gtk_widget_peek_pango_context (GtkWidget *widget); static void gtk_widget_update_pango_context (GtkWidget *widget); static void gtk_widget_propagate_state (GtkWidget *widget, const GtkStateData *data); -static void gtk_widget_update_alpha (GtkWidget *widget); - static gboolean gtk_widget_real_mnemonic_activate (GtkWidget *widget, gboolean group_cycling); static void gtk_widget_real_measure (GtkWidget *widget, @@ -2315,7 +2313,6 @@ gtk_widget_init (GTypeInstance *instance, gpointer g_class) priv->child_visible = TRUE; priv->name = NULL; priv->user_alpha = 255; - priv->alpha = 255; priv->parent = NULL; priv->first_child = NULL; priv->last_child = NULL; @@ -3396,8 +3393,6 @@ gtk_widget_realize (GtkWidget *widget) gdk_surface_set_support_multidevice (surface, TRUE); } - gtk_widget_update_alpha (widget); - if (priv->context) gtk_style_context_set_scale (priv->context, gtk_widget_get_scale_factor (widget)); else @@ -4760,8 +4755,6 @@ gtk_widget_real_css_changed (GtkWidget *widget, { GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); - gtk_widget_update_alpha (widget); - if (change) { const gboolean has_text = gtk_widget_peek_pango_context (widget) != NULL; @@ -10237,35 +10230,6 @@ gtk_widget_set_support_multidevice (GtkWidget *widget, } } -/* There are multiple alpha related sources. First of all the user can specify alpha - * in gtk_widget_set_opacity, secondly we can get it from the CSS opacity. These two - * are multiplied together to form the total alpha. Secondly, the user can specify - * an opacity group for a widget, which means we must essentially handle it as having alpha. - */ - -static void -gtk_widget_update_alpha (GtkWidget *widget) -{ - GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); - GtkCssStyle *style; - gdouble opacity; - guint8 alpha; - - style = gtk_css_node_get_style (priv->cssnode); - - opacity = _gtk_css_number_value_get (style->other->opacity, 100); - opacity = CLAMP (opacity, 0.0, 1.0); - - alpha = round (priv->user_alpha * opacity); - - if (alpha == priv->alpha) - return; - - priv->alpha = alpha; - - gtk_widget_queue_draw (widget); -} - /** * gtk_widget_set_opacity: * @widget: a #GtkWidget @@ -10303,7 +10267,7 @@ gtk_widget_set_opacity (GtkWidget *widget, priv->user_alpha = alpha; - gtk_widget_update_alpha (widget); + gtk_widget_queue_draw (widget); g_object_notify_by_pspec (G_OBJECT (widget), widget_props[PROP_OPACITY]); } @@ -11564,9 +11528,14 @@ gtk_widget_create_render_node (GtkWidget *widget, GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); GtkCssBoxes boxes; GtkCssValue *filter_value; - double opacity; + double css_opacity, opacity; + GtkCssStyle *style; + + style = gtk_css_node_get_style (priv->cssnode); + + css_opacity = _gtk_css_number_value_get (style->other->opacity, 100); + opacity = CLAMP (css_opacity, 0.0, 1.0) * priv->user_alpha / 255.0; - opacity = priv->alpha / 255.0; if (opacity <= 0.0) return NULL; diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h index d4a4003834..44b9962042 100644 --- a/gtk/gtkwidgetprivate.h +++ b/gtk/gtkwidgetprivate.h @@ -112,7 +112,6 @@ struct _GtkWidgetPrivate guint valign : 4; GtkOverflow overflow; - guint8 alpha; guint8 user_alpha; #ifdef G_ENABLE_CONSISTENCY_CHECKS