1/* 2 * Copyright (c) 2014 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/Xlib-xcb.h> 31#include <X11/xshmfence.h> 32#include <X11/Xutil.h> 33#include <X11/Xlibint.h> 34#include <X11/extensions/randr.h> 35#include <X11/extensions/Xrandr.h> 36#include <X11/extensions/Xrender.h> 37#include <X11/extensions/XShm.h> 38#if HAVE_X11_EXTENSIONS_SHMPROTO_H 39#include <X11/extensions/shmproto.h> 40#elif HAVE_X11_EXTENSIONS_SHMSTR_H 41#include <X11/extensions/shmstr.h> 42#else 43#error Failed to find the right header for X11 MIT-SHM protocol definitions 44#endif 45#include <xcb/xcb.h> 46#include <xcb/present.h> 47#include <xf86drm.h> 48#include <i915_drm.h> 49 50#include <stdio.h> 51#include <string.h> 52#include <fcntl.h> 53#include <unistd.h> 54#include <assert.h> 55#include <errno.h> 56#include <setjmp.h> 57#include <signal.h> 58 59#include <sys/mman.h> 60#include <sys/ipc.h> 61#include <sys/shm.h> 62#include <pciaccess.h> 63 64#include "dri3.h" 65 66#define ALIGN(x, y) (((x) + (y) - 1) & -(y)) 67#define PAGE_ALIGN(x) ALIGN(x, 4096) 68 69#define GTT I915_GEM_DOMAIN_GTT 70#define CPU I915_GEM_DOMAIN_CPU 71 72static int _x_error_occurred; 73static uint32_t stamp; 74 75static int 76_check_error_handler(Display *display, 77 XErrorEvent *event) 78{ 79 printf("X11 error from display %s, serial=%ld, error=%d, req=%d.%d\n", 80 DisplayString(display), 81 event->serial, 82 event->error_code, 83 event->request_code, 84 event->minor_code); 85 _x_error_occurred++; 86 return False; /* ignored */ 87} 88 89static int is_i915_device(int fd) 90{ 91 drm_version_t version; 92 char name[5] = ""; 93 94 memset(&version, 0, sizeof(version)); 95 version.name_len = 4; 96 version.name = name; 97 98 if (drmIoctl(fd, DRM_IOCTL_VERSION, &version)) 99 return 0; 100 101 return strcmp("i915", name) == 0; 102} 103 104static int is_intel(int fd) 105{ 106 struct drm_i915_getparam gp; 107 int ret; 108 109 /* Confirm that this is a i915.ko device with GEM/KMS enabled */ 110 ret = is_i915_device(fd); 111 if (ret) { 112 gp.param = I915_PARAM_HAS_GEM; 113 gp.value = &ret; 114 if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp)) 115 ret = 0; 116 } 117 return ret; 118} 119 120static void *setup_msc(Display *dpy, Window win) 121{ 122 xcb_connection_t *c = XGetXCBConnection(dpy); 123 xcb_void_cookie_t cookie; 124 uint32_t id = xcb_generate_id(c); 125 xcb_generic_error_t *error; 126 void *q; 127 128 cookie = xcb_present_select_input_checked(c, id, win, XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY); 129 q = xcb_register_for_special_xge(c, &xcb_present_id, id, &stamp); 130 131 error = xcb_request_check(c, cookie); 132 assert(error == NULL); 133 134 return q; 135} 136 137static uint64_t check_msc(Display *dpy, Window win, void *q, uint64_t last_msc) 138{ 139 xcb_connection_t *c = XGetXCBConnection(dpy); 140 uint64_t msc = 0; 141 142 xcb_present_notify_msc(c, win, 0, 0, 0, 0); 143 xcb_flush(c); 144 145 do { 146 xcb_present_complete_notify_event_t *ce; 147 xcb_generic_event_t *ev; 148 149 ev = xcb_wait_for_special_event(c, q); 150 if (ev == NULL) 151 break; 152 153 ce = (xcb_present_complete_notify_event_t *)ev; 154 if (ce->kind != XCB_PRESENT_COMPLETE_KIND_PIXMAP) 155 msc = ce->msc; 156 free(ev); 157 } while (msc == 0); 158 159 if (msc < last_msc) { 160 printf("Invalid MSC: was %llu, now %llu\n", 161 (long long)last_msc, (long long)msc); 162 } 163 164 return msc; 165} 166 167static void teardown_msc(Display *dpy, void *q) 168{ 169 xcb_unregister_for_special_event(XGetXCBConnection(dpy), q); 170} 171static int test_whole(Display *dpy) 172{ 173 Pixmap pixmap; 174 struct dri3_fence fence; 175 Window root; 176 unsigned int width, height; 177 unsigned border, depth; 178 int x, y, ret = 1; 179 180 XGetGeometry(dpy, DefaultRootWindow(dpy), 181 &root, &x, &y, &width, &height, &border, &depth); 182 183 if (dri3_create_fence(dpy, root, &fence)) 184 return 0; 185 186 printf("Testing whole screen flip: %dx%d\n", width, height); 187 _x_error_occurred = 0; 188 189 xshmfence_reset(fence.addr); 190 191 pixmap = XCreatePixmap(dpy, root, width, height, depth); 192 xcb_present_pixmap(XGetXCBConnection(dpy), 193 root, pixmap, 194 0, /* sbc */ 195 0, /* valid */ 196 0, /* update */ 197 0, /* x_off */ 198 0, /* y_off */ 199 None, 200 None, /* wait fence */ 201 fence.xid, 202 XCB_PRESENT_OPTION_NONE, 203 0, /* target msc */ 204 0, /* divisor */ 205 0, /* remainder */ 206 0, NULL); 207 XFreePixmap(dpy, pixmap); 208 209 pixmap = XCreatePixmap(dpy, root, width, height, depth); 210 xcb_present_pixmap(XGetXCBConnection(dpy), 211 root, pixmap, 212 0, /* sbc */ 213 0, /* valid */ 214 0, /* update */ 215 0, /* x_off */ 216 0, /* y_off */ 217 None, 218 None, /* wait fence */ 219 None, /* sync fence */ 220 XCB_PRESENT_OPTION_NONE, 221 0, /* target msc */ 222 0, /* divisor */ 223 0, /* remainder */ 224 0, NULL); 225 XFreePixmap(dpy, pixmap); 226 XFlush(dpy); 227 228 ret = !!xshmfence_await(fence.addr); 229 dri3_fence_free(dpy, &fence); 230 231 XSync(dpy, True); 232 ret += !!_x_error_occurred; 233 234 return ret; 235} 236 237static inline XRRScreenResources *_XRRGetScreenResourcesCurrent(Display *dpy, Window window) 238{ 239 XRRScreenResources *res; 240 241 res = XRRGetScreenResourcesCurrent(dpy, window); 242 if (res == NULL) 243 res = XRRGetScreenResources(dpy, window); 244 245 return res; 246} 247 248static XRRModeInfo *lookup_mode(XRRScreenResources *res, int id) 249{ 250 int i; 251 252 for (i = 0; i < res->nmode; i++) { 253 if (res->modes[i].id == id) 254 return &res->modes[i]; 255 } 256 257 return NULL; 258} 259 260static int for_each_crtc(Display *dpy, 261 int (*func)(Display *dpy, 262 RRCrtc crtc, 263 int width, int height, 264 void *closure), 265 void *closure) 266{ 267 XRRScreenResources *res; 268 XRRCrtcInfo **original_crtc; 269 int i, j, err = 0; 270 271 if (!XRRQueryVersion(dpy, &i, &j)) 272 return -1; 273 274 res = _XRRGetScreenResourcesCurrent(dpy, DefaultRootWindow(dpy)); 275 if (res == NULL) 276 return -1; 277 278 original_crtc = malloc(sizeof(XRRCrtcInfo *)*res->ncrtc); 279 for (i = 0; i < res->ncrtc; i++) 280 original_crtc[i] = XRRGetCrtcInfo(dpy, res, res->crtcs[i]); 281 282 printf("noutput=%d, ncrtc=%d\n", res->noutput, res->ncrtc); 283 284 for (i = 0; i < res->noutput; i++) { 285 XRROutputInfo *output; 286 XRRModeInfo *mode; 287 288 output = XRRGetOutputInfo(dpy, res, res->outputs[i]); 289 if (output == NULL) 290 continue; 291 292 mode = NULL; 293 if (res->nmode) 294 mode = lookup_mode(res, output->modes[0]); 295 296 for (j = 0; mode && j < output->ncrtc; j++) { 297 printf("[%d, %d] -- OUTPUT:%ld, CRTC:%ld\n", 298 i, j, (long)res->outputs[i], (long)output->crtcs[j]); 299 XRRSetCrtcConfig(dpy, res, output->crtcs[j], CurrentTime, 300 0, 0, output->modes[0], RR_Rotate_0, &res->outputs[i], 1); 301 XSync(dpy, True); 302 303 err += func(dpy, output->crtcs[j], mode->width, mode->height, closure); 304 305 XRRSetCrtcConfig(dpy, res, output->crtcs[j], CurrentTime, 306 0, 0, None, RR_Rotate_0, NULL, 0); 307 XSync(dpy, True); 308 } 309 310 XRRFreeOutputInfo(output); 311 } 312 313 for (i = 0; i < res->ncrtc; i++) 314 XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime, 315 original_crtc[i]->x, 316 original_crtc[i]->y, 317 original_crtc[i]->mode, 318 original_crtc[i]->rotation, 319 original_crtc[i]->outputs, 320 original_crtc[i]->noutput); 321 322 free(original_crtc); 323 XRRFreeScreenResources(res); 324 325 return j; 326} 327 328struct test_crtc { 329 Window win; 330 int depth; 331 unsigned flags; 332 333 struct dri3_fence fence; 334 void *queue; 335 uint64_t msc; 336}; 337#define SYNC 0x1 338 339static int __test_crtc(Display *dpy, RRCrtc crtc, 340 int width, int height, 341 void *closure) 342{ 343 struct test_crtc *test = closure; 344 Pixmap pixmap; 345 int err = 0; 346 347 test->msc = check_msc(dpy, test->win, test->queue, test->msc); 348 349 if (test->flags & SYNC) 350 xshmfence_reset(test->fence.addr); 351 352 pixmap = XCreatePixmap(dpy, test->win, width, height, test->depth); 353 xcb_present_pixmap(XGetXCBConnection(dpy), 354 test->win, pixmap, 355 0, /* sbc */ 356 0, /* valid */ 357 0, /* update */ 358 0, /* x_off */ 359 0, /* y_off */ 360 crtc, 361 None, /* wait fence */ 362 test->flags & SYNC ? test->fence.xid : None, 363 XCB_PRESENT_OPTION_NONE, 364 0, /* target msc */ 365 1, /* divisor */ 366 0, /* remainder */ 367 0, NULL); 368 XFreePixmap(dpy, pixmap); 369 370 if (test->flags & SYNC) { 371 pixmap = XCreatePixmap(dpy, test->win, width, height, test->depth); 372 xcb_present_pixmap(XGetXCBConnection(dpy), 373 test->win, pixmap, 374 1, /* sbc */ 375 0, /* valid */ 376 0, /* update */ 377 0, /* x_off */ 378 0, /* y_off */ 379 crtc, 380 None, /* wait fence */ 381 None, /* sync fence */ 382 XCB_PRESENT_OPTION_NONE, 383 1, /* target msc */ 384 1, /* divisor */ 385 0, /* remainder */ 386 0, NULL); 387 XFreePixmap(dpy, pixmap); 388 XFlush(dpy); 389 err += !!xshmfence_await(test->fence.addr); 390 } 391 392 test->msc = check_msc(dpy, test->win, test->queue, test->msc); 393 return err; 394} 395 396static int test_crtc(Display *dpy, void *queue, uint64_t last_msc) 397{ 398 struct test_crtc test; 399 int err = 0; 400 401 XSync(dpy, True); 402 _x_error_occurred = 0; 403 404 test.win = DefaultRootWindow(dpy); 405 test.depth = DefaultDepth(dpy, DefaultScreen(dpy)); 406 if (dri3_create_fence(dpy, test.win, &test.fence)) 407 return -1; 408 test.queue = queue; 409 test.msc = last_msc; 410 411 printf("Testing each crtc, without waiting for each flip\n"); 412 test.flags = 0; 413 err += for_each_crtc(dpy, __test_crtc, &test); 414 415 printf("Testing each crtc, waiting for flips to complete\n"); 416 test.flags = SYNC; 417 err += for_each_crtc(dpy, __test_crtc, &test); 418 419 test.msc = check_msc(dpy, test.win, test.queue, test.msc); 420 dri3_fence_free(dpy, &test.fence); 421 422 XSync(dpy, True); 423 err += !!_x_error_occurred; 424 425 if (err) 426 printf("%s: failures=%d\n", __func__, err); 427 428 return err; 429} 430 431static int 432can_use_shm(Display *dpy) 433{ 434 int major, minor, has_pixmap; 435 436 if (!XShmQueryExtension(dpy)) 437 return 0; 438 439 XShmQueryVersion(dpy, &major, &minor, &has_pixmap); 440 return has_pixmap; 441} 442 443static int test_shm(Display *dpy) 444{ 445 Window win = DefaultRootWindow(dpy); 446 XShmSegmentInfo shm; 447 Pixmap pixmap; 448 Window root; 449 unsigned int width, height; 450 unsigned border, depth; 451 int x, y, ret = 1; 452 453 if (!can_use_shm(dpy)) 454 return 0; 455 456 _x_error_occurred = 0; 457 458 XGetGeometry(dpy, win, &root, &x, &y, 459 &width, &height, &border, &depth); 460 461 printf("Using %dx%d SHM\n", width, height); 462 463 shm.shmid = shmget(IPC_PRIVATE, height * 4*width, IPC_CREAT | 0666); 464 if (shm.shmid == -1) 465 return 0; 466 467 shm.shmaddr = shmat(shm.shmid, 0, 0); 468 if (shm.shmaddr == (char *) -1) 469 goto rmid; 470 471 shm.readOnly = False; 472 XShmAttach(dpy, &shm); 473 474 pixmap = XShmCreatePixmap(dpy, DefaultRootWindow(dpy), 475 shm.shmaddr, &shm, width, height, 24); 476 if (_x_error_occurred) 477 goto detach; 478 479 xcb_present_pixmap(XGetXCBConnection(dpy), 480 win, pixmap, 481 0, /* sbc */ 482 0, /* valid */ 483 0, /* update */ 484 0, /* x_off */ 485 0, /* y_off */ 486 None, 487 None, /* wait fence */ 488 None, 489 XCB_PRESENT_OPTION_NONE, 490 0, /* target msc */ 491 0, /* divisor */ 492 0, /* remainder */ 493 0, NULL); 494 XFreePixmap(dpy, pixmap); 495 496 XSync(dpy, True); 497 if (_x_error_occurred) 498 goto detach; 499 500 ret = 0; 501detach: 502 XShmDetach(dpy, &shm); 503 shmdt(shm.shmaddr); 504 XSync(dpy, False); 505rmid: 506 shmctl(shm.shmid, IPC_RMID, NULL); 507 return ret; 508} 509 510static uint32_t gem_create(int fd, int size) 511{ 512 struct drm_i915_gem_create create; 513 514 create.handle = 0; 515 create.size = size; 516 (void)drmIoctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create); 517 518 return create.handle; 519} 520 521struct local_i915_gem_caching { 522 uint32_t handle; 523 uint32_t caching; 524}; 525 526#define LOCAL_I915_GEM_SET_CACHING 0x2f 527#define LOCAL_IOCTL_I915_GEM_SET_CACHING DRM_IOW(DRM_COMMAND_BASE + LOCAL_I915_GEM_SET_CACHING, struct local_i915_gem_caching) 528 529static int gem_set_caching(int fd, uint32_t handle, int caching) 530{ 531 struct local_i915_gem_caching arg; 532 533 arg.handle = handle; 534 arg.caching = caching; 535 536 return drmIoctl(fd, LOCAL_IOCTL_I915_GEM_SET_CACHING, &arg) == 0; 537} 538 539static int gem_export(int fd, uint32_t handle) 540{ 541 struct drm_prime_handle args; 542 543 args.handle = handle; 544 args.flags = O_CLOEXEC; 545 546 if (drmIoctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args)) 547 return -1; 548 549 return args.fd; 550} 551 552static void gem_close(int fd, uint32_t handle) 553{ 554 struct drm_gem_close close; 555 556 close.handle = handle; 557 (void)drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &close); 558} 559 560static int test_dri3(Display *dpy) 561{ 562 Window win = DefaultRootWindow(dpy); 563 Pixmap pixmap; 564 Window root; 565 unsigned int width, height; 566 unsigned border, depth; 567 unsigned stride, size; 568 int x, y, ret = 1; 569 int device, handle; 570 int bpp; 571 572 device = dri3_open(dpy); 573 if (device < 0) 574 return 0; 575 576 if (!is_intel(device)) 577 return 0; 578 579 printf("Opened Intel DRI3 device\n"); 580 581 XGetGeometry(dpy, win, &root, &x, &y, 582 &width, &height, &border, &depth); 583 584 switch (depth) { 585 case 8: bpp = 8; break; 586 case 15: case 16: bpp = 16; break; 587 case 24: case 32: bpp = 32; break; 588 default: return 0; 589 } 590 591 stride = width * bpp/8; 592 size = PAGE_ALIGN(stride * height); 593 printf("Creating DRI3 %dx%d (source stride=%d, size=%d) for GTT\n", 594 width, height, stride, size); 595 596 pixmap = 0; 597 handle = gem_create(device, size); 598 if (handle) { 599 pixmap = dri3_create_pixmap(dpy, root, 600 width, height, depth, 601 gem_export(device, handle), bpp, stride, size); 602 gem_close(device, handle); 603 } 604 if (pixmap == 0) 605 goto fail; 606 607 xcb_present_pixmap(XGetXCBConnection(dpy), 608 win, pixmap, 609 0, /* sbc */ 610 0, /* valid */ 611 0, /* update */ 612 0, /* x_off */ 613 0, /* y_off */ 614 None, 615 None, /* wait fence */ 616 None, 617 XCB_PRESENT_OPTION_NONE, 618 0, /* target msc */ 619 0, /* divisor */ 620 0, /* remainder */ 621 0, NULL); 622 XFreePixmap(dpy, pixmap); 623 624 XSync(dpy, True); 625 if (_x_error_occurred) 626 goto fail; 627 628 printf("Creating DRI3 %dx%d (source stride=%d, size=%d) for CPU\n", 629 width, height, stride, size); 630 631 pixmap = 0; 632 handle = gem_create(device, size); 633 if (handle) { 634 gem_set_caching(device, handle, CPU); 635 handle = dri3_create_pixmap(dpy, root, 636 width, height, depth, 637 gem_export(device, handle), bpp, stride, size); 638 gem_close(device, handle); 639 } 640 if (pixmap == 0) 641 goto fail; 642 643 xcb_present_pixmap(XGetXCBConnection(dpy), 644 win, pixmap, 645 0, /* sbc */ 646 0, /* valid */ 647 0, /* update */ 648 0, /* x_off */ 649 0, /* y_off */ 650 None, 651 None, /* wait fence */ 652 None, 653 XCB_PRESENT_OPTION_NONE, 654 0, /* target msc */ 655 0, /* divisor */ 656 0, /* remainder */ 657 0, NULL); 658 XFreePixmap(dpy, pixmap); 659 660 XSync(dpy, True); 661 if (_x_error_occurred) 662 goto fail; 663 664 ret = 0; 665fail: 666 close(device); 667 return ret; 668} 669 670static int has_present(Display *dpy) 671{ 672 xcb_connection_t *c = XGetXCBConnection(dpy); 673 xcb_present_query_version_reply_t *reply; 674 xcb_generic_error_t *error = NULL; 675 676 reply = xcb_present_query_version_reply(c, 677 xcb_present_query_version(c, 678 XCB_PRESENT_MAJOR_VERSION, 679 XCB_PRESENT_MINOR_VERSION), 680 &error); 681 682 free(reply); 683 free(error); 684 685 return reply != NULL; 686} 687 688int main(void) 689{ 690 Display *dpy; 691 Window root; 692 int error = 0; 693 uint64_t last_msc; 694 void *queue; 695 696 dpy = XOpenDisplay(NULL); 697 if (dpy == NULL) 698 return 77; 699 700 if (!has_present(dpy)) 701 return 77; 702 703 root = DefaultRootWindow(dpy); 704 705 signal(SIGALRM, SIG_IGN); 706 XSetErrorHandler(_check_error_handler); 707 708 queue = setup_msc(dpy, root); 709 last_msc = check_msc(dpy, root, queue, 0); 710 711 error += test_whole(dpy); 712 last_msc = check_msc(dpy, root, queue, last_msc); 713 714 error += test_crtc(dpy, queue, last_msc); 715 last_msc = check_msc(dpy, root, queue, last_msc); 716 717 error += test_shm(dpy); 718 last_msc = check_msc(dpy, root, queue, last_msc); 719 720 error += test_dri3(dpy); 721 last_msc = check_msc(dpy, root, queue, last_msc); 722 723 teardown_msc(dpy, queue); 724 725 return !!error; 726} 727