1/* 2 * Copyright © 2018 Broadcom 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 * IN THE SOFTWARE. 22 */ 23 24/** @file 25 * 26 * Touch-testing of the damage extension's implementation of various 27 * primitives. The core initializes the pixmap with some contents, 28 * turns on damage and each per-primitive test gets to just make a 29 * rendering call that draws some pixels. Afterwards, the core checks 30 * what pixels were modified and makes sure the damage report contains 31 * them. 32 */ 33 34/* Test relies on assert() */ 35#undef NDEBUG 36 37#include <assert.h> 38#include <stdbool.h> 39#include <stdio.h> 40#include <stdlib.h> 41#include <string.h> 42#include <limits.h> 43#include <xcb/damage.h> 44 45struct test_setup { 46 xcb_connection_t *c; 47 xcb_drawable_t d; 48 xcb_drawable_t start_drawable; 49 uint32_t *start_drawable_contents; 50 xcb_screen_t *screen; 51 xcb_gc_t gc; 52 int width, height; 53 uint32_t *expected; 54 uint32_t *damaged; 55}; 56 57#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 58 59/** 60 * Performs a synchronous GetImage for a test pixmap, returning 61 * uint32_t per pixel. 62 */ 63static uint32_t * 64get_image(struct test_setup *setup, xcb_drawable_t drawable) 65{ 66 xcb_get_image_cookie_t cookie = 67 xcb_get_image(setup->c, XCB_IMAGE_FORMAT_Z_PIXMAP, drawable, 68 0, 0, setup->width, setup->height, ~0); 69 xcb_get_image_reply_t *reply = 70 xcb_get_image_reply(setup->c, cookie, NULL); 71 uint8_t *data = xcb_get_image_data(reply); 72 int len = xcb_get_image_data_length(reply); 73 74 /* Do I understand X protocol and our test environment? */ 75 assert(reply->depth == 24); 76 assert(len == 4 * setup->width * setup->height); 77 78 uint32_t *result = malloc(sizeof(uint32_t) * 79 setup->width * setup->height); 80 memcpy(result, data, len); 81 free(reply); 82 83 return result; 84} 85 86/** 87 * Gets the image drawn by the test and compares it to the starting 88 * image, producing a bitmask of what pixels were damaged. 89 */ 90static void 91compute_expected_damage(struct test_setup *setup) 92{ 93 uint32_t *results = get_image(setup, setup->d); 94 bool any_modified_pixels = false; 95 96 for (int i = 0; i < setup->width * setup->height; i++) { 97 if (results[i] != setup->start_drawable_contents[i]) { 98 setup->expected[i / 32] |= 1 << (i % 32); 99 any_modified_pixels = true; 100 } 101 } 102 103 /* Make sure that the testcases actually render something! */ 104 assert(any_modified_pixels); 105} 106 107/** 108 * Processes a damage event, filling in the bitmask of pixels reported 109 * to be damaged. 110 */ 111static bool 112damage_event_handle(struct test_setup *setup, 113 struct xcb_damage_notify_event_t *event) 114{ 115 xcb_rectangle_t *rect = &event->area; 116 assert(event->drawable == setup->d); 117 for (int y = rect->y; y < rect->y + rect->height; y++) { 118 for (int x = rect->x; x < rect->x + rect->width; x++) { 119 int bit = y * setup->width + x; 120 setup->damaged[bit / 32] |= 1 << (bit % 32); 121 } 122 } 123 124 return event->level & 0x80; /* XXX: MORE is missing from xcb. */ 125} 126 127/** 128 * Collects a series of damage events (while the MORE flag is set) 129 * into the damaged bitmask. 130 */ 131static void 132collect_damage(struct test_setup *setup) 133{ 134 const xcb_query_extension_reply_t *ext = 135 xcb_get_extension_data(setup->c, &xcb_damage_id); 136 xcb_generic_event_t *ge; 137 138 xcb_flush(setup->c); 139 while ((ge = xcb_wait_for_event(setup->c))) { 140 int event = ge->response_type & ~0x80; 141 142 if (event == (ext->first_event + XCB_DAMAGE_NOTIFY)) { 143 if (!damage_event_handle(setup, 144 (struct xcb_damage_notify_event_t *)ge)) { 145 return; 146 } 147 } else { 148 switch (ge->response_type) { 149 case 0: { 150 xcb_generic_error_t *error = (xcb_generic_error_t *)ge; 151 fprintf(stderr, "X error %d at sequence %d\n", 152 error->error_code, error->sequence); 153 exit(1); 154 } 155 156 case XCB_GRAPHICS_EXPOSURE: 157 case XCB_NO_EXPOSURE: 158 break; 159 160 default: 161 fprintf(stderr, "Unexpected event %d\n", ge->response_type); 162 exit(1); 163 } 164 } 165 } 166 167 fprintf(stderr, "I/O error\n"); 168 exit(1); 169} 170 171/** 172 * Wrapper to set up the test pixmap, attach damage to it, and see if 173 * the reported damage matches the testcase's rendering. 174 */ 175static bool 176damage_test(struct test_setup *setup, 177 void (*test)(struct test_setup *setup), 178 const char *name) 179{ 180 uint32_t expected[32] = { 0 }; 181 uint32_t damaged[32] = { 0 }; 182 183 printf("Testing %s\n", name); 184 185 setup->expected = expected; 186 setup->damaged = damaged; 187 188 /* Create our pixmap for this test and fill it with the 189 * starting image. 190 */ 191 setup->d = xcb_generate_id(setup->c); 192 xcb_create_pixmap(setup->c, setup->screen->root_depth, 193 setup->d, setup->screen->root, 194 setup->width, setup->height); 195 196 setup->gc = xcb_generate_id(setup->c); 197 uint32_t values[] = { setup->screen->black_pixel }; 198 xcb_create_gc(setup->c, setup->gc, setup->screen->root, 199 XCB_GC_FOREGROUND, values); 200 201 xcb_copy_area(setup->c, 202 setup->start_drawable, 203 setup->d, 204 setup->gc, 205 0, 0, 206 0, 0, 207 setup->width, setup->height); 208 209 /* Start watching for damage now that we have the initial contents. */ 210 xcb_damage_damage_t damage = xcb_generate_id(setup->c); 211 xcb_damage_create(setup->c, damage, setup->d, 212 XCB_DAMAGE_REPORT_LEVEL_RAW_RECTANGLES); 213 214 test(setup); 215 216 compute_expected_damage(setup); 217 218 xcb_damage_destroy(setup->c, damage); 219 xcb_free_gc(setup->c, setup->gc); 220 xcb_free_pixmap(setup->c, setup->d); 221 collect_damage(setup); 222 223 for (int bit = 0; bit < setup->width * setup->height; bit++) { 224 if ((expected[bit / 32] & (1 << bit % 32)) && 225 !(damaged[bit / 32] & (1 << bit % 32))) { 226 fprintf(stderr, " fail: %s(): Damage report missed %d, %d\n", 227 name, bit % setup->width, bit / setup->width); 228 return false; 229 } 230 } 231 232 return true; 233} 234 235/** 236 * Creates the pixmap of contents that will be the initial state of 237 * each test's drawable. 238 */ 239static void 240create_start_pixmap(struct test_setup *setup) 241{ 242 setup->start_drawable = xcb_generate_id(setup->c); 243 xcb_create_pixmap(setup->c, setup->screen->root_depth, 244 setup->start_drawable, setup->screen->root, 245 setup->width, setup->height); 246 247 /* Fill pixmap so it has defined contents */ 248 xcb_gc_t fill = xcb_generate_id(setup->c); 249 uint32_t fill_values[] = { setup->screen->white_pixel }; 250 xcb_create_gc(setup->c, fill, setup->screen->root, 251 XCB_GC_FOREGROUND, fill_values); 252 253 xcb_rectangle_t rect_all = { 0, 0, setup->width, setup->height}; 254 xcb_poly_fill_rectangle(setup->c, setup->start_drawable, 255 fill, 1, &rect_all); 256 xcb_free_gc(setup->c, fill); 257 258 /* Draw a rectangle */ 259 xcb_gc_t gc = xcb_generate_id(setup->c); 260 uint32_t values[] = { 0xaaaaaaaa }; 261 xcb_create_gc(setup->c, gc, setup->screen->root, 262 XCB_GC_FOREGROUND, values); 263 264 xcb_rectangle_t rect = { 5, 5, 10, 15 }; 265 xcb_poly_rectangle(setup->c, setup->start_drawable, gc, 1, &rect); 266 267 xcb_free_gc(setup->c, gc); 268 269 /* Capture the rendered start contents once, for comparing each 270 * test's rendering output to the start contents. 271 */ 272 setup->start_drawable_contents = get_image(setup, setup->start_drawable); 273} 274 275static void 276test_poly_point_origin(struct test_setup *setup) 277{ 278 struct xcb_point_t points[] = { {1, 2}, {3, 4} }; 279 xcb_poly_point(setup->c, XCB_COORD_MODE_ORIGIN, setup->d, setup->gc, 280 ARRAY_SIZE(points), points); 281} 282 283static void 284test_poly_point_previous(struct test_setup *setup) 285{ 286 struct xcb_point_t points[] = { {1, 2}, {3, 4} }; 287 xcb_poly_point(setup->c, XCB_COORD_MODE_PREVIOUS, setup->d, setup->gc, 288 ARRAY_SIZE(points), points); 289} 290 291static void 292test_poly_line_origin(struct test_setup *setup) 293{ 294 struct xcb_point_t points[] = { {1, 2}, {3, 4}, {5, 6} }; 295 xcb_poly_line(setup->c, XCB_COORD_MODE_ORIGIN, setup->d, setup->gc, 296 ARRAY_SIZE(points), points); 297} 298 299static void 300test_poly_line_previous(struct test_setup *setup) 301{ 302 struct xcb_point_t points[] = { {1, 2}, {3, 4}, {5, 6} }; 303 xcb_poly_line(setup->c, XCB_COORD_MODE_PREVIOUS, setup->d, setup->gc, 304 ARRAY_SIZE(points), points); 305} 306 307static void 308test_poly_fill_rectangle(struct test_setup *setup) 309{ 310 struct xcb_rectangle_t rects[] = { {1, 2, 3, 4}, 311 {5, 6, 7, 8} }; 312 xcb_poly_fill_rectangle(setup->c, setup->d, setup->gc, 313 ARRAY_SIZE(rects), rects); 314} 315 316static void 317test_poly_rectangle(struct test_setup *setup) 318{ 319 struct xcb_rectangle_t rects[] = { {1, 2, 3, 4}, 320 {5, 6, 7, 8} }; 321 xcb_poly_rectangle(setup->c, setup->d, setup->gc, 322 ARRAY_SIZE(rects), rects); 323} 324 325static void 326test_poly_segment(struct test_setup *setup) 327{ 328 struct xcb_segment_t segs[] = { {1, 2, 3, 4}, 329 {5, 6, 7, 8} }; 330 xcb_poly_segment(setup->c, setup->d, setup->gc, 331 ARRAY_SIZE(segs), segs); 332} 333 334int main(int argc, char **argv) 335{ 336 int screen; 337 xcb_connection_t *c = xcb_connect(NULL, &screen); 338 const xcb_query_extension_reply_t *ext = 339 xcb_get_extension_data(c, &xcb_damage_id); 340 341 if (!ext->present) { 342 printf("No XDamage present\n"); 343 exit(77); 344 } 345 346 struct test_setup setup = { 347 .c = c, 348 .width = 32, 349 .height = 32, 350 }; 351 352 /* Get the screen so we have the root window. */ 353 xcb_screen_iterator_t iter; 354 iter = xcb_setup_roots_iterator (xcb_get_setup (c)); 355 setup.screen = iter.data; 356 357 xcb_damage_query_version(c, 1, 1); 358 359 create_start_pixmap(&setup); 360 361 bool pass = true; 362#define test(x) pass = damage_test(&setup, x, #x) && pass; 363 364 test(test_poly_point_origin); 365 test(test_poly_point_previous); 366 test(test_poly_line_origin); 367 test(test_poly_line_previous); 368 test(test_poly_fill_rectangle); 369 test(test_poly_rectangle); 370 test(test_poly_segment); 371 372 xcb_disconnect(c); 373 exit(pass ? 0 : 1); 374} 375