450 lines
15 KiB
C
450 lines
15 KiB
C
|
/*
|
||
|
* 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);
|
||
|
}
|