[cairo] Reduce number of floating point operations

Aivars Kalvans aivars.kalvans at inbox.lv
Thu Sep 14 15:30:06 PDT 2006


This is basicly the same patch I sent a while ago to
performance-list at gnome.org
It simplifies matrix operations for identity matrices which are very
common. I also wrote a very simple performance test although
improvements will be noticeable on platforms without FPU.

Some profiles
Before: http://www.o-hand.com/~jorn/pango-benchmarks/210-softfloat/cairo.txt
After:
http://www.o-hand.com/~jorn/pango-benchmarks/210-softfloat/cairo-floating-point.txt

-- 
Aivars

-------------- next part --------------
diff --git a/perf/Makefile.am b/perf/Makefile.am
index d10c3d0..679187d 100644
--- a/perf/Makefile.am
+++ b/perf/Makefile.am
@@ -18,6 +18,7 @@ cairo_perf_SOURCES =		\
 	cairo-perf.c		\
 	cairo-perf.h		\
 	paint.c			\
+	text.c			\
 	tessellate.c
 
 if CAIRO_HAS_WIN32_SURFACE
diff --git a/perf/cairo-perf.c b/perf/cairo-perf.c
index 7b927b3..8438298 100644
--- a/perf/cairo-perf.c
+++ b/perf/cairo-perf.c
@@ -201,5 +201,6 @@ cairo_perf_t perfs[] = {
     { "tessellate-16",  tessellate_16,  100, 100},
     { "tessellate-64",  tessellate_64,  100, 100},
     { "tessellate-256", tessellate_256, 100, 100},
+    { "show_text", show_text, 400, 400},
     { NULL }
 };
diff --git a/perf/cairo-perf.h b/perf/cairo-perf.h
index e365263..545f94f 100644
--- a/perf/cairo-perf.h
+++ b/perf/cairo-perf.h
@@ -61,5 +61,6 @@ CAIRO_PERF_DECL (paint_alpha);
 CAIRO_PERF_DECL (tessellate_16);
 CAIRO_PERF_DECL (tessellate_64);
 CAIRO_PERF_DECL (tessellate_256);
+CAIRO_PERF_DECL (show_text);
 
 #endif
diff --git a/perf/text.c b/perf/text.c
new file mode 100644
index 0000000..f8dfedf
--- /dev/null
+++ b/perf/text.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "cairo-perf.h"
+
+cairo_perf_ticks_t
+show_text (cairo_t *cr, int width, int height)
+{
+    int i;
+    static const char text[] = "This is a pretty long string with some"
+	    		       " accented chars: Jõe ääres";
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_set_font_size (cr, 9);
+    cairo_move_to (cr, 10, 10);
+
+    cairo_perf_timer_start ();
+
+    for (i = 0; i < 256; i++) {
+	cairo_show_text (cr, text);
+    }
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index 2f9079b..7129222 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -564,15 +564,10 @@ _cairo_gstate_get_matrix (cairo_gstate_t
 cairo_status_t
 _cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty)
 {
-    cairo_matrix_t tmp;
-
     _cairo_gstate_unset_scaled_font (gstate);
 
-    cairo_matrix_init_translate (&tmp, tx, ty);
-    cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm);
-
-    cairo_matrix_init_translate (&tmp, -tx, -ty);
-    cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp);
+    cairo_matrix_translate (&gstate->ctm, tx, ty);
+    _cairo_matrix_translate_inverse (&gstate->ctm_inverse, tx, ty);
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -580,18 +575,13 @@ _cairo_gstate_translate (cairo_gstate_t 
 cairo_status_t
 _cairo_gstate_scale (cairo_gstate_t *gstate, double sx, double sy)
 {
-    cairo_matrix_t tmp;
-
     if (sx == 0 || sy == 0)
 	return CAIRO_STATUS_INVALID_MATRIX;
 
     _cairo_gstate_unset_scaled_font (gstate);
 
-    cairo_matrix_init_scale (&tmp, sx, sy);
-    cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm);
-
-    cairo_matrix_init_scale (&tmp, 1/sx, 1/sy);
-    cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp);
+    cairo_matrix_scale (&gstate->ctm, sx, sy);
+    _cairo_matrix_scale_inverse (&gstate->ctm_inverse, sx, sy);
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -1409,6 +1399,59 @@ _cairo_gstate_glyph_extents (cairo_gstat
     return CAIRO_STATUS_SUCCESS;
 }
 
+static void
+_cairo_gstate_transform_glyphs (cairo_gstate_t *gstate,
+		   		cairo_glyph_t *glyphs,
+		   		cairo_glyph_t *transformed_glyphs,
+		   		int num_glyphs)
+{
+    double tx, ty;
+    int i;
+
+    tx = gstate->font_matrix.x0;
+    ty = gstate->font_matrix.y0;
+
+    if (_cairo_matrix_is_identity (&gstate->ctm) &&
+	_cairo_matrix_is_identity (&gstate->target->device_transform))
+    {
+
+	/* Translation is constant for all glyphs, calculate it once */
+	tx += gstate->ctm.x0 + gstate->target->device_transform.x0;
+	ty += gstate->ctm.y0 + gstate->target->device_transform.y0;
+
+	for (i = 0; i < num_glyphs; ++i)
+	{
+	    transformed_glyphs[i] = glyphs[i];
+	    transformed_glyphs[i].x += tx;
+	    transformed_glyphs[i].y += ty;
+	}
+    } 
+    else
+    {
+	/* Merge both matrices and calculate final transformation */
+	cairo_matrix_t transformation;
+	cairo_matrix_multiply (&transformation,
+			       &gstate->ctm,
+			       &gstate->target->device_transform);
+
+	/* Pre-calculate translation, so we can use
+	 * ..._transform_distance() instead of ..._transform_point()
+	 * and save 2 add-s per iteration */
+	cairo_matrix_transform_point (&transformation, &tx, &ty);
+
+	for (i = 0; i < num_glyphs; ++i)
+	{
+	    transformed_glyphs[i] = glyphs[i];
+	    cairo_matrix_transform_distance (&transformation,
+					     &transformed_glyphs[i].x,
+					     &transformed_glyphs[i].y);
+	    transformed_glyphs[i].x += tx;
+	    transformed_glyphs[i].y += ty;
+	}
+    }
+
+ }
+
 cairo_status_t
 _cairo_gstate_show_glyphs (cairo_gstate_t *gstate,
 			   cairo_glyph_t *glyphs,
@@ -1417,7 +1460,6 @@ _cairo_gstate_show_glyphs (cairo_gstate_
     cairo_status_t status;
     cairo_pattern_union_t source_pattern;
     cairo_glyph_t *transformed_glyphs;
-    int i;
 
     if (gstate->source->status)
 	return gstate->source->status;
@@ -1434,15 +1476,10 @@ _cairo_gstate_show_glyphs (cairo_gstate_
     if (transformed_glyphs == NULL)
 	return CAIRO_STATUS_NO_MEMORY;
 
-    for (i = 0; i < num_glyphs; ++i)
-    {
-	transformed_glyphs[i].index = glyphs[i].index;
-	transformed_glyphs[i].x = glyphs[i].x + gstate->font_matrix.x0;
-	transformed_glyphs[i].y = glyphs[i].y + gstate->font_matrix.y0;
-	_cairo_gstate_user_to_backend (gstate,
-				       &transformed_glyphs[i].x,
-				       &transformed_glyphs[i].y);
-    }
+    _cairo_gstate_transform_glyphs (gstate,
+		    		    glyphs,
+				    transformed_glyphs,
+				    num_glyphs);
 
     _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
 
@@ -1466,7 +1503,6 @@ _cairo_gstate_glyph_path (cairo_gstate_t
 			  cairo_path_fixed_t *path)
 {
     cairo_status_t status;
-    int i;
     cairo_glyph_t *transformed_glyphs = NULL;
 
     status = _cairo_gstate_ensure_scaled_font (gstate);
@@ -1477,15 +1513,10 @@ _cairo_gstate_glyph_path (cairo_gstate_t
     if (transformed_glyphs == NULL)
 	return CAIRO_STATUS_NO_MEMORY;
 
-    for (i = 0; i < num_glyphs; ++i)
-    {
-	transformed_glyphs[i].index = glyphs[i].index;
-	transformed_glyphs[i].x = glyphs[i].x + gstate->font_matrix.x0;
-	transformed_glyphs[i].y = glyphs[i].y + gstate->font_matrix.y0;
-	_cairo_gstate_user_to_backend (gstate,
-				       &(transformed_glyphs[i].x),
-				       &(transformed_glyphs[i].y));
-    }
+    _cairo_gstate_transform_glyphs (gstate,
+		    		    glyphs,
+				    transformed_glyphs,
+				    num_glyphs);
 
     status = _cairo_scaled_font_glyph_path (gstate->scaled_font,
 					    transformed_glyphs, num_glyphs,
diff --git a/src/cairo-matrix.c b/src/cairo-matrix.c
index 60cdca3..fe33301 100644
--- a/src/cairo-matrix.c
+++ b/src/cairo-matrix.c
@@ -164,11 +164,15 @@ slim_hidden_def(cairo_matrix_init_transl
 void
 cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty)
 {
-    cairo_matrix_t tmp;
-
-    cairo_matrix_init_translate (&tmp, tx, ty);
+    matrix->x0 += tx * matrix->xx + ty * matrix->xy;
+    matrix->y0 += tx * matrix->yx + ty * matrix->yy;
+}
 
-    cairo_matrix_multiply (matrix, &tmp, matrix);
+void
+_cairo_matrix_translate_inverse (cairo_matrix_t *matrix, double tx, double ty)
+{
+    matrix->x0 -= tx;
+    matrix->y0 -= ty;
 }
 slim_hidden_def (cairo_matrix_translate);
 
@@ -205,14 +209,30 @@ slim_hidden_def(cairo_matrix_init_scale)
 void
 cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy)
 {
-    cairo_matrix_t tmp;
-
-    cairo_matrix_init_scale (&tmp, sx, sy);
+    matrix->xx *= sx;
+    matrix->yx *= sx;
 
-    cairo_matrix_multiply (matrix, &tmp, matrix);
+    matrix->xy *= sy;
+    matrix->yy *= sy;
 }
 slim_hidden_def(cairo_matrix_scale);
 
+void
+_cairo_matrix_scale_inverse (cairo_matrix_t *matrix, double sx, double sy)
+{
+    sx = 1.0 / sx;
+    sy = 1.0 / sy;
+
+    matrix->xx *= sx;
+    matrix->yx *= sy;
+
+    matrix->xy *= sx;
+    matrix->yy *= sy;
+
+    matrix->x0 *= sx;
+    matrix->y0 *= sy;
+}
+
 /**
  * cairo_matrix_init_rotate:
  * @matrix: a cairo_matrix_t
@@ -530,11 +550,28 @@ _cairo_matrix_compute_scale_factors (con
 }
 
 cairo_bool_t
+_cairo_matrix_has_transform (const cairo_matrix_t *matrix)
+{
+    return (matrix->xx != 1.0 || matrix->yx != 0.0  ||
+	    matrix->xy != 0.0 || matrix->yy != 1.0  ||
+	    matrix->x0 != 0.0 || matrix->y0 != 0.0);
+}
+
+cairo_bool_t
 _cairo_matrix_is_identity (const cairo_matrix_t *matrix)
 {
+#if defined (__arm__)
+    /* memcmp() should be faster than 4 soft float operations */
+    static const cairo_matrix_t identity = {
+	    1.0, 0.0,
+	    0.0, 1.0,
+	    0.0, 0.0
+    };
+    return (memcmp (matrix, &identity, 4 * sizeof(double)) == 0);
+#else 
     return (matrix->xx == 1.0 && matrix->yx == 0.0 &&
-	    matrix->xy == 0.0 && matrix->yy == 1.0 &&
-	    matrix->x0 == 0.0 && matrix->y0 == 0.0);
+	    matrix->xy == 0.0 && matrix->yy == 1.0);
+#endif
 }
 
 cairo_bool_t
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index cbbe89a..a80697a 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -863,7 +863,7 @@ slim_hidden_def (cairo_surface_set_fallb
 cairo_bool_t
 _cairo_surface_has_device_transform (cairo_surface_t *surface)
 {
-    return ! _cairo_matrix_is_identity (&surface->device_transform);
+    return _cairo_matrix_has_transform (&surface->device_transform);
 }
 
 /**
diff --git a/src/cairoint.h b/src/cairoint.h
index bf7d288..6b24bb3 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -2143,6 +2143,15 @@ cairo_private void
 _cairo_matrix_to_pixman_matrix (const cairo_matrix_t	*matrix,
 				pixman_transform_t	*pixman_transform);
 
+cairo_private cairo_bool_t
+_cairo_matrix_has_transform (const cairo_matrix_t *matrix);
+
+cairo_private void
+_cairo_matrix_translate_inverse (cairo_matrix_t *matrix, double tx, double ty);
+
+cairo_private void
+_cairo_matrix_scale_inverse (cairo_matrix_t *matrix, double sx, double sy);
+
 /* cairo_traps.c */
 cairo_private void
 _cairo_traps_init (cairo_traps_t *traps);



More information about the cairo mailing list