[gtk: 1/2] Avoid quadratic slowdown in gtk_widget_add()



commit d3e0a1f68cd80019bdd39e566851371f8b03d1c1
Author: Alexander Larsson <alexl redhat com>
Date:   Thu Jun 4 10:15:03 2020 +0200

    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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]