From 2050d03f520a5027450b65c2617ab02a92287d23 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 23 Jul 2015 14:01:34 +0100 Subject: Start splitting out the benchmarks from test/ First comes the swap overhead measurement for various window modes. Signed-off-by: Chris Wilson --- Makefile.am | 2 +- benchmarks/.gitignore | 2 + benchmarks/Makefile.am | 14 ++ benchmarks/dri2-swap.c | 588 ++++++++++++++++++++++++++++++++++++++++++++++++ benchmarks/dri3-swap.c | 595 +++++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 2 + 6 files changed, 1202 insertions(+), 1 deletion(-) create mode 100644 benchmarks/.gitignore create mode 100644 benchmarks/Makefile.am create mode 100644 benchmarks/dri2-swap.c create mode 100644 benchmarks/dri3-swap.c diff --git a/Makefile.am b/Makefile.am index 418fdc92..853e6221 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,7 +25,7 @@ SUBDIRS = man libobj xvmc src tools MAINTAINERCLEANFILES = ChangeLog INSTALL if HAVE_X11 -SUBDIRS += test +SUBDIRS += test benchmarks endif .PHONY: ChangeLog INSTALL diff --git a/benchmarks/.gitignore b/benchmarks/.gitignore new file mode 100644 index 00000000..301c0129 --- /dev/null +++ b/benchmarks/.gitignore @@ -0,0 +1,2 @@ +dri2-swap +dri3-swap diff --git a/benchmarks/Makefile.am b/benchmarks/Makefile.am new file mode 100644 index 00000000..4976e8a3 --- /dev/null +++ b/benchmarks/Makefile.am @@ -0,0 +1,14 @@ +AM_CFLAGS = @CWARNFLAGS@ $(X11_CFLAGS) $(DRM_CFLAGS) +LDADD = $(X11_LIBS) $(DRM_LIBS) $(CLOCK_GETTIME_LIBS) + +check_PROGRAMS = + +if DRI2 +check_PROGRAMS += dri2-swap +endif + +if DRI3 +check_PROGRAMS += dri3-swap +AM_CFLAGS += $(X11_DRI3_CFLAGS) +LDADD += $(X11_DRI3_LIBS) +endif diff --git a/benchmarks/dri2-swap.c b/benchmarks/dri2-swap.c new file mode 100644 index 00000000..3d9d30aa --- /dev/null +++ b/benchmarks/dri2-swap.c @@ -0,0 +1,588 @@ +/* + * Copyright (c) 2015 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static char dri2ExtensionName[] = DRI2_NAME; +static XExtensionInfo *dri2Info; +static XEXT_GENERATE_CLOSE_DISPLAY (DRI2CloseDisplay, dri2Info) + +static Bool +DRI2WireToEvent(Display *dpy, XEvent *event, xEvent *wire); +static Status +DRI2EventToWire(Display *dpy, XEvent *event, xEvent *wire); +static int +DRI2Error(Display *display, xError *err, XExtCodes *codes, int *ret_code); + +static /* const */ XExtensionHooks dri2ExtensionHooks = { + NULL, /* create_gc */ + NULL, /* copy_gc */ + NULL, /* flush_gc */ + NULL, /* free_gc */ + NULL, /* create_font */ + NULL, /* free_font */ + DRI2CloseDisplay, /* close_display */ + DRI2WireToEvent, /* wire_to_event */ + DRI2EventToWire, /* event_to_wire */ + DRI2Error, /* error */ + NULL, /* error_string */ +}; + +static XEXT_GENERATE_FIND_DISPLAY (DRI2FindDisplay, + dri2Info, + dri2ExtensionName, + &dri2ExtensionHooks, + 0, NULL) + +static Bool +DRI2WireToEvent(Display *dpy, XEvent *event, xEvent *wire) +{ + XExtDisplayInfo *info = DRI2FindDisplay(dpy); + + XextCheckExtension(dpy, info, dri2ExtensionName, False); + + switch ((wire->u.u.type & 0x7f) - info->codes->first_event) { +#ifdef X_DRI2SwapBuffers + case DRI2_BufferSwapComplete: + return False; +#endif +#ifdef DRI2_InvalidateBuffers + case DRI2_InvalidateBuffers: + return False; +#endif + default: + /* client doesn't support server event */ + break; + } + + return False; +} + +/* We don't actually support this. It doesn't make sense for clients to + * send each other DRI2 events. + */ +static Status +DRI2EventToWire(Display *dpy, XEvent *event, xEvent *wire) +{ + XExtDisplayInfo *info = DRI2FindDisplay(dpy); + + XextCheckExtension(dpy, info, dri2ExtensionName, False); + + switch (event->type) { + default: + /* client doesn't support server event */ + break; + } + + return Success; +} + +static int +DRI2Error(Display *display, xError *err, XExtCodes *codes, int *ret_code) +{ + if (err->majorCode == codes->major_opcode && + err->errorCode == BadDrawable && + err->minorCode == X_DRI2CopyRegion) + return True; + + /* If the X drawable was destroyed before the GLX drawable, the + * DRI2 drawble will be gone by the time we call + * DRI2DestroyDrawable. So just ignore BadDrawable here. */ + if (err->majorCode == codes->major_opcode && + err->errorCode == BadDrawable && + err->minorCode == X_DRI2DestroyDrawable) + return True; + + /* If the server is non-local DRI2Connect will raise BadRequest. + * Swallow this so that DRI2Connect can signal this in its return code */ + if (err->majorCode == codes->major_opcode && + err->minorCode == X_DRI2Connect && + err->errorCode == BadRequest) { + *ret_code = False; + return True; + } + + return False; +} + +static Bool +DRI2QueryExtension(Display * dpy, int *eventBase, int *errorBase) +{ + XExtDisplayInfo *info = DRI2FindDisplay(dpy); + + if (XextHasExtension(info)) { + *eventBase = info->codes->first_event; + *errorBase = info->codes->first_error; + return True; + } + + return False; +} + +static Bool +DRI2Connect(Display * dpy, XID window, char **driverName, char **deviceName) +{ + XExtDisplayInfo *info = DRI2FindDisplay(dpy); + xDRI2ConnectReply rep; + xDRI2ConnectReq *req; + + XextCheckExtension(dpy, info, dri2ExtensionName, False); + + LockDisplay(dpy); + GetReq(DRI2Connect, req); + req->reqType = info->codes->major_opcode; + req->dri2ReqType = X_DRI2Connect; + req->window = window; + req->driverType = DRI2DriverDRI; + if (!_XReply(dpy, (xReply *) & rep, 0, xFalse)) { + UnlockDisplay(dpy); + SyncHandle(); + return False; + } + + if (rep.driverNameLength == 0 && rep.deviceNameLength == 0) { + UnlockDisplay(dpy); + SyncHandle(); + return False; + } + + *driverName = Xmalloc(rep.driverNameLength + 1); + if (*driverName == NULL) { + _XEatData(dpy, + ((rep.driverNameLength + 3) & ~3) + + ((rep.deviceNameLength + 3) & ~3)); + UnlockDisplay(dpy); + SyncHandle(); + return False; + } + _XReadPad(dpy, *driverName, rep.driverNameLength); + (*driverName)[rep.driverNameLength] = '\0'; + + *deviceName = Xmalloc(rep.deviceNameLength + 1); + if (*deviceName == NULL) { + Xfree(*driverName); + _XEatData(dpy, ((rep.deviceNameLength + 3) & ~3)); + UnlockDisplay(dpy); + SyncHandle(); + return False; + } + _XReadPad(dpy, *deviceName, rep.deviceNameLength); + (*deviceName)[rep.deviceNameLength] = '\0'; + + UnlockDisplay(dpy); + SyncHandle(); + + return True; +} + +static Bool +DRI2Authenticate(Display * dpy, XID window, unsigned int magic) +{ + XExtDisplayInfo *info = DRI2FindDisplay(dpy); + xDRI2AuthenticateReq *req; + xDRI2AuthenticateReply rep; + + XextCheckExtension(dpy, info, dri2ExtensionName, False); + + LockDisplay(dpy); + GetReq(DRI2Authenticate, req); + req->reqType = info->codes->major_opcode; + req->dri2ReqType = X_DRI2Authenticate; + req->window = window; + req->magic = magic; + + if (!_XReply(dpy, (xReply *) & rep, 0, xFalse)) { + UnlockDisplay(dpy); + SyncHandle(); + return False; + } + + UnlockDisplay(dpy); + SyncHandle(); + + return rep.authenticated; +} + +static void +DRI2CreateDrawable(Display * dpy, XID drawable) +{ + XExtDisplayInfo *info = DRI2FindDisplay(dpy); + xDRI2CreateDrawableReq *req; + + XextSimpleCheckExtension(dpy, info, dri2ExtensionName); + + LockDisplay(dpy); + GetReq(DRI2CreateDrawable, req); + req->reqType = info->codes->major_opcode; + req->dri2ReqType = X_DRI2CreateDrawable; + req->drawable = drawable; + UnlockDisplay(dpy); + SyncHandle(); +} + +static void DRI2SwapInterval(Display *dpy, XID drawable, int interval) +{ + XExtDisplayInfo *info = DRI2FindDisplay(dpy); + xDRI2SwapIntervalReq *req; + + XextSimpleCheckExtension (dpy, info, dri2ExtensionName); + + LockDisplay(dpy); + GetReq(DRI2SwapInterval, req); + req->reqType = info->codes->major_opcode; + req->dri2ReqType = X_DRI2SwapInterval; + req->drawable = drawable; + req->interval = interval; + UnlockDisplay(dpy); + SyncHandle(); +} + +static int _x_error_occurred; + +static int +_check_error_handler(Display *display, + XErrorEvent *event) +{ + fprintf(stderr, + "X11 error from display %s, serial=%ld, error=%d, req=%d.%d\n", + DisplayString(display), + event->serial, + event->error_code, + event->request_code, + event->minor_code); + _x_error_occurred++; + return False; /* ignored */ +} + +static double elapsed(const struct timespec *start, + const struct timespec *end) +{ + return 1e6*(end->tv_sec - start->tv_sec) + (end->tv_nsec - start->tv_nsec)/1000; +} + +static void run(Display *dpy, Window win) +{ + xcb_connection_t *c = XGetXCBConnection(dpy); + struct timespec start, end; + int n, completed = 0; + + clock_gettime(CLOCK_MONOTONIC, &start); + do { + for (n = 0; n < 1000; n++) { + unsigned int attachments[] = { DRI2BufferBackLeft }; + unsigned int seq[2]; + + seq[0] = xcb_dri2_swap_buffers_unchecked(c, win, + 0, 0, 0, 0, 0, 0).sequence; + + + seq[1] = xcb_dri2_get_buffers_unchecked(c, win, + 1, 1, attachments).sequence; + + xcb_flush(c); + xcb_discard_reply(c, seq[0]); + xcb_discard_reply(c, seq[1]); + completed++; + } + clock_gettime(CLOCK_MONOTONIC, &end); + } while (end.tv_sec < start.tv_sec + 10); + + printf("%f\n", completed / (elapsed(&start, &end) / 1000000)); +} + +static inline XRRScreenResources *_XRRGetScreenResourcesCurrent(Display *dpy, Window window) +{ + XRRScreenResources *res; + + res = XRRGetScreenResourcesCurrent(dpy, window); + if (res == NULL) + res = XRRGetScreenResources(dpy, window); + + return res; +} + +static XRRModeInfo *lookup_mode(XRRScreenResources *res, int id) +{ + int i; + + for (i = 0; i < res->nmode; i++) { + if (res->modes[i].id == id) + return &res->modes[i]; + } + + return NULL; +} + +static int dri2_open(Display *dpy) +{ + drm_auth_t auth; + char *driver, *device; + int fd; + + if (!DRI2QueryExtension(dpy, &fd, &fd)) + return -1; + + if (!DRI2Connect(dpy, DefaultRootWindow(dpy), &driver, &device)) + return -1; + + fd = open(device, O_RDWR); + if (fd < 0) + return -1; + + if (drmIoctl(fd, DRM_IOCTL_GET_MAGIC, &auth)) + return -1; + + if (!DRI2Authenticate(dpy, DefaultRootWindow(dpy), auth.magic)) + return -1; + + return fd; +} + +static void fullscreen(Display *dpy, Window win) +{ + Atom atom = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + XChangeProperty(dpy, win, + XInternAtom(dpy, "_NET_WM_STATE", False), + XA_ATOM, 32, PropModeReplace, + (unsigned char *)&atom, 1); +} + +static int has_composite(Display *dpy) +{ + int event, error; + int major, minor; + + if (!XDamageQueryExtension (dpy, &event, &error)) + return 0; + + if (!XCompositeQueryExtension(dpy, &event, &error)) + return 0; + + XCompositeQueryVersion(dpy, &major, &minor); + + return major > 0 || minor >= 4; +} + +int main(int argc, char **argv) +{ + Display *dpy; + Window root, win; + XRRScreenResources *res; + XRRCrtcInfo **original_crtc; + XSetWindowAttributes attr; + enum window { ROOT, FULLSCREEN, WINDOW } w = FULLSCREEN; + enum visible {REDIRECTED, NORMAL } v = NORMAL; + enum display { OFF, ON } d = OFF; + int width, height; + int i, fd; + int c; + + while ((c = getopt(argc, argv, "d:v:w:")) != -1) { + switch (c) { + case 'd': + if (strcmp(optarg, "off") == 0) + d = OFF; + else if (strcmp(optarg, "on") == 0) + d = ON; + else + abort(); + break; + + case 'v': + if (strcmp(optarg, "redirected") == 0) + v = REDIRECTED; + else if (strcmp(optarg, "normal") == 0) + v = NORMAL; + else + abort(); + break; + + case 'w': + if (strcmp(optarg, "fullscreen") == 0) + w = FULLSCREEN; + else if (strcmp(optarg, "window") == 0) + w = WINDOW; + else if (strcmp(optarg, "root") == 0) + w = ROOT; + else + abort(); + break; + } + } + + attr.override_redirect = 1; + + dpy = XOpenDisplay(NULL); + if (dpy == NULL) + return 77; + + width = DisplayWidth(dpy, DefaultScreen(dpy)); + height = DisplayHeight(dpy, DefaultScreen(dpy)); + + fd = dri2_open(dpy); + if (fd < 0) + return 77; + + if (DPMSQueryExtension(dpy, &i, &i)) + DPMSDisable(dpy); + + root = DefaultRootWindow(dpy); + + signal(SIGALRM, SIG_IGN); + XSetErrorHandler(_check_error_handler); + + res = NULL; + if (XRRQueryVersion(dpy, &i, &i)) + res = _XRRGetScreenResourcesCurrent(dpy, root); + if (res == NULL) + return 77; + + if (v == REDIRECTED && !has_composite(dpy)) + return 77; + + original_crtc = malloc(sizeof(XRRCrtcInfo *)*res->ncrtc); + for (i = 0; i < res->ncrtc; i++) + original_crtc[i] = XRRGetCrtcInfo(dpy, res, res->crtcs[i]); + + for (i = 0; i < res->ncrtc; i++) + XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime, + 0, 0, None, RR_Rotate_0, NULL, 0); + + DRI2CreateDrawable(dpy, root); + DRI2SwapInterval(dpy, root, 0); + + if (d != OFF) { + for (i = 0; i < res->noutput; i++) { + XRROutputInfo *output; + XRRModeInfo *mode; + + output = XRRGetOutputInfo(dpy, res, res->outputs[i]); + if (output == NULL) + continue; + + mode = NULL; + if (res->nmode) + mode = lookup_mode(res, output->modes[0]); + if (mode == NULL) + continue; + + XRRSetCrtcConfig(dpy, res, output->crtcs[0], CurrentTime, + 0, 0, output->modes[0], RR_Rotate_0, &res->outputs[i], 1); + width = mode->width; + height = mode->height; + break; + } + if (i == res->noutput) { + _x_error_occurred = 77; + goto restore; + } + } + + if (w == ROOT) { + run(dpy, root); + } else if (w == FULLSCREEN) { + win = XCreateWindow(dpy, root, + 0, 0, width, height, 0, + DefaultDepth(dpy, DefaultScreen(dpy)), + InputOutput, + DefaultVisual(dpy, DefaultScreen(dpy)), + CWOverrideRedirect, &attr); + DRI2CreateDrawable(dpy, win); + DRI2SwapInterval(dpy, win, 0); + if (v == REDIRECTED) { + XCompositeRedirectWindow(dpy, win, CompositeRedirectManual); + XDamageCreate(dpy, win, XDamageReportRawRectangles); + } else + fullscreen(dpy, win); + XMapWindow(dpy, win); + run(dpy, win); + } else if (w == WINDOW) { + win = XCreateWindow(dpy, root, + 0, 0, width/2, height/2, 0, + DefaultDepth(dpy, DefaultScreen(dpy)), + InputOutput, + DefaultVisual(dpy, DefaultScreen(dpy)), + CWOverrideRedirect, &attr); + DRI2CreateDrawable(dpy, win); + DRI2SwapInterval(dpy, win, 0); + if (v == REDIRECTED) { + XCompositeRedirectWindow(dpy, win, CompositeRedirectManual); + XDamageCreate(dpy, win, XDamageReportRawRectangles); + } + XMapWindow(dpy, win); + run(dpy, win); + } + +restore: + for (i = 0; i < res->ncrtc; i++) + XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime, + 0, 0, None, RR_Rotate_0, NULL, 0); + + for (i = 0; i < res->ncrtc; i++) + XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime, + original_crtc[i]->x, + original_crtc[i]->y, + original_crtc[i]->mode, + original_crtc[i]->rotation, + original_crtc[i]->outputs, + original_crtc[i]->noutput); + + if (DPMSQueryExtension(dpy, &i, &i)) + DPMSEnable(dpy); + + XSync(dpy, True); + return _x_error_occurred; +} diff --git a/benchmarks/dri3-swap.c b/benchmarks/dri3-swap.c new file mode 100644 index 00000000..4dd423b3 --- /dev/null +++ b/benchmarks/dri3-swap.c @@ -0,0 +1,595 @@ +/* + * Copyright (c) 2015 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +struct dri3_fence { + XID xid; + void *addr; +}; + +static int _x_error_occurred; +static uint32_t stamp; + +struct list { + struct list *next, *prev; +}; + +static void +list_init(struct list *list) +{ + list->next = list->prev = list; +} + +static inline void +__list_add(struct list *entry, + struct list *prev, + struct list *next) +{ + next->prev = entry; + entry->next = next; + entry->prev = prev; + prev->next = entry; +} + +static inline void +list_add(struct list *entry, struct list *head) +{ + __list_add(entry, head, head->next); +} + +static inline void +__list_del(struct list *prev, struct list *next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void +_list_del(struct list *entry) +{ + __list_del(entry->prev, entry->next); +} + +static inline void +list_move(struct list *list, struct list *head) +{ + if (list->prev != head) { + _list_del(list); + list_add(list, head); + } +} + +#define __container_of(ptr, sample, member) \ + (void *)((char *)(ptr) - ((char *)&(sample)->member - (char *)(sample))) + +#define list_for_each_entry(pos, head, member) \ + for (pos = __container_of((head)->next, pos, member); \ + &pos->member != (head); \ + pos = __container_of(pos->member.next, pos, member)) + +static int +_check_error_handler(Display *display, + XErrorEvent *event) +{ + printf("X11 error from display %s, serial=%ld, error=%d, req=%d.%d\n", + DisplayString(display), + event->serial, + event->error_code, + event->request_code, + event->minor_code); + _x_error_occurred++; + return False; /* ignored */ +} + +static int dri3_create_fence(Display *dpy, + Pixmap pixmap, + struct dri3_fence *fence) +{ + xcb_connection_t *c = XGetXCBConnection(dpy); + struct dri3_fence f; + int fd; + + fd = xshmfence_alloc_shm(); + if (fd < 0) + return -1; + + f.addr = xshmfence_map_shm(fd); + if (f.addr == NULL) { + close(fd); + return -1; + } + + f.xid = xcb_generate_id(c); + xcb_dri3_fence_from_fd(c, pixmap, f.xid, 0, fd); + + *fence = f; + return 0; +} + +static double elapsed(const struct timespec *start, + const struct timespec *end) +{ + return 1e6*(end->tv_sec - start->tv_sec) + (end->tv_nsec - start->tv_nsec)/1000; +} + +struct buffer { + struct list link; + Pixmap pixmap; + struct dri3_fence fence; + int fd; + int busy; +}; + +static void run(Display *dpy, Window win) +{ + xcb_connection_t *c = XGetXCBConnection(dpy); + struct timespec start, end; +#define N_BACK 8 + struct buffer buffer[N_BACK]; + struct list mru; + Window root; + unsigned int width, height; + unsigned border, depth; + unsigned present_flags = XCB_PRESENT_OPTION_ASYNC; + xcb_xfixes_region_t update = 0; + int completed = 0; + int queued = 0; + uint32_t eid; + void *Q; + int i, n; + + list_init(&mru); + + XGetGeometry(dpy, win, + &root, &i, &n, &width, &height, &border, &depth); + + _x_error_occurred = 0; + + for (n = 0; n < N_BACK; n++) { + xcb_dri3_buffer_from_pixmap_reply_t *reply; + int *fds; + + buffer[n].pixmap = + XCreatePixmap(dpy, win, width, height, depth); + buffer[n].fence.xid = 0; + buffer[n].fd = -1; + + if (dri3_create_fence(dpy, win, &buffer[n].fence)) + return; + + reply = xcb_dri3_buffer_from_pixmap_reply (c, + xcb_dri3_buffer_from_pixmap(c, buffer[n].pixmap), + NULL); + if (reply == NULL) + return; + + fds = xcb_dri3_buffer_from_pixmap_reply_fds (c, reply); + buffer[n].fd = fds[0]; + free(reply); + + /* start idle */ + xshmfence_trigger(buffer[n].fence.addr); + buffer[n].busy = 0; + list_add(&buffer[n].link, &mru); + } + + eid = xcb_generate_id(c); + xcb_present_select_input(c, eid, win, + XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY | + XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY); + Q = xcb_register_for_special_xge(c, &xcb_present_id, eid, &stamp); + + clock_gettime(CLOCK_MONOTONIC, &start); + do { + for (n = 0; n < 1000; n++) { + struct buffer *tmp, *b = NULL; + list_for_each_entry(tmp, &mru, link) { + if (!tmp->busy) { + b = tmp; + break; + } + } + while (b == NULL) { + xcb_present_generic_event_t *ev; + + ev = (xcb_present_generic_event_t *) + xcb_wait_for_special_event(c, Q); + if (ev == NULL) + abort(); + + do { + switch (ev->evtype) { + case XCB_PRESENT_COMPLETE_NOTIFY: + completed++; + queued--; + break; + + case XCB_PRESENT_EVENT_IDLE_NOTIFY: + { + xcb_present_idle_notify_event_t *ie = (xcb_present_idle_notify_event_t *)ev; + assert(ie->serial < N_BACK); + buffer[ie->serial].busy = 0; + if (b == NULL) + b = &buffer[ie->serial]; + break; + } + } + free(ev); + } while ((ev = (xcb_present_generic_event_t *)xcb_poll_for_special_event(c, Q))); + } + + b->busy = 1; + if (b->fence.xid) { + xshmfence_await(b->fence.addr); + xshmfence_reset(b->fence.addr); + } + xcb_present_pixmap(c, win, b->pixmap, b - buffer, + 0, /* valid */ + update, /* update */ + 0, /* x_off */ + 0, /* y_off */ + None, + None, /* wait fence */ + b->fence.xid, + present_flags, + 0, /* target msc */ + 0, /* divisor */ + 0, /* remainder */ + 0, NULL); + list_move(&b->link, &mru); + queued++; + xcb_flush(c); + } + clock_gettime(CLOCK_MONOTONIC, &end); + } while (end.tv_sec < start.tv_sec + 10); + + while (queued) { + xcb_present_generic_event_t *ev; + + ev = (xcb_present_generic_event_t *) + xcb_wait_for_special_event(c, Q); + if (ev == NULL) + abort(); + + do { + switch (ev->evtype) { + case XCB_PRESENT_COMPLETE_NOTIFY: + completed++; + queued--; + break; + + case XCB_PRESENT_EVENT_IDLE_NOTIFY: + break; + } + free(ev); + } while ((ev = (xcb_present_generic_event_t *)xcb_poll_for_special_event(c, Q))); + } + clock_gettime(CLOCK_MONOTONIC, &end); + + printf("%f\n", completed / (elapsed(&start, &end) / 1000000)); +} + +static int has_present(Display *dpy) +{ + xcb_connection_t *c = XGetXCBConnection(dpy); + xcb_generic_error_t *error = NULL; + void *reply; + + reply = xcb_present_query_version_reply(c, + xcb_present_query_version(c, + XCB_PRESENT_MAJOR_VERSION, + XCB_PRESENT_MINOR_VERSION), + &error); + + free(reply); + free(error); + if (reply == NULL) { + fprintf(stderr, "Present not supported on %s\n", DisplayString(dpy)); + return 0; + } + + return 1; +} + +static int has_composite(Display *dpy) +{ + int event, error; + int major, minor; + + if (!XDamageQueryExtension (dpy, &event, &error)) + return 0; + + if (!XCompositeQueryExtension(dpy, &event, &error)) + return 0; + + XCompositeQueryVersion(dpy, &major, &minor); + + return major > 0 || minor >= 4; +} + +static inline XRRScreenResources *_XRRGetScreenResourcesCurrent(Display *dpy, Window window) +{ + XRRScreenResources *res; + + res = XRRGetScreenResourcesCurrent(dpy, window); + if (res == NULL) + res = XRRGetScreenResources(dpy, window); + + return res; +} + +static XRRModeInfo *lookup_mode(XRRScreenResources *res, int id) +{ + int i; + + for (i = 0; i < res->nmode; i++) { + if (res->modes[i].id == id) + return &res->modes[i]; + } + + return NULL; +} + +static void fullscreen(Display *dpy, Window win) +{ + Atom atom = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + XChangeProperty(dpy, win, + XInternAtom(dpy, "_NET_WM_STATE", False), + XA_ATOM, 32, PropModeReplace, + (unsigned char *)&atom, 1); +} + +static int dri3_query_version(Display *dpy, int *major, int *minor) +{ + xcb_connection_t *c = XGetXCBConnection(dpy); + xcb_dri3_query_version_reply_t *reply; + xcb_generic_error_t *error; + + *major = *minor = -1; + + reply = xcb_dri3_query_version_reply(c, + xcb_dri3_query_version(c, + XCB_DRI3_MAJOR_VERSION, + XCB_DRI3_MINOR_VERSION), + &error); + free(error); + if (reply == NULL) + return -1; + + *major = reply->major_version; + *minor = reply->minor_version; + free(reply); + + return 0; +} + +static int has_dri3(Display *dpy) +{ + const xcb_query_extension_reply_t *ext; + int major, minor; + + ext = xcb_get_extension_data(XGetXCBConnection(dpy), &xcb_dri3_id); + if (ext == NULL || !ext->present) + return 0; + + if (dri3_query_version(dpy, &major, &minor) < 0) + return 0; + + return major >= 0; +} + +int main(int argc, char **argv) +{ + Display *dpy; + Window root, win; + XRRScreenResources *res; + XRRCrtcInfo **original_crtc; + XSetWindowAttributes attr; + enum window { ROOT, FULLSCREEN, WINDOW } w = FULLSCREEN; + enum visible {REDIRECTED, NORMAL } v = NORMAL; + enum display { OFF, ON } d = OFF; + int width, height; + int i; + + while ((i = getopt(argc, argv, "d:v:w:")) != -1) { + switch (i) { + case 'd': + if (strcmp(optarg, "off") == 0) + d = OFF; + else if (strcmp(optarg, "on") == 0) + d = ON; + else + abort(); + break; + + case 'v': + if (strcmp(optarg, "redirected") == 0) + v = REDIRECTED; + else if (strcmp(optarg, "normal") == 0) + v = NORMAL; + else + abort(); + break; + + case 'w': + if (strcmp(optarg, "fullscreen") == 0) + w = FULLSCREEN; + else if (strcmp(optarg, "window") == 0) + w = WINDOW; + else if (strcmp(optarg, "root") == 0) + w = ROOT; + else + abort(); + break; + } + } + + attr.override_redirect = 1; + + dpy = XOpenDisplay(NULL); + if (dpy == NULL) + return 77; + + width = DisplayWidth(dpy, DefaultScreen(dpy)); + height = DisplayHeight(dpy, DefaultScreen(dpy)); + + if (!has_present(dpy)) + return 77; + + if (!has_dri3(dpy)) + return 77; + + if (DPMSQueryExtension(dpy, &i, &i)) + DPMSDisable(dpy); + + root = DefaultRootWindow(dpy); + + signal(SIGALRM, SIG_IGN); + XSetErrorHandler(_check_error_handler); + + res = NULL; + if (XRRQueryVersion(dpy, &i, &i)) + res = _XRRGetScreenResourcesCurrent(dpy, root); + if (res == NULL) + return 77; + + if (v == REDIRECTED && !has_composite(dpy)) + return 77; + + original_crtc = malloc(sizeof(XRRCrtcInfo *)*res->ncrtc); + for (i = 0; i < res->ncrtc; i++) + original_crtc[i] = XRRGetCrtcInfo(dpy, res, res->crtcs[i]); + + for (i = 0; i < res->ncrtc; i++) + XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime, + 0, 0, None, RR_Rotate_0, NULL, 0); + + if (d != OFF) { + for (i = 0; i < res->noutput; i++) { + XRROutputInfo *output; + XRRModeInfo *mode; + + output = XRRGetOutputInfo(dpy, res, res->outputs[i]); + if (output == NULL) + continue; + + mode = NULL; + if (res->nmode) + mode = lookup_mode(res, output->modes[0]); + if (mode == NULL) + continue; + + XRRSetCrtcConfig(dpy, res, output->crtcs[0], CurrentTime, + 0, 0, output->modes[0], RR_Rotate_0, &res->outputs[i], 1); + width = mode->width; + height = mode->height; + break; + } + if (i == res->noutput) { + _x_error_occurred = 77; + goto restore; + } + } + + if (w == ROOT) { + run(dpy, root); + } else if (w == FULLSCREEN) { + win = XCreateWindow(dpy, root, + 0, 0, width, height, 0, + DefaultDepth(dpy, DefaultScreen(dpy)), + InputOutput, + DefaultVisual(dpy, DefaultScreen(dpy)), + CWOverrideRedirect, &attr); + if (v == REDIRECTED) { + XCompositeRedirectWindow(dpy, win, CompositeRedirectManual); + XDamageCreate(dpy, win, XDamageReportRawRectangles); + } else + fullscreen(dpy, win); + XMapWindow(dpy, win); + run(dpy, win); + } else if (w == WINDOW) { + win = XCreateWindow(dpy, root, + 0, 0, width/2, height/2, 0, + DefaultDepth(dpy, DefaultScreen(dpy)), + InputOutput, + DefaultVisual(dpy, DefaultScreen(dpy)), + CWOverrideRedirect, &attr); + if (v == REDIRECTED) { + XCompositeRedirectWindow(dpy, win, CompositeRedirectManual); + XDamageCreate(dpy, win, XDamageReportRawRectangles); + } + XMapWindow(dpy, win); + run(dpy, win); + } + +restore: + for (i = 0; i < res->ncrtc; i++) + XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime, + 0, 0, None, RR_Rotate_0, NULL, 0); + + for (i = 0; i < res->ncrtc; i++) + XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime, + original_crtc[i]->x, + original_crtc[i]->y, + original_crtc[i]->mode, + original_crtc[i]->rotation, + original_crtc[i]->outputs, + original_crtc[i]->noutput); + + if (DPMSQueryExtension(dpy, &i, &i)) + DPMSEnable(dpy); + + XSync(dpy, True); + return _x_error_occurred; +} diff --git a/configure.ac b/configure.ac index 0b6358b8..9aa7d977 100644 --- a/configure.ac +++ b/configure.ac @@ -212,6 +212,7 @@ fi PKG_CHECK_MODULES(X11, [x11 x11-xcb xcb-dri2 xcomposite xdamage xrender xrandr xext xfixes cairo cairo-xlib-xrender pixman-1 libpng], [x11="yes"], [x11="no"]) AM_CONDITIONAL(HAVE_X11, test "x$x11" = "xyes") +echo X11_CLFAGS="$X11_CLFAGS" X11_LIBS="$X11_LIBS" cpuid="yes" AC_TRY_LINK([ @@ -864,6 +865,7 @@ AC_CONFIG_FILES([ xvmc/shader/mc/Makefile xvmc/shader/vld/Makefile test/Makefile + benchmarks/Makefile tools/Makefile tools/org.x.xf86-video-intel.backlight-helper.policy ]) -- cgit v1.2.3