present-race.c revision fe8aea9e
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/dpms.h> 35#include <X11/extensions/randr.h> 36#include <X11/extensions/Xcomposite.h> 37#include <X11/extensions/Xrandr.h> 38#include <X11/extensions/Xrender.h> 39#include <X11/extensions/XShm.h> 40#if HAVE_X11_EXTENSIONS_SHMPROTO_H 41#include <X11/extensions/shmproto.h> 42#elif HAVE_X11_EXTENSIONS_SHMSTR_H 43#include <X11/extensions/shmstr.h> 44#else 45#error Failed to find the right header for X11 MIT-SHM protocol definitions 46#endif 47#include <xcb/xcb.h> 48#include <xcb/present.h> 49#include <xcb/xfixes.h> 50#include <xcb/dri3.h> 51#include <xf86drm.h> 52#include <i915_drm.h> 53 54#include <stdio.h> 55#include <string.h> 56#include <fcntl.h> 57#include <unistd.h> 58#include <assert.h> 59#include <errno.h> 60#include <setjmp.h> 61#include <signal.h> 62 63#include <sys/mman.h> 64#include <sys/ipc.h> 65#include <sys/shm.h> 66#include <pciaccess.h> 67 68#include "dri3.h" 69 70static int _x_error_occurred; 71static uint32_t stamp; 72 73static int 74_check_error_handler(Display *display, 75 XErrorEvent *event) 76{ 77 printf("X11 error from display %s, serial=%ld, error=%d, req=%d.%d\n", 78 DisplayString(display), 79 event->serial, 80 event->error_code, 81 event->request_code, 82 event->minor_code); 83 _x_error_occurred++; 84 return False; /* ignored */ 85} 86 87static int has_composite(Display *dpy) 88{ 89 int event, error; 90 int major, minor; 91 92 if (!XCompositeQueryExtension(dpy, &event, &error)) 93 return 0; 94 95 XCompositeQueryVersion(dpy, &major, &minor); 96 97 return major > 0 || minor >= 4; 98} 99 100static void *setup_msc(Display *dpy, Window win) 101{ 102 xcb_connection_t *c = XGetXCBConnection(dpy); 103 xcb_void_cookie_t cookie; 104 uint32_t id = xcb_generate_id(c); 105 xcb_generic_error_t *error; 106 void *q; 107 108 cookie = xcb_present_select_input_checked(c, id, win, XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY); 109 q = xcb_register_for_special_xge(c, &xcb_present_id, id, &stamp); 110 111 error = xcb_request_check(c, cookie); 112 assert(error == NULL); 113 114 return q; 115} 116 117static void teardown_msc(Display *dpy, void *q) 118{ 119 xcb_unregister_for_special_event(XGetXCBConnection(dpy), q); 120} 121 122static uint64_t wait_vblank(Display *dpy, Window win) 123{ 124 xcb_connection_t *c = XGetXCBConnection(dpy); 125 static uint32_t serial = 1; 126 uint64_t msc = 0; 127 int complete = 0; 128 void *q; 129 130 if (win == 0) 131 win = DefaultRootWindow(dpy); 132 133 q = setup_msc(dpy, win); 134 135 xcb_present_notify_msc(c, win, serial ^ 0xdeadbeef, 0, 1, 0); 136 xcb_flush(c); 137 138 do { 139 xcb_present_complete_notify_event_t *ce; 140 xcb_generic_event_t *ev; 141 142 ev = xcb_wait_for_special_event(c, q); 143 if (ev == NULL) 144 break; 145 146 ce = (xcb_present_complete_notify_event_t *)ev; 147 if (ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC && 148 ce->serial == (serial ^ 0xdeadbeef)) { 149 msc = ce->msc; 150 complete = 1; 151 } 152 free(ev); 153 } while (!complete); 154 155 if (++serial == 0) 156 serial = 1; 157 158 teardown_msc(dpy, q); 159 160 return msc; 161} 162 163static int test_basic(Display *dpy, int dummy) 164{ 165 xcb_connection_t *c = XGetXCBConnection(dpy); 166 XSetWindowAttributes attr; 167 Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy)); 168 Pixmap pixmap; 169 struct dri3_fence fence; 170 Window root, win; 171 unsigned int width, height; 172 unsigned border, depth; 173 int x, y, ret = 1; 174 const char *phase; 175 uint64_t msc; 176 177 root = DefaultRootWindow(dpy); 178 XGetGeometry(dpy, root, 179 &win, &x, &y, 180 &width, &height, &border, &depth); 181 182 _x_error_occurred = 0; 183 attr.override_redirect = 1; 184 switch (dummy) { 185 case 0: 186 win = root; 187 phase = "root"; 188 break; 189 case 1: 190 win = XCreateWindow(dpy, root, 191 0, 0, width, height, 0, depth, 192 InputOutput, visual, 193 CWOverrideRedirect, &attr); 194 phase = "fullscreen"; 195 break; 196 case 2: 197 width /= 2; 198 height /= 2; 199 win = XCreateWindow(dpy, root, 200 0, 0, width, height, 0, depth, 201 InputOutput, visual, 202 CWOverrideRedirect, &attr); 203 phase = "window"; 204 break; 205 case 3: 206 if (!has_composite(dpy)) 207 return 0; 208 209 win = XCreateWindow(dpy, root, 210 0, 0, width, height, 0, 211 DefaultDepth(dpy, DefaultScreen(dpy)), 212 InputOutput, 213 DefaultVisual(dpy, DefaultScreen(dpy)), 214 CWOverrideRedirect, &attr); 215 XCompositeRedirectWindow(dpy, win, CompositeRedirectManual); 216 phase = "composite"; 217 break; 218 219 default: 220 phase = "broken"; 221 win = root; 222 abort(); 223 break; 224 } 225 226 XMapWindow(dpy, win); 227 XSync(dpy, True); 228 if (_x_error_occurred) 229 return 1; 230 231 if (dri3_create_fence(dpy, win, &fence)) 232 return 0; 233 234 printf("%s: Testing basic flip: %dx%d\n", phase, width, height); 235 fflush(stdout); 236 _x_error_occurred = 0; 237 238 xshmfence_reset(fence.addr); 239 msc = wait_vblank(dpy, win); 240 241 pixmap = XCreatePixmap(dpy, win, width, height, depth); 242 xcb_present_pixmap(c, win, pixmap, 0, 243 0, /* valid */ 244 0, /* update */ 245 0, /* x_off */ 246 0, /* y_off */ 247 None, 248 None, /* wait fence */ 249 fence.xid, 250 XCB_PRESENT_OPTION_NONE, 251 (msc + 64) & -64, /* target msc */ 252 64, /* divisor */ 253 32, /* remainder */ 254 0, NULL); 255 XFreePixmap(dpy, pixmap); 256 257 pixmap = XCreatePixmap(dpy, win, width, height, depth); 258 xcb_present_pixmap(c, win, pixmap, 0, 259 0, /* valid */ 260 0, /* update */ 261 0, /* x_off */ 262 0, /* y_off */ 263 None, 264 None, /* wait fence */ 265 None, /* sync fence */ 266 XCB_PRESENT_OPTION_NONE, 267 (msc + 64) & -64, /* target msc */ 268 64, /* divisor */ 269 48, /* remainder */ 270 0, NULL); 271 XFreePixmap(dpy, pixmap); 272 XDestroyWindow(dpy, win); 273 XFlush(dpy); 274 275 ret = !!xshmfence_await(fence.addr); 276 dri3_fence_free(dpy, &fence); 277 278 XSync(dpy, True); 279 ret += !!_x_error_occurred; 280 281 return ret; 282} 283 284static int test_race(Display *dpy, int dummy) 285{ 286 Display *mgr = XOpenDisplay(NULL); 287 xcb_connection_t *c = XGetXCBConnection(dpy); 288 XSetWindowAttributes attr; 289 Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy)); 290 Pixmap pixmap; 291 struct dri3_fence fence; 292 Window root, win; 293 unsigned int width, height; 294 unsigned border, depth; 295 int x, y, ret = 1; 296 const char *phase; 297 uint64_t msc; 298 299 root = DefaultRootWindow(dpy); 300 XGetGeometry(dpy, root, 301 &win, &x, &y, 302 &width, &height, &border, &depth); 303 304 _x_error_occurred = 0; 305 attr.override_redirect = 1; 306 switch (dummy) { 307 case 0: 308 win = root; 309 phase = "root"; 310 break; 311 case 1: 312 win = XCreateWindow(dpy, root, 313 0, 0, width, height, 0, depth, 314 InputOutput, visual, 315 CWOverrideRedirect, &attr); 316 phase = "fullscreen"; 317 break; 318 case 2: 319 width /= 2; 320 height /= 2; 321 win = XCreateWindow(dpy, root, 322 0, 0, width, height, 0, depth, 323 InputOutput, visual, 324 CWOverrideRedirect, &attr); 325 phase = "window"; 326 break; 327 case 3: 328 if (!has_composite(dpy)) 329 return 0; 330 331 win = XCreateWindow(dpy, root, 332 0, 0, width, height, 0, 333 DefaultDepth(dpy, DefaultScreen(dpy)), 334 InputOutput, 335 DefaultVisual(dpy, DefaultScreen(dpy)), 336 CWOverrideRedirect, &attr); 337 XCompositeRedirectWindow(dpy, win, CompositeRedirectManual); 338 phase = "composite"; 339 break; 340 341 default: 342 phase = "broken"; 343 win = root; 344 abort(); 345 break; 346 } 347 348 XMapWindow(dpy, win); 349 XSync(dpy, True); 350 if (_x_error_occurred) 351 return 1; 352 353 if (dri3_create_fence(dpy, win, &fence)) 354 return 0; 355 356 printf("%s: Testing race with manager: %dx%d\n", phase, width, height); 357 fflush(stdout); 358 _x_error_occurred = 0; 359 360 xshmfence_reset(fence.addr); 361 msc = wait_vblank(dpy, win); 362 363 pixmap = XCreatePixmap(dpy, win, width, height, depth); 364 xcb_present_pixmap(c, win, pixmap, 0, 365 0, /* valid */ 366 0, /* update */ 367 0, /* x_off */ 368 0, /* y_off */ 369 None, 370 None, /* wait fence */ 371 fence.xid, 372 XCB_PRESENT_OPTION_NONE, 373 (msc + 64) & -64, /* target msc */ 374 64, /* divisor */ 375 32, /* remainder */ 376 0, NULL); 377 XFreePixmap(dpy, pixmap); 378 379 XFlush(dpy); 380 XDestroyWindow(mgr, win); 381 XFlush(mgr); 382 383 pixmap = XCreatePixmap(dpy, win, width, height, depth); 384 xcb_present_pixmap(c, win, pixmap, 0, 385 0, /* valid */ 386 0, /* update */ 387 0, /* x_off */ 388 0, /* y_off */ 389 None, 390 None, /* wait fence */ 391 None, /* sync fence */ 392 XCB_PRESENT_OPTION_NONE, 393 (msc + 64) & -64, /* target msc */ 394 64, /* divisor */ 395 48, /* remainder */ 396 0, NULL); 397 XFreePixmap(dpy, pixmap); 398 XFlush(dpy); 399 400 ret = !!xshmfence_await(fence.addr); 401 dri3_fence_free(dpy, &fence); 402 403 XSync(dpy, True); 404 ret += !!_x_error_occurred; 405 406 XCloseDisplay(mgr); 407 408 return ret; 409} 410 411static int has_present(Display *dpy) 412{ 413 xcb_connection_t *c = XGetXCBConnection(dpy); 414 xcb_generic_error_t *error = NULL; 415 void *reply; 416 417 reply = xcb_xfixes_query_version_reply(c, 418 xcb_xfixes_query_version(c, 419 XCB_XFIXES_MAJOR_VERSION, 420 XCB_XFIXES_MINOR_VERSION), 421 &error); 422 free(reply); 423 free(error); 424 if (reply == NULL) { 425 fprintf(stderr, "XFixes not supported on %s\n", DisplayString(dpy)); 426 return 0; 427 } 428 429 reply = xcb_dri3_query_version_reply(c, 430 xcb_dri3_query_version(c, 431 XCB_DRI3_MAJOR_VERSION, 432 XCB_DRI3_MINOR_VERSION), 433 &error); 434 free(reply); 435 free(error); 436 if (reply == NULL) { 437 fprintf(stderr, "DRI3 not supported on %s\n", DisplayString(dpy)); 438 return 0; 439 } 440 441 reply = xcb_present_query_version_reply(c, 442 xcb_present_query_version(c, 443 XCB_PRESENT_MAJOR_VERSION, 444 XCB_PRESENT_MINOR_VERSION), 445 &error); 446 447 free(reply); 448 free(error); 449 if (reply == NULL) { 450 fprintf(stderr, "Present not supported on %s\n", DisplayString(dpy)); 451 return 0; 452 } 453 454 return 1; 455} 456 457int main(void) 458{ 459 Display *dpy; 460 int dummy; 461 int error = 0; 462 463 dpy = XOpenDisplay(NULL); 464 if (dpy == NULL) 465 return 77; 466 467 if (!has_present(dpy)) 468 return 77; 469 470 if (DPMSQueryExtension(dpy, &dummy, &dummy)) 471 DPMSDisable(dpy); 472 473 signal(SIGALRM, SIG_IGN); 474 XSetErrorHandler(_check_error_handler); 475 476 for (dummy = 0; dummy <= 3; dummy++) { 477 error += test_basic(dpy, dummy); 478 error += test_race(dpy, dummy); 479 } 480 481 if (DPMSQueryExtension(dpy, &dummy, &dummy)) 482 DPMSEnable(dpy); 483 return !!error; 484} 485