[gtk+/optimize-height-for-width] Futher optimize height-for-width resize performance.



commit 28b573e9f610aaedb497ac37d70b6d3708e79e11
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date:   Sat Mar 5 21:24:16 2011 +0900

    Futher optimize height-for-width resize performance.
    
    This patch makes the size request cache a height for a range of
    widths and vise versa (for instance, if height 100 is returned for
    width 50 and width 200, then every height for widths between 50
    and 200 are assumed to be the same).
    
    Furthermore, this patch reduces the cache size by using guint16
    (GdkWindows are already limited to windows of size 65535 so everything
    should fit in a uint16).

 gtk/gtksizerequest.c   |  304 +++++++++++++++++++++++++++++-------------------
 gtk/gtkwidgetprivate.h |   21 ++--
 2 files changed, 195 insertions(+), 130 deletions(-)
---
diff --git a/gtk/gtksizerequest.c b/gtk/gtksizerequest.c
index b27fd05..da2403b 100644
--- a/gtk/gtksizerequest.c
+++ b/gtk/gtksizerequest.c
@@ -31,13 +31,57 @@
 #include "gtksizegroup-private.h"
 #include "gtkwidgetprivate.h"
 
-/* looks for a cached size request for this for_size. If not
- * found, returns the oldest entry so it can be overwritten
- *
- * Note that this caching code was originally derived from
- * the Clutter toolkit.
- */
 
+#ifndef G_DISABLE_CHECKS
+static GQuark recursion_check_quark = 0;
+#endif /* G_DISABLE_CHECKS */
+
+static void
+push_recursion_check (GtkWidget       *widget,
+                      GtkSizeGroupMode orientation,
+                      gint             for_size)
+{
+#ifndef G_DISABLE_CHECKS
+  const char *previous_method;
+  const char *method;
+
+  if (recursion_check_quark == 0)
+    recursion_check_quark = g_quark_from_static_string ("gtk-size-request-in-progress");
+
+  previous_method = g_object_get_qdata (G_OBJECT (widget), recursion_check_quark);
+
+  if (orientation == GTK_SIZE_GROUP_HORIZONTAL)
+    {
+      method = for_size < 0 ? "get_width" : "get_width_for_height";
+    }
+  else
+    {
+      method = for_size < 0 ? "get_height" : "get_height_for_width";
+    }
+
+  if (previous_method != NULL)
+    {
+      g_warning ("%s %p: widget tried to gtk_widget_%s inside "
+                 " GtkWidget     ::%s implementation. "
+                 "Should just invoke GTK_WIDGET_GET_CLASS(widget)->%s "
+                 "directly rather than using gtk_widget_%s",
+                 G_OBJECT_TYPE_NAME (widget), widget,
+                 method, previous_method,
+                 method, method);
+    }
+
+  g_object_set_qdata (G_OBJECT (widget), recursion_check_quark, (char*) method);
+#endif /* G_DISABLE_CHECKS */
+}
+
+static void
+pop_recursion_check (GtkWidget       *widget,
+                     GtkSizeGroupMode orientation)
+{
+#ifndef G_DISABLE_CHECKS
+  g_object_set_qdata (G_OBJECT (widget), recursion_check_quark, NULL);
+#endif
+}
 
 /* This function checks if 'request_needed' flag is present
  * and resets the cache state if a request is needed for
@@ -71,12 +115,11 @@ init_cache (GtkWidget        *widget,
 	    cache->sizes = g_slice_new0 (ContextualSizes);
 
           memset (cache->sizes->widths, 0x0, GTK_SIZE_REQUEST_CACHED_SIZES * sizeof (SizeRequest));
-          cache->sizes->cached_widths     = 0;
-          cache->sizes->last_cached_width = 0;
+          cache->cached_widths     = 0;
+          cache->last_cached_width = 0;
 	}
 
-      cache->cached_width.minimum_size = -1;
-      cache->cached_width.natural_size = -1;
+      cache->cached_base_width = FALSE;
     }
   else if (orientation == GTK_SIZE_GROUP_VERTICAL &&
 	   _gtk_widget_get_height_request_needed (widget))
@@ -97,17 +140,22 @@ init_cache (GtkWidget        *widget,
 	    cache->sizes = g_slice_new0 (ContextualSizes);
 
           memset (cache->sizes->heights, 0x0, GTK_SIZE_REQUEST_CACHED_SIZES * sizeof (SizeRequest));
-          cache->sizes->cached_heights     = 0;
-          cache->sizes->last_cached_height = 0;
+          cache->cached_heights     = 0;
+          cache->last_cached_height = 0;
 	}
 
-      cache->cached_height.minimum_size = -1;
-      cache->cached_height.natural_size = -1;
+      cache->cached_base_height = FALSE;
     }
 
   return cache;
 }
 
+/* looks for a cached size request for this for_size. If not
+ * found, returns the oldest entry so it can be overwritten
+ *
+ * Note that this caching code was originally derived from
+ * the Clutter toolkit but has evolved for other GTK+ requirements.
+ */
 static gboolean
 get_cached_size (GtkWidget         *widget,
 		 GtkSizeGroupMode   orientation,
@@ -123,126 +171,138 @@ get_cached_size (GtkWidget         *widget,
   if (for_size < 0)
     {
       if (orientation == GTK_SIZE_GROUP_HORIZONTAL)
-	*result = &cache->cached_width;
-      else
-	*result = &cache->cached_height;
-
-      if ((*result)->minimum_size < 0)
-	return FALSE;
+	{
+	  *result = &cache->cached_width;
+	  return cache->cached_base_width;
+	}
       else
-	return TRUE;
+	{
+	  *result = &cache->cached_height;
+	  return cache->cached_base_height;
+	}
     }
 
   if (orientation == GTK_SIZE_GROUP_HORIZONTAL)
     {
       cached_sizes = cache->sizes->widths;
-      n_sizes = cache->sizes->cached_widths;
+      n_sizes = cache->cached_widths;
     }
   else
     {
       cached_sizes = cache->sizes->heights;
-      n_sizes = cache->sizes->cached_widths;
+      n_sizes = cache->cached_heights;
     }
 
   /* Search for an already cached size */
   for (i = 0; i < n_sizes; i++)
     {
-      if (cached_sizes[i].for_size == for_size)
+      if (cached_sizes[i].lower_for_size <= for_size &&
+	  cached_sizes[i].upper_for_size >= for_size)
 	{
 	  *result = &cached_sizes[i].cached_size;
 	  return TRUE;
 	}
     }
 
+  return FALSE;
+}
+
+static void
+commit_cached_size (GtkWidget         *widget,
+		    GtkSizeGroupMode   orientation,
+		    gint               for_size,
+		    guint16            minimum_size,
+		    guint16            natural_size)
+{
+  SizeRequestCache *cache;
+  SizeRequest      *cached_sizes;
+  guint             i, n_sizes;
+
+  cache = _gtk_widget_peek_request_cache (widget);
+
+  /* First handle caching of the base requests */
+  if (for_size < 0)
+    {
+      if (orientation == GTK_SIZE_GROUP_HORIZONTAL)
+	{
+	  cache->cached_width.minimum_size = minimum_size;
+	  cache->cached_width.natural_size = natural_size;
+	  cache->cached_base_width = TRUE;
+	}
+      else
+	{
+	  cache->cached_height.minimum_size = minimum_size;
+	  cache->cached_height.natural_size = natural_size;
+	  cache->cached_base_height = TRUE;
+	}
+      return;
+    }
+
+  /* Check if the minimum_size and natural_size is already
+   * in the cache and if this result can be used to extend
+   * that cache entry 
+   */
+  if (orientation == GTK_SIZE_GROUP_HORIZONTAL)
+    {
+      cached_sizes = cache->sizes->widths;
+      n_sizes = cache->cached_widths;
+    }
+  else
+    {
+      cached_sizes = cache->sizes->heights;
+      n_sizes = cache->cached_heights;
+    }
+
+  for (i = 0; i < n_sizes; i++)
+    {
+      if (cached_sizes[i].cached_size.minimum_size == minimum_size &&
+	  cached_sizes[i].cached_size.natural_size == natural_size)
+	{
+	  cached_sizes[i].lower_for_size = MIN (cached_sizes[i].lower_for_size, for_size);
+	  cached_sizes[i].upper_for_size = MAX (cached_sizes[i].upper_for_size, for_size);
+	  return;
+	}
+    }
+
   /* If not found, pull a new size from the cache, the returned size cache
    * will immediately be used to cache the new computed size so we go ahead
    * and increment the last_cached_width/height right away */
   if (orientation == GTK_SIZE_GROUP_HORIZONTAL)
     {
-      if (cache->sizes->cached_widths < GTK_SIZE_REQUEST_CACHED_SIZES)
+      if (cache->cached_widths < GTK_SIZE_REQUEST_CACHED_SIZES)
 	{
-	  cache->sizes->cached_widths++;
-	  cache->sizes->last_cached_width = cache->sizes->cached_widths - 1;
+	  cache->cached_widths++;
+	  cache->last_cached_width = cache->cached_widths - 1;
 	}
       else
 	{
-	  if (++cache->sizes->last_cached_width == GTK_SIZE_REQUEST_CACHED_SIZES)
-	    cache->sizes->last_cached_width = 0;
+	  if (++cache->last_cached_width == GTK_SIZE_REQUEST_CACHED_SIZES)
+	    cache->last_cached_width = 0;
 	}
 
-      cache->sizes->widths[cache->sizes->last_cached_width].for_size = for_size;
-      *result = &cache->sizes->widths[cache->sizes->last_cached_width].cached_size;
+      cache->sizes->widths[cache->last_cached_width].lower_for_size = for_size;
+      cache->sizes->widths[cache->last_cached_width].upper_for_size = for_size;
+      cache->sizes->widths[cache->last_cached_width].cached_size.minimum_size = minimum_size;
+      cache->sizes->widths[cache->last_cached_width].cached_size.natural_size = natural_size;
     }
   else /* GTK_SIZE_GROUP_VERTICAL */
     {
-      if (cache->sizes->cached_heights < GTK_SIZE_REQUEST_CACHED_SIZES)
+      if (cache->cached_heights < GTK_SIZE_REQUEST_CACHED_SIZES)
 	{
-	  cache->sizes->cached_heights++;
-	  cache->sizes->last_cached_height = cache->sizes->cached_heights - 1;
+	  cache->cached_heights++;
+	  cache->last_cached_height = cache->cached_heights - 1;
 	}
       else
 	{
-	  if (++cache->sizes->last_cached_height == GTK_SIZE_REQUEST_CACHED_SIZES)
-	    cache->sizes->last_cached_height = 0;
+	  if (++cache->last_cached_height == GTK_SIZE_REQUEST_CACHED_SIZES)
+	    cache->last_cached_height = 0;
 	}
 
-      cache->sizes->heights[cache->sizes->last_cached_height].for_size = for_size;
-      *result = &cache->sizes->heights[cache->sizes->last_cached_height].cached_size;
+      cache->sizes->heights[cache->last_cached_height].lower_for_size = for_size;
+      cache->sizes->heights[cache->last_cached_height].upper_for_size = for_size;
+      cache->sizes->heights[cache->last_cached_height].cached_size.minimum_size = minimum_size;
+      cache->sizes->heights[cache->last_cached_height].cached_size.natural_size = natural_size;
     }
-
-  return FALSE;
-}
-
-
-#ifndef G_DISABLE_CHECKS
-static GQuark recursion_check_quark = 0;
-#endif /* G_DISABLE_CHECKS */
-
-static void
-push_recursion_check (GtkWidget       *widget,
-                      GtkSizeGroupMode orientation,
-                      gint             for_size)
-{
-#ifndef G_DISABLE_CHECKS
-  const char *previous_method;
-  const char *method;
-
-  if (recursion_check_quark == 0)
-    recursion_check_quark = g_quark_from_static_string ("gtk-size-request-in-progress");
-
-  previous_method = g_object_get_qdata (G_OBJECT (widget), recursion_check_quark);
-
-  if (orientation == GTK_SIZE_GROUP_HORIZONTAL)
-    {
-      method = for_size < 0 ? "get_width" : "get_width_for_height";
-    }
-  else
-    {
-      method = for_size < 0 ? "get_height" : "get_height_for_width";
-    }
-
-  if (previous_method != NULL)
-    {
-      g_warning ("%s %p: widget tried to gtk_widget_%s inside "
-                 " GtkWidget     ::%s implementation. "
-                 "Should just invoke GTK_WIDGET_GET_CLASS(widget)->%s "
-                 "directly rather than using gtk_widget_%s",
-                 G_OBJECT_TYPE_NAME (widget), widget,
-                 method, previous_method,
-                 method, method);
-    }
-
-  g_object_set_qdata (G_OBJECT (widget), recursion_check_quark, (char*) method);
-#endif /* G_DISABLE_CHECKS */
-}
-
-static void
-pop_recursion_check (GtkWidget       *widget,
-                     GtkSizeGroupMode orientation)
-{
-#ifndef G_DISABLE_CHECKS
-  g_object_set_qdata (G_OBJECT (widget), recursion_check_quark, NULL);
-#endif
 }
 
 /* This is the main function that checks for a cached size and
@@ -259,14 +319,14 @@ compute_size_for_orientation (GtkWidget         *widget,
 {
   CachedSize       *cached_size;
   gboolean          found_in_cache = FALSE;
-  gint              adjusted_min, adjusted_natural;
+  gint              min_size = 0;
+  gint              nat_size = 0;
 
   found_in_cache = get_cached_size (widget, orientation, for_size, &cached_size);
 
   if (!found_in_cache)
     {
-      gint min_size = 0;
-      gint nat_size = 0;
+      gint adjusted_min, adjusted_natural, adjusted_for_size = for_size;
 
       if (orientation == GTK_SIZE_GROUP_HORIZONTAL)
         {
@@ -292,11 +352,11 @@ compute_size_for_orientation (GtkWidget         *widget,
                                                                      &minimum_height,
 								     &natural_height,
                                                                      &ignored_position,
-                                                                     &for_size);
+                                                                     &adjusted_for_size);
 
 	      push_recursion_check (widget, orientation, for_size);
               GTK_WIDGET_GET_CLASS (widget)->get_preferred_width_for_height (widget, 
-									     MAX (for_size, minimum_height),
+									     MAX (adjusted_for_size, minimum_height),
 									     &min_size, &nat_size);
 	      pop_recursion_check (widget, orientation);
             }
@@ -311,9 +371,9 @@ compute_size_for_orientation (GtkWidget         *widget,
             }
           else
             {
-              int ignored_position = 0;
-              int minimum_width;
-              int natural_width;
+              gint ignored_position = 0;
+              gint minimum_width;
+              gint natural_width;
 
 	      /* Pull the base natural width from the cache as it's needed to adjust
 	       * the proposed 'for_size' */
@@ -325,11 +385,11 @@ compute_size_for_orientation (GtkWidget         *widget,
 								     &minimum_width,
                                                                      &natural_width,
                                                                      &ignored_position,
-                                                                     &for_size);
+                                                                     &adjusted_for_size);
 
 	      push_recursion_check (widget, orientation, for_size);
               GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, 
-									     MAX (for_size, minimum_width),
+									     MAX (adjusted_for_size, minimum_width),
 									     &min_size, &nat_size);
 	      pop_recursion_check (widget, orientation);
             }
@@ -341,16 +401,13 @@ compute_size_for_orientation (GtkWidget         *widget,
                      G_OBJECT_TYPE_NAME (widget), widget, min_size, nat_size);
         }
 
-      cached_size->minimum_size = min_size;
-      cached_size->natural_size = nat_size;
-
       if (orientation == GTK_SIZE_GROUP_HORIZONTAL)
           _gtk_widget_set_width_request_needed (widget, FALSE);
       else
           _gtk_widget_set_height_request_needed (widget, FALSE);
 
-      adjusted_min = cached_size->minimum_size;
-      adjusted_natural = cached_size->natural_size;
+      adjusted_min     = min_size;
+      adjusted_natural = nat_size;
       GTK_WIDGET_GET_CLASS (widget)->adjust_size_request (widget,
                                                           orientation == GTK_SIZE_GROUP_HORIZONTAL ?
                                                           GTK_ORIENTATION_HORIZONTAL :
@@ -358,14 +415,14 @@ compute_size_for_orientation (GtkWidget         *widget,
                                                           &adjusted_min,
                                                           &adjusted_natural);
 
-      if (adjusted_min < cached_size->minimum_size ||
-          adjusted_natural < cached_size->natural_size)
+      if (adjusted_min < min_size ||
+          adjusted_natural < nat_size)
         {
           g_warning ("%s %p adjusted size %s min %d natural %d must not decrease below min %d natural %d",
                      G_OBJECT_TYPE_NAME (widget), widget,
                      orientation == GTK_SIZE_GROUP_VERTICAL ? "vertical" : "horizontal",
                      adjusted_min, adjusted_natural,
-                     cached_size->minimum_size, cached_size->natural_size);
+                     min_size, nat_size);
           /* don't use the adjustment */
         }
       else if (adjusted_min > adjusted_natural)
@@ -374,14 +431,14 @@ compute_size_for_orientation (GtkWidget         *widget,
                      G_OBJECT_TYPE_NAME (widget), widget,
                      orientation == GTK_SIZE_GROUP_VERTICAL ? "vertical" : "horizontal",
                      adjusted_min, adjusted_natural,
-                     cached_size->minimum_size, cached_size->natural_size);
+                     min_size, nat_size);
           /* don't use the adjustment */
         }
       else
         {
           /* adjustment looks good */
-          cached_size->minimum_size = adjusted_min;
-          cached_size->natural_size = adjusted_natural;
+          min_size = adjusted_min;
+          nat_size = adjusted_natural;
         }
 
       /* Update size-groups with our request and update our cached requests
@@ -389,26 +446,31 @@ compute_size_for_orientation (GtkWidget         *widget,
        */
       _gtk_size_group_bump_requisition (widget,
 					orientation,
-					&cached_size->minimum_size,
-					&cached_size->natural_size);
+					&min_size,
+					&nat_size);
+
+      commit_cached_size (widget, orientation, for_size, min_size, nat_size);
+    }
+  else
+    {
+      min_size = cached_size->minimum_size;
+      nat_size = cached_size->natural_size;
     }
 
   if (minimum_size)
-    *minimum_size = cached_size->minimum_size;
+    *minimum_size = min_size;
 
   if (natural_size)
-    *natural_size = cached_size->natural_size;
+    *natural_size = nat_size;
 
-  g_assert (cached_size->minimum_size <= cached_size->natural_size);
+  g_assert (min_size <= nat_size);
 
   GTK_NOTE (SIZE_REQUEST,
             g_print ("[%p] %s\t%s: %d is minimum %d and natural: %d (hit cache: %s)\n",
                      widget, G_OBJECT_TYPE_NAME (widget),
                      orientation == GTK_SIZE_GROUP_HORIZONTAL ?
                      "width for height" : "height for width" ,
-                     for_size,
-                     cached_size->minimum_size,
-                     cached_size->natural_size,
+                     for_size, min_size, nat_size,
                      found_in_cache ? "yes" : "no"));
 
 }
diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h
index 9dfec65..a26027d 100644
--- a/gtk/gtkwidgetprivate.h
+++ b/gtk/gtkwidgetprivate.h
@@ -34,28 +34,24 @@ G_BEGIN_DECLS
  * (Note this define is limited by the bitfield sizes
  * defined on the SizeRequestCache structure).
  */
-#define GTK_SIZE_REQUEST_CACHED_SIZES   (2)
+#define GTK_SIZE_REQUEST_CACHED_SIZES   (3)
 
 typedef struct {
-  gint minimum_size;
-  gint natural_size;
+  guint16 minimum_size;
+  guint16 natural_size;
 } CachedSize;
 
 typedef struct
 {
   /* the size this request is for */
-  gint       for_size;
+  guint16  lower_for_size;
+  guint16  upper_for_size;
   CachedSize cached_size;
 } SizeRequest;
 
 typedef struct {
   SizeRequest widths[GTK_SIZE_REQUEST_CACHED_SIZES];
   SizeRequest heights[GTK_SIZE_REQUEST_CACHED_SIZES];
-
-  guint       cached_widths      : 2;
-  guint       cached_heights     : 2;
-  guint       last_cached_width  : 2;
-  guint       last_cached_height : 2;
 } ContextualSizes;
 
 typedef struct {
@@ -63,6 +59,13 @@ typedef struct {
 
   CachedSize cached_width;
   CachedSize cached_height;
+
+  guint       cached_widths      : 2;
+  guint       cached_heights     : 2;
+  guint       last_cached_width  : 2;
+  guint       last_cached_height : 2;
+  guint       cached_base_width  : 1;
+  guint       cached_base_height : 1;
 } SizeRequestCache;
 
 void         _gtk_widget_set_visible_flag   (GtkWidget *widget,



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