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