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 */
77static 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. */
88static 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 */
100static int exact;
101
102static pixman_image_t *
103create_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
143static pixman_fixed_t
144random_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
162static pixman_fixed_t
163calc_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
192static pixman_fixed_t
193random_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
217static void
218check_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
255static uint32_t
256test_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
433int
434main (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