| /* |
| * Copyright © 2015 RISC OS Open Ltd |
| * |
| * Permission to use, copy, modify, distribute, and sell this software and its |
| * documentation for any purpose is hereby granted without fee, provided that |
| * the above copyright notice appear in all copies and that both that |
| * copyright notice and this permission notice appear in supporting |
| * documentation, and that the name of the copyright holders not be used in |
| * advertising or publicity pertaining to distribution of the software without |
| * specific, written prior permission. The copyright holders make no |
| * representations about the suitability of this software for any purpose. It |
| * is provided "as is" without express or implied warranty. |
| * |
| * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS |
| * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND |
| * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY |
| * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN |
| * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
| * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
| * SOFTWARE. |
| * |
| * Author: Ben Avison (bavison@riscosopen.org) |
| * |
| */ |
| |
| /* |
| * This test aims to verify both numerical correctness and the honouring of |
| * array bounds for scaled plots (both nearest-neighbour and bilinear) at or |
| * close to the boundary conditions for applicability of "cover" type fast paths |
| * and iter fetch routines. |
| * |
| * It has a secondary purpose: by setting the env var EXACT (to any value) it |
| * will only test plots that are exactly on the boundary condition. This makes |
| * it possible to ensure that "cover" routines are being used to the maximum, |
| * although this requires the use of a debugger or code instrumentation to |
| * verify. |
| */ |
| |
| #include "utils.h" |
| #include <stdlib.h> |
| #include <stdio.h> |
| |
| /* Approximate limits for random scale factor generation - these ensure we can |
| * get at least 8x reduction and 8x enlargement. |
| */ |
| #define LOG2_MAX_FACTOR (3) |
| |
| /* 1/sqrt(2) (or sqrt(0.5), or 2^-0.5) as a 0.32 fixed-point number */ |
| #define INV_SQRT_2_0POINT32_FIXED (0xB504F334u) |
| |
| /* The largest increment that can be generated by random_scale_factor(). |
| * This occurs when the "mantissa" part is 0xFFFFFFFF and the "exponent" |
| * part is -LOG2_MAX_FACTOR. |
| */ |
| #define MAX_INC ((pixman_fixed_t) \ |
| (INV_SQRT_2_0POINT32_FIXED >> (31 - 16 - LOG2_MAX_FACTOR))) |
| |
| /* Minimum source width (in pixels) based on a typical page size of 4K and |
| * maximum colour depth of 32bpp. |
| */ |
| #define MIN_SRC_WIDTH (4096 / 4) |
| |
| /* Derive the destination width so that at max increment we fit within source */ |
| #define DST_WIDTH (MIN_SRC_WIDTH * pixman_fixed_1 / MAX_INC) |
| |
| /* Calculate heights the other way round. |
| * No limits due to page alignment here. |
| */ |
| #define DST_HEIGHT 3 |
| #define SRC_HEIGHT ((DST_HEIGHT * MAX_INC + pixman_fixed_1 - 1) / pixman_fixed_1) |
| |
| /* At the time of writing, all the scaled fast paths use SRC, OVER or ADD |
| * Porter-Duff operators. XOR is included in the list to ensure good |
| * representation of iter scanline fetch routines. |
| */ |
| static const pixman_op_t op_list[] = { |
| PIXMAN_OP_SRC, |
| PIXMAN_OP_OVER, |
| PIXMAN_OP_ADD, |
| PIXMAN_OP_XOR, |
| }; |
| |
| /* At the time of writing, all the scaled fast paths use a8r8g8b8, x8r8g8b8 |
| * or r5g6b5, or red-blue swapped versions of the same. When a mask channel is |
| * used, it is always a8 (and so implicitly not component alpha). a1r5g5b5 is |
| * included because it is the only other format to feature in any iters. */ |
| static const pixman_format_code_t img_fmt_list[] = { |
| PIXMAN_a8r8g8b8, |
| PIXMAN_x8r8g8b8, |
| PIXMAN_r5g6b5, |
| PIXMAN_a1r5g5b5 |
| }; |
| |
| /* This is a flag reflecting the environment variable EXACT. It can be used |
| * to ensure that source coordinates corresponding exactly to the "cover" limits |
| * are used, rather than any "near misses". This can, for example, be used in |
| * conjunction with a debugger to ensure that only COVER fast paths are used. |
| */ |
| static int exact; |
| |
| static pixman_image_t * |
| create_src_image (pixman_format_code_t fmt) |
| { |
| pixman_image_t *tmp_img, *img; |
| |
| /* We need the left-most and right-most MIN_SRC_WIDTH pixels to have |
| * predictable values, even though fence_image_create_bits() may allocate |
| * an image somewhat larger than that, by an amount that varies depending |
| * upon the page size on the current platform. The solution is to create a |
| * temporary non-fenced image that is exactly MIN_SRC_WIDTH wide and blit it |
| * into the fenced image. |
| */ |
| tmp_img = pixman_image_create_bits (fmt, MIN_SRC_WIDTH, SRC_HEIGHT, |
| NULL, 0); |
| if (tmp_img == NULL) |
| return NULL; |
| |
| img = fence_image_create_bits (fmt, MIN_SRC_WIDTH, SRC_HEIGHT, TRUE); |
| if (img == NULL) |
| { |
| pixman_image_unref (tmp_img); |
| return NULL; |
| } |
| |
| prng_randmemset (tmp_img->bits.bits, |
| tmp_img->bits.rowstride * SRC_HEIGHT * sizeof (uint32_t), |
| 0); |
| image_endian_swap (tmp_img); |
| |
| pixman_image_composite (PIXMAN_OP_SRC, tmp_img, NULL, img, |
| 0, 0, 0, 0, 0, 0, |
| MIN_SRC_WIDTH, SRC_HEIGHT); |
| pixman_image_composite (PIXMAN_OP_SRC, tmp_img, NULL, img, |
| 0, 0, 0, 0, img->bits.width - MIN_SRC_WIDTH, 0, |
| MIN_SRC_WIDTH, SRC_HEIGHT); |
| |
| pixman_image_unref (tmp_img); |
| |
| return img; |
| } |
| |
| static pixman_fixed_t |
| random_scale_factor(void) |
| { |
| /* Get a random number with top bit set. */ |
| uint32_t f = prng_rand () | 0x80000000u; |
| |
| /* In log(2) space, this is still approximately evenly spread between 31 |
| * and 32. Divide by sqrt(2) to centre the distribution on 2^31. |
| */ |
| f = ((uint64_t) f * INV_SQRT_2_0POINT32_FIXED) >> 32; |
| |
| /* Now shift right (ie divide by an integer power of 2) to spread the |
| * distribution between centres at 2^(16 +/- LOG2_MAX_FACTOR). |
| */ |
| f >>= 31 - 16 + prng_rand_n (2 * LOG2_MAX_FACTOR + 1) - LOG2_MAX_FACTOR; |
| |
| return f; |
| } |
| |
| 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) |
| { |
| pixman_fixed_t ref_src, ref_dst, scaled_dst; |
| |
| if (low_align) |
| { |
| ref_src = bilinear ? pixman_fixed_1 / 2 : pixman_fixed_e; |
| ref_dst = pixman_fixed_1 / 2; |
| } |
| else |
| { |
| ref_src = pixman_int_to_fixed (src_size) - |
| bilinear * pixman_fixed_1 / 2; |
| ref_dst = pixman_int_to_fixed (dst_size) - pixman_fixed_1 / 2; |
| } |
| |
| scaled_dst = ((uint64_t) ref_dst * scale + pixman_fixed_1 / 2) / |
| pixman_fixed_1; |
| |
| /* We need the translation to be set such that when ref_dst is fed through |
| * the transformation matrix, we get ref_src as the result. |
| */ |
| return ref_src - scaled_dst; |
| } |
| |
| static pixman_fixed_t |
| random_offset (void) |
| { |
| pixman_fixed_t offset = 0; |
| |
| /* Ensure we test the exact case quite a lot */ |
| if (prng_rand_n (2)) |
| return offset; |
| |
| /* What happens when we are close to the edge of the first |
| * interpolation step? |
| */ |
| if (prng_rand_n (2)) |
| offset += (pixman_fixed_1 >> BILINEAR_INTERPOLATION_BITS) - 16; |
| |
| /* Try fine-grained variations */ |
| offset += prng_rand_n (32); |
| |
| /* Test in both directions */ |
| if (prng_rand_n (2)) |
| offset = -offset; |
| |
| return offset; |
| } |
| |
| static void |
| check_transform (pixman_image_t *dst_img, |
| pixman_image_t *src_img, |
| pixman_transform_t *transform, |
| pixman_bool_t bilinear) |
| { |
| pixman_vector_t v1, v2; |
| |
| v1.vector[0] = pixman_fixed_1 / 2; |
| v1.vector[1] = pixman_fixed_1 / 2; |
| v1.vector[2] = pixman_fixed_1; |
| assert (pixman_transform_point (transform, &v1)); |
| |
| v2.vector[0] = pixman_int_to_fixed (dst_img->bits.width) - |
| pixman_fixed_1 / 2; |
| v2.vector[1] = pixman_int_to_fixed (dst_img->bits.height) - |
| pixman_fixed_1 / 2; |
| v2.vector[2] = pixman_fixed_1; |
| assert (pixman_transform_point (transform, &v2)); |
| |
| if (bilinear) |
| { |
| assert (v1.vector[0] >= pixman_fixed_1 / 2); |
| assert (v1.vector[1] >= pixman_fixed_1 / 2); |
| assert (v2.vector[0] <= pixman_int_to_fixed (src_img->bits.width) - |
| pixman_fixed_1 / 2); |
| assert (v2.vector[1] <= pixman_int_to_fixed (src_img->bits.height) - |
| pixman_fixed_1 / 2); |
| } |
| else |
| { |
| assert (v1.vector[0] >= pixman_fixed_e); |
| assert (v1.vector[1] >= pixman_fixed_e); |
| assert (v2.vector[0] <= pixman_int_to_fixed (src_img->bits.width)); |
| assert (v2.vector[1] <= pixman_int_to_fixed (src_img->bits.height)); |
| } |
| } |
| |
| static uint32_t |
| test_cover (int testnum, int verbose) |
| { |
| pixman_fixed_t x_scale, y_scale; |
| pixman_bool_t left_align, top_align; |
| pixman_bool_t bilinear; |
| pixman_filter_t filter; |
| pixman_op_t op; |
| size_t src_fmt_index; |
| pixman_format_code_t src_fmt, dst_fmt, mask_fmt; |
| pixman_image_t *src_img, *dst_img, *mask_img; |
| pixman_transform_t src_transform, mask_transform; |
| pixman_fixed_t fuzz[4]; |
| uint32_t crc32; |
| |
| /* We allocate one fenced image for each pixel format up-front. This is to |
| * avoid spending a lot of time on memory management rather than on testing |
| * Pixman optimisations. We need one per thread because the transformation |
| * matrices and filtering are properties of the source and mask images. |
| */ |
| static pixman_image_t *src_imgs[ARRAY_LENGTH (img_fmt_list)]; |
| static pixman_image_t *mask_bits_img; |
| static pixman_bool_t fence_images_created; |
| #ifdef USE_OPENMP |
| #pragma omp threadprivate (src_imgs) |
| #pragma omp threadprivate (mask_bits_img) |
| #pragma omp threadprivate (fence_images_created) |
| #endif |
| |
| if (!fence_images_created) |
| { |
| int i; |
| |
| prng_srand (0); |
| |
| for (i = 0; i < ARRAY_LENGTH (img_fmt_list); i++) |
| src_imgs[i] = create_src_image (img_fmt_list[i]); |
| |
| mask_bits_img = create_src_image (PIXMAN_a8); |
| |
| fence_images_created = TRUE; |
| } |
| |
| prng_srand (testnum); |
| |
| x_scale = random_scale_factor (); |
| y_scale = random_scale_factor (); |
| left_align = prng_rand_n (2); |
| top_align = prng_rand_n (2); |
| bilinear = prng_rand_n (2); |
| filter = bilinear ? PIXMAN_FILTER_BILINEAR : PIXMAN_FILTER_NEAREST; |
| |
| op = op_list[prng_rand_n (ARRAY_LENGTH (op_list))]; |
| |
| dst_fmt = img_fmt_list[prng_rand_n (ARRAY_LENGTH (img_fmt_list))]; |
| dst_img = pixman_image_create_bits (dst_fmt, DST_WIDTH, DST_HEIGHT, |
| NULL, 0); |
| prng_randmemset (dst_img->bits.bits, |
| dst_img->bits.rowstride * DST_HEIGHT * sizeof (uint32_t), |
| 0); |
| image_endian_swap (dst_img); |
| |
| src_fmt_index = prng_rand_n (ARRAY_LENGTH (img_fmt_list)); |
| src_fmt = img_fmt_list[src_fmt_index]; |
| src_img = src_imgs[src_fmt_index]; |
| pixman_image_set_filter (src_img, filter, NULL, 0); |
| pixman_transform_init_scale (&src_transform, x_scale, y_scale); |
| src_transform.matrix[0][2] = calc_translate (dst_img->bits.width, |
| src_img->bits.width, |
| x_scale, left_align, bilinear); |
| src_transform.matrix[1][2] = calc_translate (dst_img->bits.height, |
| src_img->bits.height, |
| y_scale, top_align, bilinear); |
| |
| if (prng_rand_n (2)) |
| { |
| /* No mask */ |
| mask_fmt = PIXMAN_null; |
| mask_img = NULL; |
| } |
| else if (prng_rand_n (2)) |
| { |
| /* a8 bitmap mask */ |
| mask_fmt = PIXMAN_a8; |
| mask_img = mask_bits_img; |
| pixman_image_set_filter (mask_img, filter, NULL, 0); |
| pixman_transform_init_scale (&mask_transform, x_scale, y_scale); |
| mask_transform.matrix[0][2] = calc_translate (dst_img->bits.width, |
| mask_img->bits.width, |
| x_scale, left_align, |
| bilinear); |
| mask_transform.matrix[1][2] = calc_translate (dst_img->bits.height, |
| mask_img->bits.height, |
| y_scale, top_align, |
| bilinear); |
| } |
| else |
| { |
| /* Solid mask */ |
| pixman_color_t color; |
| memset (&color, 0xAA, sizeof color); |
| mask_fmt = PIXMAN_solid; |
| mask_img = pixman_image_create_solid_fill (&color); |
| } |
| |
| if (!exact) |
| { |
| int i = 0; |
| |
| while (i < 4) |
| fuzz[i++] = random_offset (); |
| |
| src_transform.matrix[0][2] += fuzz[0]; |
| src_transform.matrix[1][2] += fuzz[1]; |
| mask_transform.matrix[0][2] += fuzz[2]; |
| mask_transform.matrix[1][2] += fuzz[3]; |
| } |
| |
| pixman_image_set_transform (src_img, &src_transform); |
| if (mask_fmt == PIXMAN_a8) |
| pixman_image_set_transform (mask_img, &mask_transform); |
| |
| if (verbose) |
| { |
| printf ("op=%s\n", operator_name (op)); |
| printf ("src_fmt=%s, dst_fmt=%s, mask_fmt=%s\n", |
| format_name (src_fmt), format_name (dst_fmt), |
| format_name (mask_fmt)); |
| printf ("x_scale=0x%08X, y_scale=0x%08X, align %s/%s, %s\n", |
| x_scale, y_scale, |
| left_align ? "left" : "right", top_align ? "top" : "bottom", |
| bilinear ? "bilinear" : "nearest"); |
| |
| if (!exact) |
| { |
| int i = 0; |
| |
| printf ("fuzz factors"); |
| while (i < 4) |
| printf (" %d", fuzz[i++]); |
| printf ("\n"); |
| } |
| } |
| |
| if (exact) |
| { |
| check_transform (dst_img, src_img, &src_transform, bilinear); |
| if (mask_fmt == PIXMAN_a8) |
| check_transform (dst_img, mask_img, &mask_transform, bilinear); |
| } |
| |
| pixman_image_composite (op, src_img, mask_img, dst_img, |
| 0, 0, 0, 0, 0, 0, |
| dst_img->bits.width, dst_img->bits.height); |
| |
| if (verbose) |
| print_image (dst_img); |
| |
| crc32 = compute_crc32_for_image (0, dst_img); |
| |
| pixman_image_unref (dst_img); |
| if (mask_fmt == PIXMAN_solid) |
| pixman_image_unref (mask_img); |
| |
| return crc32; |
| } |
| |
| #if BILINEAR_INTERPOLATION_BITS == 7 |
| #define CHECKSUM_FUZZ 0x6B56F607 |
| #define CHECKSUM_EXACT 0xA669F4A3 |
| #elif BILINEAR_INTERPOLATION_BITS == 4 |
| #define CHECKSUM_FUZZ 0x83119ED0 |
| #define CHECKSUM_EXACT 0x0D3382CD |
| #else |
| #define CHECKSUM_FUZZ 0x00000000 |
| #define CHECKSUM_EXACT 0x00000000 |
| #endif |
| |
| int |
| main (int argc, const char *argv[]) |
| { |
| unsigned long page_size; |
| |
| page_size = fence_get_page_size (); |
| if (page_size == 0 || page_size > 16 * 1024) |
| return 77; /* automake SKIP */ |
| |
| exact = getenv ("EXACT") != NULL; |
| if (exact) |
| printf ("Doing plots that are exactly aligned to boundaries\n"); |
| |
| return fuzzer_test_main ("cover", 2000000, |
| exact ? CHECKSUM_EXACT : CHECKSUM_FUZZ, |
| test_cover, argc, argv); |
| } |