added MouseWheel event support for Silverlight 3.0
[moon.git] / cairo / perf / cairo-perf.c
blob613d15cf9df90dd1d6f09c708997540cd88781dc
1 /* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
2 /*
3 * Copyright © 2006 Mozilla Corporation
4 * Copyright © 2006 Red Hat, Inc.
6 * Permission to use, copy, modify, distribute, and sell this software
7 * and its documentation for any purpose is hereby granted without
8 * fee, provided that the above copyright notice appear in all copies
9 * and that both that copyright notice and this permission notice
10 * appear in supporting documentation, and that the name of
11 * the authors not be used in advertising or publicity pertaining to
12 * distribution of the software without specific, written prior
13 * permission. The authors make no representations about the
14 * suitability of this software for any purpose. It is provided "as
15 * is" without express or implied warranty.
17 * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
18 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
19 * FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
20 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
21 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
22 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
23 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 * Authors: Vladimir Vukicevic <vladimir@pobox.com>
26 * Carl Worth <cworth@cworth.org>
29 #define _GNU_SOURCE 1 /* for sched_getaffinity() */
31 #include "cairo-perf.h"
32 #include "cairo-stats.h"
34 #include "cairo-boilerplate-getopt.h"
36 /* For basename */
37 #ifdef HAVE_LIBGEN_H
38 #include <libgen.h>
39 #endif
41 #if HAVE_FCFINI
42 #include <fontconfig/fontconfig.h>
43 #endif
45 #ifdef HAVE_SCHED_H
46 #include <sched.h>
47 #endif
49 #define CAIRO_PERF_ITERATIONS_DEFAULT 100
50 #define CAIRO_PERF_LOW_STD_DEV 0.03
51 #define CAIRO_PERF_STABLE_STD_DEV_COUNT 5
53 typedef struct _cairo_perf_case {
54 CAIRO_PERF_DECL (*run);
55 unsigned int min_size;
56 unsigned int max_size;
57 } cairo_perf_case_t;
59 const cairo_perf_case_t perf_cases[];
61 /* Some targets just aren't that interesting for performance testing,
62 * (not least because many of these surface types use a meta-surface
63 * and as such defer the "real" rendering to later, so our timing
64 * loops wouldn't count the real work, just the recording by the
65 * meta-surface. */
66 static cairo_bool_t
67 target_is_measurable (cairo_boilerplate_target_t *target)
69 switch (target->expected_type) {
70 case CAIRO_SURFACE_TYPE_IMAGE:
71 if (strcmp (target->name, "pdf") == 0 ||
72 strcmp (target->name, "ps") == 0)
74 return FALSE;
76 else
78 return TRUE;
80 case CAIRO_SURFACE_TYPE_XLIB:
81 if (strcmp (target->name, "xlib-fallback") == 0)
83 return FALSE;
85 else
87 return TRUE;
89 case CAIRO_SURFACE_TYPE_XCB:
90 case CAIRO_SURFACE_TYPE_GLITZ:
91 case CAIRO_SURFACE_TYPE_QUARTZ:
92 case CAIRO_SURFACE_TYPE_WIN32:
93 case CAIRO_SURFACE_TYPE_BEOS:
94 case CAIRO_SURFACE_TYPE_DIRECTFB:
95 #if CAIRO_VERSION_MAJOR > 1 || (CAIRO_VERSION_MAJOR == 1 && CAIRO_VERSION_MINOR > 2)
96 case CAIRO_SURFACE_TYPE_OS2:
97 #endif
98 return TRUE;
99 case CAIRO_SURFACE_TYPE_PDF:
100 case CAIRO_SURFACE_TYPE_PS:
101 case CAIRO_SURFACE_TYPE_SVG:
102 default:
103 return FALSE;
107 static const char *
108 _content_to_string (cairo_content_t content, cairo_bool_t similar)
110 switch (content|similar) {
111 case CAIRO_CONTENT_COLOR:
112 return "rgb";
113 case CAIRO_CONTENT_COLOR|1:
114 return "rgb&";
115 case CAIRO_CONTENT_ALPHA:
116 return "a";
117 case CAIRO_CONTENT_ALPHA|1:
118 return "a&";
119 case CAIRO_CONTENT_COLOR_ALPHA:
120 return "rgba";
121 case CAIRO_CONTENT_COLOR_ALPHA|1:
122 return "rgba&";
123 default:
124 return "<unknown_content>";
128 static cairo_bool_t
129 cairo_perf_has_similar (cairo_perf_t *perf)
131 cairo_surface_t *target = cairo_get_target (perf->cr);
133 /* exclude the image backend */
134 if (cairo_surface_get_type (target) == CAIRO_SURFACE_TYPE_IMAGE)
135 return FALSE;
137 return TRUE;
140 void
141 cairo_perf_run (cairo_perf_t *perf,
142 const char *name,
143 cairo_perf_func_t perf_func)
145 static cairo_bool_t first_run = TRUE;
146 unsigned int i, similar, has_similar;
147 cairo_perf_ticks_t *times;
148 cairo_stats_t stats = {0.0, 0.0};
149 int low_std_dev_count;
151 if (perf->num_names) {
152 for (i = 0; i < perf->num_names; i++)
153 if (strstr (name, perf->names[i]))
154 goto NAME_FOUND;
155 return;
157 NAME_FOUND:
159 if (perf->list_only) {
160 printf ("%s\n", name);
161 return;
164 if (first_run) {
165 if (perf->raw)
166 printf ("[ # ] %s-%-s %s %s %s ...\n",
167 "backend", "content", "test-size", "ticks-per-ms", "time(ticks)");
168 else
169 printf ("[ # ] %8s-%-4s %28s %8s %8s %5s %5s %s\n",
170 "backend", "content", "test-size", "min(ticks)", "min(ms)", "median(ms)",
171 "stddev.", "iterations");
172 first_run = FALSE;
175 times = perf->times;
177 has_similar = cairo_perf_has_similar (perf);
178 for (similar = 0; similar <= has_similar; similar++) {
179 /* We run one iteration in advance to warm caches, etc. */
180 cairo_perf_yield ();
181 if (similar)
182 cairo_push_group_with_content (perf->cr,
183 cairo_boilerplate_content (perf->target->content));
184 (perf_func) (perf->cr, perf->size, perf->size);
185 if (similar)
186 cairo_pattern_destroy (cairo_pop_group (perf->cr));
188 low_std_dev_count = 0;
189 for (i =0; i < perf->iterations; i++) {
190 cairo_perf_yield ();
191 if (similar)
192 cairo_push_group_with_content (perf->cr,
193 cairo_boilerplate_content (perf->target->content));
194 times[i] = (perf_func) (perf->cr, perf->size, perf->size);
195 if (similar)
196 cairo_pattern_destroy (cairo_pop_group (perf->cr));
198 if (perf->raw) {
199 if (i == 0)
200 printf ("[*] %s-%s %s-%d %g",
201 perf->target->name,
202 _content_to_string (perf->target->content, similar),
203 name, perf->size,
204 cairo_perf_ticks_per_second () / 1000.0);
205 printf (" %lld", (long long) times[i]);
206 } else if (! perf->exact_iterations) {
207 if (i > 0) {
208 _cairo_stats_compute (&stats, times, i+1);
210 if (stats.std_dev <= CAIRO_PERF_LOW_STD_DEV)
212 low_std_dev_count++;
213 if (low_std_dev_count >= CAIRO_PERF_STABLE_STD_DEV_COUNT)
214 break;
215 } else {
216 low_std_dev_count = 0;
222 if (perf->raw) {
223 printf ("\n");
224 } else {
225 _cairo_stats_compute (&stats, times, i);
226 printf ("[%3d] %8s-%-5s %26s-%-3d ",
227 perf->test_number, perf->target->name,
228 _content_to_string (perf->target->content, similar),
229 name, perf->size);
231 printf ("%10lld %#8.3f %#8.3f %#5.2f%% %3d\n",
232 (long long) stats.min_ticks,
233 (stats.min_ticks * 1000.0) / cairo_perf_ticks_per_second (),
234 (stats.median_ticks * 1000.0) / cairo_perf_ticks_per_second (),
235 stats.std_dev * 100.0, stats.iterations);
238 perf->test_number++;
242 static void
243 usage (const char *argv0)
245 fprintf (stderr,
246 "Usage: %s [-l] [-r] [-i iterations] [test-names ...]\n"
247 " %s -l\n"
248 "\n"
249 "Run the cairo performance test suite over the given tests (all by default)\n"
250 "The command-line arguments are interpreted as follows:\n"
251 "\n"
252 " -r raw; display each time measurement instead of summary statistics\n"
253 " -i iterations; specify the number of iterations per test case\n"
254 " -l list only; just list selected test case names without executing\n"
255 "\n"
256 "If test names are given they are used as sub-string matches so a command\n"
257 "such as \"cairo-perf text\" can be used to run all text test cases.\n",
258 argv0, argv0);
261 static void
262 parse_options (cairo_perf_t *perf, int argc, char *argv[])
264 int c;
265 const char *iters;
266 char *end;
268 if ((iters = getenv("CAIRO_PERF_ITERATIONS")) && *iters)
269 perf->iterations = strtol(iters, NULL, 0);
270 else
271 perf->iterations = CAIRO_PERF_ITERATIONS_DEFAULT;
272 perf->exact_iterations = 0;
274 perf->raw = FALSE;
275 perf->list_only = FALSE;
276 perf->names = NULL;
277 perf->num_names = 0;
279 while (1) {
280 c = _cairo_getopt (argc, argv, "i:lr");
281 if (c == -1)
282 break;
284 switch (c) {
285 case 'i':
286 perf->exact_iterations = TRUE;
287 perf->iterations = strtoul (optarg, &end, 10);
288 if (*end != '\0') {
289 fprintf (stderr, "Invalid argument for -i (not an integer): %s\n",
290 optarg);
291 exit (1);
293 break;
294 case 'l':
295 perf->list_only = TRUE;
296 break;
297 case 'r':
298 perf->raw = TRUE;
299 break;
300 default:
301 fprintf (stderr, "Internal error: unhandled option: %c\n", c);
302 /* fall-through */
303 case '?':
304 usage (argv[0]);
305 exit (1);
308 if (optind < argc) {
309 perf->names = &argv[optind];
310 perf->num_names = argc - optind;
314 static int
315 check_cpu_affinity(void)
317 #ifdef HAVE_SCHED_GETAFFINITY
319 cpu_set_t affinity;
320 int i, cpu_count;
322 if (sched_getaffinity(0, sizeof(affinity), &affinity)) {
323 perror("sched_getaffinity");
324 return -1;
327 for(i = 0, cpu_count = 0; i < CPU_SETSIZE; ++i) {
328 if (CPU_ISSET(i, &affinity))
329 ++cpu_count;
332 if (cpu_count > 1) {
333 fputs(
334 "WARNING: cairo-perf has not been bound to a single CPU.\n",
335 stderr);
336 return -1;
339 return 0;
340 #else
341 fputs(
342 "WARNING: Cannot check CPU affinity for this platform.\n",
343 stderr);
344 return -1;
345 #endif
348 static void
349 cairo_perf_fini (cairo_perf_t *perf)
351 cairo_boilerplate_free_targets (perf->targets);
352 free (perf->times);
353 cairo_debug_reset_static_data ();
354 #if HAVE_FCFINI
355 FcFini ();
356 #endif
361 main (int argc, char *argv[])
363 int i, j;
364 cairo_perf_t perf;
365 cairo_surface_t *surface;
367 parse_options (&perf, argc, argv);
369 if (check_cpu_affinity()) {
370 fputs(
371 "NOTICE: cairo-perf and the X server should be bound to CPUs (either the same\n"
372 "or separate) on SMP systems. Not doing so causes random results when the X\n"
373 "server is moved to or from cairo-perf's CPU during the benchmarks:\n"
374 "\n"
375 " $ sudo taskset -cp 0 $(pidof X)\n"
376 " $ taskset -cp 1 $$\n"
377 "\n"
378 "See taskset(1) for information about changing CPU affinity.\n",
379 stderr);
382 perf.targets = cairo_boilerplate_get_targets (&perf.num_targets, NULL);
383 perf.times = xmalloc (perf.iterations * sizeof (cairo_perf_ticks_t));
385 for (i = 0; i < perf.num_targets; i++) {
386 cairo_boilerplate_target_t *target = perf.targets[i];
388 if (! target_is_measurable (target))
389 continue;
391 perf.target = target;
392 perf.test_number = 0;
394 for (j = 0; perf_cases[j].run; j++) {
395 const cairo_perf_case_t *perf_case = &perf_cases[j];
397 for (perf.size = perf_case->min_size;
398 perf.size <= perf_case->max_size;
399 perf.size *= 2)
401 void *closure;
403 surface = (target->create_surface) (NULL,
404 target->content,
405 perf.size, perf.size,
406 perf.size, perf.size,
407 CAIRO_BOILERPLATE_MODE_PERF,
409 &closure);
410 if (surface == NULL) {
411 fprintf (stderr,
412 "Error: Failed to create target surface: %s\n",
413 target->name);
414 continue;
417 cairo_perf_timer_set_synchronize (target->synchronize, closure);
419 perf.cr = cairo_create (surface);
421 perf_case->run (&perf, perf.cr, perf.size, perf.size);
423 if (cairo_status (perf.cr)) {
424 fprintf (stderr, "Error: Test left cairo in an error state: %s\n",
425 cairo_status_to_string (cairo_status (perf.cr)));
428 cairo_destroy (perf.cr);
429 cairo_surface_destroy (surface);
431 if (target->cleanup)
432 target->cleanup (closure);
437 cairo_perf_fini (&perf);
439 return 0;
442 const cairo_perf_case_t perf_cases[] = {
443 { paint, 256, 512},
444 { paint_with_alpha, 256, 512},
445 { fill, 64, 256},
446 { stroke, 64, 256},
447 { text, 64, 256},
448 { tessellate, 100, 100},
449 { subimage_copy, 16, 512},
450 { pattern_create_radial, 16, 16},
451 { zrusin, 415, 415},
452 { world_map, 800, 800},
453 { box_outline, 100, 100},
454 { mosaic, 800, 800 },
455 { long_lines, 100, 100},
456 { unaligned_clip, 100, 100},
457 { rectangles, 512, 512},
458 { rounded_rectangles, 512, 512},
459 { long_dashed_lines, 512, 512},
460 { composite_checker, 16, 512},
461 { NULL }