[cairo] Composite logging

Jeff Muizelaar jeff at infidigm.net
Tue Mar 27 12:25:36 PDT 2007


The included patch adds support for logging the compositing operations
that cairo does. It logs pretty much all of the information except for
image data for the compositing operation to composite.log in a binary
format. An included program 'cairo-log-print' pretty prints some of the
information that is loggged. It is currently pretty simplistic but could
easily do more.

The patch is primarily meant for debugging and is pretty hackish,
however it gets the job done.

-Jeff

diff --git a/Makefile.am b/Makefile.am
index 63b47d8..0fb2606 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,5 +1,5 @@
-DIST_SUBDIRS = pixman src boilerplate test perf doc
-SUBDIRS = pixman src doc
+DIST_SUBDIRS = pixman src boilerplate test perf log doc
+SUBDIRS = pixman src doc log
 # libpng is required for our test programs
 if CAIRO_HAS_PNG_FUNCTIONS
 SUBDIRS += boilerplate test
@@ -22,6 +22,9 @@ check-valgrind: all
 perf: all
 	cd perf && $(MAKE) $(AM_MAKEFLAGS) perf
 
+log: all
+	cd log && $(MAKE) $(AM_MAKEFLAGS) log
+
 EXTRA_DIST = \
 	BUGS \
 	CODING_STYLE \
diff --git a/configure.in b/configure.in
index 8acca66..a6227b1 100644
--- a/configure.in
+++ b/configure.in
@@ -682,6 +682,10 @@ esac
 AC_SUBST(PKGCONFIG_REQUIRES)
 
 dnl ===========================================================================
+dnl Set large file
+CFLAGS="$CFLAGS -D_FILE_OFFSET_BITS=64"
+
+dnl ===========================================================================
 dnl Check for MMX
 
 MMX_CFLAGS="-mmmx -Winline --param inline-unit-growth=10000 --param large-function-growth=10000"
@@ -873,6 +877,7 @@ src/Makefile
 test/Makefile
 test/pdiff/Makefile
 perf/Makefile
+log/Makefile
 doc/Makefile
 doc/public/Makefile
 doc/public/version.xml
diff --git a/log/Makefile.am b/log/Makefile.am
new file mode 100644
index 0000000..0c67778
--- /dev/null
+++ b/log/Makefile.am
@@ -0,0 +1,28 @@
+# We're using _GNU_SOURCE to get the prototype for asprintf. This may
+# not be the most portable approach, but it is pragmatic and I'm
+# willing to do something cleaner as soon as it causes someone a
+# problem.
+INCLUDES =					\
+	-D_GNU_SOURCE				\
+	-I$(srcdir)				\
+	-I$(top_srcdir)/boilerplate		\
+	-I$(top_srcdir)/src			\
+	-I$(top_builddir)/src			\
+	-I$(top_srcdir)/pixman/src		\
+	$(CAIRO_CFLAGS)
+
+noinst_PROGRAMS = cairo-log-print
+
+cairo_log_print_SOURCES =	\
+	cairo-log-print.c
+
+LDADD = $(top_builddir)/boilerplate/libcairoboilerplate.la \
+	$(top_builddir)/src/libcairo.la
+
+$(top_builddir)/boilerplate/libcairoboilerplate.la:
+	cd $(top_builddir)/boilerplate && $(MAKE) $(AM_MAKEFLAGS) libcairoboilerplate.la
+
+$(top_builddir)/src/libcairo.la:
+	cd $(top_builddir)/src && $(MAKE) $(AM_MAKEFLAGS) libcairo.la
+
+log: cairo-log-print
diff --git a/log/cairo-log-print.c b/log/cairo-log-print.c
new file mode 100644
index 0000000..afc0107
--- /dev/null
+++ b/log/cairo-log-print.c
@@ -0,0 +1,174 @@
+#include <cairo.h>
+#include <cairoint.h>
+#include <stdio.h>
+#include <cairo-xlib.h>
+struct composite_log {
+    cairo_operator_t op;
+    cairo_pattern_union_t *src;
+    cairo_pattern_union_t *mask;
+    cairo_surface_t *dst;
+    int src_x;
+    int src_y;
+    int mask_x;
+    int mask_y;
+    int dst_x;
+    int dst_y;
+    int width;
+    int height;
+};
+#include <X11/Xlib.h>
+
+#include <X11/extensions/Xrender.h>
+#include <X11/extensions/renderproto.h>
+typedef struct _cairo_xlib_screen_info cairo_xlib_screen_info_t;
+
+struct _cairo_xlib_screen_info {
+    cairo_xlib_screen_info_t *next;
+
+    Display *display;
+    Screen *screen;
+    cairo_bool_t has_render;
+
+    cairo_font_options_t font_options;
+};
+
+
+struct _cairo_xlib_surface {
+    cairo_surface_t base;
+
+    Display *dpy;
+    cairo_xlib_screen_info_t *screen_info;
+
+    GC gc;
+    Drawable drawable;
+    Screen *screen;
+    cairo_bool_t owns_pixmap;
+    Visual *visual;
+
+    int use_pixmap;
+
+    int render_major;
+    int render_minor;
+
+    /* TRUE if the server has a bug with repeating pictures
+     *
+     *  https://bugs.freedesktop.org/show_bug.cgi?id=3566
+     *
+     * We can't test for this because it depends on whether the
+     * picture is in video memory or not.
+     *
+     * We also use this variable as a guard against a second
+     * independent bug with transformed repeating pictures:
+     *
+     * http://lists.freedesktop.org/archives/cairo/2004-September/001839.html
+     *
+     * Both are fixed in xorg >= 6.9 and hopefully in > 6.8.2, so
+     * we can reuse the test for now.
+     */
+    cairo_bool_t buggy_repeat;
+
+    int width;
+    int height;
+    int depth;
+
+    Picture dst_picture, src_picture;
+
+    cairo_bool_t have_clip_rects;
+    XRectangle *clip_rects;
+    int num_clip_rects;
+
+    XRenderPictFormat *xrender_format;
+};
+
+typedef struct _cairo_xlib_surface cairo_xlib_surface_t;
+
+
+struct composite_log *composite_log_load(void);
+extern const char *log_name;
+#define OP(a) if (op == a) return #a
+const char * op_name(cairo_operator_t op);
+const char * op_name(cairo_operator_t op)
+{
+    OP(CAIRO_OPERATOR_CLEAR);
+
+    OP(CAIRO_OPERATOR_SOURCE);
+    OP(CAIRO_OPERATOR_OVER);
+    OP(CAIRO_OPERATOR_IN);
+    OP(CAIRO_OPERATOR_OUT);
+    OP(CAIRO_OPERATOR_ATOP);
+
+    OP(CAIRO_OPERATOR_DEST);
+    OP(CAIRO_OPERATOR_DEST_OVER);
+    OP(CAIRO_OPERATOR_DEST_IN);
+    OP(CAIRO_OPERATOR_DEST_OUT);
+    OP(CAIRO_OPERATOR_DEST_ATOP);
+
+    OP(CAIRO_OPERATOR_XOR);
+    OP(CAIRO_OPERATOR_ADD);
+    OP(CAIRO_OPERATOR_SATURATE);
+    return "unknown";
+}
+#define FORMAT(a) if (op == a) return #a
+const char * format_name(cairo_format_t fmt);
+const char * format_name(cairo_format_t fmt)
+{
+    if (fmt == CAIRO_FORMAT_ARGB32)
+	return "ARGB32";
+    if (fmt == CAIRO_FORMAT_RGB24)
+	return "RGB24";
+    if (fmt == CAIRO_FORMAT_A8)
+	return "A8";
+    if (fmt == CAIRO_FORMAT_A1)
+	return "A1";
+    return "unknown";
+}
+
+void print_surface(cairo_surface_t *s);
+void print_surface(cairo_surface_t *s)
+{
+    if (s->type == CAIRO_SURFACE_TYPE_IMAGE) {
+	cairo_image_surface_t *is = (cairo_image_surface_t*)s;
+	printf("is(%s, %d, %d)", format_name(is->format), is->height, is->width);
+    } else if (s->type == CAIRO_SURFACE_TYPE_XLIB) {
+	cairo_xlib_surface_t *xs = (cairo_xlib_surface_t*)s;
+	printf("xs(%d, %d)", xs->height, xs->width);
+    } else
+	printf("%p", s);
+}
+
+void print_pattern(cairo_pattern_union_t *pat);
+void print_pattern(cairo_pattern_union_t *pat)
+{
+    if (pat) {
+	if (pat->base.type == CAIRO_PATTERN_TYPE_SURFACE)
+	    print_surface(pat->surface.surface);
+	else if (pat->base.type == CAIRO_PATTERN_TYPE_SOLID)
+	    printf("solid");
+	else if (pat->base.type == CAIRO_PATTERN_TYPE_LINEAR)
+	    printf("linear");
+	else if (pat->base.type == CAIRO_PATTERN_TYPE_RADIAL)
+	    printf("radial");
+	else
+	    printf("%d (%d %d)-%p", pat->base.type, pat->base.ref_count, pat->base.status, pat);
+    } else
+	printf("%p", pat);
+}
+
+int main(int argc, char **argv)
+{
+    log_name = "perf-log";
+    if (argc > 1)
+	log_name = argv[1];
+    struct composite_log *e;
+    while ((e = composite_log_load())) {
+	printf("op: %s(%dx%d)", op_name(e->op), e->height, e->width);
+	putchar(' ');
+	print_pattern(e->src);
+	putchar(' ');
+	print_pattern(e->mask);
+	putchar(' ');
+	print_surface(e->dst);
+	putchar('\n');
+    }
+    return 0;
+}
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index 6279012..eb3f9ed 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -912,6 +912,21 @@ _cairo_image_surface_fill_rectangles (void		      *abstract_surface,
 
     return CAIRO_STATUS_SUCCESS;
 }
+struct composite_log {
+    cairo_operator_t op;
+    cairo_pattern_union_t *src;
+    cairo_pattern_union_t *mask;
+    cairo_surface_t *dst;
+    int src_x;
+    int src_y;
+    int mask_x;
+    int mask_y;
+    int dst_x;
+    int dst_y;
+    int width;
+    int height;
+};
+void composite_log_save(struct composite_log *log);
 
 static cairo_int_status_t
 _cairo_image_surface_composite_trapezoids (cairo_operator_t	op,
@@ -933,6 +948,7 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t	op,
     cairo_int_status_t		status;
     pixman_image_t		*mask;
     pixman_format_t		*format;
+    cairo_format_t		cairo_format;
     pixman_bits_t		*mask_data;
     int				mask_stride;
     int				mask_bpp;
@@ -975,6 +991,7 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t	op,
     switch (antialias) {
     case CAIRO_ANTIALIAS_NONE:
 	format = pixman_format_create (PIXMAN_FORMAT_NAME_A1);
+	cairo_format = CAIRO_FORMAT_A1;
 	mask_stride = (width + 31)/8;
 	mask_bpp = 1;
  	break;
@@ -983,6 +1000,7 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t	op,
     case CAIRO_ANTIALIAS_DEFAULT:
     default:
 	format = pixman_format_create (PIXMAN_FORMAT_NAME_A8);
+	cairo_format = CAIRO_FORMAT_A8;
 	mask_stride = (width + 3) & ~3;
 	mask_bpp = 8;
  	break;
@@ -1012,6 +1030,24 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t	op,
      * somehow. */
     pixman_add_trapezoids (mask, - dst_x, - dst_y,
 			   (pixman_trapezoid_t *) traps, num_traps);
+    cairo_surface_t *mask_i = _cairo_image_surface_create_for_pixman_image (mask, cairo_format);
+    cairo_pattern_t *mask_p = cairo_pattern_create_for_surface(mask_i);
+    struct composite_log log = {
+            .op = op,
+            .src = (cairo_pattern_union_t*)pattern,
+            .mask = (cairo_pattern_union_t*)mask_p,
+            .dst = (cairo_surface_t*)dst,
+            .src_x = src_x + attributes.x_offset,
+            .src_y = src_y + attributes.y_offset,
+            .mask_x = 0,
+            .mask_y = 0,
+            .dst_x = dst_x,
+	    .dst_y = dst_y,
+            .width = width,
+            .height = height
+    };
+    composite_log_save(&log);
+    cairo_pattern_destroy(mask_p);
 
     pixman_composite (_pixman_operator (op),
 		      src->pixman_image,
@@ -1030,7 +1066,8 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t	op,
 								 src_x, src_y,
 								 0, 0,
 								 dst_x, dst_y, width, height);
-    pixman_image_destroy (mask);
+    //pixman_image_destroy (mask);
+    cairo_surface_destroy(mask_i);
 
  CLEANUP_IMAGE_DATA:
     free (mask_data);
diff --git a/src/cairo-mutex-list.h b/src/cairo-mutex-list.h
index c054e11..59d4f4b 100644
--- a/src/cairo-mutex-list.h
+++ b/src/cairo-mutex-list.h
@@ -39,6 +39,8 @@ CAIRO_MUTEX_DECLARE (_cairo_pattern_solid_cache_lock);
 CAIRO_MUTEX_DECLARE (_cairo_font_face_mutex);
 CAIRO_MUTEX_DECLARE (_cairo_scaled_font_map_mutex);
 
+CAIRO_MUTEX_DECLARE(_composite_log_lock);
+
 #if CAIRO_HAS_FT_FONT
 CAIRO_MUTEX_DECLARE (_cairo_ft_unscaled_font_map_mutex);
 #endif
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index aa4f3b8..570b83a 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -1057,6 +1057,194 @@ _cairo_surface_snapshot (cairo_surface_t *surface)
     return _cairo_surface_fallback_snapshot (surface);
 }
 
+struct composite_log {
+    cairo_operator_t op;
+    cairo_pattern_union_t *src;
+    cairo_pattern_union_t *mask;
+    cairo_surface_t *dst;
+    int src_x;
+    int src_y;
+    int mask_x;
+    int mask_y;
+    int dst_x;
+    int dst_y;
+    int width;
+    int height;
+};
+#include <stdio.h>
+FILE *log_fd;
+#define save(a) fwrite((a), sizeof(*(a)), 1, log_fd)
+static void *__load(size_t size)
+{
+    void *dst = malloc(size);
+    fread(dst, size, 1, log_fd);
+    return dst;
+}
+#define load(a) (typeof(a))__load(sizeof(*(a)))
+static void *__reload(void *dst, size_t orig_size, size_t size)
+{
+    void *new;
+    new = malloc(size);
+    memcpy(new, dst, orig_size);
+    fread((char*)new + orig_size, size - orig_size, 1, log_fd);
+    return new;
+}
+#define reload(a, new_type) (typeof(a))__reload(a, sizeof(*(a)), sizeof(new_type))
+#include <X11/Xlib.h>
+
+#include <X11/extensions/Xrender.h>
+#include <X11/extensions/renderproto.h>
+typedef struct _cairo_xlib_screen_info cairo_xlib_screen_info_t;
+
+struct _cairo_xlib_screen_info {
+    cairo_xlib_screen_info_t *next;
+
+    Display *display;
+    Screen *screen;
+    cairo_bool_t has_render;
+
+    cairo_font_options_t font_options;
+};
+
+
+struct _cairo_xlib_surface {
+    cairo_surface_t base;
+
+    Display *dpy;
+    cairo_xlib_screen_info_t *screen_info;
+
+    GC gc;
+    Drawable drawable;
+    Screen *screen;
+    cairo_bool_t owns_pixmap;
+    Visual *visual;
+
+    int use_pixmap;
+
+    int render_major;
+    int render_minor;
+
+    /* TRUE if the server has a bug with repeating pictures
+     *
+     *  https://bugs.freedesktop.org/show_bug.cgi?id=3566
+     *
+     * We can't test for this because it depends on whether the
+     * picture is in video memory or not.
+     *
+     * We also use this variable as a guard against a second
+     * independent bug with transformed repeating pictures:
+     *
+     * http://lists.freedesktop.org/archives/cairo/2004-September/001839.html
+     *
+     * Both are fixed in xorg >= 6.9 and hopefully in > 6.8.2, so
+     * we can reuse the test for now.
+     */
+    cairo_bool_t buggy_repeat;
+
+    int width;
+    int height;
+    int depth;
+
+    Picture dst_picture, src_picture;
+
+    cairo_bool_t have_clip_rects;
+    XRectangle *clip_rects;
+    int num_clip_rects;
+
+    XRenderPictFormat *xrender_format;
+};
+
+typedef struct _cairo_xlib_surface cairo_xlib_surface_t;
+
+static void surface_save(cairo_surface_t *s)
+{
+    if (s->type == CAIRO_SURFACE_TYPE_IMAGE)
+	save((cairo_image_surface_t*)s);
+    else if (s->type == CAIRO_SURFACE_TYPE_XLIB)
+	save((cairo_xlib_surface_t*)s);
+    else {
+	    printf("unexpected surface type: %d\n", s->type);
+	    save(s);
+    }
+}
+
+static cairo_surface_t *surface_load(void)
+{
+    cairo_surface_t *ret;
+    ret = load(ret);
+
+    if (ret->type == CAIRO_SURFACE_TYPE_IMAGE)
+	ret = reload(ret, cairo_image_surface_t);
+    else if (ret->type == CAIRO_SURFACE_TYPE_XLIB)
+	ret = reload(ret, cairo_xlib_surface_t);
+    else {
+	    printf("unexpected surface type: %d\n", ret->type);
+    }
+    return ret;
+}
+
+static void pattern_save(cairo_pattern_union_t *pat)
+{
+    save(pat);
+    if (pat->base.type == CAIRO_PATTERN_TYPE_SURFACE) {
+	surface_save(pat->surface.surface);
+    }
+}
+
+static cairo_pattern_union_t *pattern_load(void)
+{
+    cairo_pattern_union_t *pat;
+    pat = load(pat);
+    if (pat->base.type == CAIRO_PATTERN_TYPE_SURFACE) {
+	pat->surface.surface = surface_load();
+    }
+    return pat;
+}
+
+void composite_log_save(struct composite_log *log)
+{
+    CAIRO_MUTEX_LOCK(_composite_log_lock);
+    static long last_pos;
+    if (!log_fd) {
+	log_fd = fopen("composite.log", "w+");
+    }
+    unsigned int magic = 0xdeadbabe;
+    save(&magic);
+    save(log);
+    if (log->src)
+	pattern_save(log->src);
+    if (log->mask)
+	pattern_save(log->mask);
+    if (log->dst)
+	surface_save(log->dst);
+    //printf("wrote: %d bytes\n", ftell(log_fd) - last_pos);
+    last_pos = ftell(log_fd);
+    CAIRO_MUTEX_UNLOCK(_composite_log_lock);
+}
+
+char *log_name;
+
+struct composite_log *composite_log_load(void)
+{
+    if (!log_fd)
+	log_fd = fopen(log_name, "r");
+    if (feof(log_fd))
+	return NULL;
+    unsigned int *magic;
+    magic = load(magic);
+    if (*magic != 0xdeadbabe)
+	printf("missing magic: %x\n", *magic);
+    struct composite_log *log;
+    log = load(log);
+    if (log->src)
+	log->src = pattern_load();
+    if (log->mask)
+	log->mask = pattern_load();
+    if (log->dst)
+	log->dst = surface_load();
+    return log;
+}
+
 cairo_status_t
 _cairo_surface_composite (cairo_operator_t	op,
 			  cairo_pattern_t	*src,
@@ -1072,7 +1260,21 @@ _cairo_surface_composite (cairo_operator_t	op,
 			  unsigned int		height)
 {
     cairo_int_status_t status;
-
+    struct composite_log log = {
+	    .op = op,
+	    .src = (cairo_pattern_union_t*)src,
+	    .mask = (cairo_pattern_union_t*)mask,
+	    .dst = dst,
+	    .src_x = src_x,
+	    .src_y = src_y,
+	    .mask_x = mask_x,
+	    .mask_y = mask_y,
+	    .dst_x = dst_x,
+	    .dst_y = dst_y,
+	    .width = width,
+	    .height = height
+    };
+    composite_log_save(&log);
     assert (! dst->is_snapshot);
 
     if (mask) {


More information about the cairo mailing list