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
This commit is contained in:
Alexander Larsson 2020-06-04 10:15:03 +02:00
parent 1998b673f4
commit d3e0a1f68c
2 changed files with 8 additions and 40 deletions

View File

@ -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;

View File

@ -112,7 +112,6 @@ struct _GtkWidgetPrivate
guint valign : 4;
GtkOverflow overflow;
guint8 alpha;
guint8 user_alpha;
#ifdef G_ENABLE_CONSISTENCY_CHECKS