• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2015 RISC OS Open Ltd
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of the copyright holders not be used in
9  * advertising or publicity pertaining to distribution of the software without
10  * specific, written prior permission.  The copyright holders make no
11  * representations about the suitability of this software for any purpose.  It
12  * is provided "as is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
19  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
20  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
21  * SOFTWARE.
22  *
23  * Author:  Ben Avison (bavison@riscosopen.org)
24  *
25  */
26 
27 /*
28  * This test aims to verify both numerical correctness and the honouring of
29  * array bounds for scaled plots (both nearest-neighbour and bilinear) at or
30  * close to the boundary conditions for applicability of "cover" type fast paths
31  * and iter fetch routines.
32  *
33  * It has a secondary purpose: by setting the env var EXACT (to any value) it
34  * will only test plots that are exactly on the boundary condition. This makes
35  * it possible to ensure that "cover" routines are being used to the maximum,
36  * although this requires the use of a debugger or code instrumentation to
37  * verify.
38  */
39 
40 #include "utils.h"
41 #include <stdlib.h>
42 #include <stdio.h>
43 
44 /* Approximate limits for random scale factor generation - these ensure we can
45  * get at least 8x reduction and 8x enlargement.
46  */
47 #define LOG2_MAX_FACTOR (3)
48 
49 /* 1/sqrt(2) (or sqrt(0.5), or 2^-0.5) as a 0.32 fixed-point number */
50 #define INV_SQRT_2_0POINT32_FIXED (0xB504F334u)
51 
52 /* The largest increment that can be generated by random_scale_factor().
53  * This occurs when the "mantissa" part is 0xFFFFFFFF and the "exponent"
54  * part is -LOG2_MAX_FACTOR.
55  */
56 #define MAX_INC ((pixman_fixed_t) \
57                  (INV_SQRT_2_0POINT32_FIXED >> (31 - 16 - LOG2_MAX_FACTOR)))
58 
59 /* Minimum source width (in pixels) based on a typical page size of 4K and
60  * maximum colour depth of 32bpp.
61  */
62 #define MIN_SRC_WIDTH (4096 / 4)
63 
64 /* Derive the destination width so that at max increment we fit within source */
65 #define DST_WIDTH (MIN_SRC_WIDTH * pixman_fixed_1 / MAX_INC)
66 
67 /* Calculate heights the other way round.
68  * No limits due to page alignment here.
69  */
70 #define DST_HEIGHT 3
71 #define SRC_HEIGHT ((DST_HEIGHT * MAX_INC + pixman_fixed_1 - 1) / pixman_fixed_1)
72 
73 /* At the time of writing, all the scaled fast paths use SRC, OVER or ADD
74  * Porter-Duff operators. XOR is included in the list to ensure good
75  * representation of iter scanline fetch routines.
76  */
77 static const pixman_op_t op_list[] = {
78     PIXMAN_OP_SRC,
79     PIXMAN_OP_OVER,
80     PIXMAN_OP_ADD,
81     PIXMAN_OP_XOR,
82 };
83 
84 /* At the time of writing, all the scaled fast paths use a8r8g8b8, x8r8g8b8
85  * or r5g6b5, or red-blue swapped versions of the same. When a mask channel is
86  * used, it is always a8 (and so implicitly not component alpha). a1r5g5b5 is
87  * included because it is the only other format to feature in any iters. */
88 static const pixman_format_code_t img_fmt_list[] = {
89     PIXMAN_a8r8g8b8,
90     PIXMAN_x8r8g8b8,
91     PIXMAN_r5g6b5,
92     PIXMAN_a1r5g5b5
93 };
94 
95 /* This is a flag reflecting the environment variable EXACT. It can be used
96  * to ensure that source coordinates corresponding exactly to the "cover" limits
97  * are used, rather than any "near misses". This can, for example, be used in
98  * conjunction with a debugger to ensure that only COVER fast paths are used.
99  */
100 static int exact;
101 
102 static pixman_image_t *
create_src_image(pixman_format_code_t fmt)103 create_src_image (pixman_format_code_t fmt)
104 {
105     pixman_image_t *tmp_img, *img;
106 
107     /* We need the left-most and right-most MIN_SRC_WIDTH pixels to have
108      * predictable values, even though fence_image_create_bits() may allocate
109      * an image somewhat larger than that, by an amount that varies depending
110      * upon the page size on the current platform. The solution is to create a
111      * temporary non-fenced image that is exactly MIN_SRC_WIDTH wide and blit it
112      * into the fenced image.
113      */
114     tmp_img = pixman_image_create_bits (fmt, MIN_SRC_WIDTH, SRC_HEIGHT,
115                                         NULL, 0);
116     if (tmp_img == NULL)
117         return NULL;
118 
119     img = fence_image_create_bits (fmt, MIN_SRC_WIDTH, SRC_HEIGHT, TRUE);
120     if (img == NULL)
121     {
122         pixman_image_unref (tmp_img);
123         return NULL;
124     }
125 
126     prng_randmemset (tmp_img->bits.bits,
127                      tmp_img->bits.rowstride * SRC_HEIGHT * sizeof (uint32_t),
128                      0);
129     image_endian_swap (tmp_img);
130 
131     pixman_image_composite (PIXMAN_OP_SRC, tmp_img, NULL, img,
132                             0, 0, 0, 0, 0, 0,
133                             MIN_SRC_WIDTH, SRC_HEIGHT);
134     pixman_image_composite (PIXMAN_OP_SRC, tmp_img, NULL, img,
135                             0, 0, 0, 0, img->bits.width - MIN_SRC_WIDTH, 0,
136                             MIN_SRC_WIDTH, SRC_HEIGHT);
137 
138     pixman_image_unref (tmp_img);
139 
140     return img;
141 }
142 
143 static pixman_fixed_t
random_scale_factor(void)144 random_scale_factor(void)
145 {
146     /* Get a random number with top bit set. */
147     uint32_t f = prng_rand () | 0x80000000u;
148 
149     /* In log(2) space, this is still approximately evenly spread between 31
150      * and 32. Divide by sqrt(2) to centre the distribution on 2^31.
151      */
152     f = ((uint64_t) f * INV_SQRT_2_0POINT32_FIXED) >> 32;
153 
154     /* Now shift right (ie divide by an integer power of 2) to spread the
155      * distribution between centres at 2^(16 +/- LOG2_MAX_FACTOR).
156      */
157     f >>= 31 - 16 + prng_rand_n (2 * LOG2_MAX_FACTOR + 1) - LOG2_MAX_FACTOR;
158 
159     return f;
160 }
161 
162 static pixman_fixed_t
calc_translate(int dst_size,int src_size,pixman_fixed_t scale,pixman_bool_t low_align,pixman_bool_t bilinear)163 calc_translate (int            dst_size,
164                 int            src_size,
165                 pixman_fixed_t scale,
166                 pixman_bool_t  low_align,
167                 pixman_bool_t  bilinear)
168 {
169     pixman_fixed_t ref_src, ref_dst, scaled_dst;
170 
171     if (low_align)
172     {
173         ref_src = bilinear ? pixman_fixed_1 / 2 : pixman_fixed_e;
174         ref_dst = pixman_fixed_1 / 2;
175     }
176     else
177     {
178         ref_src = pixman_int_to_fixed (src_size) -
179                   bilinear * pixman_fixed_1 / 2;
180         ref_dst = pixman_int_to_fixed (dst_size) - pixman_fixed_1 / 2;
181     }
182 
183     scaled_dst = ((uint64_t) ref_dst * scale + pixman_fixed_1 / 2) /
184                  pixman_fixed_1;
185 
186     /* We need the translation to be set such that when ref_dst is fed through
187      * the transformation matrix, we get ref_src as the result.
188      */
189     return ref_src - scaled_dst;
190 }
191 
192 static pixman_fixed_t
random_offset(void)193 random_offset (void)
194 {
195     pixman_fixed_t offset = 0;
196 
197     /* Ensure we test the exact case quite a lot */
198     if (prng_rand_n (2))
199         return offset;
200 
201     /* What happens when we are close to the edge of the first
202      * interpolation step?
203      */
204     if (prng_rand_n (2))
205         offset += (pixman_fixed_1 >> BILINEAR_INTERPOLATION_BITS) - 16;
206 
207     /* Try fine-grained variations */
208     offset += prng_rand_n (32);
209 
210     /* Test in both directions */
211     if (prng_rand_n (2))
212         offset = -offset;
213 
214     return offset;
215 }
216 
217 static void
check_transform(pixman_image_t * dst_img,pixman_image_t * src_img,pixman_transform_t * transform,pixman_bool_t bilinear)218 check_transform (pixman_image_t     *dst_img,
219                  pixman_image_t     *src_img,
220                  pixman_transform_t *transform,
221                  pixman_bool_t       bilinear)
222 {
223     pixman_vector_t v1, v2;
224 
225     v1.vector[0] = pixman_fixed_1 / 2;
226     v1.vector[1] = pixman_fixed_1 / 2;
227     v1.vector[2] = pixman_fixed_1;
228     assert (pixman_transform_point (transform, &v1));
229 
230     v2.vector[0] = pixman_int_to_fixed (dst_img->bits.width) -
231                    pixman_fixed_1 / 2;
232     v2.vector[1] = pixman_int_to_fixed (dst_img->bits.height) -
233                    pixman_fixed_1 / 2;
234     v2.vector[2] = pixman_fixed_1;
235     assert (pixman_transform_point (transform, &v2));
236 
237     if (bilinear)
238     {
239         assert (v1.vector[0] >= pixman_fixed_1 / 2);
240         assert (v1.vector[1] >= pixman_fixed_1 / 2);
241         assert (v2.vector[0] <= pixman_int_to_fixed (src_img->bits.width) -
242                                     pixman_fixed_1 / 2);
243         assert (v2.vector[1] <= pixman_int_to_fixed (src_img->bits.height) -
244                                     pixman_fixed_1 / 2);
245     }
246     else
247     {
248         assert (v1.vector[0] >= pixman_fixed_e);
249         assert (v1.vector[1] >= pixman_fixed_e);
250         assert (v2.vector[0] <= pixman_int_to_fixed (src_img->bits.width));
251         assert (v2.vector[1] <= pixman_int_to_fixed (src_img->bits.height));
252     }
253 }
254 
255 static uint32_t
test_cover(int testnum,int verbose)256 test_cover (int testnum, int verbose)
257 {
258     pixman_fixed_t         x_scale, y_scale;
259     pixman_bool_t          left_align, top_align;
260     pixman_bool_t          bilinear;
261     pixman_filter_t        filter;
262     pixman_op_t            op;
263     size_t                 src_fmt_index;
264     pixman_format_code_t   src_fmt, dst_fmt, mask_fmt;
265     pixman_image_t        *src_img, *dst_img, *mask_img;
266     pixman_transform_t     src_transform, mask_transform;
267     pixman_fixed_t         fuzz[4];
268     uint32_t               crc32;
269 
270     /* We allocate one fenced image for each pixel format up-front. This is to
271      * avoid spending a lot of time on memory management rather than on testing
272      * Pixman optimisations. We need one per thread because the transformation
273      * matrices and filtering are properties of the source and mask images.
274      */
275     static pixman_image_t *src_imgs[ARRAY_LENGTH (img_fmt_list)];
276     static pixman_image_t *mask_bits_img;
277     static pixman_bool_t   fence_images_created;
278 #ifdef USE_OPENMP
279 #pragma omp threadprivate (src_imgs)
280 #pragma omp threadprivate (mask_bits_img)
281 #pragma omp threadprivate (fence_images_created)
282 #endif
283 
284     if (!fence_images_created)
285     {
286         int i;
287 
288         prng_srand (0);
289 
290         for (i = 0; i < ARRAY_LENGTH (img_fmt_list); i++)
291             src_imgs[i] = create_src_image (img_fmt_list[i]);
292 
293         mask_bits_img = create_src_image (PIXMAN_a8);
294 
295         fence_images_created = TRUE;
296     }
297 
298     prng_srand (testnum);
299 
300     x_scale = random_scale_factor ();
301     y_scale = random_scale_factor ();
302     left_align = prng_rand_n (2);
303     top_align = prng_rand_n (2);
304     bilinear = prng_rand_n (2);
305     filter = bilinear ? PIXMAN_FILTER_BILINEAR : PIXMAN_FILTER_NEAREST;
306 
307     op = op_list[prng_rand_n (ARRAY_LENGTH (op_list))];
308 
309     dst_fmt = img_fmt_list[prng_rand_n (ARRAY_LENGTH (img_fmt_list))];
310     dst_img = pixman_image_create_bits (dst_fmt, DST_WIDTH, DST_HEIGHT,
311                                         NULL, 0);
312     prng_randmemset (dst_img->bits.bits,
313                      dst_img->bits.rowstride * DST_HEIGHT * sizeof (uint32_t),
314                      0);
315     image_endian_swap (dst_img);
316 
317     src_fmt_index = prng_rand_n (ARRAY_LENGTH (img_fmt_list));
318     src_fmt = img_fmt_list[src_fmt_index];
319     src_img = src_imgs[src_fmt_index];
320     pixman_image_set_filter (src_img, filter, NULL, 0);
321     pixman_transform_init_scale (&src_transform, x_scale, y_scale);
322     src_transform.matrix[0][2] = calc_translate (dst_img->bits.width,
323                                                  src_img->bits.width,
324                                                  x_scale, left_align, bilinear);
325     src_transform.matrix[1][2] = calc_translate (dst_img->bits.height,
326                                                  src_img->bits.height,
327                                                  y_scale, top_align, bilinear);
328 
329     if (prng_rand_n (2))
330     {
331         /* No mask */
332         mask_fmt = PIXMAN_null;
333         mask_img = NULL;
334     }
335     else if (prng_rand_n (2))
336     {
337         /* a8 bitmap mask */
338         mask_fmt = PIXMAN_a8;
339         mask_img = mask_bits_img;
340         pixman_image_set_filter (mask_img, filter, NULL, 0);
341         pixman_transform_init_scale (&mask_transform, x_scale, y_scale);
342         mask_transform.matrix[0][2] = calc_translate (dst_img->bits.width,
343                                                       mask_img->bits.width,
344                                                       x_scale, left_align,
345                                                       bilinear);
346         mask_transform.matrix[1][2] = calc_translate (dst_img->bits.height,
347                                                       mask_img->bits.height,
348                                                       y_scale, top_align,
349                                                       bilinear);
350     }
351     else
352     {
353         /* Solid mask */
354         pixman_color_t color;
355         memset (&color, 0xAA, sizeof color);
356         mask_fmt = PIXMAN_solid;
357         mask_img = pixman_image_create_solid_fill (&color);
358     }
359 
360     if (!exact)
361     {
362         int i = 0;
363 
364         while (i < 4)
365             fuzz[i++] = random_offset ();
366 
367         src_transform.matrix[0][2] += fuzz[0];
368         src_transform.matrix[1][2] += fuzz[1];
369         mask_transform.matrix[0][2] += fuzz[2];
370         mask_transform.matrix[1][2] += fuzz[3];
371     }
372 
373     pixman_image_set_transform (src_img, &src_transform);
374     if (mask_fmt == PIXMAN_a8)
375         pixman_image_set_transform (mask_img, &mask_transform);
376 
377     if (verbose)
378     {
379         printf ("op=%s\n", operator_name (op));
380         printf ("src_fmt=%s, dst_fmt=%s, mask_fmt=%s\n",
381                 format_name (src_fmt), format_name (dst_fmt),
382                 format_name (mask_fmt));
383         printf ("x_scale=0x%08X, y_scale=0x%08X, align %s/%s, %s\n",
384                 x_scale, y_scale,
385                 left_align ? "left" : "right", top_align ? "top" : "bottom",
386                 bilinear ? "bilinear" : "nearest");
387 
388         if (!exact)
389         {
390             int i = 0;
391 
392             printf ("fuzz factors");
393             while (i < 4)
394                 printf (" %d", fuzz[i++]);
395             printf ("\n");
396         }
397     }
398 
399     if (exact)
400     {
401         check_transform (dst_img, src_img, &src_transform, bilinear);
402         if (mask_fmt == PIXMAN_a8)
403             check_transform (dst_img, mask_img, &mask_transform, bilinear);
404     }
405 
406     pixman_image_composite (op, src_img, mask_img, dst_img,
407                             0, 0, 0, 0, 0, 0,
408                             dst_img->bits.width, dst_img->bits.height);
409 
410     if (verbose)
411         print_image (dst_img);
412 
413     crc32 = compute_crc32_for_image (0, dst_img);
414 
415     pixman_image_unref (dst_img);
416     if (mask_fmt == PIXMAN_solid)
417         pixman_image_unref (mask_img);
418 
419     return crc32;
420 }
421 
422 #if BILINEAR_INTERPOLATION_BITS == 7
423 #define CHECKSUM_FUZZ  0x6B56F607
424 #define CHECKSUM_EXACT 0xA669F4A3
425 #elif BILINEAR_INTERPOLATION_BITS == 4
426 #define CHECKSUM_FUZZ  0x83119ED0
427 #define CHECKSUM_EXACT 0x0D3382CD
428 #else
429 #define CHECKSUM_FUZZ  0x00000000
430 #define CHECKSUM_EXACT 0x00000000
431 #endif
432 
433 int
main(int argc,const char * argv[])434 main (int argc, const char *argv[])
435 {
436     unsigned long page_size;
437 
438     page_size = fence_get_page_size ();
439     if (page_size == 0 || page_size > 16 * 1024)
440         return 77; /* automake SKIP */
441 
442     exact = getenv ("EXACT") != NULL;
443     if (exact)
444         printf ("Doing plots that are exactly aligned to boundaries\n");
445 
446     return fuzzer_test_main ("cover", 2000000,
447                              exact ? CHECKSUM_EXACT : CHECKSUM_FUZZ,
448                              test_cover, argc, argv);
449 }
450