[cairo] [PATCH] Use the new pixman_glyph_cache_t API that will be in pixman 0.28.0

Søren Sandmann sandmann at cs.au.dk
Wed May 30 17:30:01 PDT 2012


From: Søren Sandmann Pedersen <ssp at redhat.com>

This new pixman API allows glyphs to be cached and composited in one
go, which reduces overhead compared to individual calls to
pixman_image_composite_region32().

Notes:

- There is an explicit call to _cairo_image_scaled_glyph_fini(). This
  could instead be done with a private, but I chose not to do that
  since we don't need to store any actual data; we only need
  notification when the glyph dies.

- The mask format used with pixman_composite_glyphs() is that of the
  first glyph. This makes the implicit assumption that all the glyphs
  in the font have the same format, which I believe is true.

  Note that while the current code _looks_ like it will support both
  a8 and a8r8g8b8 glyphs within the same font, that is not actually
  the case since if the mask gets upgraded, adding a8 glyphs will
  produce the wrong result since the RGB channels are considered 0 in
  this format and so they will be drawn empty.

- The slowdown in poppler-reseau is real and stable across runs. I'm
  not too concerned about it because this benchmark is only one run
  and so it is dominated by glyph cache setup costs and FreeType
  rasterizing.

Performance results, image backend:

    Speedups
         firefox-talos-gfx  5919.67 -> 4360.70:  1.36x speedup
         xfce4-terminal-a1  1600.87 -> 1264.19:  1.27x speedup
        gnome-terminal-vim  1925.08 -> 1788.56:  1.08x speedup
      firefox-planet-gnome  6906.81 -> 6480.79:  1.07x speedup

    Slowdowns
            poppler-reseau  370.01  -> 397.08:   1.07x slowdown

Performance results, image16 backend:

    Speedups
         firefox-talos-gfx  5637.39 -> 4190.41:  1.35x speedup
         xfce4-terminal-a1  2086.46 -> 1778.07:  1.17x speedup
                 evolution   994.18 ->  939.62:  1.06x speedup

    Slowdowns
            poppler-reseau   368.44 ->  403.61:  1.10x slowdown
---
 src/cairo-image-compositor.c   |  323 ++++++++++++++--------------------------
 src/cairo-mutex-list-private.h |    1 +
 src/cairo-scaled-font.c        |    2 +
 src/cairoint.h                 |    4 +
 4 files changed, 119 insertions(+), 211 deletions(-)

diff --git a/src/cairo-image-compositor.c b/src/cairo-image-compositor.c
index b0afc5d..069448b 100644
--- a/src/cairo-image-compositor.c
+++ b/src/cairo-image-compositor.c
@@ -750,187 +750,38 @@ composite_tristrip (void			*_dst,
     return  CAIRO_STATUS_SUCCESS;
 }
 
-static cairo_int_status_t
-check_composite_glyphs (const cairo_composite_rectangles_t *extents,
-			cairo_scaled_font_t *scaled_font,
-			cairo_glyph_t *glyphs,
-			int *num_glyphs)
-{
-    return CAIRO_STATUS_SUCCESS;
-}
+static pixman_glyph_cache_t *global_glyph_cache;
 
-static cairo_int_status_t
-composite_one_glyph (void				*_dst,
-		     cairo_operator_t			 op,
-		     cairo_surface_t			*_src,
-		     int				 src_x,
-		     int				 src_y,
-		     int				 dst_x,
-		     int				 dst_y,
-		     cairo_composite_glyphs_info_t	 *info)
+static inline pixman_glyph_cache_t *
+get_glyph_cache (void)
 {
-    cairo_image_surface_t *glyph_surface;
-    cairo_scaled_glyph_t *scaled_glyph;
-    cairo_status_t status;
-    int x, y;
-
-    TRACE ((stderr, "%s\n", __FUNCTION__));
-
-    status = _cairo_scaled_glyph_lookup (info->font,
-					 info->glyphs[0].index,
-					 CAIRO_SCALED_GLYPH_INFO_SURFACE,
-					 &scaled_glyph);
-
-    if (unlikely (status))
-	return status;
-
-    glyph_surface = scaled_glyph->surface;
-    if (glyph_surface->width == 0 || glyph_surface->height == 0)
-	return CAIRO_INT_STATUS_NOTHING_TO_DO;
+    if (!global_glyph_cache)
+	global_glyph_cache = pixman_glyph_cache_create ();
 
-    /* round glyph locations to the nearest pixel */
-    /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
-    x = _cairo_lround (info->glyphs[0].x -
-		       glyph_surface->base.device_transform.x0);
-    y = _cairo_lround (info->glyphs[0].y -
-		       glyph_surface->base.device_transform.y0);
-
-    pixman_image_composite32 (_pixman_operator (op),
-			      ((cairo_image_source_t *)_src)->pixman_image,
-			      glyph_surface->pixman_image,
-			      to_pixman_image (_dst),
-			      x + src_x,  y + src_y,
-			      0, 0,
-			      x - dst_x, y - dst_y,
-			      glyph_surface->width,
-			      glyph_surface->height);
-
-    return CAIRO_INT_STATUS_SUCCESS;
+    return global_glyph_cache;
 }
 
-static cairo_int_status_t
-composite_glyphs_via_mask (void				*_dst,
-			   cairo_operator_t		 op,
-			   cairo_surface_t		*_src,
-			   int				 src_x,
-			   int				 src_y,
-			   int				 dst_x,
-			   int				 dst_y,
-			   cairo_composite_glyphs_info_t *info)
+void
+_cairo_image_scaled_glyph_fini (cairo_scaled_font_t *scaled_font,
+				cairo_scaled_glyph_t *scaled_glyph)
 {
-    cairo_scaled_glyph_t *glyph_cache[64];
-    cairo_bool_t component_alpha = FALSE;
-    uint8_t buf[2048];
-    pixman_image_t *mask;
-    cairo_status_t status;
-    int i;
-
-    TRACE ((stderr, "%s\n", __FUNCTION__));
-
-    /* XXX convert the glyphs to common formats a8/a8r8g8b8 to hit
-     * optimised paths through pixman. Should we increase the bit
-     * depth of the target surface, we should reconsider the appropriate
-     * mask formats.
-     */
-    i = (info->extents.width + 3) & ~3;
-    if (i * info->extents.height > (int) sizeof (buf)) {
-	mask = pixman_image_create_bits (PIXMAN_a8,
-					info->extents.width,
-					info->extents.height,
-					NULL, 0);
-    } else {
-	memset (buf, 0, i * info->extents.height);
-	mask = pixman_image_create_bits (PIXMAN_a8,
-					info->extents.width,
-					info->extents.height,
-					(uint32_t *)buf, i);
-    }
-    if (unlikely (mask == NULL))
-	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-
-    memset (glyph_cache, 0, sizeof (glyph_cache));
-    status = CAIRO_STATUS_SUCCESS;
-
-    for (i = 0; i < info->num_glyphs; i++) {
-	cairo_image_surface_t *glyph_surface;
-	cairo_scaled_glyph_t *scaled_glyph;
-	unsigned long glyph_index = info->glyphs[i].index;
-	int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache);
-	int x, y;
-
-	scaled_glyph = glyph_cache[cache_index];
-	if (scaled_glyph == NULL ||
-	    _cairo_scaled_glyph_index (scaled_glyph) != glyph_index)
-	{
-	    status = _cairo_scaled_glyph_lookup (info->font, glyph_index,
-						 CAIRO_SCALED_GLYPH_INFO_SURFACE,
-						 &scaled_glyph);
-
-	    if (unlikely (status)) {
-		pixman_image_unref (mask);
-		return status;
-	    }
+    CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex);
 
-	    glyph_cache[cache_index] = scaled_glyph;
-	}
-
-	glyph_surface = scaled_glyph->surface;
-	if (glyph_surface->width && glyph_surface->height) {
-	    if (glyph_surface->base.content & CAIRO_CONTENT_COLOR &&
-		! component_alpha) {
-		pixman_image_t *ca_mask;
-
-		ca_mask = pixman_image_create_bits (PIXMAN_a8r8g8b8,
-						    info->extents.width,
-						    info->extents.height,
-						    NULL, 0);
-		if (unlikely (ca_mask == NULL)) {
-		    pixman_image_unref (mask);
-		    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-		}
-
-		pixman_image_composite32 (PIXMAN_OP_SRC,
-					  mask, 0, ca_mask,
-					  0, 0,
-					  0, 0,
-					  0, 0,
-					  info->extents.width,
-					  info->extents.height);
-		pixman_image_unref (mask);
-		mask = ca_mask;
-		component_alpha = TRUE;
-	    }
-
-	    /* round glyph locations to the nearest pixel */
-	    /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
-	    x = _cairo_lround (info->glyphs[i].x -
-			       glyph_surface->base.device_transform.x0);
-	    y = _cairo_lround (info->glyphs[i].y -
-			       glyph_surface->base.device_transform.y0);
-
-	    pixman_image_composite32 (PIXMAN_OP_ADD,
-				      glyph_surface->pixman_image, NULL, mask,
-                                      0, 0,
-				      0, 0,
-                                      x - info->extents.x, y - info->extents.y,
-				      glyph_surface->width,
-				      glyph_surface->height);
-	}
+    if (global_glyph_cache) {
+	pixman_glyph_cache_remove (
+	    global_glyph_cache, scaled_font,
+	    (void *)_cairo_scaled_glyph_index (scaled_glyph));
     }
 
-    if (component_alpha)
-	pixman_image_set_component_alpha (mask, TRUE);
-
-    pixman_image_composite32 (_pixman_operator (op),
-			      ((cairo_image_source_t *)_src)->pixman_image,
-			      mask,
-			      to_pixman_image (_dst),
-			      info->extents.x + src_x, info->extents.y + src_y,
-			      0, 0,
-			      info->extents.x - dst_x, info->extents.y - dst_y,
-			      info->extents.width, info->extents.height);
-    pixman_image_unref (mask);
+    CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex);
+}
 
+static cairo_int_status_t
+check_composite_glyphs (const cairo_composite_rectangles_t *extents,
+			cairo_scaled_font_t *scaled_font,
+			cairo_glyph_t *glyphs,
+			int *num_glyphs)
+{
     return CAIRO_STATUS_SUCCESS;
 }
 
@@ -944,65 +795,115 @@ composite_glyphs (void				*_dst,
 		  int				 dst_y,
 		  cairo_composite_glyphs_info_t *info)
 {
-    cairo_scaled_glyph_t *glyph_cache[64];
-    pixman_image_t *dst, *src;
     cairo_status_t status;
+    pixman_glyph_cache_t *glyph_cache;
+    pixman_glyph_t pglyphs_stack[CAIRO_STACK_ARRAY_LENGTH (pixman_glyph_t)];
+    pixman_glyph_t *pglyphs = pglyphs_stack;
+    pixman_format_code_t mask_format = 0;
+    pixman_glyph_t *pg;
     int i;
 
     TRACE ((stderr, "%s\n", __FUNCTION__));
 
-    if (info->num_glyphs == 1)
-	return composite_one_glyph(_dst, op, _src, src_x, src_y, dst_x, dst_y, info);
+    CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex);
 
-    if (info->use_mask)
-	return composite_glyphs_via_mask(_dst, op, _src, src_x, src_y, dst_x, dst_y, info);
+    glyph_cache = get_glyph_cache();
 
-    op = _pixman_operator (op);
-    dst = to_pixman_image (_dst);
-    src = ((cairo_image_source_t *)_src)->pixman_image;
+    if (unlikely (!glyph_cache)) {
+	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	goto out_no_glyph_cache;
+    }
 
-    memset (glyph_cache, 0, sizeof (glyph_cache));
-    status = CAIRO_STATUS_SUCCESS;
+    pixman_glyph_cache_freeze (glyph_cache);
+    
+    if (info->num_glyphs > ARRAY_LENGTH (pglyphs_stack)) {
+	pglyphs = _cairo_malloc_ab (info->num_glyphs, sizeof (pixman_glyph_t));
 
+	if (unlikely (!pglyphs)) {
+	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	    goto out;
+	}
+    }
+	
+    if (unlikely (!glyph_cache)) {
+	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	goto out;
+    }
+    
+    pg = pglyphs;
     for (i = 0; i < info->num_glyphs; i++) {
-	int x, y;
-	cairo_image_surface_t *glyph_surface;
-	cairo_scaled_glyph_t *scaled_glyph;
-	unsigned long glyph_index = info->glyphs[i].index;
-	int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache);
-
-	scaled_glyph = glyph_cache[cache_index];
-	if (scaled_glyph == NULL ||
-	    _cairo_scaled_glyph_index (scaled_glyph) != glyph_index)
-	{
-	    status = _cairo_scaled_glyph_lookup (info->font, glyph_index,
+	unsigned long index = info->glyphs[i].index;
+	const void *glyph;
+
+	glyph = pixman_glyph_cache_lookup (glyph_cache, info->font, (void *)index);
+	if (!glyph || !mask_format) {
+	    cairo_scaled_glyph_t *scaled_glyph;
+	    cairo_image_surface_t *glyph_surface;
+
+	    /* This call can actually end up recursing, so we have to drop the mutex
+	     * around it.
+	     */
+	    CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex);
+	    status = _cairo_scaled_glyph_lookup (info->font, index,
 						 CAIRO_SCALED_GLYPH_INFO_SURFACE,
 						 &scaled_glyph);
+	    CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex);
 
 	    if (unlikely (status))
-		break;
+		goto out;
+
+	    glyph_surface = scaled_glyph->surface;
+
+	    if (!glyph) {
+		glyph = pixman_glyph_cache_insert (glyph_cache, info->font, (void *)index,
+						   glyph_surface->base.device_transform.x0,
+						   glyph_surface->base.device_transform.y0,
+						   glyph_surface->pixman_image);
+		if (unlikely (!glyph)) {
+		    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+		    
+		    goto out;
+		}
+	    }
 
-	    glyph_cache[cache_index] = scaled_glyph;
+	    if (!mask_format)
+		mask_format = pixman_image_get_format (glyph_surface->pixman_image);
 	}
+		
+	pg->x = _cairo_lround (info->glyphs[i].x);
+	pg->y = _cairo_lround (info->glyphs[i].y);
+	pg->glyph = glyph;
+	pg++;
+    }
 
-	glyph_surface = scaled_glyph->surface;
-	if (glyph_surface->width && glyph_surface->height) {
-	    /* round glyph locations to the nearest pixel */
-	    /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
-	    x = _cairo_lround (info->glyphs[i].x -
-			       glyph_surface->base.device_transform.x0);
-	    y = _cairo_lround (info->glyphs[i].y -
-			       glyph_surface->base.device_transform.y0);
-
-	    pixman_image_composite32 (op, src, glyph_surface->pixman_image, dst,
-                                      x + src_x,  y + src_y,
-                                      0, 0,
-                                      x - dst_x, y - dst_y,
-				      glyph_surface->width,
-				      glyph_surface->height);
-	}
+    if (info->use_mask)
+    {
+	pixman_composite_glyphs (_pixman_operator (op),
+				 ((cairo_image_source_t *)_src)->pixman_image,
+				 to_pixman_image (_dst),
+				 mask_format,
+				 info->extents.x + src_x, info->extents.y + src_y,
+				 info->extents.x, info->extents.y,
+				 info->extents.x - dst_x, info->extents.y - dst_y,
+				 info->extents.width, info->extents.height,
+				 glyph_cache, pg - pglyphs, pglyphs);
     }
+    else
+    {
+	pixman_composite_glyphs_no_mask (_pixman_operator (op),
+					 ((cairo_image_source_t *)_src)->pixman_image,
+					 to_pixman_image (_dst),
+					 src_x, src_y,
+					 - dst_x, - dst_y,
+					 glyph_cache, pg - pglyphs, pglyphs);
+    }
+
+out:
+    pixman_glyph_cache_thaw (glyph_cache);
 
+out_no_glyph_cache:
+    CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex);
+    
     return status;
 }
 
diff --git a/src/cairo-mutex-list-private.h b/src/cairo-mutex-list-private.h
index 4016f8e..f46afad 100644
--- a/src/cairo-mutex-list-private.h
+++ b/src/cairo-mutex-list-private.h
@@ -45,6 +45,7 @@ CAIRO_MUTEX_DECLARE (_cairo_intern_string_mutex)
 CAIRO_MUTEX_DECLARE (_cairo_scaled_font_map_mutex)
 CAIRO_MUTEX_DECLARE (_cairo_scaled_glyph_page_cache_mutex)
 CAIRO_MUTEX_DECLARE (_cairo_scaled_font_error_mutex)
+CAIRO_MUTEX_DECLARE (_cairo_glyph_cache_mutex)
 
 #if CAIRO_HAS_FT_FONT
 CAIRO_MUTEX_DECLARE (_cairo_ft_unscaled_font_map_mutex)
diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index 59440b2..2a48978 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -212,6 +212,8 @@ _cairo_scaled_glyph_fini (cairo_scaled_font_t *scaled_font,
 	private->destroy (private, scaled_glyph, scaled_font);
     }
 
+    _cairo_image_scaled_glyph_fini (scaled_font, scaled_glyph);
+    
     if (scaled_glyph->surface != NULL)
 	cairo_surface_destroy (&scaled_glyph->surface->base);
 
diff --git a/src/cairoint.h b/src/cairoint.h
index 2c9f2dd..97be900 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1492,6 +1492,10 @@ _pixman_format_to_masks (pixman_format_code_t	 pixman_format,
 			 cairo_format_masks_t	*masks);
 
 cairo_private void
+_cairo_image_scaled_glyph_fini (cairo_scaled_font_t *scaled_font,
+				cairo_scaled_glyph_t *scaled_glyph);
+
+cairo_private void
 _cairo_image_reset_static_data (void);
 
 cairo_private cairo_surface_t *
-- 
1.7.10.2



More information about the cairo mailing list