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