lowlevel-blt-bench.c revision 6ca29ff0
1/*
2 * Copyright © 2009 Nokia Corporation
3 * Copyright © 2010 Movial Creative Technologies Oy
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include "utils.h"
29
30#define SOLID_FLAG 1
31#define CA_FLAG    2
32
33#define L1CACHE_SIZE (8 * 1024)
34#define L2CACHE_SIZE (128 * 1024)
35
36/* This is applied to both L1 and L2 tests - alternatively, you could
37 * parameterise bench_L or split it into two functions. It could be
38 * read at runtime on some architectures, but it only really matters
39 * that it's a number that's an integer divisor of both cacheline
40 * lengths, and further, it only really matters for caches that don't
41 * do allocate0on-write. */
42#define CACHELINE_LENGTH (32) /* bytes */
43
44#define WIDTH  1920
45#define HEIGHT 1080
46#define BUFSIZE (WIDTH * HEIGHT * 4)
47#define XWIDTH 256
48#define XHEIGHT 256
49#define TILEWIDTH 32
50#define TINYWIDTH 8
51
52#define EXCLUDE_OVERHEAD 1
53
54uint32_t *dst;
55uint32_t *src;
56uint32_t *mask;
57
58double bandwidth = 0.0;
59
60double
61bench_memcpy ()
62{
63    int64_t n = 0, total;
64    double  t1, t2;
65    int     x = 0;
66
67    t1 = gettime ();
68    while (1)
69    {
70	memcpy (dst, src, BUFSIZE - 64);
71	memcpy (src, dst, BUFSIZE - 64);
72	n += 4 * (BUFSIZE - 64);
73	t2 = gettime ();
74	if (t2 - t1 > 0.5)
75	    break;
76    }
77    n = total = n * 5;
78    t1 = gettime ();
79    while (n > 0)
80    {
81	if (++x >= 64)
82	    x = 0;
83	memcpy ((char *)dst + 1, (char *)src + x, BUFSIZE - 64);
84	memcpy ((char *)src + 1, (char *)dst + x, BUFSIZE - 64);
85	n -= 4 * (BUFSIZE - 64);
86    }
87    t2 = gettime ();
88    return (double)total / (t2 - t1);
89}
90
91static pixman_bool_t use_scaling = FALSE;
92static pixman_filter_t filter = PIXMAN_FILTER_NEAREST;
93static pixman_bool_t use_csv_output = FALSE;
94
95/* nearly 1x scale factor */
96static pixman_transform_t m =
97{
98    {
99        { pixman_fixed_1 + 1, 0,              0              },
100        { 0,                  pixman_fixed_1, 0              },
101        { 0,                  0,              pixman_fixed_1 }
102    }
103};
104
105static void
106pixman_image_composite_wrapper (pixman_implementation_t *impl,
107				pixman_composite_info_t *info)
108{
109    if (use_scaling)
110    {
111        pixman_image_set_filter (info->src_image, filter, NULL, 0);
112        pixman_image_set_transform(info->src_image, &m);
113    }
114    pixman_image_composite (info->op,
115			    info->src_image, info->mask_image, info->dest_image,
116			    info->src_x, info->src_y,
117			    info->mask_x, info->mask_y,
118			    info->dest_x, info->dest_y,
119			    info->width, info->height);
120}
121
122static void
123pixman_image_composite_empty (pixman_implementation_t *impl,
124			      pixman_composite_info_t *info)
125{
126    if (use_scaling)
127    {
128        pixman_image_set_filter (info->src_image, filter, NULL, 0);
129        pixman_image_set_transform(info->src_image, &m);
130    }
131    pixman_image_composite (info->op,
132			    info->src_image, info->mask_image, info->dest_image,
133			    0, 0, 0, 0, 0, 0, 1, 1);
134}
135
136static inline void
137call_func (pixman_composite_func_t func,
138	   pixman_op_t             op,
139	   pixman_image_t *        src_image,
140	   pixman_image_t *        mask_image,
141	   pixman_image_t *        dest_image,
142	   int32_t		   src_x,
143	   int32_t		   src_y,
144	   int32_t                 mask_x,
145	   int32_t                 mask_y,
146	   int32_t                 dest_x,
147	   int32_t                 dest_y,
148	   int32_t                 width,
149	   int32_t                 height)
150{
151    pixman_composite_info_t info;
152
153    info.op = op;
154    info.src_image = src_image;
155    info.mask_image = mask_image;
156    info.dest_image = dest_image;
157    info.src_x = src_x;
158    info.src_y = src_y;
159    info.mask_x = mask_x;
160    info.mask_y = mask_y;
161    info.dest_x = dest_x;
162    info.dest_y = dest_y;
163    info.width = width;
164    info.height = height;
165
166    func (0, &info);
167}
168
169double
170noinline
171bench_L  (pixman_op_t              op,
172          pixman_image_t *         src_img,
173          pixman_image_t *         mask_img,
174          pixman_image_t *         dst_img,
175          int64_t                  n,
176          pixman_composite_func_t  func,
177          int                      width,
178          int                      lines_count)
179{
180    int64_t      i, j, k;
181    int          x = 0;
182    int          q = 0;
183    volatile int qx;
184
185    for (i = 0; i < n; i++)
186    {
187        /* For caches without allocate-on-write, we need to force the
188         * destination buffer back into the cache on each iteration,
189         * otherwise if they are evicted during the test, they remain
190         * uncached. This doesn't matter for tests which read the
191         * destination buffer, or for caches that do allocate-on-write,
192         * but in those cases this loop just adds constant time, which
193         * should be successfully cancelled out.
194         */
195        for (j = 0; j < lines_count; j++)
196        {
197            for (k = 0; k < width + 62; k += CACHELINE_LENGTH / sizeof *dst)
198            {
199                q += dst[j * WIDTH + k];
200            }
201            q += dst[j * WIDTH + width + 62];
202        }
203	if (++x >= 64)
204	    x = 0;
205	call_func (func, op, src_img, mask_img, dst_img, x, 0, x, 0, 63 - x, 0, width, lines_count);
206    }
207    qx = q;
208
209    return (double)n * lines_count * width;
210}
211
212double
213noinline
214bench_M (pixman_op_t              op,
215         pixman_image_t *         src_img,
216         pixman_image_t *         mask_img,
217         pixman_image_t *         dst_img,
218         int64_t                  n,
219         pixman_composite_func_t  func)
220{
221    int64_t i;
222    int     x = 0;
223
224    for (i = 0; i < n; i++)
225    {
226	if (++x >= 64)
227	    x = 0;
228	call_func (func, op, src_img, mask_img, dst_img, x, 0, x, 0, 1, 0, WIDTH - 64, HEIGHT);
229    }
230
231    return (double)n * (WIDTH - 64) * HEIGHT;
232}
233
234double
235noinline
236bench_HT (pixman_op_t              op,
237          pixman_image_t *         src_img,
238          pixman_image_t *         mask_img,
239          pixman_image_t *         dst_img,
240          int64_t                  n,
241          pixman_composite_func_t  func)
242{
243    double  pix_cnt = 0;
244    int     x = 0;
245    int     y = 0;
246    int64_t i;
247
248    srand (0);
249    for (i = 0; i < n; i++)
250    {
251	int w = (rand () % (TILEWIDTH * 2)) + 1;
252	int h = (rand () % (TILEWIDTH * 2)) + 1;
253	if (x + w > WIDTH)
254	{
255	    x = 0;
256	    y += TILEWIDTH * 2;
257	}
258	if (y + h > HEIGHT)
259	{
260	    y = 0;
261	}
262	call_func (func, op, src_img, mask_img, dst_img, x, y, x, y, x, y, w, h);
263	x += w;
264	pix_cnt += w * h;
265    }
266    return pix_cnt;
267}
268
269double
270noinline
271bench_VT (pixman_op_t              op,
272          pixman_image_t *         src_img,
273          pixman_image_t *         mask_img,
274          pixman_image_t *         dst_img,
275          int64_t                  n,
276          pixman_composite_func_t  func)
277{
278    double  pix_cnt = 0;
279    int     x = 0;
280    int     y = 0;
281    int64_t i;
282
283    srand (0);
284    for (i = 0; i < n; i++)
285    {
286	int w = (rand () % (TILEWIDTH * 2)) + 1;
287	int h = (rand () % (TILEWIDTH * 2)) + 1;
288	if (y + h > HEIGHT)
289	{
290	    y = 0;
291	    x += TILEWIDTH * 2;
292	}
293	if (x + w > WIDTH)
294	{
295	    x = 0;
296	}
297	call_func (func, op, src_img, mask_img, dst_img, x, y, x, y, x, y, w, h);
298	y += h;
299	pix_cnt += w * h;
300    }
301    return pix_cnt;
302}
303
304double
305noinline
306bench_R (pixman_op_t              op,
307         pixman_image_t *         src_img,
308         pixman_image_t *         mask_img,
309         pixman_image_t *         dst_img,
310         int64_t                  n,
311         pixman_composite_func_t  func,
312         int                      maxw,
313         int                      maxh)
314{
315    double  pix_cnt = 0;
316    int64_t i;
317
318    if (maxw <= TILEWIDTH * 2 || maxh <= TILEWIDTH * 2)
319    {
320	printf("error: maxw <= TILEWIDTH * 2 || maxh <= TILEWIDTH * 2\n");
321        return 0;
322    }
323
324    srand (0);
325    for (i = 0; i < n; i++)
326    {
327	int w = (rand () % (TILEWIDTH * 2)) + 1;
328	int h = (rand () % (TILEWIDTH * 2)) + 1;
329	int sx = rand () % (maxw - TILEWIDTH * 2);
330	int sy = rand () % (maxh - TILEWIDTH * 2);
331	int dx = rand () % (maxw - TILEWIDTH * 2);
332	int dy = rand () % (maxh - TILEWIDTH * 2);
333	call_func (func, op, src_img, mask_img, dst_img, sx, sy, sx, sy, dx, dy, w, h);
334	pix_cnt += w * h;
335    }
336    return pix_cnt;
337}
338
339double
340noinline
341bench_RT (pixman_op_t              op,
342          pixman_image_t *         src_img,
343          pixman_image_t *         mask_img,
344          pixman_image_t *         dst_img,
345          int64_t                  n,
346          pixman_composite_func_t  func,
347          int                      maxw,
348          int                      maxh)
349{
350    double  pix_cnt = 0;
351    int64_t i;
352
353    if (maxw <= TINYWIDTH * 2 || maxh <= TINYWIDTH * 2)
354    {
355	printf("error: maxw <= TINYWIDTH * 2 || maxh <= TINYWIDTH * 2\n");
356        return 0;
357    }
358
359    srand (0);
360    for (i = 0; i < n; i++)
361    {
362	int w = (rand () % (TINYWIDTH * 2)) + 1;
363	int h = (rand () % (TINYWIDTH * 2)) + 1;
364	int sx = rand () % (maxw - TINYWIDTH * 2);
365	int sy = rand () % (maxh - TINYWIDTH * 2);
366	int dx = rand () % (maxw - TINYWIDTH * 2);
367	int dy = rand () % (maxh - TINYWIDTH * 2);
368	call_func (func, op, src_img, mask_img, dst_img, sx, sy, sx, sy, dx, dy, w, h);
369	pix_cnt += w * h;
370    }
371    return pix_cnt;
372}
373
374static double
375Mpx_per_sec (double pix_cnt, double t1, double t2, double t3)
376{
377    double overhead = t2 - t1;
378    double testtime = t3 - t2;
379
380    return pix_cnt / (testtime - overhead) / 1e6;
381}
382
383void
384bench_composite (const char *testname,
385                 int         src_fmt,
386                 int         src_flags,
387                 int         op,
388                 int         mask_fmt,
389                 int         mask_flags,
390                 int         dst_fmt,
391                 double      npix)
392{
393    pixman_image_t *                src_img;
394    pixman_image_t *                dst_img;
395    pixman_image_t *                mask_img;
396    pixman_image_t *                xsrc_img;
397    pixman_image_t *                xdst_img;
398    pixman_image_t *                xmask_img;
399    double                          t1, t2, t3, pix_cnt;
400    int64_t                         n, l1test_width, nlines;
401    double                             bytes_per_pix = 0;
402    pixman_bool_t                   bench_pixbuf = FALSE;
403
404    pixman_composite_func_t func = pixman_image_composite_wrapper;
405
406    if (!(src_flags & SOLID_FLAG))
407    {
408        bytes_per_pix += (src_fmt >> 24) / 8.0;
409        src_img = pixman_image_create_bits (src_fmt,
410                                            WIDTH, HEIGHT,
411                                            src,
412                                            WIDTH * 4);
413        xsrc_img = pixman_image_create_bits (src_fmt,
414                                             XWIDTH, XHEIGHT,
415                                             src,
416                                             XWIDTH * 4);
417    }
418    else
419    {
420        src_img = pixman_image_create_bits (src_fmt,
421                                            1, 1,
422                                            src,
423                                            4);
424        xsrc_img = pixman_image_create_bits (src_fmt,
425                                             1, 1,
426                                             src,
427                                             4);
428        pixman_image_set_repeat (src_img, PIXMAN_REPEAT_NORMAL);
429        pixman_image_set_repeat (xsrc_img, PIXMAN_REPEAT_NORMAL);
430    }
431
432    bytes_per_pix += (dst_fmt >> 24) / 8.0;
433    dst_img = pixman_image_create_bits (dst_fmt,
434                                        WIDTH, HEIGHT,
435                                        dst,
436                                        WIDTH * 4);
437
438    mask_img = NULL;
439    xmask_img = NULL;
440    if (strcmp (testname, "pixbuf") == 0 || strcmp (testname, "rpixbuf") == 0)
441    {
442        bench_pixbuf = TRUE;
443    }
444    if (!(mask_flags & SOLID_FLAG) && mask_fmt != PIXMAN_null)
445    {
446        bytes_per_pix += (mask_fmt >> 24) / ((op == PIXMAN_OP_SRC) ? 8.0 : 4.0);
447        mask_img = pixman_image_create_bits (mask_fmt,
448                                             WIDTH, HEIGHT,
449                                             bench_pixbuf ? src : mask,
450                                             WIDTH * 4);
451        xmask_img = pixman_image_create_bits (mask_fmt,
452                                             XWIDTH, XHEIGHT,
453                                             bench_pixbuf ? src : mask,
454                                             XWIDTH * 4);
455    }
456    else if (mask_fmt != PIXMAN_null)
457    {
458        mask_img = pixman_image_create_bits (mask_fmt,
459                                             1, 1,
460                                             mask,
461                                             4);
462        xmask_img = pixman_image_create_bits (mask_fmt,
463                                             1, 1,
464                                             mask,
465                                             4 * 4);
466       pixman_image_set_repeat (mask_img, PIXMAN_REPEAT_NORMAL);
467       pixman_image_set_repeat (xmask_img, PIXMAN_REPEAT_NORMAL);
468    }
469    if ((mask_flags & CA_FLAG) && mask_fmt != PIXMAN_null)
470    {
471       pixman_image_set_component_alpha (mask_img, 1);
472    }
473    xdst_img = pixman_image_create_bits (dst_fmt,
474                                         XWIDTH, XHEIGHT,
475                                         dst,
476                                         XWIDTH * 4);
477
478    if (!use_csv_output)
479        printf ("%24s %c", testname, func != pixman_image_composite_wrapper ?
480                '-' : '=');
481
482    memcpy (dst, src, BUFSIZE);
483    memcpy (src, dst, BUFSIZE);
484
485    l1test_width = L1CACHE_SIZE / 8 - 64;
486    if (l1test_width < 1)
487	l1test_width = 1;
488    if (l1test_width > WIDTH - 64)
489	l1test_width = WIDTH - 64;
490    n = 1 + npix / (l1test_width * 8);
491    t1 = gettime ();
492#if EXCLUDE_OVERHEAD
493    pix_cnt = bench_L (op, src_img, mask_img, dst_img, n, pixman_image_composite_empty, l1test_width, 1);
494#endif
495    t2 = gettime ();
496    pix_cnt = bench_L (op, src_img, mask_img, dst_img, n, func, l1test_width, 1);
497    t3 = gettime ();
498    if (use_csv_output)
499        printf ("%g,", Mpx_per_sec (pix_cnt, t1, t2, t3));
500    else
501        printf ("  L1:%7.2f", Mpx_per_sec (pix_cnt, t1, t2, t3));
502    fflush (stdout);
503
504    memcpy (dst, src, BUFSIZE);
505    memcpy (src, dst, BUFSIZE);
506
507    nlines = (L2CACHE_SIZE / l1test_width) /
508	((PIXMAN_FORMAT_BPP(src_fmt) + PIXMAN_FORMAT_BPP(dst_fmt)) / 8);
509    if (nlines < 1)
510	nlines = 1;
511    n = 1 + npix / (l1test_width * nlines);
512    t1 = gettime ();
513#if EXCLUDE_OVERHEAD
514    pix_cnt = bench_L (op, src_img, mask_img, dst_img, n, pixman_image_composite_empty, l1test_width, nlines);
515#endif
516    t2 = gettime ();
517    pix_cnt = bench_L (op, src_img, mask_img, dst_img, n, func, l1test_width, nlines);
518    t3 = gettime ();
519    if (use_csv_output)
520        printf ("%g,", Mpx_per_sec (pix_cnt, t1, t2, t3));
521    else
522        printf ("  L2:%7.2f", Mpx_per_sec (pix_cnt, t1, t2, t3));
523    fflush (stdout);
524
525    memcpy (dst, src, BUFSIZE);
526    memcpy (src, dst, BUFSIZE);
527
528    n = 1 + npix / (WIDTH * HEIGHT);
529    t1 = gettime ();
530#if EXCLUDE_OVERHEAD
531    pix_cnt = bench_M (op, src_img, mask_img, dst_img, n, pixman_image_composite_empty);
532#endif
533    t2 = gettime ();
534    pix_cnt = bench_M (op, src_img, mask_img, dst_img, n, func);
535    t3 = gettime ();
536    if (use_csv_output)
537        printf ("%g,", Mpx_per_sec (pix_cnt, t1, t2, t3));
538    else
539        printf ("  M:%6.2f (%6.2f%%)", Mpx_per_sec (pix_cnt, t1, t2, t3),
540                (pix_cnt / ((t3 - t2) - (t2 - t1)) * bytes_per_pix) * (100.0 / bandwidth) );
541    fflush (stdout);
542
543    memcpy (dst, src, BUFSIZE);
544    memcpy (src, dst, BUFSIZE);
545
546    n = 1 + npix / (8 * TILEWIDTH * TILEWIDTH);
547    t1 = gettime ();
548#if EXCLUDE_OVERHEAD
549    pix_cnt = bench_HT (op, src_img, mask_img, dst_img, n, pixman_image_composite_empty);
550#endif
551    t2 = gettime ();
552    pix_cnt = bench_HT (op, src_img, mask_img, dst_img, n, func);
553    t3 = gettime ();
554    if (use_csv_output)
555        printf ("%g,", Mpx_per_sec (pix_cnt, t1, t2, t3));
556    else
557        printf ("  HT:%6.2f", Mpx_per_sec (pix_cnt, t1, t2, t3));
558    fflush (stdout);
559
560    memcpy (dst, src, BUFSIZE);
561    memcpy (src, dst, BUFSIZE);
562
563    n = 1 + npix / (8 * TILEWIDTH * TILEWIDTH);
564    t1 = gettime ();
565#if EXCLUDE_OVERHEAD
566    pix_cnt = bench_VT (op, src_img, mask_img, dst_img, n, pixman_image_composite_empty);
567#endif
568    t2 = gettime ();
569    pix_cnt = bench_VT (op, src_img, mask_img, dst_img, n, func);
570    t3 = gettime ();
571    if (use_csv_output)
572        printf ("%g,", Mpx_per_sec (pix_cnt, t1, t2, t3));
573    else
574        printf ("  VT:%6.2f", Mpx_per_sec (pix_cnt, t1, t2, t3));
575    fflush (stdout);
576
577    memcpy (dst, src, BUFSIZE);
578    memcpy (src, dst, BUFSIZE);
579
580    n = 1 + npix / (8 * TILEWIDTH * TILEWIDTH);
581    t1 = gettime ();
582#if EXCLUDE_OVERHEAD
583    pix_cnt = bench_R (op, src_img, mask_img, dst_img, n, pixman_image_composite_empty, WIDTH, HEIGHT);
584#endif
585    t2 = gettime ();
586    pix_cnt = bench_R (op, src_img, mask_img, dst_img, n, func, WIDTH, HEIGHT);
587    t3 = gettime ();
588    if (use_csv_output)
589        printf ("%g,", Mpx_per_sec (pix_cnt, t1, t2, t3));
590    else
591        printf ("  R:%6.2f", Mpx_per_sec (pix_cnt, t1, t2, t3));
592    fflush (stdout);
593
594    memcpy (dst, src, BUFSIZE);
595    memcpy (src, dst, BUFSIZE);
596
597    n = 1 + npix / (16 * TINYWIDTH * TINYWIDTH);
598    t1 = gettime ();
599#if EXCLUDE_OVERHEAD
600    pix_cnt = bench_RT (op, src_img, mask_img, dst_img, n, pixman_image_composite_empty, WIDTH, HEIGHT);
601#endif
602    t2 = gettime ();
603    pix_cnt = bench_RT (op, src_img, mask_img, dst_img, n, func, WIDTH, HEIGHT);
604    t3 = gettime ();
605    if (use_csv_output)
606        printf ("%g\n", Mpx_per_sec (pix_cnt, t1, t2, t3));
607    else
608        printf ("  RT:%6.2f (%4.0fKops/s)\n", Mpx_per_sec (pix_cnt, t1, t2, t3), (double) n / ((t3 - t2) * 1000));
609
610    if (mask_img) {
611	pixman_image_unref (mask_img);
612	pixman_image_unref (xmask_img);
613    }
614    pixman_image_unref (src_img);
615    pixman_image_unref (dst_img);
616    pixman_image_unref (xsrc_img);
617    pixman_image_unref (xdst_img);
618}
619
620#define PIXMAN_OP_OUT_REV (PIXMAN_OP_OUT_REVERSE)
621
622struct test_entry
623{
624    const char *testname;
625    int         src_fmt;
626    int         src_flags;
627    int         op;
628    int         mask_fmt;
629    int         mask_flags;
630    int         dst_fmt;
631};
632
633typedef struct test_entry test_entry_t;
634
635static const test_entry_t tests_tbl[] =
636{
637    { "add_8_8_8",             PIXMAN_a8,          0, PIXMAN_OP_ADD,     PIXMAN_a8,       0, PIXMAN_a8 },
638    { "add_n_8_8",             PIXMAN_a8r8g8b8,    1, PIXMAN_OP_ADD,     PIXMAN_a8,       0, PIXMAN_a8 },
639    { "add_n_8_8888",          PIXMAN_a8r8g8b8,    1, PIXMAN_OP_ADD,     PIXMAN_a8,       0, PIXMAN_a8r8g8b8 },
640    { "add_n_8_x888",          PIXMAN_a8r8g8b8,    1, PIXMAN_OP_ADD,     PIXMAN_a8,       0, PIXMAN_x8r8g8b8 },
641    { "add_n_8_0565",          PIXMAN_a8r8g8b8,    1, PIXMAN_OP_ADD,     PIXMAN_a8,       0, PIXMAN_r5g6b5 },
642    { "add_n_8_1555",          PIXMAN_a8r8g8b8,    1, PIXMAN_OP_ADD,     PIXMAN_a8,       0, PIXMAN_a1r5g5b5 },
643    { "add_n_8_4444",          PIXMAN_a8r8g8b8,    1, PIXMAN_OP_ADD,     PIXMAN_a8,       0, PIXMAN_a4r4g4b4 },
644    { "add_n_8_2222",          PIXMAN_a8r8g8b8,    1, PIXMAN_OP_ADD,     PIXMAN_a8,       0, PIXMAN_a2r2g2b2 },
645    { "add_n_8_2x10",          PIXMAN_a8r8g8b8,    1, PIXMAN_OP_ADD,     PIXMAN_a8,       0, PIXMAN_x2r10g10b10 },
646    { "add_n_8_2a10",          PIXMAN_a8r8g8b8,    1, PIXMAN_OP_ADD,     PIXMAN_a8,       0, PIXMAN_a2r10g10b10 },
647    { "add_n_8",               PIXMAN_a8r8g8b8,    1, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_a8 },
648    { "add_n_8888",            PIXMAN_a8r8g8b8,    1, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_a8r8g8b8 },
649    { "add_n_x888",            PIXMAN_a8r8g8b8,    1, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_x8r8g8b8 },
650    { "add_n_0565",            PIXMAN_a8r8g8b8,    1, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_r5g6b5 },
651    { "add_n_1555",            PIXMAN_a8r8g8b8,    1, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_a1r5g5b5 },
652    { "add_n_4444",            PIXMAN_a8r8g8b8,    1, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_a4r4g4b4 },
653    { "add_n_2222",            PIXMAN_a8r8g8b8,    1, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_a2r2g2b2 },
654    { "add_n_2x10",            PIXMAN_a2r10g10b10, 1, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_x2r10g10b10 },
655    { "add_n_2a10",            PIXMAN_a2r10g10b10, 1, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_a2r10g10b10 },
656    { "add_8_8",               PIXMAN_a8,          0, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_a8 },
657    { "add_x888_x888",         PIXMAN_x8r8g8b8,    0, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_x8r8g8b8 },
658    { "add_8888_8888",         PIXMAN_a8r8g8b8,    0, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_a8r8g8b8 },
659    { "add_8888_0565",         PIXMAN_a8r8g8b8,    0, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_r5g6b5 },
660    { "add_8888_1555",         PIXMAN_a8r8g8b8,    0, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_a1r5g5b5 },
661    { "add_8888_4444",         PIXMAN_a8r8g8b8,    0, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_a4r4g4b4 },
662    { "add_8888_2222",         PIXMAN_a8r8g8b8,    0, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_a2r2g2b2 },
663    { "add_0565_0565",         PIXMAN_r5g6b5,      0, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_r5g6b5 },
664    { "add_1555_1555",         PIXMAN_a1r5g5b5,    0, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_a1r5g5b5 },
665    { "add_0565_2x10",         PIXMAN_r5g6b5,      0, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_x2r10g10b10 },
666    { "add_2a10_2a10",         PIXMAN_a2r10g10b10, 0, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_a2r10g10b10 },
667    { "in_n_8_8",              PIXMAN_a8r8g8b8,    1, PIXMAN_OP_IN,      PIXMAN_a8,       0, PIXMAN_a8 },
668    { "in_8_8",                PIXMAN_a8,          0, PIXMAN_OP_IN,      PIXMAN_null,     0, PIXMAN_a8 },
669    { "src_n_2222",            PIXMAN_a8r8g8b8,    1, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_a2r2g2b2 },
670    { "src_n_0565",            PIXMAN_a8r8g8b8,    1, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_r5g6b5 },
671    { "src_n_1555",            PIXMAN_a8r8g8b8,    1, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_a1r5g5b5 },
672    { "src_n_4444",            PIXMAN_a8r8g8b8,    1, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_a4r4g4b4 },
673    { "src_n_x888",            PIXMAN_a8r8g8b8,    1, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_x8r8g8b8 },
674    { "src_n_8888",            PIXMAN_a8r8g8b8,    1, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_a8r8g8b8 },
675    { "src_n_2x10",            PIXMAN_a2r10g10b10, 1, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_x2r10g10b10 },
676    { "src_n_2a10",            PIXMAN_a2r10g10b10, 1, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_a2r10g10b10 },
677    { "src_8888_0565",         PIXMAN_a8r8g8b8,    0, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_r5g6b5 },
678    { "src_0565_8888",         PIXMAN_r5g6b5,      0, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_a8r8g8b8 },
679    { "src_8888_4444",         PIXMAN_a8r8g8b8,    0, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_a4r4g4b4 },
680    { "src_8888_2222",         PIXMAN_a8r8g8b8,    0, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_a2r2g2b2 },
681    { "src_8888_2x10",         PIXMAN_a8r8g8b8,    0, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_x2r10g10b10 },
682    { "src_8888_2a10",         PIXMAN_a8r8g8b8,    0, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_a2r10g10b10 },
683    { "src_0888_0565",         PIXMAN_r8g8b8,      0, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_r5g6b5 },
684    { "src_0888_8888",         PIXMAN_r8g8b8,      0, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_a8r8g8b8 },
685    { "src_0888_x888",         PIXMAN_r8g8b8,      0, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_x8r8g8b8 },
686    { "src_0888_8888_rev",     PIXMAN_b8g8r8,      0, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_x8r8g8b8 },
687    { "src_0888_0565_rev",     PIXMAN_b8g8r8,      0, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_r5g6b5 },
688    { "src_x888_x888",         PIXMAN_x8r8g8b8,    0, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_x8r8g8b8 },
689    { "src_x888_8888",         PIXMAN_x8r8g8b8,    0, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_a8r8g8b8 },
690    { "src_8888_8888",         PIXMAN_a8r8g8b8,    0, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_a8r8g8b8 },
691    { "src_0565_0565",         PIXMAN_r5g6b5,      0, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_r5g6b5 },
692    { "src_1555_0565",         PIXMAN_a1r5g5b5,    0, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_r5g6b5 },
693    { "src_0565_1555",         PIXMAN_r5g6b5,      0, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_a1r5g5b5 },
694    { "src_8_8",               PIXMAN_a8,          0, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_a8 },
695    { "src_n_8",               PIXMAN_a8,          1, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_a8 },
696    { "src_n_8_0565",          PIXMAN_a8r8g8b8,    1, PIXMAN_OP_SRC,     PIXMAN_a8,       0, PIXMAN_r5g6b5 },
697    { "src_n_8_1555",          PIXMAN_a8r8g8b8,    1, PIXMAN_OP_SRC,     PIXMAN_a8,       0, PIXMAN_a1r5g5b5 },
698    { "src_n_8_4444",          PIXMAN_a8r8g8b8,    1, PIXMAN_OP_SRC,     PIXMAN_a8,       0, PIXMAN_a4r4g4b4 },
699    { "src_n_8_2222",          PIXMAN_a8r8g8b8,    1, PIXMAN_OP_SRC,     PIXMAN_a8,       0, PIXMAN_a2r2g2b2 },
700    { "src_n_8_x888",          PIXMAN_a8r8g8b8,    1, PIXMAN_OP_SRC,     PIXMAN_a8,       0, PIXMAN_x8r8g8b8 },
701    { "src_n_8_8888",          PIXMAN_a8r8g8b8,    1, PIXMAN_OP_SRC,     PIXMAN_a8,       0, PIXMAN_a8r8g8b8 },
702    { "src_n_8_2x10",          PIXMAN_a8r8g8b8,    1, PIXMAN_OP_SRC,     PIXMAN_a8,       0, PIXMAN_x2r10g10b10 },
703    { "src_n_8_2a10",          PIXMAN_a8r8g8b8,    1, PIXMAN_OP_SRC,     PIXMAN_a8,       0, PIXMAN_a2r10g10b10 },
704    { "src_8888_8_0565",       PIXMAN_a8r8g8b8,    0, PIXMAN_OP_SRC,     PIXMAN_a8,       0, PIXMAN_r5g6b5 },
705    { "src_0888_8_0565",       PIXMAN_r8g8b8,      0, PIXMAN_OP_SRC,     PIXMAN_a8,       0, PIXMAN_r5g6b5 },
706    { "src_0888_8_8888",       PIXMAN_r8g8b8,      0, PIXMAN_OP_SRC,     PIXMAN_a8,       0, PIXMAN_a8r8g8b8 },
707    { "src_0888_8_x888",       PIXMAN_r8g8b8,      0, PIXMAN_OP_SRC,     PIXMAN_a8,       0, PIXMAN_x8r8g8b8 },
708    { "src_x888_8_x888",       PIXMAN_x8r8g8b8,    0, PIXMAN_OP_SRC,     PIXMAN_a8,       0, PIXMAN_x8r8g8b8 },
709    { "src_x888_8_8888",       PIXMAN_x8r8g8b8,    0, PIXMAN_OP_SRC,     PIXMAN_a8,       0, PIXMAN_a8r8g8b8 },
710    { "src_0565_8_0565",       PIXMAN_r5g6b5,      0, PIXMAN_OP_SRC,     PIXMAN_a8,       0, PIXMAN_r5g6b5 },
711    { "src_1555_8_0565",       PIXMAN_a1r5g5b5,    0, PIXMAN_OP_SRC,     PIXMAN_a8,       0, PIXMAN_r5g6b5 },
712    { "src_0565_8_1555",       PIXMAN_r5g6b5,      0, PIXMAN_OP_SRC,     PIXMAN_a8,       0, PIXMAN_a1r5g5b5 },
713    { "over_n_x888",           PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OVER,    PIXMAN_null,     0, PIXMAN_x8r8g8b8 },
714    { "over_n_8888",           PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OVER,    PIXMAN_null,     0, PIXMAN_a8r8g8b8 },
715    { "over_n_0565",           PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OVER,    PIXMAN_null,     0, PIXMAN_r5g6b5 },
716    { "over_n_1555",           PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OVER,    PIXMAN_null,     0, PIXMAN_a1r5g5b5 },
717    { "over_8888_0565",        PIXMAN_a8r8g8b8,    0, PIXMAN_OP_OVER,    PIXMAN_null,     0, PIXMAN_r5g6b5 },
718    { "over_8888_8888",        PIXMAN_a8r8g8b8,    0, PIXMAN_OP_OVER,    PIXMAN_null,     0, PIXMAN_a8r8g8b8 },
719    { "over_8888_x888",        PIXMAN_a8r8g8b8,    0, PIXMAN_OP_OVER,    PIXMAN_null,     0, PIXMAN_x8r8g8b8 },
720    { "over_x888_8_0565",      PIXMAN_x8r8g8b8,    0, PIXMAN_OP_OVER,    PIXMAN_a8,       0, PIXMAN_r5g6b5 },
721    { "over_x888_8_8888",      PIXMAN_x8r8g8b8,    0, PIXMAN_OP_OVER,    PIXMAN_a8,       0, PIXMAN_a8r8g8b8 },
722    { "over_n_8_0565",         PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OVER,    PIXMAN_a8,       0, PIXMAN_r5g6b5 },
723    { "over_n_8_1555",         PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OVER,    PIXMAN_a8,       0, PIXMAN_a1r5g5b5 },
724    { "over_n_8_4444",         PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OVER,    PIXMAN_a8,       0, PIXMAN_a4r4g4b4 },
725    { "over_n_8_2222",         PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OVER,    PIXMAN_a8,       0, PIXMAN_a2r2g2b2 },
726    { "over_n_8_x888",         PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OVER,    PIXMAN_a8,       0, PIXMAN_x8r8g8b8 },
727    { "over_n_8_8888",         PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OVER,    PIXMAN_a8,       0, PIXMAN_a8r8g8b8 },
728    { "over_n_8_2x10",         PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OVER,    PIXMAN_a8,       0, PIXMAN_x2r10g10b10 },
729    { "over_n_8_2a10",         PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OVER,    PIXMAN_a8,       0, PIXMAN_a2r10g10b10 },
730    { "over_n_8888_8888_ca",   PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OVER,    PIXMAN_a8r8g8b8, 2, PIXMAN_a8r8g8b8 },
731    { "over_n_8888_x888_ca",   PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OVER,    PIXMAN_a8r8g8b8, 2, PIXMAN_x8r8g8b8 },
732    { "over_n_8888_0565_ca",   PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OVER,    PIXMAN_a8r8g8b8, 2, PIXMAN_r5g6b5 },
733    { "over_n_8888_1555_ca",   PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OVER,    PIXMAN_a8r8g8b8, 2, PIXMAN_a1r5g5b5 },
734    { "over_n_8888_4444_ca",   PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OVER,    PIXMAN_a8r8g8b8, 2, PIXMAN_a4r4g4b4 },
735    { "over_n_8888_2222_ca",   PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OVER,    PIXMAN_a8r8g8b8, 2, PIXMAN_a2r2g2b2 },
736    { "over_n_8888_2x10_ca",   PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OVER,    PIXMAN_a8r8g8b8, 2, PIXMAN_x2r10g10b10 },
737    { "over_n_8888_2a10_ca",   PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OVER,    PIXMAN_a8r8g8b8, 2, PIXMAN_a2r10g10b10 },
738    { "over_8888_n_8888",      PIXMAN_a8r8g8b8,    0, PIXMAN_OP_OVER,    PIXMAN_a8,       1, PIXMAN_a8r8g8b8 },
739    { "over_8888_n_x888",      PIXMAN_a8r8g8b8,    0, PIXMAN_OP_OVER,    PIXMAN_a8,       1, PIXMAN_x8r8g8b8 },
740    { "over_8888_n_0565",      PIXMAN_a8r8g8b8,    0, PIXMAN_OP_OVER,    PIXMAN_a8,       1, PIXMAN_r5g6b5 },
741    { "over_8888_n_1555",      PIXMAN_a8r8g8b8,    0, PIXMAN_OP_OVER,    PIXMAN_a8,       1, PIXMAN_a1r5g5b5 },
742    { "over_x888_n_8888",      PIXMAN_x8r8g8b8,    0, PIXMAN_OP_OVER,    PIXMAN_a8,       1, PIXMAN_a8r8g8b8 },
743    { "outrev_n_8_0565",       PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OUT_REV, PIXMAN_a8,       0, PIXMAN_r5g6b5 },
744    { "outrev_n_8_1555",       PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OUT_REV, PIXMAN_a8,       0, PIXMAN_a1r5g5b5 },
745    { "outrev_n_8_x888",       PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OUT_REV, PIXMAN_a8,       0, PIXMAN_x8r8g8b8 },
746    { "outrev_n_8_8888",       PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OUT_REV, PIXMAN_a8,       0, PIXMAN_a8r8g8b8 },
747    { "outrev_n_8888_0565_ca", PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OUT_REV, PIXMAN_a8r8g8b8, 2, PIXMAN_r5g6b5 },
748    { "outrev_n_8888_1555_ca", PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OUT_REV, PIXMAN_a8r8g8b8, 2, PIXMAN_a1r5g5b5 },
749    { "outrev_n_8888_x888_ca", PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OUT_REV, PIXMAN_a8r8g8b8, 2, PIXMAN_x8r8g8b8 },
750    { "outrev_n_8888_8888_ca", PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OUT_REV, PIXMAN_a8r8g8b8, 2, PIXMAN_a8r8g8b8 },
751    { "over_reverse_n_8888",   PIXMAN_a8r8g8b8,    1, PIXMAN_OP_OVER_REVERSE, PIXMAN_null, 0, PIXMAN_a8r8g8b8 },
752    { "in_reverse_8888_8888",  PIXMAN_a8r8g8b8,    0, PIXMAN_OP_IN_REVERSE, PIXMAN_null,  0, PIXMAN_a8r8g8b8 },
753    { "pixbuf",                PIXMAN_x8b8g8r8,    0, PIXMAN_OP_SRC,     PIXMAN_a8b8g8r8, 0, PIXMAN_a8r8g8b8 },
754    { "rpixbuf",               PIXMAN_x8b8g8r8,    0, PIXMAN_OP_SRC,     PIXMAN_a8b8g8r8, 0, PIXMAN_a8b8g8r8 },
755};
756
757static const test_entry_t special_patterns[] =
758{
759    { "add_n_2x10",            PIXMAN_a2r10g10b10, 1, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_x2r10g10b10 },
760    { "add_n_2a10",            PIXMAN_a2r10g10b10, 1, PIXMAN_OP_ADD,     PIXMAN_null,     0, PIXMAN_a2r10g10b10 },
761    { "src_n_2x10",            PIXMAN_a2r10g10b10, 1, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_x2r10g10b10 },
762    { "src_n_2a10",            PIXMAN_a2r10g10b10, 1, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_a2r10g10b10 },
763    { "src_0888_8888_rev",     PIXMAN_b8g8r8,      0, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_x8r8g8b8 },
764    { "src_0888_0565_rev",     PIXMAN_b8g8r8,      0, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_r5g6b5 },
765    { "src_n_8",               PIXMAN_a8,          1, PIXMAN_OP_SRC,     PIXMAN_null,     0, PIXMAN_a8 },
766    { "pixbuf",                PIXMAN_x8b8g8r8,    0, PIXMAN_OP_SRC,     PIXMAN_a8b8g8r8, 0, PIXMAN_a8r8g8b8 },
767    { "rpixbuf",               PIXMAN_x8b8g8r8,    0, PIXMAN_OP_SRC,     PIXMAN_a8b8g8r8, 0, PIXMAN_a8b8g8r8 },
768};
769
770/* Returns the sub-string's end pointer in string. */
771static const char *
772copy_sub_string (char       *buf,
773                 const char *string,
774                 const char *scan_from,
775                 const char *end)
776{
777    const char *delim;
778    size_t n;
779
780    delim = strchr (scan_from, '_');
781    if (!delim)
782        delim = end;
783
784    n = delim - string;
785    strncpy(buf, string, n);
786    buf[n] = '\0';
787
788    return delim;
789}
790
791static pixman_op_t
792parse_longest_operator (char *buf, const char **strp, const char *end)
793{
794    const char *p = *strp;
795    const char *sub_end;
796    const char *best_end = p;
797    pixman_op_t best_op = PIXMAN_OP_NONE;
798    pixman_op_t op;
799
800    while (p < end)
801    {
802        sub_end = copy_sub_string (buf, *strp, p, end);
803        op = operator_from_string (buf);
804        p = sub_end + 1;
805
806        if (op != PIXMAN_OP_NONE)
807        {
808            best_end = p;
809            best_op = op;
810        }
811    }
812
813    *strp = best_end;
814    return best_op;
815}
816
817static pixman_format_code_t
818parse_format (char *buf, const char **p, const char *end)
819{
820    pixman_format_code_t format;
821    const char *delim;
822
823    if (*p >= end)
824        return PIXMAN_null;
825
826    delim = copy_sub_string (buf, *p, *p, end);
827    format = format_from_string (buf);
828
829    if (format != PIXMAN_null)
830        *p = delim + 1;
831
832    return format;
833}
834
835static int
836parse_test_pattern (test_entry_t *test, const char *pattern)
837{
838    const char *p = pattern;
839    const char *end = pattern + strlen (pattern);
840    char buf[1024];
841    pixman_format_code_t format[3];
842    int i;
843
844    if (strlen (pattern) > sizeof (buf) - 1)
845        return -1;
846
847    /* Special cases that the parser cannot produce. */
848    for (i = 0; i < ARRAY_LENGTH (special_patterns); i++)
849    {
850        if (strcmp (pattern, special_patterns[i].testname) == 0)
851        {
852            *test = special_patterns[i];
853            return 0;
854        }
855    }
856
857    test->testname = pattern;
858
859    /* Extract operator, may contain delimiters,
860     * so take the longest string that matches.
861     */
862    test->op = parse_longest_operator (buf, &p, end);
863    if (test->op == PIXMAN_OP_NONE)
864        return -1;
865
866    /* extract up to three pixel formats */
867    format[0] = parse_format (buf, &p, end);
868    format[1] = parse_format (buf, &p, end);
869    format[2] = parse_format (buf, &p, end);
870
871    if (format[0] == PIXMAN_null || format[1] == PIXMAN_null)
872        return -1;
873
874    /* recognize CA flag */
875    test->mask_flags = 0;
876    if (p < end)
877    {
878        if (strcmp (p, "ca") == 0)
879            test->mask_flags |= CA_FLAG;
880        else
881            return -1; /* trailing garbage */
882    }
883
884    test->src_fmt = format[0];
885    if (format[2] == PIXMAN_null)
886    {
887        test->mask_fmt = PIXMAN_null;
888        test->dst_fmt = format[1];
889    }
890    else
891    {
892        test->mask_fmt = format[1];
893        test->dst_fmt = format[2];
894    }
895
896    test->src_flags = 0;
897    if (test->src_fmt == PIXMAN_solid)
898    {
899        test->src_fmt = PIXMAN_a8r8g8b8;
900        test->src_flags |= SOLID_FLAG;
901    }
902
903    if (test->mask_fmt == PIXMAN_solid)
904    {
905        if (test->mask_flags & CA_FLAG)
906            test->mask_fmt = PIXMAN_a8r8g8b8;
907        else
908            test->mask_fmt = PIXMAN_a8;
909
910        test->mask_flags |= SOLID_FLAG;
911    }
912
913    return 0;
914}
915
916static int
917check_int (int got, int expected, const char *name, const char *field)
918{
919    if (got == expected)
920        return 0;
921
922    printf ("%s: %s failure: expected %d, got %d.\n",
923            name, field, expected, got);
924
925    return 1;
926}
927
928static int
929check_format (int got, int expected, const char *name, const char *field)
930{
931    if (got == expected)
932        return 0;
933
934    printf ("%s: %s failure: expected %s (%#x), got %s (%#x).\n",
935            name, field,
936            format_name (expected), expected,
937            format_name (got), got);
938
939    return 1;
940}
941
942static void
943parser_self_test (void)
944{
945    const test_entry_t *ent;
946    test_entry_t test;
947    int fails = 0;
948    int i;
949
950    for (i = 0; i < ARRAY_LENGTH (tests_tbl); i++)
951    {
952        ent = &tests_tbl[i];
953
954        if (parse_test_pattern (&test, ent->testname) < 0)
955        {
956            printf ("parsing failed for '%s'\n", ent->testname);
957            fails++;
958            continue;
959        }
960
961        fails += check_format (test.src_fmt, ent->src_fmt,
962                               ent->testname, "src_fmt");
963        fails += check_format (test.mask_fmt, ent->mask_fmt,
964                               ent->testname, "mask_fmt");
965        fails += check_format (test.dst_fmt, ent->dst_fmt,
966                               ent->testname, "dst_fmt");
967        fails += check_int    (test.src_flags, ent->src_flags,
968                               ent->testname, "src_flags");
969        fails += check_int    (test.mask_flags, ent->mask_flags,
970                               ent->testname, "mask_flags");
971        fails += check_int    (test.op, ent->op, ent->testname, "op");
972    }
973
974    if (fails)
975    {
976        printf ("Parser self-test failed.\n");
977        exit (EXIT_FAILURE);
978    }
979
980    if (!use_csv_output)
981        printf ("Parser self-test complete.\n");
982}
983
984static void
985print_test_details (const test_entry_t *test)
986{
987    printf ("%s: %s, src %s%s, mask %s%s%s, dst %s\n",
988            test->testname,
989            operator_name (test->op),
990            format_name (test->src_fmt),
991            test->src_flags & SOLID_FLAG ? " solid" : "",
992            format_name (test->mask_fmt),
993            test->mask_flags & SOLID_FLAG ? " solid" : "",
994            test->mask_flags & CA_FLAG ? " CA" : "",
995            format_name (test->dst_fmt));
996}
997
998static void
999run_one_test (const char *pattern, double bandwidth_, pixman_bool_t prdetails)
1000{
1001    test_entry_t test;
1002
1003    if (parse_test_pattern (&test, pattern) < 0)
1004    {
1005        printf ("Error: Could not parse the test pattern '%s'.\n", pattern);
1006        return;
1007    }
1008
1009    if (prdetails)
1010    {
1011        print_test_details (&test);
1012        printf ("---\n");
1013    }
1014
1015    bench_composite (pattern,
1016                     test.src_fmt,
1017                     test.src_flags,
1018                     test.op,
1019                     test.mask_fmt,
1020                     test.mask_flags,
1021                     test.dst_fmt,
1022                     bandwidth_ / 8);
1023}
1024
1025static void
1026run_default_tests (double bandwidth_)
1027{
1028    int i;
1029
1030    for (i = 0; i < ARRAY_LENGTH (tests_tbl); i++)
1031        run_one_test (tests_tbl[i].testname, bandwidth_, FALSE);
1032}
1033
1034static void
1035print_explanation (void)
1036{
1037    printf ("Benchmark for a set of most commonly used functions\n");
1038    printf ("---\n");
1039    printf ("All results are presented in millions of pixels per second\n");
1040    printf ("L1  - small Xx1 rectangle (fitting L1 cache), always blitted at the same\n");
1041    printf ("      memory location with small drift in horizontal direction\n");
1042    printf ("L2  - small XxY rectangle (fitting L2 cache), always blitted at the same\n");
1043    printf ("      memory location with small drift in horizontal direction\n");
1044    printf ("M   - large %dx%d rectangle, always blitted at the same\n",
1045            WIDTH - 64, HEIGHT);
1046    printf ("      memory location with small drift in horizontal direction\n");
1047    printf ("HT  - random rectangles with %dx%d average size are copied from\n",
1048            TILEWIDTH, TILEWIDTH);
1049    printf ("      one %dx%d buffer to another, traversing from left to right\n",
1050            WIDTH, HEIGHT);
1051    printf ("      and from top to bottom\n");
1052    printf ("VT  - random rectangles with %dx%d average size are copied from\n",
1053            TILEWIDTH, TILEWIDTH);
1054    printf ("      one %dx%d buffer to another, traversing from top to bottom\n",
1055            WIDTH, HEIGHT);
1056    printf ("      and from left to right\n");
1057    printf ("R   - random rectangles with %dx%d average size are copied from\n",
1058            TILEWIDTH, TILEWIDTH);
1059    printf ("      random locations of one %dx%d buffer to another\n",
1060            WIDTH, HEIGHT);
1061    printf ("RT  - as R, but %dx%d average sized rectangles are copied\n",
1062            TINYWIDTH, TINYWIDTH);
1063    printf ("---\n");
1064}
1065
1066static void
1067print_speed_scaling (double bw)
1068{
1069    printf ("reference memcpy speed = %.1fMB/s (%.1fMP/s for 32bpp fills)\n",
1070            bw / 1000000., bw / 4000000);
1071
1072    if (use_scaling)
1073    {
1074	printf ("---\n");
1075	if (filter == PIXMAN_FILTER_BILINEAR)
1076	    printf ("BILINEAR scaling\n");
1077	else if (filter == PIXMAN_FILTER_NEAREST)
1078	    printf ("NEAREST scaling\n");
1079	else
1080	    printf ("UNKNOWN scaling\n");
1081    }
1082
1083    printf ("---\n");
1084}
1085
1086static void
1087usage (const char *progname)
1088{
1089    printf ("Usage: %s [-b] [-n] [-c] [-m M] pattern\n", progname);
1090    printf ("  -n : benchmark nearest scaling\n");
1091    printf ("  -b : benchmark bilinear scaling\n");
1092    printf ("  -c : print output as CSV data\n");
1093    printf ("  -m M : set reference memcpy speed to M MB/s instead of measuring it\n");
1094}
1095
1096int
1097main (int argc, char *argv[])
1098{
1099    int i;
1100    const char *pattern = NULL;
1101
1102    for (i = 1; i < argc; i++)
1103    {
1104	if (argv[i][0] == '-')
1105	{
1106	    if (strchr (argv[i] + 1, 'b'))
1107	    {
1108		use_scaling = TRUE;
1109		filter = PIXMAN_FILTER_BILINEAR;
1110	    }
1111	    else if (strchr (argv[i] + 1, 'n'))
1112	    {
1113		use_scaling = TRUE;
1114		filter = PIXMAN_FILTER_NEAREST;
1115	    }
1116
1117	    if (strchr (argv[i] + 1, 'c'))
1118		use_csv_output = TRUE;
1119
1120	    if (strcmp (argv[i], "-m") == 0 && i + 1 < argc)
1121		bandwidth = atof (argv[++i]) * 1e6;
1122	}
1123	else
1124	{
1125	    if (pattern)
1126	    {
1127		pattern = NULL;
1128		printf ("Error: extra arguments given.\n");
1129		break;
1130	    }
1131	    pattern = argv[i];
1132	}
1133    }
1134
1135    if (!pattern)
1136    {
1137	usage (argv[0]);
1138	return 1;
1139    }
1140
1141    parser_self_test ();
1142
1143    src = aligned_malloc (4096, BUFSIZE * 3);
1144    memset (src, 0xCC, BUFSIZE * 3);
1145    dst = src + (BUFSIZE / 4);
1146    mask = dst + (BUFSIZE / 4);
1147
1148    if (!use_csv_output)
1149        print_explanation ();
1150
1151    if (bandwidth < 1.0)
1152        bandwidth = bench_memcpy ();
1153    if (!use_csv_output)
1154        print_speed_scaling (bandwidth);
1155
1156    if (strcmp (pattern, "all") == 0)
1157        run_default_tests (bandwidth);
1158    else
1159        run_one_test (pattern, bandwidth, !use_csv_output);
1160
1161    free (src);
1162    return 0;
1163}
1164