1fe8aea9eSmrg/* 2fe8aea9eSmrg * Copyright (c) 2015 Intel Corporation 3fe8aea9eSmrg * 4fe8aea9eSmrg * Permission is hereby granted, free of charge, to any person obtaining a 5fe8aea9eSmrg * copy of this software and associated documentation files (the "Software"), 6fe8aea9eSmrg * to deal in the Software without restriction, including without limitation 7fe8aea9eSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8fe8aea9eSmrg * and/or sell copies of the Software, and to permit persons to whom the 9fe8aea9eSmrg * Software is furnished to do so, subject to the following conditions: 10fe8aea9eSmrg * 11fe8aea9eSmrg * The above copyright notice and this permission notice (including the next 12fe8aea9eSmrg * paragraph) shall be included in all copies or substantial portions of the 13fe8aea9eSmrg * Software. 14fe8aea9eSmrg * 15fe8aea9eSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16fe8aea9eSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17fe8aea9eSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18fe8aea9eSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19fe8aea9eSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20fe8aea9eSmrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21fe8aea9eSmrg * SOFTWARE. 22fe8aea9eSmrg * 23fe8aea9eSmrg */ 24fe8aea9eSmrg 25fe8aea9eSmrg#ifdef HAVE_CONFIG_H 26fe8aea9eSmrg#include "config.h" 27fe8aea9eSmrg#endif 28fe8aea9eSmrg 29fe8aea9eSmrg#include <X11/Xlib.h> 30fe8aea9eSmrg#include <X11/Xatom.h> 31fe8aea9eSmrg#include <X11/Xlib-xcb.h> 32fe8aea9eSmrg#include <X11/xshmfence.h> 33fe8aea9eSmrg#include <X11/Xutil.h> 34fe8aea9eSmrg#include <X11/Xlibint.h> 35fe8aea9eSmrg#include <X11/extensions/Xcomposite.h> 36fe8aea9eSmrg#include <X11/extensions/Xdamage.h> 37fe8aea9eSmrg#include <X11/extensions/dpms.h> 38fe8aea9eSmrg#include <X11/extensions/randr.h> 39fe8aea9eSmrg#include <X11/extensions/Xrandr.h> 40fe8aea9eSmrg#include <xcb/xcb.h> 41fe8aea9eSmrg#include <xcb/present.h> 42fe8aea9eSmrg#include <xcb/dri3.h> 43fe8aea9eSmrg#include <xcb/xfixes.h> 44fe8aea9eSmrg#include <xf86drm.h> 45fe8aea9eSmrg#include <i915_drm.h> 46fe8aea9eSmrg 47fe8aea9eSmrg#include <stdio.h> 48fe8aea9eSmrg#include <string.h> 49fe8aea9eSmrg#include <fcntl.h> 50fe8aea9eSmrg#include <unistd.h> 51fe8aea9eSmrg#include <assert.h> 52fe8aea9eSmrg#include <errno.h> 53fe8aea9eSmrg#include <setjmp.h> 54fe8aea9eSmrg#include <signal.h> 55fe8aea9eSmrg#include <sys/wait.h> 56fe8aea9eSmrg 57fe8aea9eSmrg#include "dri3.h" 58fe8aea9eSmrg 59fe8aea9eSmrgstatic int _x_error_occurred; 60fe8aea9eSmrgstatic uint32_t stamp; 61fe8aea9eSmrg 62fe8aea9eSmrgstruct list { 63fe8aea9eSmrg struct list *next, *prev; 64fe8aea9eSmrg}; 65fe8aea9eSmrg 66fe8aea9eSmrgstatic void 67fe8aea9eSmrglist_init(struct list *list) 68fe8aea9eSmrg{ 69fe8aea9eSmrg list->next = list->prev = list; 70fe8aea9eSmrg} 71fe8aea9eSmrg 72fe8aea9eSmrgstatic inline void 73fe8aea9eSmrg__list_add(struct list *entry, 74fe8aea9eSmrg struct list *prev, 75fe8aea9eSmrg struct list *next) 76fe8aea9eSmrg{ 77fe8aea9eSmrg next->prev = entry; 78fe8aea9eSmrg entry->next = next; 79fe8aea9eSmrg entry->prev = prev; 80fe8aea9eSmrg prev->next = entry; 81fe8aea9eSmrg} 82fe8aea9eSmrg 83fe8aea9eSmrgstatic inline void 84fe8aea9eSmrglist_add(struct list *entry, struct list *head) 85fe8aea9eSmrg{ 86fe8aea9eSmrg __list_add(entry, head, head->next); 87fe8aea9eSmrg} 88fe8aea9eSmrg 89fe8aea9eSmrgstatic inline void 90fe8aea9eSmrg__list_del(struct list *prev, struct list *next) 91fe8aea9eSmrg{ 92fe8aea9eSmrg next->prev = prev; 93fe8aea9eSmrg prev->next = next; 94fe8aea9eSmrg} 95fe8aea9eSmrg 96fe8aea9eSmrgstatic inline void 97fe8aea9eSmrg_list_del(struct list *entry) 98fe8aea9eSmrg{ 99fe8aea9eSmrg __list_del(entry->prev, entry->next); 100fe8aea9eSmrg} 101fe8aea9eSmrg 102fe8aea9eSmrgstatic inline void 103fe8aea9eSmrglist_move(struct list *list, struct list *head) 104fe8aea9eSmrg{ 105fe8aea9eSmrg if (list->prev != head) { 106fe8aea9eSmrg _list_del(list); 107fe8aea9eSmrg list_add(list, head); 108fe8aea9eSmrg } 109fe8aea9eSmrg} 110fe8aea9eSmrg 111fe8aea9eSmrg#define __container_of(ptr, sample, member) \ 112fe8aea9eSmrg (void *)((char *)(ptr) - ((char *)&(sample)->member - (char *)(sample))) 113fe8aea9eSmrg 114fe8aea9eSmrg#define list_for_each_entry(pos, head, member) \ 115fe8aea9eSmrg for (pos = __container_of((head)->next, pos, member); \ 116fe8aea9eSmrg &pos->member != (head); \ 117fe8aea9eSmrg pos = __container_of(pos->member.next, pos, member)) 118fe8aea9eSmrg 119fe8aea9eSmrgstatic int 120fe8aea9eSmrg_check_error_handler(Display *display, 121fe8aea9eSmrg XErrorEvent *event) 122fe8aea9eSmrg{ 123fe8aea9eSmrg if (_x_error_occurred < 0) 124fe8aea9eSmrg return True; 125fe8aea9eSmrg 126fe8aea9eSmrg printf("X11 error from display %s, serial=%ld, error=%d, req=%d.%d\n", 127fe8aea9eSmrg DisplayString(display), 128fe8aea9eSmrg event->serial, 129fe8aea9eSmrg event->error_code, 130fe8aea9eSmrg event->request_code, 131fe8aea9eSmrg event->minor_code); 132fe8aea9eSmrg _x_error_occurred++; 133fe8aea9eSmrg return False; /* ignored */ 134fe8aea9eSmrg} 135fe8aea9eSmrg 136fe8aea9eSmrgstatic double elapsed(const struct timespec *start, 137fe8aea9eSmrg const struct timespec *end) 138fe8aea9eSmrg{ 139fe8aea9eSmrg return 1e6*(end->tv_sec - start->tv_sec) + (end->tv_nsec - start->tv_nsec)/1000; 140fe8aea9eSmrg} 141fe8aea9eSmrg 142fe8aea9eSmrgstruct buffer { 143fe8aea9eSmrg struct list link; 144fe8aea9eSmrg Pixmap pixmap; 145fe8aea9eSmrg struct dri3_fence fence; 146fe8aea9eSmrg int fd; 147fe8aea9eSmrg int busy; 148fe8aea9eSmrg int id; 149fe8aea9eSmrg}; 150fe8aea9eSmrg 151fe8aea9eSmrg#define DRI3 1 152fe8aea9eSmrg#define NOCOPY 2 153fe8aea9eSmrg#define ASYNC 4 154fe8aea9eSmrgstatic void run(Display *dpy, Window win, const char *name, unsigned options) 155fe8aea9eSmrg{ 156fe8aea9eSmrg xcb_connection_t *c = XGetXCBConnection(dpy); 157fe8aea9eSmrg struct timespec start, end; 158fe8aea9eSmrg#define N_BACK 8 159fe8aea9eSmrg char test_name[128]; 160fe8aea9eSmrg struct buffer buffer[N_BACK]; 161fe8aea9eSmrg struct list mru; 162fe8aea9eSmrg Window root; 163fe8aea9eSmrg unsigned int width, height; 164fe8aea9eSmrg unsigned border, depth; 165fe8aea9eSmrg unsigned present_flags = 0; 166fe8aea9eSmrg xcb_xfixes_region_t update = 0; 167fe8aea9eSmrg int completed = 0; 168fe8aea9eSmrg int queued = 0; 169fe8aea9eSmrg uint32_t eid = 0; 170fe8aea9eSmrg void *Q = NULL; 171fe8aea9eSmrg int i, n; 172fe8aea9eSmrg 173fe8aea9eSmrg list_init(&mru); 174fe8aea9eSmrg 175fe8aea9eSmrg XGetGeometry(dpy, win, 176fe8aea9eSmrg &root, &i, &n, &width, &height, &border, &depth); 177fe8aea9eSmrg 178fe8aea9eSmrg _x_error_occurred = 0; 179fe8aea9eSmrg 180fe8aea9eSmrg for (n = 0; n < N_BACK; n++) { 181fe8aea9eSmrg buffer[n].pixmap = xcb_generate_id(c); 182fe8aea9eSmrg xcb_create_pixmap(c, depth, buffer[n].pixmap, win, 183fe8aea9eSmrg width, height); 184fe8aea9eSmrg buffer[n].fence.xid = 0; 185fe8aea9eSmrg buffer[n].fd = -1; 186fe8aea9eSmrg buffer[n].id = n; 187fe8aea9eSmrg if (options & DRI3) { 188fe8aea9eSmrg xcb_dri3_buffer_from_pixmap_reply_t *reply; 189fe8aea9eSmrg int *fds; 190fe8aea9eSmrg 191fe8aea9eSmrg if (dri3_create_fence(dpy, win, &buffer[n].fence)) 192fe8aea9eSmrg return; 193fe8aea9eSmrg 194fe8aea9eSmrg reply = xcb_dri3_buffer_from_pixmap_reply (c, 195fe8aea9eSmrg xcb_dri3_buffer_from_pixmap(c, buffer[n].pixmap), 196fe8aea9eSmrg NULL); 197fe8aea9eSmrg if (reply == NULL) 198fe8aea9eSmrg return; 199fe8aea9eSmrg 200fe8aea9eSmrg fds = xcb_dri3_buffer_from_pixmap_reply_fds (c, reply); 201fe8aea9eSmrg buffer[n].fd = fds[0]; 202fe8aea9eSmrg free(reply); 203fe8aea9eSmrg 204fe8aea9eSmrg /* start idle */ 205fe8aea9eSmrg xshmfence_trigger(buffer[n].fence.addr); 206fe8aea9eSmrg } 207fe8aea9eSmrg buffer[n].busy = 0; 208fe8aea9eSmrg list_add(&buffer[n].link, &mru); 209fe8aea9eSmrg } 210fe8aea9eSmrg if (options & ASYNC) 211fe8aea9eSmrg present_flags |= XCB_PRESENT_OPTION_ASYNC; 212fe8aea9eSmrg if (options & NOCOPY) { 213fe8aea9eSmrg update = xcb_generate_id(c); 214fe8aea9eSmrg xcb_xfixes_create_region(c, update, 0, NULL); 215fe8aea9eSmrg present_flags |= XCB_PRESENT_OPTION_COPY; 216fe8aea9eSmrg } 217fe8aea9eSmrg 218fe8aea9eSmrg if (!(options & DRI3)) { 219fe8aea9eSmrg eid = xcb_generate_id(c); 220fe8aea9eSmrg xcb_present_select_input(c, eid, win, 221fe8aea9eSmrg (options & NOCOPY ? 0 : XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY) | 222fe8aea9eSmrg XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY); 223fe8aea9eSmrg Q = xcb_register_for_special_xge(c, &xcb_present_id, eid, &stamp); 224fe8aea9eSmrg } 225fe8aea9eSmrg 226fe8aea9eSmrg clock_gettime(CLOCK_MONOTONIC, &start); 227fe8aea9eSmrg do { 228fe8aea9eSmrg for (n = 0; n < 1000; n++) { 229fe8aea9eSmrg struct buffer *tmp, *b = NULL; 230fe8aea9eSmrgretry: 231fe8aea9eSmrg list_for_each_entry(tmp, &mru, link) { 232fe8aea9eSmrg if (tmp->fence.xid) 233fe8aea9eSmrg tmp->busy = !xshmfence_query(tmp->fence.addr); 234fe8aea9eSmrg if (!tmp->busy) { 235fe8aea9eSmrg b = tmp; 236fe8aea9eSmrg break; 237fe8aea9eSmrg } 238fe8aea9eSmrg } 239fe8aea9eSmrg if (options & DRI3) { 240fe8aea9eSmrg if (b == NULL) 241fe8aea9eSmrg goto retry; 242fe8aea9eSmrg 243fe8aea9eSmrg xshmfence_reset(b->fence.addr); 244fe8aea9eSmrg queued--; 245fe8aea9eSmrg completed++; 246fe8aea9eSmrg } else while (b == NULL) { 247fe8aea9eSmrg xcb_present_generic_event_t *ev; 248fe8aea9eSmrg 249fe8aea9eSmrg ev = (xcb_present_generic_event_t *) 250fe8aea9eSmrg xcb_wait_for_special_event(c, Q); 251fe8aea9eSmrg if (ev == NULL) 252fe8aea9eSmrg abort(); 253fe8aea9eSmrg 254fe8aea9eSmrg do { 255fe8aea9eSmrg switch (ev->evtype) { 256fe8aea9eSmrg case XCB_PRESENT_COMPLETE_NOTIFY: 257fe8aea9eSmrg completed++; 258fe8aea9eSmrg queued--; 259fe8aea9eSmrg break; 260fe8aea9eSmrg 261fe8aea9eSmrg case XCB_PRESENT_EVENT_IDLE_NOTIFY: 262fe8aea9eSmrg { 263fe8aea9eSmrg xcb_present_idle_notify_event_t *ie = (xcb_present_idle_notify_event_t *)ev; 264fe8aea9eSmrg assert(ie->serial < N_BACK); 265fe8aea9eSmrg buffer[ie->serial].busy = 0; 266fe8aea9eSmrg if (b == NULL) 267fe8aea9eSmrg b = &buffer[ie->serial]; 268fe8aea9eSmrg break; 269fe8aea9eSmrg } 270fe8aea9eSmrg } 271fe8aea9eSmrg free(ev); 272fe8aea9eSmrg } while ((ev = (xcb_present_generic_event_t *)xcb_poll_for_special_event(c, Q))); 273fe8aea9eSmrg } 274fe8aea9eSmrg 275fe8aea9eSmrg b->busy = (options & NOCOPY) == 0; 276fe8aea9eSmrg xcb_present_pixmap(c, win, b->pixmap, b->id, 277fe8aea9eSmrg 0, /* valid */ 278fe8aea9eSmrg update, /* update */ 279fe8aea9eSmrg 0, /* x_off */ 280fe8aea9eSmrg 0, /* y_off */ 281fe8aea9eSmrg None, 282fe8aea9eSmrg None, /* wait fence */ 283fe8aea9eSmrg b->fence.xid, 284fe8aea9eSmrg present_flags, 285fe8aea9eSmrg 0, /* target msc */ 286fe8aea9eSmrg 0, /* divisor */ 287fe8aea9eSmrg 0, /* remainder */ 288fe8aea9eSmrg 0, NULL); 289fe8aea9eSmrg list_move(&b->link, &mru); 290fe8aea9eSmrg queued++; 291fe8aea9eSmrg xcb_flush(c); 292fe8aea9eSmrg } 293fe8aea9eSmrg clock_gettime(CLOCK_MONOTONIC, &end); 294fe8aea9eSmrg } while (end.tv_sec < start.tv_sec + 10); 295fe8aea9eSmrg 296fe8aea9eSmrg if (options & DRI3) { 297fe8aea9eSmrg struct buffer *b; 298fe8aea9eSmrg XID pixmap; 299fe8aea9eSmrg 300fe8aea9eSmrg pixmap = xcb_generate_id(c); 301fe8aea9eSmrg xcb_create_pixmap(c, depth, pixmap, win, width, height); 302fe8aea9eSmrg xcb_present_pixmap(c, win, pixmap, 0xdeadbeef, 303fe8aea9eSmrg 0, /* valid */ 304fe8aea9eSmrg None, /* update */ 305fe8aea9eSmrg 0, /* x_off */ 306fe8aea9eSmrg 0, /* y_off */ 307fe8aea9eSmrg None, 308fe8aea9eSmrg None, /* wait fence */ 309fe8aea9eSmrg None, 310fe8aea9eSmrg 0, 311fe8aea9eSmrg 0, /* target msc */ 312fe8aea9eSmrg 0, /* divisor */ 313fe8aea9eSmrg 0, /* remainder */ 314fe8aea9eSmrg 0, NULL); 315fe8aea9eSmrg xcb_flush(c); 316fe8aea9eSmrg 317fe8aea9eSmrg list_for_each_entry(b, &mru, link) 318fe8aea9eSmrg xshmfence_await(b->fence.addr); 319fe8aea9eSmrg 320fe8aea9eSmrg xcb_free_pixmap(c, pixmap); 321fe8aea9eSmrg completed += queued; 322fe8aea9eSmrg } else while (queued) { 323fe8aea9eSmrg xcb_present_generic_event_t *ev; 324fe8aea9eSmrg 325fe8aea9eSmrg ev = (xcb_present_generic_event_t *) 326fe8aea9eSmrg xcb_wait_for_special_event(c, Q); 327fe8aea9eSmrg if (ev == NULL) 328fe8aea9eSmrg abort(); 329fe8aea9eSmrg 330fe8aea9eSmrg do { 331fe8aea9eSmrg switch (ev->evtype) { 332fe8aea9eSmrg case XCB_PRESENT_COMPLETE_NOTIFY: 333fe8aea9eSmrg completed++; 334fe8aea9eSmrg queued--; 335fe8aea9eSmrg break; 336fe8aea9eSmrg 337fe8aea9eSmrg case XCB_PRESENT_EVENT_IDLE_NOTIFY: 338fe8aea9eSmrg break; 339fe8aea9eSmrg } 340fe8aea9eSmrg free(ev); 341fe8aea9eSmrg } while ((ev = (xcb_present_generic_event_t *)xcb_poll_for_special_event(c, Q))); 342fe8aea9eSmrg } 343fe8aea9eSmrg clock_gettime(CLOCK_MONOTONIC, &end); 344fe8aea9eSmrg 345fe8aea9eSmrg if (update) 346fe8aea9eSmrg xcb_xfixes_destroy_region(c, update); 347fe8aea9eSmrg for (n = 0; n < N_BACK; n++) { 348fe8aea9eSmrg if (buffer[n].fence.xid) 349fe8aea9eSmrg dri3_fence_free(dpy, &buffer[n].fence); 350fe8aea9eSmrg if (buffer[n].fd != -1) 351fe8aea9eSmrg close(buffer[n].fd); 352fe8aea9eSmrg xcb_free_pixmap(c, buffer[n].pixmap); 353fe8aea9eSmrg } 354fe8aea9eSmrg 355fe8aea9eSmrg if (Q) { 356fe8aea9eSmrg xcb_discard_reply(c, xcb_present_select_input_checked(c, eid, win, 0).sequence); 357fe8aea9eSmrg XSync(dpy, True); 358fe8aea9eSmrg xcb_unregister_for_special_event(c, Q); 359fe8aea9eSmrg } 360fe8aea9eSmrg 361fe8aea9eSmrg test_name[0] = '\0'; 362fe8aea9eSmrg if (options) { 363fe8aea9eSmrg snprintf(test_name, sizeof(test_name), "(%s%s%s )", 364fe8aea9eSmrg options & NOCOPY ? " no-copy" : "", 365fe8aea9eSmrg options & DRI3 ? " dri3" : "", 366fe8aea9eSmrg options & ASYNC ? " async" : ""); 367fe8aea9eSmrg } 368fe8aea9eSmrg printf("%s%s: Completed %d presents in %.1fs, %.3fus each (%.1f FPS)\n", 369fe8aea9eSmrg name, test_name, 370fe8aea9eSmrg completed, elapsed(&start, &end) / 1000000, 371fe8aea9eSmrg elapsed(&start, &end) / completed, 372fe8aea9eSmrg completed / (elapsed(&start, &end) / 1000000)); 373fe8aea9eSmrg} 374fe8aea9eSmrg 375fe8aea9eSmrgstruct perpixel { 376fe8aea9eSmrg Window win; 377fe8aea9eSmrg struct buffer buffer[N_BACK]; 378fe8aea9eSmrg struct list mru; 379fe8aea9eSmrg uint32_t eid; 380fe8aea9eSmrg void *Q; 381fe8aea9eSmrg int queued; 382fe8aea9eSmrg}; 383fe8aea9eSmrg 384fe8aea9eSmrgstatic void perpixel(Display *dpy, 385fe8aea9eSmrg int max_width, int max_height, unsigned options) 386fe8aea9eSmrg{ 387fe8aea9eSmrg //const int sz = max_width * max_height; 388fe8aea9eSmrg const int sz = 1048; 389fe8aea9eSmrg struct perpixel *pp; 390fe8aea9eSmrg xcb_connection_t *c = XGetXCBConnection(dpy); 391fe8aea9eSmrg struct timespec start, end; 392fe8aea9eSmrg char test_name[128]; 393fe8aea9eSmrg unsigned present_flags = 0; 394fe8aea9eSmrg xcb_xfixes_region_t update = 0; 395fe8aea9eSmrg int completed = 0; 396fe8aea9eSmrg int i, n; 397fe8aea9eSmrg 398fe8aea9eSmrg pp = calloc(sz, sizeof(*pp)); 399fe8aea9eSmrg if (!pp) 400fe8aea9eSmrg return; 401fe8aea9eSmrg 402fe8aea9eSmrg for (i = 0; i < sz; i++) { 403fe8aea9eSmrg XSetWindowAttributes attr = { .override_redirect = 1 }; 404fe8aea9eSmrg int depth = DefaultDepth(dpy, DefaultScreen(dpy)); 405fe8aea9eSmrg pp[i].win = XCreateWindow(dpy, DefaultRootWindow(dpy), 406fe8aea9eSmrg i % max_width, i / max_width, 1, 1, 0, depth, 407fe8aea9eSmrg InputOutput, 408fe8aea9eSmrg DefaultVisual(dpy, DefaultScreen(dpy)), 409fe8aea9eSmrg CWOverrideRedirect, &attr); 410fe8aea9eSmrg XMapWindow(dpy, pp[i].win); 411fe8aea9eSmrg list_init(&pp[i].mru); 412fe8aea9eSmrg for (n = 0; n < N_BACK; n++) { 413fe8aea9eSmrg pp[i].buffer[n].pixmap = xcb_generate_id(c); 414fe8aea9eSmrg xcb_create_pixmap(c, depth, pp[i].buffer[n].pixmap, 415fe8aea9eSmrg pp[i].win, 1, 1); 416fe8aea9eSmrg pp[i].buffer[n].fence.xid = 0; 417fe8aea9eSmrg pp[i].buffer[n].fd = -1; 418fe8aea9eSmrg pp[i].buffer[n].id = n; 419fe8aea9eSmrg if (options & DRI3) { 420fe8aea9eSmrg xcb_dri3_buffer_from_pixmap_reply_t *reply; 421fe8aea9eSmrg int *fds; 422fe8aea9eSmrg 423fe8aea9eSmrg if (dri3_create_fence(dpy, pp[i].win, &pp[i].buffer[n].fence)) 424fe8aea9eSmrg return; 425fe8aea9eSmrg 426fe8aea9eSmrg reply = xcb_dri3_buffer_from_pixmap_reply(c, 427fe8aea9eSmrg xcb_dri3_buffer_from_pixmap(c, pp[i].buffer[n].pixmap), 428fe8aea9eSmrg NULL); 429fe8aea9eSmrg if (reply == NULL) 430fe8aea9eSmrg return; 431fe8aea9eSmrg 432fe8aea9eSmrg fds = xcb_dri3_buffer_from_pixmap_reply_fds(c, reply); 433fe8aea9eSmrg pp[i].buffer[n].fd = fds[0]; 434fe8aea9eSmrg free(reply); 435fe8aea9eSmrg 436fe8aea9eSmrg /* start idle */ 437fe8aea9eSmrg xshmfence_trigger(pp[i].buffer[n].fence.addr); 438fe8aea9eSmrg } 439fe8aea9eSmrg pp[i].buffer[n].busy = 0; 440fe8aea9eSmrg list_add(&pp[i].buffer[n].link, &pp[i].mru); 441fe8aea9eSmrg } 442fe8aea9eSmrg 443fe8aea9eSmrg if (!(options & DRI3)) { 444fe8aea9eSmrg pp[i].eid = xcb_generate_id(c); 445fe8aea9eSmrg xcb_present_select_input(c, pp[i].eid, pp[i].win, 446fe8aea9eSmrg (options & NOCOPY ? 0 : XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY) | 447fe8aea9eSmrg XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY); 448fe8aea9eSmrg pp[i].Q = xcb_register_for_special_xge(c, &xcb_present_id, pp[i].eid, &stamp); 449fe8aea9eSmrg } 450fe8aea9eSmrg pp[i].queued = 0; 451fe8aea9eSmrg } 452fe8aea9eSmrg 453fe8aea9eSmrg XSync(dpy, True); 454fe8aea9eSmrg _x_error_occurred = 0; 455fe8aea9eSmrg 456fe8aea9eSmrg if (options & ASYNC) 457fe8aea9eSmrg present_flags |= XCB_PRESENT_OPTION_ASYNC; 458fe8aea9eSmrg if (options & NOCOPY) { 459fe8aea9eSmrg update = xcb_generate_id(c); 460fe8aea9eSmrg xcb_xfixes_create_region(c, update, 0, NULL); 461fe8aea9eSmrg present_flags |= XCB_PRESENT_OPTION_COPY; 462fe8aea9eSmrg } 463fe8aea9eSmrg 464fe8aea9eSmrg clock_gettime(CLOCK_MONOTONIC, &start); 465fe8aea9eSmrg do { 466fe8aea9eSmrg for (i = 0; i < sz; i++) { 467fe8aea9eSmrg struct buffer *tmp, *b = NULL; 468fe8aea9eSmrgretry: 469fe8aea9eSmrg list_for_each_entry(tmp, &pp[i].mru, link) { 470fe8aea9eSmrg if (tmp->fence.xid) 471fe8aea9eSmrg tmp->busy = !xshmfence_query(tmp->fence.addr); 472fe8aea9eSmrg if (!tmp->busy) { 473fe8aea9eSmrg b = tmp; 474fe8aea9eSmrg break; 475fe8aea9eSmrg } 476fe8aea9eSmrg } 477fe8aea9eSmrg if (options & DRI3) { 478fe8aea9eSmrg if (b == NULL) 479fe8aea9eSmrg goto retry; 480fe8aea9eSmrg 481fe8aea9eSmrg xshmfence_reset(b->fence.addr); 482fe8aea9eSmrg pp[i].queued--; 483fe8aea9eSmrg completed++; 484fe8aea9eSmrg } else while (b == NULL) { 485fe8aea9eSmrg xcb_present_generic_event_t *ev; 486fe8aea9eSmrg 487fe8aea9eSmrg ev = (xcb_present_generic_event_t *) 488fe8aea9eSmrg xcb_wait_for_special_event(c, pp[i].Q); 489fe8aea9eSmrg if (ev == NULL) 490fe8aea9eSmrg abort(); 491fe8aea9eSmrg 492fe8aea9eSmrg do { 493fe8aea9eSmrg switch (ev->evtype) { 494fe8aea9eSmrg case XCB_PRESENT_COMPLETE_NOTIFY: 495fe8aea9eSmrg completed++; 496fe8aea9eSmrg pp[i].queued--; 497fe8aea9eSmrg break; 498fe8aea9eSmrg 499fe8aea9eSmrg case XCB_PRESENT_EVENT_IDLE_NOTIFY: 500fe8aea9eSmrg { 501fe8aea9eSmrg xcb_present_idle_notify_event_t *ie = (xcb_present_idle_notify_event_t *)ev; 502fe8aea9eSmrg assert(ie->serial < N_BACK); 503fe8aea9eSmrg pp[i].buffer[ie->serial].busy = 0; 504fe8aea9eSmrg if (b == NULL) 505fe8aea9eSmrg b = &pp[i].buffer[ie->serial]; 506fe8aea9eSmrg break; 507fe8aea9eSmrg } 508fe8aea9eSmrg } 509fe8aea9eSmrg free(ev); 510fe8aea9eSmrg } while ((ev = (xcb_present_generic_event_t *)xcb_poll_for_special_event(c, pp[i].Q))); 511fe8aea9eSmrg } 512fe8aea9eSmrg 513fe8aea9eSmrg b->busy = (options & NOCOPY) == 0; 514fe8aea9eSmrg xcb_present_pixmap(c, pp[i].win, b->pixmap, b->id, 515fe8aea9eSmrg 0, /* valid */ 516fe8aea9eSmrg update, /* update */ 517fe8aea9eSmrg 0, /* x_off */ 518fe8aea9eSmrg 0, /* y_off */ 519fe8aea9eSmrg None, 520fe8aea9eSmrg None, /* wait fence */ 521fe8aea9eSmrg b->fence.xid, 522fe8aea9eSmrg present_flags, 523fe8aea9eSmrg 0, /* target msc */ 524fe8aea9eSmrg 0, /* divisor */ 525fe8aea9eSmrg 0, /* remainder */ 526fe8aea9eSmrg 0, NULL); 527fe8aea9eSmrg list_move(&b->link, &pp[i].mru); 528fe8aea9eSmrg pp[i].queued++; 529fe8aea9eSmrg } 530fe8aea9eSmrg xcb_flush(c); 531fe8aea9eSmrg clock_gettime(CLOCK_MONOTONIC, &end); 532fe8aea9eSmrg } while (end.tv_sec < start.tv_sec + 10); 533fe8aea9eSmrg 534fe8aea9eSmrg for (i = 0; i < sz; i++) { 535fe8aea9eSmrg if (options & DRI3) { 536fe8aea9eSmrg int depth = DefaultDepth(dpy, DefaultScreen(dpy)); 537fe8aea9eSmrg struct buffer *b; 538fe8aea9eSmrg XID pixmap; 539fe8aea9eSmrg 540fe8aea9eSmrg pixmap = xcb_generate_id(c); 541fe8aea9eSmrg xcb_create_pixmap(c, depth, pixmap, pp[i].win, 1, 1); 542fe8aea9eSmrg xcb_present_pixmap(c, pp[i].win, pixmap, 0xdeadbeef, 543fe8aea9eSmrg 0, /* valid */ 544fe8aea9eSmrg None, /* update */ 545fe8aea9eSmrg 0, /* x_off */ 546fe8aea9eSmrg 0, /* y_off */ 547fe8aea9eSmrg None, 548fe8aea9eSmrg None, /* wait fence */ 549fe8aea9eSmrg None, 550fe8aea9eSmrg 0, 551fe8aea9eSmrg 0, /* target msc */ 552fe8aea9eSmrg 0, /* divisor */ 553fe8aea9eSmrg 0, /* remainder */ 554fe8aea9eSmrg 0, NULL); 555fe8aea9eSmrg xcb_flush(c); 556fe8aea9eSmrg 557fe8aea9eSmrg list_for_each_entry(b, &pp[i].mru, link) 558fe8aea9eSmrg xshmfence_await(b->fence.addr); 559fe8aea9eSmrg 560fe8aea9eSmrg xcb_free_pixmap(c, pixmap); 561fe8aea9eSmrg completed += pp[i].queued; 562fe8aea9eSmrg } else while (pp[i].queued) { 563fe8aea9eSmrg xcb_present_generic_event_t *ev; 564fe8aea9eSmrg 565fe8aea9eSmrg ev = (xcb_present_generic_event_t *) 566fe8aea9eSmrg xcb_wait_for_special_event(c, pp[i].Q); 567fe8aea9eSmrg if (ev == NULL) 568fe8aea9eSmrg abort(); 569fe8aea9eSmrg 570fe8aea9eSmrg do { 571fe8aea9eSmrg switch (ev->evtype) { 572fe8aea9eSmrg case XCB_PRESENT_COMPLETE_NOTIFY: 573fe8aea9eSmrg completed++; 574fe8aea9eSmrg pp[i].queued--; 575fe8aea9eSmrg break; 576fe8aea9eSmrg 577fe8aea9eSmrg case XCB_PRESENT_EVENT_IDLE_NOTIFY: 578fe8aea9eSmrg break; 579fe8aea9eSmrg } 580fe8aea9eSmrg free(ev); 581fe8aea9eSmrg } while ((ev = (xcb_present_generic_event_t *)xcb_poll_for_special_event(c, pp[i].Q))); 582fe8aea9eSmrg } 583fe8aea9eSmrg } 584fe8aea9eSmrg clock_gettime(CLOCK_MONOTONIC, &end); 585fe8aea9eSmrg 586fe8aea9eSmrg if (update) 587fe8aea9eSmrg xcb_xfixes_destroy_region(c, update); 588fe8aea9eSmrg 589fe8aea9eSmrg for (i = 0; i < sz; i++) { 590fe8aea9eSmrg for (n = 0; n < N_BACK; n++) { 591fe8aea9eSmrg if (pp[i].buffer[n].fence.xid) 592fe8aea9eSmrg dri3_fence_free(dpy, &pp[i].buffer[n].fence); 593fe8aea9eSmrg if (pp[i].buffer[n].fd != -1) 594fe8aea9eSmrg close(pp[i].buffer[n].fd); 595fe8aea9eSmrg xcb_free_pixmap(c, pp[i].buffer[n].pixmap); 596fe8aea9eSmrg } 597fe8aea9eSmrg 598fe8aea9eSmrg if (pp[i].Q) { 599fe8aea9eSmrg xcb_discard_reply(c, xcb_present_select_input_checked(c, pp[i].eid, pp[i].win, 0).sequence); 600fe8aea9eSmrg XSync(dpy, True); 601fe8aea9eSmrg xcb_unregister_for_special_event(c, pp[i].Q); 602fe8aea9eSmrg } 603fe8aea9eSmrg 604fe8aea9eSmrg XDestroyWindow(dpy, pp[i].win); 605fe8aea9eSmrg } 606fe8aea9eSmrg free(pp); 607fe8aea9eSmrg 608fe8aea9eSmrg test_name[0] = '\0'; 609fe8aea9eSmrg if (options) { 610fe8aea9eSmrg snprintf(test_name, sizeof(test_name), "(%s%s%s )", 611fe8aea9eSmrg options & NOCOPY ? " no-copy" : "", 612fe8aea9eSmrg options & DRI3 ? " dri3" : "", 613fe8aea9eSmrg options & ASYNC ? " async" : ""); 614fe8aea9eSmrg } 615fe8aea9eSmrg printf("%s%s: Completed %d presents in %.1fs, %.3fus each (%.1f FPS)\n", 616fe8aea9eSmrg __func__, test_name, 617fe8aea9eSmrg completed, elapsed(&start, &end) / 1000000, 618fe8aea9eSmrg elapsed(&start, &end) / completed, 619fe8aea9eSmrg completed / (elapsed(&start, &end) / 1000000)); 620fe8aea9eSmrg} 621fe8aea9eSmrg 622fe8aea9eSmrgstatic int isqrt(int x) 623fe8aea9eSmrg{ 624fe8aea9eSmrg int i; 625fe8aea9eSmrg 626fe8aea9eSmrg for (i = 2; i*i < x; i++) 627fe8aea9eSmrg ; 628fe8aea9eSmrg return i; 629fe8aea9eSmrg} 630fe8aea9eSmrg 631fe8aea9eSmrgstruct sibling { 632fe8aea9eSmrg pthread_t thread; 633fe8aea9eSmrg Display *dpy; 634fe8aea9eSmrg int x, y; 635fe8aea9eSmrg int width, height; 636fe8aea9eSmrg unsigned options; 637fe8aea9eSmrg}; 638fe8aea9eSmrg 639fe8aea9eSmrgstatic void *sibling(void *arg) 640fe8aea9eSmrg{ 641fe8aea9eSmrg struct sibling *s = arg; 642fe8aea9eSmrg XSetWindowAttributes attr = { .override_redirect = 1 }; 643fe8aea9eSmrg Window win = XCreateWindow(s->dpy, DefaultRootWindow(s->dpy), 644fe8aea9eSmrg s->x, s->y, s->width, s->height, 0, 645fe8aea9eSmrg DefaultDepth(s->dpy, DefaultScreen(s->dpy)), 646fe8aea9eSmrg InputOutput, 647fe8aea9eSmrg DefaultVisual(s->dpy, DefaultScreen(s->dpy)), 648fe8aea9eSmrg CWOverrideRedirect, &attr); 649fe8aea9eSmrg XMapWindow(s->dpy, win); 650fe8aea9eSmrg run(s->dpy, win, "sibling", s->options); 651fe8aea9eSmrg return NULL; 652fe8aea9eSmrg} 653fe8aea9eSmrg 654fe8aea9eSmrgstatic void siblings(Display *dpy, 655fe8aea9eSmrg int max_width, int max_height, int ncpus, unsigned options) 656fe8aea9eSmrg{ 657fe8aea9eSmrg int sq_ncpus = isqrt(ncpus); 658fe8aea9eSmrg int width = max_width / sq_ncpus; 659fe8aea9eSmrg int height = max_height/ sq_ncpus; 660fe8aea9eSmrg struct sibling s[ncpus]; 661fe8aea9eSmrg int child; 662fe8aea9eSmrg 663fe8aea9eSmrg if (ncpus <= 1) 664fe8aea9eSmrg return; 665fe8aea9eSmrg 666fe8aea9eSmrg for (child = 0; child < ncpus; child++) { 667fe8aea9eSmrg s[child].dpy = dpy; 668fe8aea9eSmrg s[child].x = (child % sq_ncpus) * width; 669fe8aea9eSmrg s[child].y = (child / sq_ncpus) * height; 670fe8aea9eSmrg s[child].width = width; 671fe8aea9eSmrg s[child].height = height; 672fe8aea9eSmrg s[child].options = options; 673fe8aea9eSmrg pthread_create(&s[child].thread, NULL, sibling, &s[child]); 674fe8aea9eSmrg } 675fe8aea9eSmrg 676fe8aea9eSmrg for (child = 0; child < ncpus; child++) 677fe8aea9eSmrg pthread_join(s[child].thread, NULL); 678fe8aea9eSmrg} 679fe8aea9eSmrg 680fe8aea9eSmrgstatic void cousins(int max_width, int max_height, int ncpus, unsigned options) 681fe8aea9eSmrg{ 682fe8aea9eSmrg int sq_ncpus = isqrt(ncpus); 683fe8aea9eSmrg int width = max_width / sq_ncpus; 684fe8aea9eSmrg int height = max_height/ sq_ncpus; 685fe8aea9eSmrg int child; 686fe8aea9eSmrg 687fe8aea9eSmrg if (ncpus <= 1) 688fe8aea9eSmrg return; 689fe8aea9eSmrg 690fe8aea9eSmrg for (child = 0; child < ncpus; child++) { 691fe8aea9eSmrg for (; fork() == 0; exit(0)) { 692fe8aea9eSmrg int x = (child % sq_ncpus) * width; 693fe8aea9eSmrg int y = (child / sq_ncpus) * height; 694fe8aea9eSmrg XSetWindowAttributes attr = { .override_redirect = 1 }; 695fe8aea9eSmrg Display *dpy = XOpenDisplay(NULL); 696fe8aea9eSmrg Window win = XCreateWindow(dpy, DefaultRootWindow(dpy), 697fe8aea9eSmrg x, y, width, height, 0, 698fe8aea9eSmrg DefaultDepth(dpy, DefaultScreen(dpy)), 699fe8aea9eSmrg InputOutput, 700fe8aea9eSmrg DefaultVisual(dpy, DefaultScreen(dpy)), 701fe8aea9eSmrg CWOverrideRedirect, &attr); 702fe8aea9eSmrg XMapWindow(dpy, win); 703fe8aea9eSmrg run(dpy, win, "cousin", options); 704fe8aea9eSmrg } 705fe8aea9eSmrg } 706fe8aea9eSmrg 707fe8aea9eSmrg while (child) { 708fe8aea9eSmrg int status = -1; 709fe8aea9eSmrg pid_t pid = wait(&status); 710fe8aea9eSmrg if (pid == -1) 711fe8aea9eSmrg continue; 712fe8aea9eSmrg child--; 713fe8aea9eSmrg } 714fe8aea9eSmrg} 715fe8aea9eSmrg 716fe8aea9eSmrgstatic int has_present(Display *dpy) 717fe8aea9eSmrg{ 718fe8aea9eSmrg xcb_connection_t *c = XGetXCBConnection(dpy); 719fe8aea9eSmrg xcb_generic_error_t *error = NULL; 720fe8aea9eSmrg void *reply; 721fe8aea9eSmrg 722fe8aea9eSmrg reply = xcb_present_query_version_reply(c, 723fe8aea9eSmrg xcb_present_query_version(c, 724fe8aea9eSmrg XCB_PRESENT_MAJOR_VERSION, 725fe8aea9eSmrg XCB_PRESENT_MINOR_VERSION), 726fe8aea9eSmrg &error); 727fe8aea9eSmrg 728fe8aea9eSmrg free(reply); 729fe8aea9eSmrg free(error); 730fe8aea9eSmrg if (reply == NULL) { 731fe8aea9eSmrg fprintf(stderr, "Present not supported on %s\n", DisplayString(dpy)); 732fe8aea9eSmrg return 0; 733fe8aea9eSmrg } 734fe8aea9eSmrg 735fe8aea9eSmrg return 1; 736fe8aea9eSmrg} 737fe8aea9eSmrg 738fe8aea9eSmrgstatic int has_composite(Display *dpy) 739fe8aea9eSmrg{ 740fe8aea9eSmrg int event, error; 741fe8aea9eSmrg int major, minor; 742fe8aea9eSmrg 743fe8aea9eSmrg if (!XDamageQueryExtension (dpy, &event, &error)) 744fe8aea9eSmrg return 0; 745fe8aea9eSmrg 746fe8aea9eSmrg if (!XCompositeQueryExtension(dpy, &event, &error)) 747fe8aea9eSmrg return 0; 748fe8aea9eSmrg 749fe8aea9eSmrg XCompositeQueryVersion(dpy, &major, &minor); 750fe8aea9eSmrg 751fe8aea9eSmrg return major > 0 || minor >= 4; 752fe8aea9eSmrg} 753fe8aea9eSmrg 754fe8aea9eSmrgstatic int dri3_query_version(Display *dpy, int *major, int *minor) 755fe8aea9eSmrg{ 756fe8aea9eSmrg xcb_connection_t *c = XGetXCBConnection(dpy); 757fe8aea9eSmrg xcb_dri3_query_version_reply_t *reply; 758fe8aea9eSmrg xcb_generic_error_t *error; 759fe8aea9eSmrg 760fe8aea9eSmrg *major = *minor = -1; 761fe8aea9eSmrg 762fe8aea9eSmrg reply = xcb_dri3_query_version_reply(c, 763fe8aea9eSmrg xcb_dri3_query_version(c, 764fe8aea9eSmrg XCB_DRI3_MAJOR_VERSION, 765fe8aea9eSmrg XCB_DRI3_MINOR_VERSION), 766fe8aea9eSmrg &error); 767fe8aea9eSmrg free(error); 768fe8aea9eSmrg if (reply == NULL) 769fe8aea9eSmrg return -1; 770fe8aea9eSmrg 771fe8aea9eSmrg *major = reply->major_version; 772fe8aea9eSmrg *minor = reply->minor_version; 773fe8aea9eSmrg free(reply); 774fe8aea9eSmrg 775fe8aea9eSmrg return 0; 776fe8aea9eSmrg} 777fe8aea9eSmrg 778fe8aea9eSmrgstatic int has_dri3(Display *dpy) 779fe8aea9eSmrg{ 780fe8aea9eSmrg const xcb_query_extension_reply_t *ext; 781fe8aea9eSmrg int major, minor; 782fe8aea9eSmrg 783fe8aea9eSmrg ext = xcb_get_extension_data(XGetXCBConnection(dpy), &xcb_dri3_id); 784fe8aea9eSmrg if (ext == NULL || !ext->present) 785fe8aea9eSmrg return 0; 786fe8aea9eSmrg 787fe8aea9eSmrg if (dri3_query_version(dpy, &major, &minor) < 0) 788fe8aea9eSmrg return 0; 789fe8aea9eSmrg 790fe8aea9eSmrg return major >= 0; 791fe8aea9eSmrg} 792fe8aea9eSmrg 793fe8aea9eSmrgstatic int has_xfixes(Display *dpy) 794fe8aea9eSmrg{ 795fe8aea9eSmrg xcb_connection_t *c = XGetXCBConnection(dpy); 796fe8aea9eSmrg const xcb_query_extension_reply_t *ext; 797fe8aea9eSmrg void *reply; 798fe8aea9eSmrg 799fe8aea9eSmrg ext = xcb_get_extension_data(c, &xcb_xfixes_id); 800fe8aea9eSmrg if (ext == NULL || !ext->present) 801fe8aea9eSmrg return 0; 802fe8aea9eSmrg 803fe8aea9eSmrg reply = xcb_xfixes_query_version_reply(c, 804fe8aea9eSmrg xcb_xfixes_query_version(c, 805fe8aea9eSmrg XCB_XFIXES_MAJOR_VERSION, 806fe8aea9eSmrg XCB_XFIXES_MINOR_VERSION), 807fe8aea9eSmrg NULL); 808fe8aea9eSmrg free(reply); 809fe8aea9eSmrg 810fe8aea9eSmrg return reply != NULL; 811fe8aea9eSmrg} 812fe8aea9eSmrg 813fe8aea9eSmrgstatic inline XRRScreenResources *_XRRGetScreenResourcesCurrent(Display *dpy, Window window) 814fe8aea9eSmrg{ 815fe8aea9eSmrg XRRScreenResources *res; 816fe8aea9eSmrg 817fe8aea9eSmrg res = XRRGetScreenResourcesCurrent(dpy, window); 818fe8aea9eSmrg if (res == NULL) 819fe8aea9eSmrg res = XRRGetScreenResources(dpy, window); 820fe8aea9eSmrg 821fe8aea9eSmrg return res; 822fe8aea9eSmrg} 823fe8aea9eSmrg 824fe8aea9eSmrgstatic XRRModeInfo *lookup_mode(XRRScreenResources *res, int id) 825fe8aea9eSmrg{ 826fe8aea9eSmrg int i; 827fe8aea9eSmrg 828fe8aea9eSmrg for (i = 0; i < res->nmode; i++) { 829fe8aea9eSmrg if (res->modes[i].id == id) 830fe8aea9eSmrg return &res->modes[i]; 831fe8aea9eSmrg } 832fe8aea9eSmrg 833fe8aea9eSmrg return NULL; 834fe8aea9eSmrg} 835fe8aea9eSmrg 836fe8aea9eSmrgstatic void fullscreen(Display *dpy, Window win) 837fe8aea9eSmrg{ 838fe8aea9eSmrg Atom atom = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); 839fe8aea9eSmrg XChangeProperty(dpy, win, 840fe8aea9eSmrg XInternAtom(dpy, "_NET_WM_STATE", False), 841fe8aea9eSmrg XA_ATOM, 32, PropModeReplace, 842fe8aea9eSmrg (unsigned char *)&atom, 1); 843fe8aea9eSmrg} 844fe8aea9eSmrg 845fe8aea9eSmrgstatic void loop(Display *dpy, XRRScreenResources *res, unsigned options) 846fe8aea9eSmrg{ 847fe8aea9eSmrg Window root = DefaultRootWindow(dpy); 848fe8aea9eSmrg Window win; 849fe8aea9eSmrg XSetWindowAttributes attr; 850fe8aea9eSmrg int i, j; 851fe8aea9eSmrg 852fe8aea9eSmrg attr.override_redirect = 1; 853fe8aea9eSmrg 854fe8aea9eSmrg run(dpy, root, "off", options); 855fe8aea9eSmrg XSync(dpy, True); 856fe8aea9eSmrg 857fe8aea9eSmrg for (i = 0; i < res->noutput; i++) { 858fe8aea9eSmrg XRROutputInfo *output; 859fe8aea9eSmrg XRRModeInfo *mode; 860fe8aea9eSmrg 861fe8aea9eSmrg output = XRRGetOutputInfo(dpy, res, res->outputs[i]); 862fe8aea9eSmrg if (output == NULL) 863fe8aea9eSmrg continue; 864fe8aea9eSmrg 865fe8aea9eSmrg mode = NULL; 866fe8aea9eSmrg if (res->nmode) 867fe8aea9eSmrg mode = lookup_mode(res, output->modes[0]); 868fe8aea9eSmrg 869fe8aea9eSmrg for (j = 0; mode && j < 2*output->ncrtc; j++) { 870fe8aea9eSmrg int c = j; 871fe8aea9eSmrg if (c >= output->ncrtc) 872fe8aea9eSmrg c = 2*output->ncrtc - j - 1; 873fe8aea9eSmrg 874fe8aea9eSmrg printf("[%d, %d] -- OUTPUT:%ld, CRTC:%ld: %dx%d\n", 875fe8aea9eSmrg i, c, (long)res->outputs[i], (long)output->crtcs[c], 876fe8aea9eSmrg mode->width, mode->height); 877fe8aea9eSmrg XRRSetCrtcConfig(dpy, res, output->crtcs[c], CurrentTime, 878fe8aea9eSmrg 0, 0, output->modes[0], RR_Rotate_0, &res->outputs[i], 1); 879fe8aea9eSmrg 880fe8aea9eSmrg run(dpy, root, "root", options); 881fe8aea9eSmrg XSync(dpy, True); 882fe8aea9eSmrg 883fe8aea9eSmrg win = XCreateWindow(dpy, root, 884fe8aea9eSmrg 0, 0, mode->width, mode->height, 0, 885fe8aea9eSmrg DefaultDepth(dpy, DefaultScreen(dpy)), 886fe8aea9eSmrg InputOutput, 887fe8aea9eSmrg DefaultVisual(dpy, DefaultScreen(dpy)), 888fe8aea9eSmrg CWOverrideRedirect, &attr); 889fe8aea9eSmrg fullscreen(dpy, win); 890fe8aea9eSmrg XMapWindow(dpy, win); 891fe8aea9eSmrg run(dpy, win, "fullscreen", options); 892fe8aea9eSmrg XDestroyWindow(dpy, win); 893fe8aea9eSmrg XSync(dpy, True); 894fe8aea9eSmrg 895fe8aea9eSmrg win = XCreateWindow(dpy, root, 896fe8aea9eSmrg 0, 0, mode->width, mode->height, 0, 897fe8aea9eSmrg DefaultDepth(dpy, DefaultScreen(dpy)), 898fe8aea9eSmrg InputOutput, 899fe8aea9eSmrg DefaultVisual(dpy, DefaultScreen(dpy)), 900fe8aea9eSmrg CWOverrideRedirect, &attr); 901fe8aea9eSmrg XMapWindow(dpy, win); 902fe8aea9eSmrg run(dpy, win, "windowed", options); 903fe8aea9eSmrg XDestroyWindow(dpy, win); 904fe8aea9eSmrg XSync(dpy, True); 905fe8aea9eSmrg 906fe8aea9eSmrg if (has_composite(dpy)) { 907fe8aea9eSmrg Damage damage; 908fe8aea9eSmrg 909fe8aea9eSmrg _x_error_occurred = 0; 910fe8aea9eSmrg win = XCreateWindow(dpy, root, 911fe8aea9eSmrg 0, 0, mode->width, mode->height, 0, 912fe8aea9eSmrg DefaultDepth(dpy, DefaultScreen(dpy)), 913fe8aea9eSmrg InputOutput, 914fe8aea9eSmrg DefaultVisual(dpy, DefaultScreen(dpy)), 915fe8aea9eSmrg CWOverrideRedirect, &attr); 916fe8aea9eSmrg XCompositeRedirectWindow(dpy, win, CompositeRedirectManual); 917fe8aea9eSmrg damage = XDamageCreate(dpy, win, XDamageReportNonEmpty); 918fe8aea9eSmrg XMapWindow(dpy, win); 919fe8aea9eSmrg XSync(dpy, True); 920fe8aea9eSmrg if (!_x_error_occurred) 921fe8aea9eSmrg run(dpy, win, "composited", options); 922fe8aea9eSmrg XDamageDestroy(dpy, damage); 923fe8aea9eSmrg XDestroyWindow(dpy, win); 924fe8aea9eSmrg XSync(dpy, True); 925fe8aea9eSmrg } 926fe8aea9eSmrg 927fe8aea9eSmrg win = XCreateWindow(dpy, root, 928fe8aea9eSmrg 0, 0, mode->width/2, mode->height/2, 0, 929fe8aea9eSmrg DefaultDepth(dpy, DefaultScreen(dpy)), 930fe8aea9eSmrg InputOutput, 931fe8aea9eSmrg DefaultVisual(dpy, DefaultScreen(dpy)), 932fe8aea9eSmrg CWOverrideRedirect, &attr); 933fe8aea9eSmrg XMapWindow(dpy, win); 934fe8aea9eSmrg run(dpy, win, "half", options); 935fe8aea9eSmrg XDestroyWindow(dpy, win); 936fe8aea9eSmrg XSync(dpy, True); 937fe8aea9eSmrg 938fe8aea9eSmrg perpixel(dpy, mode->width, mode->height, options); 939fe8aea9eSmrg 940fe8aea9eSmrg siblings(dpy, mode->width, mode->height, 941fe8aea9eSmrg sysconf(_SC_NPROCESSORS_ONLN), 942fe8aea9eSmrg options); 943fe8aea9eSmrg 944fe8aea9eSmrg cousins(mode->width, mode->height, 945fe8aea9eSmrg sysconf(_SC_NPROCESSORS_ONLN), 946fe8aea9eSmrg options); 947fe8aea9eSmrg 948fe8aea9eSmrg XRRSetCrtcConfig(dpy, res, output->crtcs[c], CurrentTime, 949fe8aea9eSmrg 0, 0, None, RR_Rotate_0, NULL, 0); 950fe8aea9eSmrg } 951fe8aea9eSmrg 952fe8aea9eSmrg XRRFreeOutputInfo(output); 953fe8aea9eSmrg } 954fe8aea9eSmrg 955fe8aea9eSmrg} 956fe8aea9eSmrg 957fe8aea9eSmrgint main(void) 958fe8aea9eSmrg{ 959fe8aea9eSmrg Display *dpy; 960fe8aea9eSmrg XRRScreenResources *res; 961fe8aea9eSmrg XRRCrtcInfo **original_crtc; 962fe8aea9eSmrg int i; 963fe8aea9eSmrg 964fe8aea9eSmrg XInitThreads(); 965fe8aea9eSmrg 966fe8aea9eSmrg dpy = XOpenDisplay(NULL); 967fe8aea9eSmrg if (dpy == NULL) 968fe8aea9eSmrg return 77; 969fe8aea9eSmrg 970fe8aea9eSmrg if (!has_present(dpy)) 971fe8aea9eSmrg return 77; 972fe8aea9eSmrg 973fe8aea9eSmrg if (DPMSQueryExtension(dpy, &i, &i)) 974fe8aea9eSmrg DPMSDisable(dpy); 975fe8aea9eSmrg 976fe8aea9eSmrg signal(SIGALRM, SIG_IGN); 977fe8aea9eSmrg XSetErrorHandler(_check_error_handler); 978fe8aea9eSmrg 979fe8aea9eSmrg res = NULL; 980fe8aea9eSmrg if (XRRQueryVersion(dpy, &i, &i)) 981fe8aea9eSmrg res = _XRRGetScreenResourcesCurrent(dpy, DefaultRootWindow(dpy)); 982fe8aea9eSmrg if (res == NULL) 983fe8aea9eSmrg return 77; 984fe8aea9eSmrg 985fe8aea9eSmrg original_crtc = malloc(sizeof(XRRCrtcInfo *)*res->ncrtc); 986fe8aea9eSmrg for (i = 0; i < res->ncrtc; i++) 987fe8aea9eSmrg original_crtc[i] = XRRGetCrtcInfo(dpy, res, res->crtcs[i]); 988fe8aea9eSmrg 989fe8aea9eSmrg printf("noutput=%d, ncrtc=%d\n", res->noutput, res->ncrtc); 990fe8aea9eSmrg for (i = 0; i < res->ncrtc; i++) 991fe8aea9eSmrg XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime, 992fe8aea9eSmrg 0, 0, None, RR_Rotate_0, NULL, 0); 993fe8aea9eSmrg 994fe8aea9eSmrg loop(dpy, res, 0); 995fe8aea9eSmrg loop(dpy, res, ASYNC); 996fe8aea9eSmrg if (has_xfixes(dpy)) 997fe8aea9eSmrg loop(dpy, res, NOCOPY); 998fe8aea9eSmrg if (has_dri3(dpy)) { 999fe8aea9eSmrg loop(dpy, res, DRI3); 1000fe8aea9eSmrg loop(dpy, res, DRI3 | ASYNC); 1001fe8aea9eSmrg } 1002fe8aea9eSmrg 1003fe8aea9eSmrg for (i = 0; i < res->ncrtc; i++) 1004fe8aea9eSmrg XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime, 1005fe8aea9eSmrg original_crtc[i]->x, 1006fe8aea9eSmrg original_crtc[i]->y, 1007fe8aea9eSmrg original_crtc[i]->mode, 1008fe8aea9eSmrg original_crtc[i]->rotation, 1009fe8aea9eSmrg original_crtc[i]->outputs, 1010fe8aea9eSmrg original_crtc[i]->noutput); 1011fe8aea9eSmrg 1012fe8aea9eSmrg if (DPMSQueryExtension(dpy, &i, &i)) 1013fe8aea9eSmrg DPMSEnable(dpy); 1014fe8aea9eSmrg return 0; 1015fe8aea9eSmrg} 1016