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