xserver

xserver with xephyr scale patch
git clone https://git.neptards.moe/u3shit/xserver.git
Log | Files | Refs | README | LICENSE

primitives.c (11630B)


      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 
     45 struct 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  */
     63 static uint32_t *
     64 get_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  */
     90 static void
     91 compute_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  */
    111 static bool
    112 damage_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  */
    131 static void
    132 collect_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  */
    175 static bool
    176 damage_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  */
    239 static void
    240 create_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 
    275 static void
    276 test_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 
    283 static void
    284 test_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 
    291 static void
    292 test_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 
    299 static void
    300 test_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 
    307 static void
    308 test_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 
    316 static void
    317 test_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 
    325 static void
    326 test_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 
    334 int 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 }