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