dri2-swap.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/Xutil.h> 33#include <X11/Xlibint.h> 34#include <X11/extensions/dpms.h> 35#include <X11/extensions/randr.h> 36#include <X11/extensions/Xcomposite.h> 37#include <X11/extensions/Xdamage.h> 38#include <X11/extensions/Xrandr.h> 39#include <xcb/xcb.h> 40#include <xcb/dri2.h> 41#include <xf86drm.h> 42 43#include <stdio.h> 44#include <string.h> 45#include <fcntl.h> 46#include <unistd.h> 47#include <assert.h> 48#include <errno.h> 49#include <setjmp.h> 50#include <signal.h> 51 52#include <X11/Xlibint.h> 53#include <X11/extensions/Xext.h> 54#include <X11/extensions/extutil.h> 55#include <X11/extensions/dri2proto.h> 56#include <X11/extensions/dri2tokens.h> 57#include <X11/extensions/Xfixes.h> 58 59static char dri2ExtensionName[] = DRI2_NAME; 60static XExtensionInfo *dri2Info; 61static XEXT_GENERATE_CLOSE_DISPLAY (DRI2CloseDisplay, dri2Info) 62 63static Bool 64DRI2WireToEvent(Display *dpy, XEvent *event, xEvent *wire); 65static Status 66DRI2EventToWire(Display *dpy, XEvent *event, xEvent *wire); 67static int 68DRI2Error(Display *display, xError *err, XExtCodes *codes, int *ret_code); 69 70static /* const */ XExtensionHooks dri2ExtensionHooks = { 71 NULL, /* create_gc */ 72 NULL, /* copy_gc */ 73 NULL, /* flush_gc */ 74 NULL, /* free_gc */ 75 NULL, /* create_font */ 76 NULL, /* free_font */ 77 DRI2CloseDisplay, /* close_display */ 78 DRI2WireToEvent, /* wire_to_event */ 79 DRI2EventToWire, /* event_to_wire */ 80 DRI2Error, /* error */ 81 NULL, /* error_string */ 82}; 83 84static XEXT_GENERATE_FIND_DISPLAY (DRI2FindDisplay, 85 dri2Info, 86 dri2ExtensionName, 87 &dri2ExtensionHooks, 88 0, NULL) 89 90static Bool 91DRI2WireToEvent(Display *dpy, XEvent *event, xEvent *wire) 92{ 93 XExtDisplayInfo *info = DRI2FindDisplay(dpy); 94 95 XextCheckExtension(dpy, info, dri2ExtensionName, False); 96 97 switch ((wire->u.u.type & 0x7f) - info->codes->first_event) { 98#ifdef X_DRI2SwapBuffers 99 case DRI2_BufferSwapComplete: 100 return False; 101#endif 102#ifdef DRI2_InvalidateBuffers 103 case DRI2_InvalidateBuffers: 104 return False; 105#endif 106 default: 107 /* client doesn't support server event */ 108 break; 109 } 110 111 return False; 112} 113 114/* We don't actually support this. It doesn't make sense for clients to 115 * send each other DRI2 events. 116 */ 117static Status 118DRI2EventToWire(Display *dpy, XEvent *event, xEvent *wire) 119{ 120 XExtDisplayInfo *info = DRI2FindDisplay(dpy); 121 122 XextCheckExtension(dpy, info, dri2ExtensionName, False); 123 124 switch (event->type) { 125 default: 126 /* client doesn't support server event */ 127 break; 128 } 129 130 return Success; 131} 132 133static int 134DRI2Error(Display *display, xError *err, XExtCodes *codes, int *ret_code) 135{ 136 if (err->majorCode == codes->major_opcode && 137 err->errorCode == BadDrawable && 138 err->minorCode == X_DRI2CopyRegion) 139 return True; 140 141 /* If the X drawable was destroyed before the GLX drawable, the 142 * DRI2 drawble will be gone by the time we call 143 * DRI2DestroyDrawable. So just ignore BadDrawable here. */ 144 if (err->majorCode == codes->major_opcode && 145 err->errorCode == BadDrawable && 146 err->minorCode == X_DRI2DestroyDrawable) 147 return True; 148 149 /* If the server is non-local DRI2Connect will raise BadRequest. 150 * Swallow this so that DRI2Connect can signal this in its return code */ 151 if (err->majorCode == codes->major_opcode && 152 err->minorCode == X_DRI2Connect && 153 err->errorCode == BadRequest) { 154 *ret_code = False; 155 return True; 156 } 157 158 return False; 159} 160 161static Bool 162DRI2QueryExtension(Display * dpy, int *eventBase, int *errorBase) 163{ 164 XExtDisplayInfo *info = DRI2FindDisplay(dpy); 165 166 if (XextHasExtension(info)) { 167 *eventBase = info->codes->first_event; 168 *errorBase = info->codes->first_error; 169 return True; 170 } 171 172 return False; 173} 174 175static Bool 176DRI2Connect(Display * dpy, XID window, char **driverName, char **deviceName) 177{ 178 XExtDisplayInfo *info = DRI2FindDisplay(dpy); 179 xDRI2ConnectReply rep; 180 xDRI2ConnectReq *req; 181 182 XextCheckExtension(dpy, info, dri2ExtensionName, False); 183 184 LockDisplay(dpy); 185 GetReq(DRI2Connect, req); 186 req->reqType = info->codes->major_opcode; 187 req->dri2ReqType = X_DRI2Connect; 188 req->window = window; 189 req->driverType = DRI2DriverDRI; 190 if (!_XReply(dpy, (xReply *) & rep, 0, xFalse)) { 191 UnlockDisplay(dpy); 192 SyncHandle(); 193 return False; 194 } 195 196 if (rep.driverNameLength == 0 && rep.deviceNameLength == 0) { 197 UnlockDisplay(dpy); 198 SyncHandle(); 199 return False; 200 } 201 202 *driverName = Xmalloc(rep.driverNameLength + 1); 203 if (*driverName == NULL) { 204 _XEatData(dpy, 205 ((rep.driverNameLength + 3) & ~3) + 206 ((rep.deviceNameLength + 3) & ~3)); 207 UnlockDisplay(dpy); 208 SyncHandle(); 209 return False; 210 } 211 _XReadPad(dpy, *driverName, rep.driverNameLength); 212 (*driverName)[rep.driverNameLength] = '\0'; 213 214 *deviceName = Xmalloc(rep.deviceNameLength + 1); 215 if (*deviceName == NULL) { 216 Xfree(*driverName); 217 _XEatData(dpy, ((rep.deviceNameLength + 3) & ~3)); 218 UnlockDisplay(dpy); 219 SyncHandle(); 220 return False; 221 } 222 _XReadPad(dpy, *deviceName, rep.deviceNameLength); 223 (*deviceName)[rep.deviceNameLength] = '\0'; 224 225 UnlockDisplay(dpy); 226 SyncHandle(); 227 228 return True; 229} 230 231static Bool 232DRI2Authenticate(Display * dpy, XID window, unsigned int magic) 233{ 234 XExtDisplayInfo *info = DRI2FindDisplay(dpy); 235 xDRI2AuthenticateReq *req; 236 xDRI2AuthenticateReply rep; 237 238 XextCheckExtension(dpy, info, dri2ExtensionName, False); 239 240 LockDisplay(dpy); 241 GetReq(DRI2Authenticate, req); 242 req->reqType = info->codes->major_opcode; 243 req->dri2ReqType = X_DRI2Authenticate; 244 req->window = window; 245 req->magic = magic; 246 247 if (!_XReply(dpy, (xReply *) & rep, 0, xFalse)) { 248 UnlockDisplay(dpy); 249 SyncHandle(); 250 return False; 251 } 252 253 UnlockDisplay(dpy); 254 SyncHandle(); 255 256 return rep.authenticated; 257} 258 259static void 260DRI2CreateDrawable(Display * dpy, XID drawable) 261{ 262 XExtDisplayInfo *info = DRI2FindDisplay(dpy); 263 xDRI2CreateDrawableReq *req; 264 265 XextSimpleCheckExtension(dpy, info, dri2ExtensionName); 266 267 LockDisplay(dpy); 268 GetReq(DRI2CreateDrawable, req); 269 req->reqType = info->codes->major_opcode; 270 req->dri2ReqType = X_DRI2CreateDrawable; 271 req->drawable = drawable; 272 UnlockDisplay(dpy); 273 SyncHandle(); 274} 275 276static void DRI2SwapInterval(Display *dpy, XID drawable, int interval) 277{ 278 XExtDisplayInfo *info = DRI2FindDisplay(dpy); 279 xDRI2SwapIntervalReq *req; 280 281 XextSimpleCheckExtension (dpy, info, dri2ExtensionName); 282 283 LockDisplay(dpy); 284 GetReq(DRI2SwapInterval, req); 285 req->reqType = info->codes->major_opcode; 286 req->dri2ReqType = X_DRI2SwapInterval; 287 req->drawable = drawable; 288 req->interval = interval; 289 UnlockDisplay(dpy); 290 SyncHandle(); 291} 292 293static int _x_error_occurred; 294 295static int 296_check_error_handler(Display *display, 297 XErrorEvent *event) 298{ 299 fprintf(stderr, 300 "X11 error from display %s, serial=%ld, error=%d, req=%d.%d\n", 301 DisplayString(display), 302 event->serial, 303 event->error_code, 304 event->request_code, 305 event->minor_code); 306 _x_error_occurred++; 307 return False; /* ignored */ 308} 309 310static double elapsed(const struct timespec *start, 311 const struct timespec *end) 312{ 313 return 1e6*(end->tv_sec - start->tv_sec) + (end->tv_nsec - start->tv_nsec)/1000; 314} 315 316static void run(Display *dpy, Window win) 317{ 318 xcb_connection_t *c = XGetXCBConnection(dpy); 319 struct timespec start, end; 320 int n, completed = 0; 321 322 clock_gettime(CLOCK_MONOTONIC, &start); 323 do { 324 for (n = 0; n < 1000; n++) { 325 unsigned int attachments[] = { DRI2BufferBackLeft }; 326 unsigned int seq[2]; 327 328 seq[0] = xcb_dri2_swap_buffers_unchecked(c, win, 329 0, 0, 0, 0, 0, 0).sequence; 330 331 332 seq[1] = xcb_dri2_get_buffers_unchecked(c, win, 333 1, 1, attachments).sequence; 334 335 xcb_flush(c); 336 xcb_discard_reply(c, seq[0]); 337 xcb_discard_reply(c, seq[1]); 338 completed++; 339 } 340 clock_gettime(CLOCK_MONOTONIC, &end); 341 } while (end.tv_sec < start.tv_sec + 10); 342 343 printf("%f\n", completed / (elapsed(&start, &end) / 1000000)); 344} 345 346static inline XRRScreenResources *_XRRGetScreenResourcesCurrent(Display *dpy, Window window) 347{ 348 XRRScreenResources *res; 349 350 res = XRRGetScreenResourcesCurrent(dpy, window); 351 if (res == NULL) 352 res = XRRGetScreenResources(dpy, window); 353 354 return res; 355} 356 357static XRRModeInfo *lookup_mode(XRRScreenResources *res, int id) 358{ 359 int i; 360 361 for (i = 0; i < res->nmode; i++) { 362 if (res->modes[i].id == id) 363 return &res->modes[i]; 364 } 365 366 return NULL; 367} 368 369static int dri2_open(Display *dpy) 370{ 371 drm_auth_t auth; 372 char *driver, *device; 373 int fd; 374 375 if (!DRI2QueryExtension(dpy, &fd, &fd)) 376 return -1; 377 378 if (!DRI2Connect(dpy, DefaultRootWindow(dpy), &driver, &device)) 379 return -1; 380 381 fd = open(device, O_RDWR); 382 if (fd < 0) 383 return -1; 384 385 if (drmIoctl(fd, DRM_IOCTL_GET_MAGIC, &auth)) 386 return -1; 387 388 if (!DRI2Authenticate(dpy, DefaultRootWindow(dpy), auth.magic)) 389 return -1; 390 391 return fd; 392} 393 394static void fullscreen(Display *dpy, Window win) 395{ 396 Atom atom = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); 397 XChangeProperty(dpy, win, 398 XInternAtom(dpy, "_NET_WM_STATE", False), 399 XA_ATOM, 32, PropModeReplace, 400 (unsigned char *)&atom, 1); 401} 402 403static int has_composite(Display *dpy) 404{ 405 int event, error; 406 int major, minor; 407 408 if (!XDamageQueryExtension (dpy, &event, &error)) 409 return 0; 410 411 if (!XCompositeQueryExtension(dpy, &event, &error)) 412 return 0; 413 414 XCompositeQueryVersion(dpy, &major, &minor); 415 416 return major > 0 || minor >= 4; 417} 418 419int main(int argc, char **argv) 420{ 421 Display *dpy; 422 Window root, win; 423 XRRScreenResources *res; 424 XRRCrtcInfo **original_crtc; 425 XSetWindowAttributes attr; 426 enum window { ROOT, FULLSCREEN, WINDOW } w = FULLSCREEN; 427 enum visible {REDIRECTED, NORMAL } v = NORMAL; 428 enum display { OFF, ON } d = OFF; 429 int width, height; 430 int i, fd; 431 int c; 432 433 while ((c = getopt(argc, argv, "d:v:w:")) != -1) { 434 switch (c) { 435 case 'd': 436 if (strcmp(optarg, "off") == 0) 437 d = OFF; 438 else if (strcmp(optarg, "on") == 0) 439 d = ON; 440 else 441 abort(); 442 break; 443 444 case 'v': 445 if (strcmp(optarg, "redirected") == 0) 446 v = REDIRECTED; 447 else if (strcmp(optarg, "normal") == 0) 448 v = NORMAL; 449 else 450 abort(); 451 break; 452 453 case 'w': 454 if (strcmp(optarg, "fullscreen") == 0) 455 w = FULLSCREEN; 456 else if (strcmp(optarg, "window") == 0) 457 w = WINDOW; 458 else if (strcmp(optarg, "root") == 0) 459 w = ROOT; 460 else 461 abort(); 462 break; 463 } 464 } 465 466 attr.override_redirect = 1; 467 468 dpy = XOpenDisplay(NULL); 469 if (dpy == NULL) 470 return 77; 471 472 width = DisplayWidth(dpy, DefaultScreen(dpy)); 473 height = DisplayHeight(dpy, DefaultScreen(dpy)); 474 475 fd = dri2_open(dpy); 476 if (fd < 0) 477 return 77; 478 479 if (DPMSQueryExtension(dpy, &i, &i)) 480 DPMSDisable(dpy); 481 482 root = DefaultRootWindow(dpy); 483 484 signal(SIGALRM, SIG_IGN); 485 XSetErrorHandler(_check_error_handler); 486 487 res = NULL; 488 if (XRRQueryVersion(dpy, &i, &i)) 489 res = _XRRGetScreenResourcesCurrent(dpy, root); 490 if (res == NULL) 491 return 77; 492 493 if (v == REDIRECTED && !has_composite(dpy)) 494 return 77; 495 496 original_crtc = malloc(sizeof(XRRCrtcInfo *)*res->ncrtc); 497 for (i = 0; i < res->ncrtc; i++) 498 original_crtc[i] = XRRGetCrtcInfo(dpy, res, res->crtcs[i]); 499 500 for (i = 0; i < res->ncrtc; i++) 501 XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime, 502 0, 0, None, RR_Rotate_0, NULL, 0); 503 504 DRI2CreateDrawable(dpy, root); 505 DRI2SwapInterval(dpy, root, 0); 506 507 if (d != OFF) { 508 for (i = 0; i < res->noutput; i++) { 509 XRROutputInfo *output; 510 XRRModeInfo *mode; 511 512 output = XRRGetOutputInfo(dpy, res, res->outputs[i]); 513 if (output == NULL) 514 continue; 515 516 mode = NULL; 517 if (res->nmode) 518 mode = lookup_mode(res, output->modes[0]); 519 if (mode == NULL) 520 continue; 521 522 XRRSetCrtcConfig(dpy, res, output->crtcs[0], CurrentTime, 523 0, 0, output->modes[0], RR_Rotate_0, &res->outputs[i], 1); 524 width = mode->width; 525 height = mode->height; 526 break; 527 } 528 if (i == res->noutput) { 529 _x_error_occurred = 77; 530 goto restore; 531 } 532 } 533 534 if (w == ROOT) { 535 run(dpy, root); 536 } else if (w == FULLSCREEN) { 537 win = XCreateWindow(dpy, root, 538 0, 0, width, height, 0, 539 DefaultDepth(dpy, DefaultScreen(dpy)), 540 InputOutput, 541 DefaultVisual(dpy, DefaultScreen(dpy)), 542 CWOverrideRedirect, &attr); 543 DRI2CreateDrawable(dpy, win); 544 DRI2SwapInterval(dpy, win, 0); 545 if (v == REDIRECTED) { 546 XCompositeRedirectWindow(dpy, win, CompositeRedirectManual); 547 XDamageCreate(dpy, win, XDamageReportRawRectangles); 548 } else 549 fullscreen(dpy, win); 550 XMapWindow(dpy, win); 551 run(dpy, win); 552 } else if (w == WINDOW) { 553 win = XCreateWindow(dpy, root, 554 0, 0, width/2, height/2, 0, 555 DefaultDepth(dpy, DefaultScreen(dpy)), 556 InputOutput, 557 DefaultVisual(dpy, DefaultScreen(dpy)), 558 CWOverrideRedirect, &attr); 559 DRI2CreateDrawable(dpy, win); 560 DRI2SwapInterval(dpy, win, 0); 561 if (v == REDIRECTED) { 562 XCompositeRedirectWindow(dpy, win, CompositeRedirectManual); 563 XDamageCreate(dpy, win, XDamageReportRawRectangles); 564 } 565 XMapWindow(dpy, win); 566 run(dpy, win); 567 } 568 569restore: 570 for (i = 0; i < res->ncrtc; i++) 571 XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime, 572 0, 0, None, RR_Rotate_0, NULL, 0); 573 574 for (i = 0; i < res->ncrtc; i++) 575 XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime, 576 original_crtc[i]->x, 577 original_crtc[i]->y, 578 original_crtc[i]->mode, 579 original_crtc[i]->rotation, 580 original_crtc[i]->outputs, 581 original_crtc[i]->noutput); 582 583 if (DPMSQueryExtension(dpy, &i, &i)) 584 DPMSEnable(dpy); 585 586 XSync(dpy, True); 587 return _x_error_occurred; 588} 589