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 "dri2.h" 53 54static int _x_error_occurred; 55 56static int 57_check_error_handler(Display *display, 58 XErrorEvent *event) 59{ 60 printf("X11 error from display %s, serial=%ld, error=%d, req=%d.%d\n", 61 DisplayString(display), 62 event->serial, 63 event->error_code, 64 event->request_code, 65 event->minor_code); 66 _x_error_occurred++; 67 return False; /* ignored */ 68} 69 70static double elapsed(const struct timespec *start, 71 const struct timespec *end) 72{ 73 return 1e6*(end->tv_sec - start->tv_sec) + (end->tv_nsec - start->tv_nsec)/1000; 74} 75 76static void run(Display *dpy, Window win, const char *name) 77{ 78 xcb_connection_t *c = XGetXCBConnection(dpy); 79 struct timespec start, end; 80 int n, completed = 0; 81 82 _x_error_occurred = 0; 83 84 clock_gettime(CLOCK_MONOTONIC, &start); 85 do { 86 for (n = 0; n < 1000; n++) { 87 unsigned int attachments[] = { DRI2BufferBackLeft }; 88 unsigned int seq[2]; 89 90 seq[0] = xcb_dri2_swap_buffers_unchecked(c, win, 91 0, 0, 0, 0, 0, 0).sequence; 92 93 94 seq[1] = xcb_dri2_get_buffers_unchecked(c, win, 95 1, 1, attachments).sequence; 96 97 xcb_flush(c); 98 xcb_discard_reply(c, seq[0]); 99 xcb_discard_reply(c, seq[1]); 100 completed++; 101 } 102 clock_gettime(CLOCK_MONOTONIC, &end); 103 } while (end.tv_sec < start.tv_sec + 10); 104 105 XSync(dpy, True); 106 if (_x_error_occurred) 107 abort(); 108 109 printf("%s: Completed %d swaps in %.1fs, %.3fus each (%.1f FPS)\n", 110 name, completed, elapsed(&start, &end) / 1000000, 111 elapsed(&start, &end) / completed, 112 completed / (elapsed(&start, &end) / 1000000)); 113} 114 115static inline XRRScreenResources *_XRRGetScreenResourcesCurrent(Display *dpy, Window window) 116{ 117 XRRScreenResources *res; 118 119 res = XRRGetScreenResourcesCurrent(dpy, window); 120 if (res == NULL) 121 res = XRRGetScreenResources(dpy, window); 122 123 return res; 124} 125 126static XRRModeInfo *lookup_mode(XRRScreenResources *res, int id) 127{ 128 int i; 129 130 for (i = 0; i < res->nmode; i++) { 131 if (res->modes[i].id == id) 132 return &res->modes[i]; 133 } 134 135 return NULL; 136} 137 138static int dri2_open(Display *dpy) 139{ 140 drm_auth_t auth; 141 char *driver, *device; 142 int fd; 143 144 if (!DRI2Connect(dpy, DefaultRootWindow(dpy), &driver, &device)) 145 return -1; 146 147 printf ("Connecting to %s driver on %s\n", driver, device); 148 149 fd = open(device, O_RDWR); 150 if (fd < 0) 151 return -1; 152 153 if (drmIoctl(fd, DRM_IOCTL_GET_MAGIC, &auth)) 154 return -1; 155 156 if (!DRI2Authenticate(dpy, DefaultRootWindow(dpy), auth.magic)) 157 return -1; 158 159 return fd; 160} 161 162static void fullscreen(Display *dpy, Window win) 163{ 164 Atom atom = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); 165 XChangeProperty(dpy, win, 166 XInternAtom(dpy, "_NET_WM_STATE", False), 167 XA_ATOM, 32, PropModeReplace, 168 (unsigned char *)&atom, 1); 169} 170 171static int has_composite(Display *dpy) 172{ 173 int event, error; 174 int major, minor; 175 176 if (!XDamageQueryExtension (dpy, &event, &error)) 177 return 0; 178 179 if (!XCompositeQueryExtension(dpy, &event, &error)) 180 return 0; 181 182 XCompositeQueryVersion(dpy, &major, &minor); 183 184 return major > 0 || minor >= 4; 185} 186 187int main(void) 188{ 189 Display *dpy; 190 Window root, win; 191 XRRScreenResources *res; 192 XRRCrtcInfo **original_crtc; 193 XSetWindowAttributes attr; 194 int i, j, fd; 195 196 attr.override_redirect = 1; 197 198 dpy = XOpenDisplay(NULL); 199 if (dpy == NULL) 200 return 77; 201 202 fd = dri2_open(dpy); 203 if (fd < 0) 204 return 77; 205 206 if (DPMSQueryExtension(dpy, &i, &i)) 207 DPMSDisable(dpy); 208 209 root = DefaultRootWindow(dpy); 210 211 signal(SIGALRM, SIG_IGN); 212 XSetErrorHandler(_check_error_handler); 213 214 res = NULL; 215 if (XRRQueryVersion(dpy, &i, &i)) 216 res = _XRRGetScreenResourcesCurrent(dpy, root); 217 if (res == NULL) 218 return 77; 219 220 original_crtc = malloc(sizeof(XRRCrtcInfo *)*res->ncrtc); 221 for (i = 0; i < res->ncrtc; i++) 222 original_crtc[i] = XRRGetCrtcInfo(dpy, res, res->crtcs[i]); 223 224 printf("noutput=%d, ncrtc=%d\n", res->noutput, res->ncrtc); 225 for (i = 0; i < res->ncrtc; i++) 226 XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime, 227 0, 0, None, RR_Rotate_0, NULL, 0); 228 229 DRI2CreateDrawable(dpy, root); 230 DRI2SwapInterval(dpy, root, 0); 231 run(dpy, root, "off"); 232 XSync(dpy, True); 233 234 for (i = 0; i < res->noutput; i++) { 235 XRROutputInfo *output; 236 XRRModeInfo *mode; 237 238 output = XRRGetOutputInfo(dpy, res, res->outputs[i]); 239 if (output == NULL) 240 continue; 241 242 mode = NULL; 243 if (res->nmode) 244 mode = lookup_mode(res, output->modes[0]); 245 246 for (j = 0; mode && j < 2*output->ncrtc; j++) { 247 int c = j; 248 if (c >= output->ncrtc) 249 c = 2*output->ncrtc - j - 1; 250 251 printf("[%d, %d] -- OUTPUT:%ld, CRTC:%ld: %dx%d\n", 252 i, c, (long)res->outputs[i], (long)output->crtcs[c], 253 mode->width, mode->height); 254 XRRSetCrtcConfig(dpy, res, output->crtcs[c], CurrentTime, 255 0, 0, output->modes[0], RR_Rotate_0, &res->outputs[i], 1); 256 257 run(dpy, root, "root"); 258 XSync(dpy, True); 259 260 win = XCreateWindow(dpy, root, 261 0, 0, mode->width, mode->height, 0, 262 DefaultDepth(dpy, DefaultScreen(dpy)), 263 InputOutput, 264 DefaultVisual(dpy, DefaultScreen(dpy)), 265 CWOverrideRedirect, &attr); 266 DRI2CreateDrawable(dpy, win); 267 DRI2SwapInterval(dpy, win, 0); 268 fullscreen(dpy, win); 269 XMapWindow(dpy, win); 270 run(dpy, win, "fullscreen"); 271 XDestroyWindow(dpy, win); 272 XSync(dpy, True); 273 274 win = XCreateWindow(dpy, root, 275 0, 0, mode->width, mode->height, 0, 276 DefaultDepth(dpy, DefaultScreen(dpy)), 277 InputOutput, 278 DefaultVisual(dpy, DefaultScreen(dpy)), 279 CWOverrideRedirect, &attr); 280 DRI2CreateDrawable(dpy, win); 281 DRI2SwapInterval(dpy, win, 0); 282 XMapWindow(dpy, win); 283 run(dpy, win, "windowed"); 284 XDestroyWindow(dpy, win); 285 XSync(dpy, True); 286 287 if (has_composite(dpy)) { 288 Damage damage; 289 290 _x_error_occurred = 0; 291 win = XCreateWindow(dpy, root, 292 0, 0, mode->width, mode->height, 0, 293 DefaultDepth(dpy, DefaultScreen(dpy)), 294 InputOutput, 295 DefaultVisual(dpy, DefaultScreen(dpy)), 296 CWOverrideRedirect, &attr); 297 XCompositeRedirectWindow(dpy, win, CompositeRedirectManual); 298 damage = XDamageCreate(dpy, win, XDamageReportRawRectangles); 299 DRI2CreateDrawable(dpy, win); 300 DRI2SwapInterval(dpy, win, 0); 301 XMapWindow(dpy, win); 302 XSync(dpy, True); 303 if (!_x_error_occurred) 304 run(dpy, win, "composited"); 305 XDamageDestroy(dpy, damage); 306 XDestroyWindow(dpy, win); 307 XSync(dpy, True); 308 } 309 310 win = XCreateWindow(dpy, root, 311 0, 0, mode->width/2, mode->height/2, 0, 312 DefaultDepth(dpy, DefaultScreen(dpy)), 313 InputOutput, 314 DefaultVisual(dpy, DefaultScreen(dpy)), 315 CWOverrideRedirect, &attr); 316 DRI2CreateDrawable(dpy, win); 317 DRI2SwapInterval(dpy, win, 0); 318 XMapWindow(dpy, win); 319 run(dpy, win, "half"); 320 XDestroyWindow(dpy, win); 321 XSync(dpy, True); 322 323 XRRSetCrtcConfig(dpy, res, output->crtcs[c], CurrentTime, 324 0, 0, None, RR_Rotate_0, NULL, 0); 325 } 326 327 XRRFreeOutputInfo(output); 328 } 329 330 for (i = 0; i < res->ncrtc; i++) 331 XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime, 332 original_crtc[i]->x, 333 original_crtc[i]->y, 334 original_crtc[i]->mode, 335 original_crtc[i]->rotation, 336 original_crtc[i]->outputs, 337 original_crtc[i]->noutput); 338 339 if (DPMSQueryExtension(dpy, &i, &i)) 340 DPMSEnable(dpy); 341 return 0; 342} 343