diff --git a/Makefile.am b/Makefile.am index 418fdc9..853e622 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/NEWS b/NEWS index 604b9cc..0e20033 100644 --- a/NEWS +++ b/NEWS @@ -21,7 +21,7 @@ should make one more snapshot before an imminent release. Before kernel 3.19, O_NONBLOCK support is broken and so we must avoid reading if we are not expecting an event. - * Backwards compatibilty fix for fake triple buffering with PRIME and + * Backwards compatibility fix for fake triple buffering with PRIME and Xorg-1.15 https://bugs.freedesktop.org/show_bug.cgi?id=85144#c12 @@ -51,7 +51,7 @@ should make one more snapshot before an imminent release. Snapshot 2.99.916 (2014-09-08) ============================== Quick update for MST in UXA - we need to hook up the RandR outputs for -dynamicaly added connectors. +dynamically added connectors. Snapshot 2.99.915 (2014-09-08) @@ -503,7 +503,7 @@ release. backlight property is queried whilst the connector is disabled https://bugs.freedesktop.org/show_bug.cgi?id=70406 - * Pad GETCONNECTOR ioctl for compatability between 32/64-bit userspace + * Pad GETCONNECTOR ioctl for compatibility between 32/64-bit userspace and kernel * Handle long glyph runs correctly @@ -523,7 +523,7 @@ snapshot beforehand to push out the bug fixes from the last week. * Fix video output using sprites when changing the image size - * Apply more restrictive tile constaints for 915g class devices + * Apply more restrictive tile constraints for 915g class devices https://bugs.launchpad.net/ubuntu/+source/xserver-xorg-video-intel/+bug/1232546 * Ensure all overlapping rectangles are drawn for XRenderFillRectangles @@ -1132,7 +1132,7 @@ operation. * Explicitly prevent ring-switching for synchronized rendering to scanouts (for vsync). - * Clip dirty region to slave pixmaps (otherwise UDL is nigh unusuable) + * Clip dirty region to slave pixmaps (otherwise UDL is nigh unusable) https://bugs.freedesktop.org/show_bug.cgi?id=59539 @@ -1226,7 +1226,7 @@ Release 2.20.15 (2012-12-03) ============================ And lo, enabling more of the common acceleration paths for gen4 revealed another lurking bug - something is wrong with how we prepare Y-tiling -surfaces for rendering. For the time being, we can surreptiously disable +surfaces for rendering. For the time being, we can surreptitiously disable them for gen4 and avoid hitting GPU hangs. * Avoid clobbering the render state after failing to convert the @@ -1515,7 +1515,7 @@ Release 2.20.5 (2012-08-26) Another silly bug found, another small bugfix release. The goal was for the driver to bind to all Intel devices supported by the kernel. Unfortunately we were too successful and started claiming Pouslbo, -Medfield and Cedarview devices which are still encumbered by propietary +Medfield and Cedarview devices which are still encumbered by proprietary IP and not supported by this driver. Bugs fixed since 2.20.4: diff --git a/benchmarks/.gitignore b/benchmarks/.gitignore new file mode 100644 index 0000000..301c012 --- /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 0000000..4976e8a --- /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 0000000..3d9d30a --- /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 0000000..4dd423b --- /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 61bea43..9aa7d97 100644 --- a/configure.ac +++ b/configure.ac @@ -195,18 +195,24 @@ AC_ARG_ENABLE(udev, [UDEV="$enableval"], [UDEV=auto]) +udev_msg=" disabled" if test "x$UDEV" != "xno"; then PKG_CHECK_MODULES(UDEV, [libudev], [udev="yes"], [udev="no"]) + AC_CHECK_HEADERS([sys/stat.h], [], [udev="no"]) if test "x$UDEV" = "xyes" -a "x$udev" != "xyes"; then AC_MSG_ERROR([udev support requested but not found (libudev)]) fi if test "x$udev" = "xyes"; then AC_DEFINE(HAVE_UDEV,1,[Enable udev-based monitor hotplug detection]) + udev_msg=" yes" + else + udev_msg=" no" fi fi -PKG_CHECK_MODULES(X11, [x11 xrender xrandr xext xfixes cairo cairo-xlib-xrender pixman-1 libpng], [x11="yes"], [x11="no"]) +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([ @@ -270,7 +276,7 @@ if test "x$shm" = "xyes"; then AC_DEFINE([HAVE_MIT_SHM], 1, [Define to 1 if MIT-SHM is available]) fi -PKG_CHECK_MODULES(X11_DRI3, [xcb-dri3 xcb-sync xcb-present x11-xcb xshmfence x11 xrender xext libdrm], [x11_dri3="yes"], [x11_dri3="no"]) +PKG_CHECK_MODULES(X11_DRI3, [xcb-dri3 xcb-sync xcb-xfixes xcb-present x11-xcb xshmfence x11 xcomposite xdamage xrender xrandr xxf86vm xext libdrm], [x11_dri3="yes"], [x11_dri3="no"]) AM_CONDITIONAL(X11_DRI3, test "x$x11_dri3" = "xyes" -a "x$shm" = "xyes") AM_CONDITIONAL(X11_SHM, test "x$shm" = "xyes") @@ -307,6 +313,8 @@ if test "x$tools" != "xno"; then tools="no" fi + PKG_CHECK_MODULES(TOOL_CURSOR, [xfixes x11 libpng], [cursor="yes"], [ivo="no"]) + IVO_CFLAGS="$IVO_CFLAGS $extra_cflags" fi if test "x$tools" != "xno"; then @@ -315,6 +323,7 @@ fi AC_MSG_CHECKING([whether to build additional tools]) AC_MSG_RESULT([$tools]) AM_CONDITIONAL(BUILD_TOOLS, test "x$tools" != "xno") +AM_CONDITIONAL(BUILD_TOOL_CURSOR, test "x$cursor" = "xyes") # Define a configure option for an alternate module directory AC_ARG_WITH(xorg-module-dir, @@ -339,10 +348,20 @@ AC_ARG_ENABLE(dri2, [DRI2=$enableval], [DRI2=yes]) AC_ARG_ENABLE(dri3, - AS_HELP_STRING([--enable-dri3], - [Enable DRI3 support [[default=no]]]), + AS_HELP_STRING([--disable-dri3], + [Disable DRI3 support [[default=yes]]]), [DRI3=$enableval], - [DRI3=no]) + [DRI3=yes]) +AC_ARG_WITH(default-dri, + AS_HELP_STRING([--with-default-dri], + [Select the default maximum DRI level [default 2]]), + [DRI_DEFAULT=$withval], + [DRI_DEFAULT=2]) +if test "x$DRI_DEFAULT" = "x0"; then + AC_DEFINE(DEFAULT_DRI_LEVEL, 0,[Default DRI level]) +else + AC_DEFINE(DEFAULT_DRI_LEVEL, ~0, [Default DRI level]) +fi AC_ARG_ENABLE(xvmc, AS_HELP_STRING([--disable-xvmc], [Disable XvMC support [[default=yes]]]), @@ -375,14 +394,12 @@ AC_ARG_ENABLE(ums-only, required_xorg_server_version=1.6 required_pixman_version=0.16 -if pkg-config --exists 'pixman-1 >= 0.27.1'; then - AC_DEFINE([HAS_PIXMAN_GLYPHS], 1, [Enable pixman glyph cache]) -fi - -if pkg-config --exists 'pixman-1 >= 0.24.0'; then - AC_DEFINE([HAS_PIXMAN_TRIANGLES], 1, [Enable pixman triangle rasterisation]) -fi - +PKG_CHECK_EXISTS([pixman-1 >= 0.24.0], + AC_DEFINE([HAS_PIXMAN_TRIANGLES], 1, [Enable pixman triangle rasterisation]) + []) +PKG_CHECK_EXISTS([pixman-1 >= 0.27.1], + [AC_DEFINE([HAS_PIXMAN_GLYPHS], 1, [Enable pixman glyph cache])], + []) # Store the list of server defined optional extensions in REQUIRED_MODULES XORG_DRIVER_CHECK_EXT(RANDR, randrproto) XORG_DRIVER_CHECK_EXT(RENDER, renderproto) @@ -398,24 +415,25 @@ AC_ARG_ENABLE(sna, [SNA="$enableval"], [SNA=auto]) +AC_CHECK_HEADERS([dev/wscons/wsconsio.h]) +AC_FUNC_ALLOCA +AC_HEADER_MAJOR + if test "x$SNA" != "xno"; then AC_DEFINE(USE_SNA, 1, [Enable SNA support]) AC_CHECK_HEADERS([sys/sysinfo.h], AC_CHECK_MEMBERS([struct sysinfo.totalram], [], [], [[#include ]])) fi uxa_requires_libdrm=2.4.52 +uxa_requires_pixman=0.24.0 + AC_ARG_ENABLE(uxa, AS_HELP_STRING([--enable-uxa], [Enable Unified Acceleration Architecture (UXA) [default=auto]]), [UXA="$enableval"], [UXA=auto]) if test "x$UXA" = "xauto"; then - if ! pkg-config --exists "libdrm_intel >= $uxa_requires_libdrm"; then - UXA=no - fi - if ! pkg-config --exists 'pixman-1 >= 0.24.0'; then - UXA=no - fi + PKG_CHECK_EXISTS([libdrm_intel >= $uxa_requires_libdrm pixman-1 >= $uxa_requires_pixman], [], [UXA=no]) fi if test "x$UXA" != "xno"; then AC_DEFINE(USE_UXA, 1, [Enable UXA support]) @@ -426,6 +444,8 @@ fi PKG_CHECK_MODULES(XORG, [xorg-server >= $required_xorg_server_version xproto fontsproto pixman-1 >= $required_pixman_version $REQUIRED_MODULES]) ABI_VERSION=`$PKG_CONFIG --variable=abi_videodrv xorg-server` +XSERVER_VERSION=`$PKG_CONFIG --modversion xorg-server` +PIXMAN_VERSION=`$PKG_CONFIG --modversion pixman-1` if test "x$ONLY_UMS" = "xyes"; then UMS="yes" @@ -519,7 +539,12 @@ AC_MSG_RESULT([$have_dri1]) AM_CONDITIONAL(DRI1, test "x$have_dri1" != "xno") if test "x$have_dri1" != "xno"; then AC_DEFINE(HAVE_DRI1,1,[Enable DRI1 driver support]) - dri_msg="$dri_msg DRI1" + str="DRI1" + if test "x$DRI_DEFAULT" = "x1"; then + AC_DEFINE(DEFAULT_DRI_LEVEL,1,[Default DRI level]) + str="*$str" + fi + dri_msg="$dri_msg $str" else DRI1_CFLAGS="" DRI1_LIBS="" @@ -576,7 +601,12 @@ AM_CONDITIONAL(DRI2, test "x$have_dri2" != "xno") AC_MSG_RESULT([$have_dri2]) if test "x$have_dri2" != "xno"; then AC_DEFINE(HAVE_DRI2,1,[Enable DRI2 driver support]) - dri_msg="$dri_msg DRI2" + str="DRI2" + if test "x$DRI_DEFAULT" = "x2"; then + AC_DEFINE(DEFAULT_DRI_LEVEL,2,[Default DRI level]) + str="*$str" + fi + dri_msg="$dri_msg $str" else if test "x$DRI" = "xyes" -a "x$DRI2" != "xno" -a "x$KMS" = "xyes"; then AC_MSG_ERROR([DRI2 requested but prerequisites not found]) @@ -591,13 +621,21 @@ AM_CONDITIONAL(DRI3, test "x$have_dri3" != "xno") AC_MSG_RESULT([$have_dri3]) if test "x$have_dri3" != "xno"; then AC_DEFINE(HAVE_DRI3,1,[Enable DRI3 driver support]) - dri_msg="$dri_msg DRI3" + str="DRI3" + if test "x$DRI_DEFAULT" = "x3"; then + AC_DEFINE(DEFAULT_DRI_LEVEL,3,[Default DRI level]) + str="*$str" + fi + dri_msg="$dri_msg $str" else if test "x$DRI" = "xyes" -a "x$DRI3" != "xno" -a "x$KMS" = "xyes"; then AC_MSG_ERROR([DRI3 requested but prerequisites not found]) fi fi +AC_MSG_CHECKING([default DRI support]) +AC_MSG_RESULT([$DEFAULT_DRI_DEFAULT]) + AC_CHECK_HEADERS([X11/extensions/dpmsconst.h]) PRESENT="no" @@ -711,27 +749,6 @@ if test "x$TEARFREE" = "xyes"; then xp_msg="$xp_msg TearFree" fi -AC_ARG_ENABLE(rendernode, - AS_HELP_STRING([--enable-rendernode], - [Enable use of render nodes (experimental) [default=no]]), - [RENDERNODE="$enableval"], - [RENDERNODE="no"]) -AM_CONDITIONAL(USE_RENDERNODE, test "x$RENDERNODE" = "xyes") -if test "x$RENDERNODE" = "xyes"; then - AC_DEFINE(USE_RENDERNODE,1,[Assume "rendernode" support]) - xp_msg="$xp_msg rendernode" -fi - -AC_ARG_ENABLE(wc-mmap, - AS_HELP_STRING([--enable-wc-mmap], - [Enable use of WriteCombining mmaps [default=no]]), - [WC_MMAP="$enableval"], - [WC_MMAP="no"]) -if test "x$WC_MMAP" = "xyes"; then - AC_DEFINE(USE_WC_MMAP,1,[Enable use of WriteCombining mmaps]) - xp_msg="$xp_msg mmap(wc)" -fi - AC_ARG_ENABLE(create2, AS_HELP_STRING([--enable-create2], [Enable use of create2 ioctl (experimental) [default=no]]), @@ -848,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 ]) @@ -855,7 +873,7 @@ AC_OUTPUT echo "" echo "" -test -e `pwd $0`/README && cat `pwd $0`/README +cat $srcdir/README accel_msg="" if test "x$SNA" != "xno"; then @@ -895,13 +913,15 @@ fi echo "" echo "AC_PACKAGE_STRING will be compiled with:" -echo " Xorg Video ABI version: $ABI_VERSION" +echo " Xorg Video ABI version: $ABI_VERSION (xorg-server-$XSERVER_VERSION)" +echo " pixman version: pixman-1-$PIXMAN_VERSION" echo " Acceleration backends:$accel_msg" echo " Additional debugging support?$debug_msg" echo " Support for Kernel Mode Setting? $KMS" echo " Support for legacy User Mode Setting (for i810)? $UMS" echo " Support for Direct Rendering Infrastructure:$dri_msg" echo " Support for Xv motion compensation (XvMC and libXvMC):$xvmc_msg" +echo " Support for display hotplug notifications (udev):$udev_msg" echo " Build additional tools and utilities?$tools_msg" if test -n "$xp_msg"; then echo " Experimental support:$xp_msg" diff --git a/libobj/alloca.c b/libobj/alloca.c new file mode 100644 index 0000000..883e1e9 --- /dev/null +++ b/libobj/alloca.c @@ -0,0 +1,4 @@ +void *alloca(size_t sz) +{ + return NULL; +} diff --git a/man/intel.man b/man/intel.man index 1751520..8da496e 100644 --- a/man/intel.man +++ b/man/intel.man @@ -112,8 +112,8 @@ The default is 8192 if AGP allocable memory is < 128 MB, 16384 if < 192 MB, 24576 if higher. DRI require at least a value of 16384. Higher values may give better 3D performance, at expense of available system memory. .TP -.BI "Option \*qNoAccel\*q \*q" boolean \*q -Disable or enable acceleration. +.BI "Option \*qAccel\*q \*q" boolean \*q +Enable or disable acceleration. .IP Default: acceleration is enabled. @@ -122,8 +122,8 @@ The following driver .B Options are supported for the 830M and later chipsets: .TP -.BI "Option \*qNoAccel\*q \*q" boolean \*q -Disable or enable acceleration. +.BI "Option \*qAccel\*q \*q" boolean \*q +Enable or disable acceleration. .IP Default: acceleration is enabled. .TP @@ -201,6 +201,16 @@ that choice by specifying the entry under /sys/class/backlight to use. .IP Default: Automatic selection. .TP +.BI "Option \*qCustomEDID\*q \*q" string \*q +Override the probed EDID on particular outputs. Sometimes the manufacturer +supplied EDID is corrupt or lacking a few usable modes and supplying a +corrected EDID may be easier than specifying every modeline. This option +allows to pass the path to load an EDID from per output. The format is a +comma separated string of output:path pairs, e.g. +DP1:/path/to/dp1.edid,DP2:/path/to/dp2.edid +.IP +Default: No override, use manufacturer supplied EDIDs. +.TP .BI "Option \*qFallbackDebug\*q \*q" boolean \*q Enable printing of debugging information on acceleration fallbacks to the server log. @@ -225,6 +235,15 @@ i.e. perform synchronous rendering. .IP Default: Disabled .TP +.BI "Option \*qHWRotation\*q \*q" boolean \*q +Override the use of native hardware rotation and force the use of software, +but GPU accelerated where possible, rotation. On some platforms the hardware +can scanout directly into a rotated output bypassing the intermediate rendering +and extra allocations required for software implemented rotation (i.e. native +rotation uses less resources, is quicker and uses less power). This allows you +to disable the native rotation in case of errors. +.IP +Default: Enabled (use hardware rotation) .TP .BI "Option \*qVSync\*q \*q" boolean \*q This option controls the use of commands to synchronise rendering with the @@ -324,13 +343,29 @@ Default: 0 .BI "Option \*qZaphodHeads\*q \*q" string \*q .IP Specify the randr output(s) to use with zaphod mode for a particular driver -instance. If you this option you must use it with all instances of the -driver +instance. If you set this option you must use it with all instances of the +driver. By default, each head is assigned only one CRTC (which limits +using multiple outputs with that head to cloned mode). CRTC can be manually +assigned to individual heads by preceding the output names with a comma +delimited list of pipe numbers followed by a colon. Note that different pipes +may be limited in their functionality and some outputs may only work with +different pipes. .br For example: + +.RS .B Option \*qZaphodHeads\*q \*qLVDS1,VGA1\*q -will assign xrandr outputs LVDS1 and VGA0 to this instance of the driver. + +will assign xrandr outputs LVDS1 and VGA1 to this instance of the driver. +.RE + +.RS +.B +Option \*qZaphodHeads\*q \*q0,2:HDMI1,DP2\*q + +will assign xrandr outputs HDMI1 and DP2 and CRTCs 0 and 2 to this instance of the driver. +.RE .SH OUTPUT CONFIGURATION On 830M and better chipsets, the driver supports runtime configuration of @@ -431,11 +466,11 @@ First DVI SDVO output Second DVI SDVO output .SS "TMDS-1", "TMDS-2", "HDMI-1", "HDMI-2" -DVI/HDMI outputs. Avaliable common properties include: +DVI/HDMI outputs. Available common properties include: .TP \fBBROADCAST_RGB\fP - method used to set RGB color range Adjusting this property allows you to set RGB color range on each -channel in order to match HDTV requirment(default 0 for full +channel in order to match HDTV requirement(default 0 for full range). Setting 1 means RGB color range is 16-235, 0 means RGB color range is 0-255 on each channel. (Full range is 0-255, not 16-235) diff --git a/src/backlight.c b/src/backlight.c index 9f23986..d020a7c 100644 --- a/src/backlight.c +++ b/src/backlight.c @@ -34,6 +34,12 @@ #include #include +#if MAJOR_IN_MKDEV +#include +#elif MAJOR_IN_SYSMACROS +#include +#endif + #include #include #include @@ -84,7 +90,7 @@ void backlight_init(struct backlight *b) b->has_power = 0; } -#ifdef __OpenBSD__ +#ifdef HAVE_DEV_WSCONS_WSCONSIO_H #include #include @@ -146,12 +152,9 @@ int backlight_open(struct backlight *b, char *iface) return param.curval; } -enum backlight_type backlight_exists(const char *iface) +int backlight_exists(const char *iface) { - if (iface != NULL) - return BL_NONE; - - return BL_PLATFORM; + return iface == NULL; } int backlight_on(struct backlight *b) @@ -244,10 +247,10 @@ static const char *known_interfaces[] = { "intel_backlight", }; -static enum backlight_type __backlight_type(const char *iface) +static int __backlight_type(const char *iface) { char buf[1024]; - int fd, v; + int fd, v, i; v = -1; fd = __backlight_open(iface, "type", O_RDONLY); @@ -261,39 +264,41 @@ static enum backlight_type __backlight_type(const char *iface) buf[v] = '\0'; if (strcmp(buf, "raw") == 0) - v = BL_RAW; + v = BL_RAW << 8; else if (strcmp(buf, "platform") == 0) - v = BL_PLATFORM; + v = BL_PLATFORM << 8; else if (strcmp(buf, "firmware") == 0) - v = BL_FIRMWARE; + v = BL_FIRMWARE << 8; else - v = BL_NAMED; + v = BL_NAMED << 8; } else - v = BL_NAMED; + v = BL_NAMED << 8; - if (v == BL_NAMED) { - int i; - for (i = 0; i < ARRAY_SIZE(known_interfaces); i++) { - if (strcmp(iface, known_interfaces[i]) == 0) - break; - } - v += i; + for (i = 0; i < ARRAY_SIZE(known_interfaces); i++) { + if (strcmp(iface, known_interfaces[i]) == 0) + break; } + v += i; return v; } -enum backlight_type backlight_exists(const char *iface) +static int __backlight_exists(const char *iface) { if (__backlight_read(iface, "brightness") < 0) - return BL_NONE; + return -1; if (__backlight_read(iface, "max_brightness") <= 0) - return BL_NONE; + return -1; return __backlight_type(iface); } +int backlight_exists(const char *iface) +{ + return __backlight_exists(iface) != -1; +} + static int __backlight_init(struct backlight *b, char *iface, int fd) { b->fd = fd_move_cloexec(fd_set_nonblock(fd)); @@ -399,7 +404,10 @@ __backlight_find(void) continue; /* Fallback to priority list of known iface for old kernels */ - v = backlight_exists(de->d_name); + v = __backlight_exists(de->d_name); + if (v < 0) + continue; + if (v < best_type) { char *copy = strdup(de->d_name); if (copy) { @@ -416,14 +424,17 @@ __backlight_find(void) int backlight_open(struct backlight *b, char *iface) { - int level; + int level, type; if (iface == NULL) iface = __backlight_find(); if (iface == NULL) goto err; - b->type = __backlight_type(iface); + type = __backlight_type(iface); + if (type < 0) + goto err; + b->type = type >> 8; b->max = __backlight_read(iface, "max_brightness"); if (b->max <= 0) @@ -517,7 +528,7 @@ void backlight_disable(struct backlight *b) void backlight_close(struct backlight *b) { backlight_disable(b); - if (b->pid) + if (b->pid > 0) waitpid(b->pid, NULL, 0); } @@ -543,7 +554,10 @@ char *backlight_find_for_device(struct pci_device *pci) if (*de->d_name == '.') continue; - v = backlight_exists(de->d_name); + v = __backlight_exists(de->d_name); + if (v < 0) + continue; + if (v < best_type) { char *copy = strdup(de->d_name); if (copy) { diff --git a/src/backlight.h b/src/backlight.h index bb0e28b..ba17755 100644 --- a/src/backlight.h +++ b/src/backlight.h @@ -43,7 +43,7 @@ struct backlight { int pid, fd; }; -enum backlight_type backlight_exists(const char *iface); +int backlight_exists(const char *iface); void backlight_init(struct backlight *backlight); int backlight_open(struct backlight *backlight, char *iface); diff --git a/src/compat-api.h b/src/compat-api.h index d09e1fb..293e9d7 100644 --- a/src/compat-api.h +++ b/src/compat-api.h @@ -39,7 +39,13 @@ #ifndef XF86_HAS_SCRN_CONV #define xf86ScreenToScrn(s) xf86Screens[(s)->myNum] +#if XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(1,1,0,0,0) #define xf86ScrnToScreen(s) screenInfo.screens[(s)->scrnIndex] +#else +#define xf86ScrnToScreen(s) ((s)->pScreen) +#endif +#else +#define xf86ScrnToScreen(s) ((s)->pScreen) #endif #ifndef XF86_SCRN_INTERFACE @@ -131,6 +137,17 @@ region_rects(const RegionRec *r) return r->data ? (const BoxRec *)(r->data + 1) : &r->extents; } +inline static void +region_get_boxes(const RegionRec *r, const BoxRec **s, const BoxRec **e) +{ + int n; + if (r->data) + *s = region_boxptr(r), n = r->data->numRects; + else + *s = &r->extents, n = 1; + *e = *s + n; +} + #ifndef INCLUDE_LEGACY_REGION_DEFINES #define RegionCreate(r, s) REGION_CREATE(NULL, r, s) #define RegionBreak(r) REGION_BREAK(NULL, r) @@ -223,4 +240,14 @@ static inline void FreePixmap(PixmapPtr pixmap) dstx, dsty) #endif +#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,12,99,901,0) +#define isGPU(S) (S)->is_gpu +#else +#define isGPU(S) 0 +#endif + +#endif + +#if HAS_DIRTYTRACKING_ROTATION +#define PixmapSyncDirtyHelper(d, dd) PixmapSyncDirtyHelper(d) #endif diff --git a/src/i915_pciids.h b/src/i915_pciids.h index 180ad0e..f1a113e 100644 --- a/src/i915_pciids.h +++ b/src/i915_pciids.h @@ -208,40 +208,41 @@ #define INTEL_VLV_D_IDS(info) \ INTEL_VGA_DEVICE(0x0155, info) -#define _INTEL_BDW_M(gt, id, info) \ - INTEL_VGA_DEVICE((((gt) - 1) << 4) | (id), info) -#define _INTEL_BDW_D(gt, id, info) \ - INTEL_VGA_DEVICE((((gt) - 1) << 4) | (id), info) - -#define _INTEL_BDW_M_IDS(gt, info) \ - _INTEL_BDW_M(gt, 0x1602, info), /* ULT */ \ - _INTEL_BDW_M(gt, 0x1606, info), /* ULT */ \ - _INTEL_BDW_M(gt, 0x160B, info), /* Iris */ \ - _INTEL_BDW_M(gt, 0x160E, info) /* ULX */ - -#define _INTEL_BDW_D_IDS(gt, info) \ - _INTEL_BDW_D(gt, 0x160A, info), /* Server */ \ - _INTEL_BDW_D(gt, 0x160D, info) /* Workstation */ - -#define INTEL_BDW_GT12M_IDS(info) \ - _INTEL_BDW_M_IDS(1, info), \ - _INTEL_BDW_M_IDS(2, info) +#define INTEL_BDW_GT12M_IDS(info) \ + INTEL_VGA_DEVICE(0x1602, info), /* GT1 ULT */ \ + INTEL_VGA_DEVICE(0x1606, info), /* GT1 ULT */ \ + INTEL_VGA_DEVICE(0x160B, info), /* GT1 Iris */ \ + INTEL_VGA_DEVICE(0x160E, info), /* GT1 ULX */ \ + INTEL_VGA_DEVICE(0x1612, info), /* GT2 Halo */ \ + INTEL_VGA_DEVICE(0x1616, info), /* GT2 ULT */ \ + INTEL_VGA_DEVICE(0x161B, info), /* GT2 ULT */ \ + INTEL_VGA_DEVICE(0x161E, info) /* GT2 ULX */ #define INTEL_BDW_GT12D_IDS(info) \ - _INTEL_BDW_D_IDS(1, info), \ - _INTEL_BDW_D_IDS(2, info) + INTEL_VGA_DEVICE(0x160A, info), /* GT1 Server */ \ + INTEL_VGA_DEVICE(0x160D, info), /* GT1 Workstation */ \ + INTEL_VGA_DEVICE(0x161A, info), /* GT2 Server */ \ + INTEL_VGA_DEVICE(0x161D, info) /* GT2 Workstation */ #define INTEL_BDW_GT3M_IDS(info) \ - _INTEL_BDW_M_IDS(3, info) + INTEL_VGA_DEVICE(0x1622, info), /* ULT */ \ + INTEL_VGA_DEVICE(0x1626, info), /* ULT */ \ + INTEL_VGA_DEVICE(0x162B, info), /* Iris */ \ + INTEL_VGA_DEVICE(0x162E, info) /* ULX */ #define INTEL_BDW_GT3D_IDS(info) \ - _INTEL_BDW_D_IDS(3, info) + INTEL_VGA_DEVICE(0x162A, info), /* Server */ \ + INTEL_VGA_DEVICE(0x162D, info) /* Workstation */ #define INTEL_BDW_RSVDM_IDS(info) \ - _INTEL_BDW_M_IDS(4, info) + INTEL_VGA_DEVICE(0x1632, info), /* ULT */ \ + INTEL_VGA_DEVICE(0x1636, info), /* ULT */ \ + INTEL_VGA_DEVICE(0x163B, info), /* Iris */ \ + INTEL_VGA_DEVICE(0x163E, info) /* ULX */ #define INTEL_BDW_RSVDD_IDS(info) \ - _INTEL_BDW_D_IDS(4, info) + INTEL_VGA_DEVICE(0x163A, info), /* Server */ \ + INTEL_VGA_DEVICE(0x163D, info) /* Workstation */ #define INTEL_BDW_M_IDS(info) \ INTEL_BDW_GT12M_IDS(info), \ @@ -259,21 +260,71 @@ INTEL_VGA_DEVICE(0x22b2, info), \ INTEL_VGA_DEVICE(0x22b3, info) -#define INTEL_SKL_IDS(info) \ - INTEL_VGA_DEVICE(0x1916, info), /* ULT GT2 */ \ +#define INTEL_SKL_GT1_IDS(info) \ INTEL_VGA_DEVICE(0x1906, info), /* ULT GT1 */ \ - INTEL_VGA_DEVICE(0x1926, info), /* ULT GT3 */ \ - INTEL_VGA_DEVICE(0x1921, info), /* ULT GT2F */ \ INTEL_VGA_DEVICE(0x190E, info), /* ULX GT1 */ \ + INTEL_VGA_DEVICE(0x1902, info), /* DT GT1 */ \ + INTEL_VGA_DEVICE(0x190B, info), /* Halo GT1 */ \ + INTEL_VGA_DEVICE(0x190A, info) /* SRV GT1 */ + +#define INTEL_SKL_GT2_IDS(info) \ + INTEL_VGA_DEVICE(0x1916, info), /* ULT GT2 */ \ + INTEL_VGA_DEVICE(0x1921, info), /* ULT GT2F */ \ INTEL_VGA_DEVICE(0x191E, info), /* ULX GT2 */ \ INTEL_VGA_DEVICE(0x1912, info), /* DT GT2 */ \ - INTEL_VGA_DEVICE(0x1902, info), /* DT GT1 */ \ INTEL_VGA_DEVICE(0x191B, info), /* Halo GT2 */ \ - INTEL_VGA_DEVICE(0x192B, info), /* Halo GT3 */ \ - INTEL_VGA_DEVICE(0x190B, info), /* Halo GT1 */ \ INTEL_VGA_DEVICE(0x191A, info), /* SRV GT2 */ \ - INTEL_VGA_DEVICE(0x192A, info), /* SRV GT3 */ \ - INTEL_VGA_DEVICE(0x190A, info), /* SRV GT1 */ \ INTEL_VGA_DEVICE(0x191D, info) /* WKS GT2 */ +#define INTEL_SKL_GT3_IDS(info) \ + INTEL_VGA_DEVICE(0x1926, info), /* ULT GT3 */ \ + INTEL_VGA_DEVICE(0x192B, info), /* Halo GT3 */ \ + INTEL_VGA_DEVICE(0x192A, info) /* SRV GT3 */ \ + +#define INTEL_SKL_IDS(info) \ + INTEL_SKL_GT1_IDS(info), \ + INTEL_SKL_GT2_IDS(info), \ + INTEL_SKL_GT3_IDS(info) + +#define INTEL_BXT_IDS(info) \ + INTEL_VGA_DEVICE(0x0A84, info), \ + INTEL_VGA_DEVICE(0x1A84, info), \ + INTEL_VGA_DEVICE(0x5A84, info) + +#define INTEL_KBL_GT1_IDS(info) \ + INTEL_VGA_DEVICE(0x5913, info), /* ULT GT1.5 */ \ + INTEL_VGA_DEVICE(0x5915, info), /* ULX GT1.5 */ \ + INTEL_VGA_DEVICE(0x5917, info), /* DT GT1.5 */ \ + INTEL_VGA_DEVICE(0x5906, info), /* ULT GT1 */ \ + INTEL_VGA_DEVICE(0x590E, info), /* ULX GT1 */ \ + INTEL_VGA_DEVICE(0x5902, info), /* DT GT1 */ \ + INTEL_VGA_DEVICE(0x590B, info), /* Halo GT1 */ \ + INTEL_VGA_DEVICE(0x590A, info) /* SRV GT1 */ + +#define INTEL_KBL_GT2_IDS(info) \ + INTEL_VGA_DEVICE(0x5916, info), /* ULT GT2 */ \ + INTEL_VGA_DEVICE(0x5921, info), /* ULT GT2F */ \ + INTEL_VGA_DEVICE(0x591E, info), /* ULX GT2 */ \ + INTEL_VGA_DEVICE(0x5912, info), /* DT GT2 */ \ + INTEL_VGA_DEVICE(0x591B, info), /* Halo GT2 */ \ + INTEL_VGA_DEVICE(0x591A, info), /* SRV GT2 */ \ + INTEL_VGA_DEVICE(0x591D, info) /* WKS GT2 */ + +#define INTEL_KBL_GT3_IDS(info) \ + INTEL_VGA_DEVICE(0x5926, info), /* ULT GT3 */ \ + INTEL_VGA_DEVICE(0x592B, info), /* Halo GT3 */ \ + INTEL_VGA_DEVICE(0x592A, info) /* SRV GT3 */ + +#define INTEL_KBL_GT4_IDS(info) \ + INTEL_VGA_DEVICE(0x5932, info), /* DT GT4 */ \ + INTEL_VGA_DEVICE(0x593B, info), /* Halo GT4 */ \ + INTEL_VGA_DEVICE(0x593A, info), /* SRV GT4 */ \ + INTEL_VGA_DEVICE(0x593D, info) /* WKS GT4 */ + +#define INTEL_KBL_IDS(info) \ + INTEL_KBL_GT1_IDS(info), \ + INTEL_KBL_GT2_IDS(info), \ + INTEL_KBL_GT3_IDS(info), \ + INTEL_KBL_GT4_IDS(info) + #endif /* _I915_PCIIDS_H */ diff --git a/src/intel_device.c b/src/intel_device.c index 140e153..54c1443 100644 --- a/src/intel_device.c +++ b/src/intel_device.c @@ -38,6 +38,12 @@ #include #include +#if MAJOR_IN_MKDEV +#include +#elif MAJOR_IN_SYSMACROS +#include +#endif + #include #include @@ -197,9 +203,15 @@ static inline struct intel_device *intel_device(ScrnInfoPtr scrn) return xf86GetEntityPrivate(scrn->entityList[0], intel_device_key)->ptr; } +static const char *kernel_module_names[] ={ + "i915", + NULL, +}; + static int is_i915_device(int fd) { drm_version_t version; + const char **kn; char name[5] = ""; memset(&version, 0, sizeof(version)); @@ -209,7 +221,22 @@ static int is_i915_device(int fd) if (drmIoctl(fd, DRM_IOCTL_VERSION, &version)) return 0; - return strcmp("i915", name) == 0; + for (kn = kernel_module_names; *kn; kn++) + if (strcmp(*kn, name) == 0) + return 1; + + return 0; +} + +static int load_i915_kernel_module(void) +{ + const char **kn; + + for (kn = kernel_module_names; *kn; kn++) + if (xf86LoadKernelModule(*kn) == 0) + return 0; + + return -1; } static int is_i915_gem(int fd) @@ -336,7 +363,7 @@ static int __intel_open_device__pci(const struct pci_device *pci) sprintf(path + base, "driver"); if (stat(path, &st)) { - if (xf86LoadKernelModule("i915")) + if (load_i915_kernel_module()) return -1; (void)xf86LoadKernelModule("fbcon"); } @@ -399,7 +426,7 @@ static int __intel_open_device__legacy(const struct pci_device *pci) ret = drmCheckModesettingSupported(id); if (ret) { - if (xf86LoadKernelModule("i915")) + if (load_i915_kernel_module() == 0) ret = drmCheckModesettingSupported(id); if (ret) return -1; @@ -461,9 +488,9 @@ static int is_render_node(int fd, struct stat *st) static char *find_render_node(int fd) { -#if defined(USE_RENDERNODE) struct stat master, render; char buf[128]; + int i; /* Are we a render-node ourselves? */ if (is_render_node(fd, &master)) @@ -472,9 +499,17 @@ static char *find_render_node(int fd) sprintf(buf, "/dev/dri/renderD%d", (int)((master.st_rdev | 0x80) & 0xbf)); if (stat(buf, &render) == 0 && master.st_mode == render.st_mode && - render.st_rdev == ((master.st_rdev | 0x80) & 0xbf)) + render.st_rdev == (master.st_rdev | 0x80)) return strdup(buf); -#endif + + /* Misaligned card <-> renderD, do a full search */ + for (i = 0; i < 16; i++) { + sprintf(buf, "/dev/dri/renderD%d", i + 128); + if (stat(buf, &render) == 0 && + master.st_mode == render.st_mode && + render.st_rdev == (master.st_rdev | 0x80)) + return strdup(buf); + } return NULL; } @@ -672,6 +707,12 @@ struct intel_device *intel_get_device(ScrnInfoPtr scrn, int *fd) return dev; } +const char *intel_get_master_name(struct intel_device *dev) +{ + assert(dev && dev->master_node); + return dev->master_node; +} + const char *intel_get_client_name(struct intel_device *dev) { assert(dev && dev->render_node); diff --git a/src/intel_driver.h b/src/intel_driver.h index 28ed1a0..fc9beaf 100644 --- a/src/intel_driver.h +++ b/src/intel_driver.h @@ -127,6 +127,7 @@ int intel_open_device(int entity_num, int __intel_peek_fd(ScrnInfoPtr scrn); struct intel_device *intel_get_device(ScrnInfoPtr scrn, int *fd); int intel_has_render_node(struct intel_device *dev); +const char *intel_get_master_name(struct intel_device *dev); const char *intel_get_client_name(struct intel_device *dev); int intel_get_client_fd(struct intel_device *dev); int intel_get_device_id(struct intel_device *dev); diff --git a/src/intel_list.h b/src/intel_list.h index 51af825..c8a3187 100644 --- a/src/intel_list.h +++ b/src/intel_list.h @@ -306,8 +306,7 @@ list_is_empty(const struct list *head) list_entry((ptr)->prev, type, member) #define __container_of(ptr, sample, member) \ - (void *)((char *)(ptr) \ - - ((char *)&(sample)->member - (char *)(sample))) + (void *)((char *)(ptr) - ((char *)&(sample)->member - (char *)(sample))) /** * Loop through the list given by head and set pos to struct in the list. * @@ -392,17 +391,50 @@ static inline void list_move_tail(struct list *list, struct list *head) #define list_last_entry(ptr, type, member) \ list_entry((ptr)->prev, type, member) -#define list_for_each_entry_reverse(pos, head, member) \ +#define list_for_each_entry_reverse(pos, head, member) \ for (pos = __container_of((head)->prev, pos, member); \ &pos->member != (head); \ pos = __container_of(pos->member.prev, pos, member)) #endif +#define list_for_each_entry_safe_from(pos, tmp, head, member) \ + for (tmp = __container_of(pos->member.next, pos, member); \ + &pos->member != (head); \ + pos = tmp, tmp = __container_of(tmp->member.next, tmp, member)) + #undef container_of #define container_of(ptr, type, member) \ ((type *)((char *)(ptr) - (char *) &((type *)0)->member)) +static inline void __list_splice(const struct list *list, + struct list *prev, + struct list *next) +{ + struct list *first = list->next; + struct list *last = list->prev; + + first->prev = prev; + prev->next = first; + + last->next = next; + next->prev = last; +} + +static inline void list_splice(const struct list *list, + struct list *head) +{ + if (!list_is_empty(list)) + __list_splice(list, head, head->next); +} + +static inline void list_splice_tail(const struct list *list, + struct list *head) +{ + if (!list_is_empty(list)) + __list_splice(list, head->prev, head); +} + static inline int list_is_singular(const struct list *list) { return list->next == list->prev; diff --git a/src/intel_module.c b/src/intel_module.c index 102d52a..60835b9 100644 --- a/src/intel_module.c +++ b/src/intel_module.c @@ -126,6 +126,13 @@ static const struct intel_device_info intel_skylake_info = { .gen = 0110, }; +static const struct intel_device_info intel_broxton_info = { + .gen = 0111, +}; + +static const struct intel_device_info intel_kabylake_info = { + .gen = 0112, +}; static const SymTabRec intel_chipsets[] = { {PCI_CHIP_I810, "i810"}, @@ -234,30 +241,36 @@ static const SymTabRec intel_chipsets[] = { {0x0157, "HD Graphics"}, /* Broadwell Marketing names */ - {0x1602, "HD graphics"}, - {0x1606, "HD graphics"}, - {0x160B, "HD graphics"}, - {0x160A, "HD graphics"}, - {0x160D, "HD graphics"}, - {0x160E, "HD graphics"}, - {0x1612, "HD graphics 5600"}, - {0x1616, "HD graphics 5500"}, - {0x161B, "HD graphics"}, - {0x161A, "HD graphics"}, - {0x161D, "HD graphics"}, - {0x161E, "HD graphics 5300"}, - {0x1622, "Iris Pro graphics 6200"}, - {0x1626, "HD graphics 6000"}, - {0x162B, "Iris graphics 6100"}, - {0x162A, "Iris Pro graphics P6300"}, - {0x162D, "HD graphics"}, - {0x162E, "HD graphics"}, - {0x1632, "HD graphics"}, - {0x1636, "HD graphics"}, - {0x163B, "HD graphics"}, - {0x163A, "HD graphics"}, - {0x163D, "HD graphics"}, - {0x163E, "HD graphics"}, + {0x1602, "HD Graphics"}, + {0x1606, "HD Graphics"}, + {0x160B, "HD Graphics"}, + {0x160A, "HD Graphics"}, + {0x160D, "HD Graphics"}, + {0x160E, "HD Graphics"}, + {0x1612, "HD Graphics 5600"}, + {0x1616, "HD Graphics 5500"}, + {0x161B, "HD Graphics"}, + {0x161A, "HD Graphics"}, + {0x161D, "HD Graphics"}, + {0x161E, "HD Graphics 5300"}, + {0x1622, "Iris Pro Graphics 6200"}, + {0x1626, "HD Graphics 6000"}, + {0x162B, "Iris Graphics 6100"}, + {0x162A, "Iris Pro Graphics P6300"}, + {0x162D, "HD Graphics"}, + {0x162E, "HD Graphics"}, + {0x1632, "HD Graphics"}, + {0x1636, "HD Graphics"}, + {0x163B, "HD Graphics"}, + {0x163A, "HD Graphics"}, + {0x163D, "HD Graphics"}, + {0x163E, "HD Graphics"}, + + /* Cherryview (Cherrytrail/Braswell) */ + {0x22b0, "HD Graphics"}, + {0x22b1, "HD Graphics"}, + {0x22b2, "HD Graphics"}, + {0x22b3, "HD Graphics"}, /* When adding new identifiers, also update: * 1. intel_identify() @@ -318,6 +331,10 @@ static const struct pci_id_match intel_device_match[] = { INTEL_SKL_IDS(&intel_skylake_info), + INTEL_BXT_IDS(&intel_broxton_info), + + INTEL_KBL_IDS(&intel_kabylake_info), + INTEL_VGA_DEVICE(PCI_MATCH_ANY, &intel_generic_info), #endif @@ -508,6 +525,9 @@ static enum accel_method { NOACCEL, SNA, UXA } get_accel_method(void) if (hosted()) return SNA; + if (xf86configptr == NULL) /* X -configure */ + return SNA; + dev = _xf86findDriver("intel", xf86configptr->conf_device_lst); if (dev && dev->dev_option_lst) { const char *s; @@ -582,10 +602,17 @@ intel_scrn_create(DriverPtr driver, case NOACCEL: #endif case UXA: - return intel_init_scrn(scrn); + return intel_init_scrn(scrn); #endif - default: break; + default: +#if USE_SNA + return sna_init_scrn(scrn, entity_num); +#elif USE_UXA + return intel_init_scrn(scrn); +#else + break; +#endif } #endif diff --git a/src/intel_options.c b/src/intel_options.c index ff8541a..7f253ac 100644 --- a/src/intel_options.c +++ b/src/intel_options.c @@ -2,18 +2,24 @@ #include "config.h" #endif +#include +#include +#include + #include "intel_options.h" const OptionInfoRec intel_options[] = { - {OPTION_ACCEL_DISABLE, "NoAccel", OPTV_BOOLEAN, {0}, 0}, + {OPTION_ACCEL_ENABLE, "Accel", OPTV_BOOLEAN, {0}, 0}, {OPTION_ACCEL_METHOD, "AccelMethod", OPTV_STRING, {0}, 0}, {OPTION_BACKLIGHT, "Backlight", OPTV_STRING, {0}, 0}, + {OPTION_EDID, "CustomEDID", OPTV_STRING, {0}, 0}, {OPTION_DRI, "DRI", OPTV_STRING, {0}, 0}, {OPTION_PRESENT, "Present", OPTV_BOOLEAN, {0}, 1}, {OPTION_COLOR_KEY, "ColorKey", OPTV_INTEGER, {0}, 0}, {OPTION_VIDEO_KEY, "VideoKey", OPTV_INTEGER, {0}, 0}, {OPTION_TILING_2D, "Tiling", OPTV_BOOLEAN, {0}, 1}, {OPTION_TILING_FB, "LinearFramebuffer", OPTV_BOOLEAN, {0}, 0}, + {OPTION_ROTATION, "HWRotation", OPTV_BOOLEAN, {0}, 1}, {OPTION_VSYNC, "VSync", OPTV_BOOLEAN, {0}, 1}, {OPTION_PAGEFLIP, "PageFlip", OPTV_BOOLEAN, {0}, 1}, {OPTION_SWAPBUFFERS_WAIT, "SwapbuffersWait", OPTV_BOOLEAN, {0}, 1}, @@ -21,7 +27,6 @@ const OptionInfoRec intel_options[] = { {OPTION_PREFER_OVERLAY, "XvPreferOverlay", OPTV_BOOLEAN, {0}, 0}, {OPTION_HOTPLUG, "HotPlug", OPTV_BOOLEAN, {0}, 1}, {OPTION_REPROBE, "ReprobeOutputs", OPTV_BOOLEAN, {0}, 0}, - {OPTION_DELETE_DP12, "DeleteUnusedDP12Displays", OPTV_BOOLEAN, {0}, 0}, #ifdef INTEL_XVMC {OPTION_XVMC, "XvMC", OPTV_BOOLEAN, {0}, 1}, #endif @@ -54,3 +59,85 @@ OptionInfoPtr intel_options_get(ScrnInfoPtr scrn) return options; } + +Bool intel_option_cast_to_bool(OptionInfoPtr options, int id, Bool val) +{ +#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,7,99,901,0) + xf86getBoolValue(&val, xf86GetOptValString(options, id)); +#endif + return val; +} + +static int +namecmp(const char *s1, const char *s2) +{ + char c1, c2; + + if (!s1 || *s1 == 0) { + if (!s2 || *s2 == 0) + return 0; + else + return 1; + } + + while (*s1 == '_' || *s1 == ' ' || *s1 == '\t') + s1++; + + while (*s2 == '_' || *s2 == ' ' || *s2 == '\t') + s2++; + + c1 = isupper(*s1) ? tolower(*s1) : *s1; + c2 = isupper(*s2) ? tolower(*s2) : *s2; + while (c1 == c2) { + if (c1 == '\0') + return 0; + + s1++; + while (*s1 == '_' || *s1 == ' ' || *s1 == '\t') + s1++; + + s2++; + while (*s2 == '_' || *s2 == ' ' || *s2 == '\t') + s2++; + + c1 = isupper(*s1) ? tolower(*s1) : *s1; + c2 = isupper(*s2) ? tolower(*s2) : *s2; + } + + return c1 - c2; +} + +unsigned intel_option_cast_to_unsigned(OptionInfoPtr options, int id, unsigned val) +{ +#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,7,99,901,0) + const char *str = xf86GetOptValString(options, id); +#else + const char *str = NULL; +#endif + unsigned v; + + if (str == NULL || *str == '\0') + return val; + + if (namecmp(str, "on") == 0) + return val; + if (namecmp(str, "true") == 0) + return val; + if (namecmp(str, "yes") == 0) + return val; + + if (namecmp(str, "0") == 0) + return 0; + if (namecmp(str, "off") == 0) + return 0; + if (namecmp(str, "false") == 0) + return 0; + if (namecmp(str, "no") == 0) + return 0; + + v = atoi(str); + if (v) + return v; + + return val; +} diff --git a/src/intel_options.h b/src/intel_options.h index 7e2cbd9..43635f1 100644 --- a/src/intel_options.h +++ b/src/intel_options.h @@ -12,15 +12,17 @@ */ enum intel_options { - OPTION_ACCEL_DISABLE, + OPTION_ACCEL_ENABLE, OPTION_ACCEL_METHOD, OPTION_BACKLIGHT, + OPTION_EDID, OPTION_DRI, OPTION_PRESENT, OPTION_VIDEO_KEY, OPTION_COLOR_KEY, OPTION_TILING_2D, OPTION_TILING_FB, + OPTION_ROTATION, OPTION_VSYNC, OPTION_PAGEFLIP, OPTION_SWAPBUFFERS_WAIT, @@ -28,7 +30,6 @@ enum intel_options { OPTION_PREFER_OVERLAY, OPTION_HOTPLUG, OPTION_REPROBE, - OPTION_DELETE_DP12, #if defined(XvMCExtension) && defined(ENABLE_XVMC) OPTION_XVMC, #define INTEL_XVMC 1 @@ -51,5 +52,7 @@ enum intel_options { extern const OptionInfoRec intel_options[]; OptionInfoPtr intel_options_get(ScrnInfoPtr scrn); +unsigned intel_option_cast_to_unsigned(OptionInfoPtr, int id, unsigned val); +Bool intel_option_cast_to_bool(OptionInfoPtr, int id, Bool val); #endif /* INTEL_OPTIONS_H */ diff --git a/src/legacy/i810/i810_common.h b/src/legacy/i810/i810_common.h index 4cc10e8..8355708 100644 --- a/src/legacy/i810/i810_common.h +++ b/src/legacy/i810/i810_common.h @@ -52,7 +52,7 @@ #define ALIGN(i,m) (((i) + (m) - 1) & ~((m) - 1)) -/* Using usleep() makes things noticably slow. */ +/* Using usleep() makes things noticeably slow. */ #if 0 #define DELAY(x) usleep(x) #else @@ -185,7 +185,7 @@ enum { * - zbuffer linear offset and pitch -- also invarient * - drawing origin in back and depth buffers. * - * Keep the depth/back buffer state here to acommodate private buffers + * Keep the depth/back buffer state here to accommodate private buffers * in the future. */ #define I810_DESTREG_DI0 0 /* CMD_OP_DESTBUFFER_INFO (2 dwords) */ diff --git a/src/legacy/i810/i810_hwmc.c b/src/legacy/i810/i810_hwmc.c index 7cb9c1a..58661b0 100644 --- a/src/legacy/i810/i810_hwmc.c +++ b/src/legacy/i810/i810_hwmc.c @@ -171,7 +171,7 @@ static XF86MCAdaptorPtr ppAdapt[1] = * * I810InitMC * - * Initialize the hardware motion compenstation extention for this + * Initialize the hardware motion compensation extension for this * hardware. The initialization routines want the address of the pointers * to the structures, not the address of the structures. This means we * allocate (or create static?) the pointer memory and pass that diff --git a/src/legacy/i810/i810_memory.c b/src/legacy/i810/i810_memory.c index c3de277..6f27483 100644 --- a/src/legacy/i810/i810_memory.c +++ b/src/legacy/i810/i810_memory.c @@ -76,7 +76,7 @@ I810AllocateGARTMemory(ScrnInfoPtr pScrn) unsigned long size = pScrn->videoRam * 1024UL; I810Ptr pI810 = I810PTR(pScrn); int key; - long tom = 0; + unsigned long tom = 0; unsigned long physical; if (!xf86AgpGARTSupported() || !xf86AcquireGART(pScrn->scrnIndex)) { @@ -132,8 +132,8 @@ I810AllocateGARTMemory(ScrnInfoPtr pScrn) * Keep it 512K aligned for the sake of tiled regions. */ - tom += 0x7ffff; - tom &= ~0x7ffff; + tom += 0x7ffffUL; + tom &= ~0x7ffffUL; if ((key = xf86AllocateGARTMemory(pScrn->scrnIndex, size, 1, NULL)) != -1) { pI810->DcacheOffset = tom; diff --git a/src/legacy/i810/i810_reg.h b/src/legacy/i810/i810_reg.h index 54faeb3..fa091c5 100644 --- a/src/legacy/i810/i810_reg.h +++ b/src/legacy/i810/i810_reg.h @@ -245,7 +245,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * not sure they refer to local (graphics) memory. * * These details are for the local memory control registers, - * (pp301-310). The test machines are not equiped with local memory, + * (pp301-310). The test machines are not equipped with local memory, * so nothing is tested. Only a single row seems to be supported. */ #define DRAM_ROW_TYPE 0x3000 diff --git a/src/legacy/i810/xvmc/I810XvMC.c b/src/legacy/i810/xvmc/I810XvMC.c index e6b63d3..a538e99 100644 --- a/src/legacy/i810/xvmc/I810XvMC.c +++ b/src/legacy/i810/xvmc/I810XvMC.c @@ -61,7 +61,7 @@ static int event_base; // Arguments: pI810XvMC private data structure from the current context. // Notes: We faked the drmMapBufs for the i810's security so now we have // to insert an allocated page into the correct spot in the faked -// list to keep up appearences. +// list to keep up appearances. // Concept for this function was taken from Mesa sources. // Returns: drmBufPtr containing the information about the allocated page. ***************************************************************************/ @@ -188,7 +188,7 @@ _X_EXPORT Status XvMCCreateContext(Display *display, XvPortID port, /* Check for drm */ if(! drmAvailable()) { - printf("Direct Rendering is not avilable on this system!\n"); + printf("Direct Rendering is not available on this system!\n"); return BadAlloc; } @@ -3279,7 +3279,7 @@ _X_EXPORT Status XvMCSyncSurface(Display *display,XvMCSurface *surface) { // display - Connection to X server // surface - Surface to flush // Info: -// This command is a noop for i810 becuase we always dispatch buffers in +// This command is a noop for i810 because we always dispatch buffers in // render. There is little gain to be had with 4k buffers. // Returns: Status ***************************************************************************/ diff --git a/src/render_program/exa_wm.g4i b/src/render_program/exa_wm.g4i index 5d3d45b..587b581 100644 --- a/src/render_program/exa_wm.g4i +++ b/src/render_program/exa_wm.g4i @@ -57,7 +57,7 @@ define(`mask_dw_dy', `g6.4<0,1,0>F') define(`mask_wo', `g6.12<0,1,0>F') /* - * Local variables. Pairs must be aligned on even reg boundry + * Local variables. Pairs must be aligned on even reg boundary */ /* this holds the X dest coordinates */ diff --git a/src/render_program/exa_wm_yuv_rgb.g8a b/src/render_program/exa_wm_yuv_rgb.g8a index 7def093..34973ba 100644 --- a/src/render_program/exa_wm_yuv_rgb.g8a +++ b/src/render_program/exa_wm_yuv_rgb.g8a @@ -76,7 +76,7 @@ add (16) Cbn<1>F Cb<8,8,1>F -0.501961F { compr align1 }; /* * R = Y + Cr * 1.596 */ -mov (8) acc0<1>F Yn<8,8,1>F { compr align1 }; +mov (8) acc0<1>F Yn_01<8,8,1>F { compr align1 }; mac.sat(8) src_sample_r_01<1>F Crn_01<8,8,1>F 1.596F { compr align1 }; mov (8) acc0<1>F Yn_23<8,8,1>F { compr align1 }; @@ -84,7 +84,7 @@ mac.sat(8) src_sample_r_23<1>F Crn_23<8,8,1>F 1.596F { compr align1 }; /* * G = Crn * -0.813 + Cbn * -0.392 + Y */ -mov (8) acc0<1>F Yn_23<8,8,1>F { compr align1 }; +mov (8) acc0<1>F Yn_01<8,8,1>F { compr align1 }; mac (8) acc0<1>F Crn_01<8,8,1>F -0.813F { compr align1 }; mac.sat(8) src_sample_g_01<1>F Cbn_01<8,8,1>F -0.392F { compr align1 }; diff --git a/src/render_program/exa_wm_yuv_rgb.g8b b/src/render_program/exa_wm_yuv_rgb.g8b index 4494953..2cd6fc4 100644 --- a/src/render_program/exa_wm_yuv_rgb.g8b +++ b/src/render_program/exa_wm_yuv_rgb.g8b @@ -6,7 +6,7 @@ { 0x80600048, 0x21c03ae8, 0x3e8d02c0, 0x3fcc49ba }, { 0x00600001, 0x24003ae0, 0x008d0320, 0x00000000 }, { 0x80600048, 0x21e03ae8, 0x3e8d02e0, 0x3fcc49ba }, - { 0x00600001, 0x24003ae0, 0x008d0320, 0x00000000 }, + { 0x00600001, 0x24003ae0, 0x008d0300, 0x00000000 }, { 0x00600048, 0x24003ae0, 0x3e8d02c0, 0xbf5020c5 }, { 0x80600048, 0x22003ae8, 0x3e8d0340, 0xbec8b439 }, { 0x00600001, 0x24003ae0, 0x008d0320, 0x00000000 }, diff --git a/src/sna/blt.c b/src/sna/blt.c index b5bfee6..2dae9c2 100644 --- a/src/sna/blt.c +++ b/src/sna/blt.c @@ -30,6 +30,7 @@ #endif #include "sna.h" +#include #if __x86_64__ #define USE_SSE2 1 @@ -333,420 +334,270 @@ memcpy_from_tiled_x__swizzle_0(const void *src, void *dst, int bpp, } } -fast_memcpy static void -memcpy_to_tiled_x__swizzle_9(const void *src, void *dst, int bpp, - int32_t src_stride, int32_t dst_stride, - int16_t src_x, int16_t src_y, - int16_t dst_x, int16_t dst_y, - uint16_t width, uint16_t height) -{ - const unsigned tile_width = 512; - const unsigned tile_height = 8; - const unsigned tile_size = 4096; - - const unsigned cpp = bpp / 8; - const unsigned stride_tiles = dst_stride / tile_width; - const unsigned swizzle_pixels = 64 / cpp; - const unsigned tile_pixels = ffs(tile_width / cpp) - 1; - const unsigned tile_mask = (1 << tile_pixels) - 1; - - unsigned x, y; - - DBG(("%s(bpp=%d): src=(%d, %d), dst=(%d, %d), size=%dx%d, pitch=%d/%d\n", - __FUNCTION__, bpp, src_x, src_y, dst_x, dst_y, width, height, src_stride, dst_stride)); - - src = (const uint8_t *)src + src_y * src_stride + src_x * cpp; - - for (y = 0; y < height; ++y) { - const uint32_t dy = y + dst_y; - const uint32_t tile_row = - (dy / tile_height * stride_tiles * tile_size + - (dy & (tile_height-1)) * tile_width); - const uint8_t *src_row = (const uint8_t *)src + src_stride * y; - uint32_t dx = dst_x, offset; - - x = width * cpp; - if (dx & (swizzle_pixels - 1)) { - const uint32_t swizzle_bound_pixels = ALIGN(dx + 1, swizzle_pixels); - const uint32_t length = min(dst_x + width, swizzle_bound_pixels) - dx; - offset = tile_row + - (dx >> tile_pixels) * tile_size + - (dx & tile_mask) * cpp; - offset ^= (offset >> 3) & 64; - - memcpy((char *)dst + offset, src_row, length * cpp); - - src_row += length * cpp; - x -= length * cpp; - dx += length; - } - while (x >= 64) { - offset = tile_row + - (dx >> tile_pixels) * tile_size + - (dx & tile_mask) * cpp; - offset ^= (offset >> 3) & 64; - - memcpy((char *)dst + offset, src_row, 64); - - src_row += 64; - x -= 64; - dx += swizzle_pixels; - } - if (x) { - offset = tile_row + - (dx >> tile_pixels) * tile_size + - (dx & tile_mask) * cpp; - offset ^= (offset >> 3) & 64; - memcpy((char *)dst + offset, src_row, x); - } - } +#define memcpy_to_tiled_x(swizzle) \ +fast_memcpy static void \ +memcpy_to_tiled_x__##swizzle (const void *src, void *dst, int bpp, \ + int32_t src_stride, int32_t dst_stride, \ + int16_t src_x, int16_t src_y, \ + int16_t dst_x, int16_t dst_y, \ + uint16_t width, uint16_t height) \ +{ \ + const unsigned tile_width = 512; \ + const unsigned tile_height = 8; \ + const unsigned tile_size = 4096; \ + const unsigned cpp = bpp / 8; \ + const unsigned stride_tiles = dst_stride / tile_width; \ + const unsigned swizzle_pixels = 64 / cpp; \ + const unsigned tile_pixels = ffs(tile_width / cpp) - 1; \ + const unsigned tile_mask = (1 << tile_pixels) - 1; \ + unsigned x, y; \ + DBG(("%s(bpp=%d): src=(%d, %d), dst=(%d, %d), size=%dx%d, pitch=%d/%d\n", \ + __FUNCTION__, bpp, src_x, src_y, dst_x, dst_y, width, height, src_stride, dst_stride)); \ + src = (const uint8_t *)src + src_y * src_stride + src_x * cpp; \ + for (y = 0; y < height; ++y) { \ + const uint32_t dy = y + dst_y; \ + const uint32_t tile_row = \ + (dy / tile_height * stride_tiles * tile_size + \ + (dy & (tile_height-1)) * tile_width); \ + const uint8_t *src_row = (const uint8_t *)src + src_stride * y; \ + uint32_t dx = dst_x; \ + x = width * cpp; \ + if (dx & (swizzle_pixels - 1)) { \ + const uint32_t swizzle_bound_pixels = ALIGN(dx + 1, swizzle_pixels); \ + const uint32_t length = min(dst_x + width, swizzle_bound_pixels) - dx; \ + uint32_t offset = \ + tile_row + \ + (dx >> tile_pixels) * tile_size + \ + (dx & tile_mask) * cpp; \ + memcpy((char *)dst + swizzle(offset), src_row, length * cpp); \ + src_row += length * cpp; \ + x -= length * cpp; \ + dx += length; \ + } \ + while (x >= 64) { \ + uint32_t offset = \ + tile_row + \ + (dx >> tile_pixels) * tile_size + \ + (dx & tile_mask) * cpp; \ + memcpy((char *)dst + swizzle(offset), src_row, 64); \ + src_row += 64; \ + x -= 64; \ + dx += swizzle_pixels; \ + } \ + if (x) { \ + uint32_t offset = \ + tile_row + \ + (dx >> tile_pixels) * tile_size + \ + (dx & tile_mask) * cpp; \ + memcpy((char *)dst + swizzle(offset), src_row, x); \ + } \ + } \ } -fast_memcpy static void -memcpy_from_tiled_x__swizzle_9(const void *src, void *dst, int bpp, - int32_t src_stride, int32_t dst_stride, - int16_t src_x, int16_t src_y, - int16_t dst_x, int16_t dst_y, - uint16_t width, uint16_t height) -{ - const unsigned tile_width = 512; - const unsigned tile_height = 8; - const unsigned tile_size = 4096; - - const unsigned cpp = bpp / 8; - const unsigned stride_tiles = src_stride / tile_width; - const unsigned swizzle_pixels = 64 / cpp; - const unsigned tile_pixels = ffs(tile_width / cpp) - 1; - const unsigned tile_mask = (1 << tile_pixels) - 1; +#define memcpy_from_tiled_x(swizzle) \ +fast_memcpy static void \ +memcpy_from_tiled_x__##swizzle (const void *src, void *dst, int bpp, \ + int32_t src_stride, int32_t dst_stride, \ + int16_t src_x, int16_t src_y, \ + int16_t dst_x, int16_t dst_y, \ + uint16_t width, uint16_t height) \ +{ \ + const unsigned tile_width = 512; \ + const unsigned tile_height = 8; \ + const unsigned tile_size = 4096; \ + const unsigned cpp = bpp / 8; \ + const unsigned stride_tiles = src_stride / tile_width; \ + const unsigned swizzle_pixels = 64 / cpp; \ + const unsigned tile_pixels = ffs(tile_width / cpp) - 1; \ + const unsigned tile_mask = (1 << tile_pixels) - 1; \ + unsigned x, y; \ + DBG(("%s(bpp=%d): src=(%d, %d), dst=(%d, %d), size=%dx%d, pitch=%d/%d\n", \ + __FUNCTION__, bpp, src_x, src_y, dst_x, dst_y, width, height, src_stride, dst_stride)); \ + dst = (uint8_t *)dst + dst_y * dst_stride + dst_x * cpp; \ + for (y = 0; y < height; ++y) { \ + const uint32_t sy = y + src_y; \ + const uint32_t tile_row = \ + (sy / tile_height * stride_tiles * tile_size + \ + (sy & (tile_height-1)) * tile_width); \ + uint8_t *dst_row = (uint8_t *)dst + dst_stride * y; \ + uint32_t sx = src_x; \ + x = width * cpp; \ + if (sx & (swizzle_pixels - 1)) { \ + const uint32_t swizzle_bound_pixels = ALIGN(sx + 1, swizzle_pixels); \ + const uint32_t length = min(src_x + width, swizzle_bound_pixels) - sx; \ + uint32_t offset = \ + tile_row + \ + (sx >> tile_pixels) * tile_size + \ + (sx & tile_mask) * cpp; \ + memcpy(dst_row, (const char *)src + swizzle(offset), length * cpp); \ + dst_row += length * cpp; \ + x -= length * cpp; \ + sx += length; \ + } \ + while (x >= 64) { \ + uint32_t offset = \ + tile_row + \ + (sx >> tile_pixels) * tile_size + \ + (sx & tile_mask) * cpp; \ + memcpy(dst_row, (const char *)src + swizzle(offset), 64); \ + dst_row += 64; \ + x -= 64; \ + sx += swizzle_pixels; \ + } \ + if (x) { \ + uint32_t offset = \ + tile_row + \ + (sx >> tile_pixels) * tile_size + \ + (sx & tile_mask) * cpp; \ + memcpy(dst_row, (const char *)src + swizzle(offset), x); \ + } \ + } \ +} - unsigned x, y; +#define swizzle_9(X) ((X) ^ (((X) >> 3) & 64)) +memcpy_to_tiled_x(swizzle_9) +memcpy_from_tiled_x(swizzle_9) +#undef swizzle_9 - DBG(("%s(bpp=%d): src=(%d, %d), dst=(%d, %d), size=%dx%d, pitch=%d/%d\n", - __FUNCTION__, bpp, src_x, src_y, dst_x, dst_y, width, height, src_stride, dst_stride)); - - dst = (uint8_t *)dst + dst_y * dst_stride + dst_x * cpp; - - for (y = 0; y < height; ++y) { - const uint32_t sy = y + src_y; - const uint32_t tile_row = - (sy / tile_height * stride_tiles * tile_size + - (sy & (tile_height-1)) * tile_width); - uint8_t *dst_row = (uint8_t *)dst + dst_stride * y; - uint32_t sx = src_x, offset; - - x = width * cpp; - if (sx & (swizzle_pixels - 1)) { - const uint32_t swizzle_bound_pixels = ALIGN(sx + 1, swizzle_pixels); - const uint32_t length = min(src_x + width, swizzle_bound_pixels) - sx; - offset = tile_row + - (sx >> tile_pixels) * tile_size + - (sx & tile_mask) * cpp; - offset ^= (offset >> 3) & 64; - - memcpy(dst_row, (const char *)src + offset, length * cpp); - - dst_row += length * cpp; - x -= length * cpp; - sx += length; - } - while (x >= 64) { - offset = tile_row + - (sx >> tile_pixels) * tile_size + - (sx & tile_mask) * cpp; - offset ^= (offset >> 3) & 64; +#define swizzle_9_10(X) ((X) ^ ((((X) ^ ((X) >> 1)) >> 3) & 64)) +memcpy_to_tiled_x(swizzle_9_10) +memcpy_from_tiled_x(swizzle_9_10) +#undef swizzle_9_10 - memcpy(dst_row, (const char *)src + offset, 64); +#define swizzle_9_11(X) ((X) ^ ((((X) ^ ((X) >> 2)) >> 3) & 64)) +memcpy_to_tiled_x(swizzle_9_11) +memcpy_from_tiled_x(swizzle_9_11) +#undef swizzle_9_11 - dst_row += 64; - x -= 64; - sx += swizzle_pixels; - } - if (x) { - offset = tile_row + - (sx >> tile_pixels) * tile_size + - (sx & tile_mask) * cpp; - offset ^= (offset >> 3) & 64; - memcpy(dst_row, (const char *)src + offset, x); - } - } -} +#define swizzle_9_10_11(X) ((X) ^ ((((X) ^ ((X) >> 1) ^ ((X) >> 2)) >> 3) & 64)) +memcpy_to_tiled_x(swizzle_9_10_11) +memcpy_from_tiled_x(swizzle_9_10_11) +#undef swizzle_9_10_11 -fast_memcpy static void -memcpy_to_tiled_x__swizzle_9_10(const void *src, void *dst, int bpp, - int32_t src_stride, int32_t dst_stride, - int16_t src_x, int16_t src_y, - int16_t dst_x, int16_t dst_y, - uint16_t width, uint16_t height) +static fast_memcpy void +memcpy_to_tiled_x__gen2(const void *src, void *dst, int bpp, + int32_t src_stride, int32_t dst_stride, + int16_t src_x, int16_t src_y, + int16_t dst_x, int16_t dst_y, + uint16_t width, uint16_t height) { - const unsigned tile_width = 512; - const unsigned tile_height = 8; - const unsigned tile_size = 4096; + const unsigned tile_width = 128; + const unsigned tile_height = 16; + const unsigned tile_size = 2048; const unsigned cpp = bpp / 8; - const unsigned stride_tiles = dst_stride / tile_width; - const unsigned swizzle_pixels = 64 / cpp; - const unsigned tile_pixels = ffs(tile_width / cpp) - 1; - const unsigned tile_mask = (1 << tile_pixels) - 1; - - unsigned x, y; + const unsigned tile_pixels = tile_width / cpp; + const unsigned tile_shift = ffs(tile_pixels) - 1; + const unsigned tile_mask = tile_pixels - 1; DBG(("%s(bpp=%d): src=(%d, %d), dst=(%d, %d), size=%dx%d, pitch=%d/%d\n", __FUNCTION__, bpp, src_x, src_y, dst_x, dst_y, width, height, src_stride, dst_stride)); + assert(src != dst); - src = (const uint8_t *)src + src_y * src_stride + src_x * cpp; - - for (y = 0; y < height; ++y) { - const uint32_t dy = y + dst_y; - const uint32_t tile_row = - (dy / tile_height * stride_tiles * tile_size + - (dy & (tile_height-1)) * tile_width); - const uint8_t *src_row = (const uint8_t *)src + src_stride * y; - uint32_t dx = dst_x, offset; - - x = width * cpp; - if (dx & (swizzle_pixels - 1)) { - const uint32_t swizzle_bound_pixels = ALIGN(dx + 1, swizzle_pixels); - const uint32_t length = min(dst_x + width, swizzle_bound_pixels) - dx; - offset = tile_row + - (dx >> tile_pixels) * tile_size + - (dx & tile_mask) * cpp; - offset ^= ((offset ^ (offset >> 1)) >> 3) & 64; - - memcpy((char *)dst + offset, src_row, length * cpp); - - src_row += length * cpp; - x -= length * cpp; - dx += length; - } - while (x >= 64) { - offset = tile_row + - (dx >> tile_pixels) * tile_size + - (dx & tile_mask) * cpp; - offset ^= ((offset ^ (offset >> 1)) >> 3) & 64; - - memcpy((char *)dst + offset, src_row, 64); - - src_row += 64; - x -= 64; - dx += swizzle_pixels; - } - if (x) { - offset = tile_row + - (dx >> tile_pixels) * tile_size + - (dx & tile_mask) * cpp; - offset ^= ((offset ^ (offset >> 1)) >> 3) & 64; - memcpy((char *)dst + offset, src_row, x); - } - } -} - -fast_memcpy static void -memcpy_from_tiled_x__swizzle_9_10(const void *src, void *dst, int bpp, - int32_t src_stride, int32_t dst_stride, - int16_t src_x, int16_t src_y, - int16_t dst_x, int16_t dst_y, - uint16_t width, uint16_t height) -{ - const unsigned tile_width = 512; - const unsigned tile_height = 8; - const unsigned tile_size = 4096; - - const unsigned cpp = bpp / 8; - const unsigned stride_tiles = src_stride / tile_width; - const unsigned swizzle_pixels = 64 / cpp; - const unsigned tile_pixels = ffs(tile_width / cpp) - 1; - const unsigned tile_mask = (1 << tile_pixels) - 1; + if (src_x | src_y) + src = (const uint8_t *)src + src_y * src_stride + src_x * cpp; + assert(src_stride >= width * cpp); + src_stride -= width * cpp; - unsigned x, y; + while (height--) { + unsigned w = width * cpp; + uint8_t *tile_row = dst; - DBG(("%s(bpp=%d): src=(%d, %d), dst=(%d, %d), size=%dx%d, pitch=%d/%d\n", - __FUNCTION__, bpp, src_x, src_y, dst_x, dst_y, width, height, src_stride, dst_stride)); + tile_row += dst_y / tile_height * dst_stride * tile_height; + tile_row += (dst_y & (tile_height-1)) * tile_width; + if (dst_x) { + tile_row += (dst_x >> tile_shift) * tile_size; + if (dst_x & tile_mask) { + const unsigned x = (dst_x & tile_mask) * cpp; + const unsigned len = min(tile_width - x, w); + memcpy(tile_row + x, src, len); - dst = (uint8_t *)dst + dst_y * dst_stride + dst_x * cpp; - - for (y = 0; y < height; ++y) { - const uint32_t sy = y + src_y; - const uint32_t tile_row = - (sy / tile_height * stride_tiles * tile_size + - (sy & (tile_height-1)) * tile_width); - uint8_t *dst_row = (uint8_t *)dst + dst_stride * y; - uint32_t sx = src_x, offset; - - x = width * cpp; - if (sx & (swizzle_pixels - 1)) { - const uint32_t swizzle_bound_pixels = ALIGN(sx + 1, swizzle_pixels); - const uint32_t length = min(src_x + width, swizzle_bound_pixels) - sx; - offset = tile_row + - (sx >> tile_pixels) * tile_size + - (sx & tile_mask) * cpp; - offset ^= ((offset ^ (offset >> 1)) >> 3) & 64; - - memcpy(dst_row, (const char *)src + offset, length * cpp); - - dst_row += length * cpp; - x -= length * cpp; - sx += length; + tile_row += tile_size; + src = (const uint8_t *)src + len; + w -= len; + } } - while (x >= 64) { - offset = tile_row + - (sx >> tile_pixels) * tile_size + - (sx & tile_mask) * cpp; - offset ^= ((offset ^ (offset >> 1)) >> 3) & 64; - - memcpy(dst_row, (const char *)src + offset, 64); + while (w >= tile_width) { + memcpy(tile_row, src, tile_width); - dst_row += 64; - x -= 64; - sx += swizzle_pixels; - } - if (x) { - offset = tile_row + - (sx >> tile_pixels) * tile_size + - (sx & tile_mask) * cpp; - offset ^= ((offset ^ (offset >> 1)) >> 3) & 64; - memcpy(dst_row, (const char *)src + offset, x); + tile_row += tile_size; + src = (const uint8_t *)src + tile_width; + w -= tile_width; } + memcpy(tile_row, src, w); + src = (const uint8_t *)src + src_stride + w; + dst_y++; } } -fast_memcpy static void -memcpy_to_tiled_x__swizzle_9_11(const void *src, void *dst, int bpp, - int32_t src_stride, int32_t dst_stride, - int16_t src_x, int16_t src_y, - int16_t dst_x, int16_t dst_y, - uint16_t width, uint16_t height) +static fast_memcpy void +memcpy_from_tiled_x__gen2(const void *src, void *dst, int bpp, + int32_t src_stride, int32_t dst_stride, + int16_t src_x, int16_t src_y, + int16_t dst_x, int16_t dst_y, + uint16_t width, uint16_t height) { - const unsigned tile_width = 512; - const unsigned tile_height = 8; - const unsigned tile_size = 4096; + const unsigned tile_width = 128; + const unsigned tile_height = 16; + const unsigned tile_size = 2048; const unsigned cpp = bpp / 8; - const unsigned stride_tiles = dst_stride / tile_width; - const unsigned swizzle_pixels = 64 / cpp; - const unsigned tile_pixels = ffs(tile_width / cpp) - 1; - const unsigned tile_mask = (1 << tile_pixels) - 1; - - unsigned x, y; + const unsigned tile_pixels = tile_width / cpp; + const unsigned tile_shift = ffs(tile_pixels) - 1; + const unsigned tile_mask = tile_pixels - 1; DBG(("%s(bpp=%d): src=(%d, %d), dst=(%d, %d), size=%dx%d, pitch=%d/%d\n", __FUNCTION__, bpp, src_x, src_y, dst_x, dst_y, width, height, src_stride, dst_stride)); + assert(src != dst); - src = (const uint8_t *)src + src_y * src_stride + src_x * cpp; - - for (y = 0; y < height; ++y) { - const uint32_t dy = y + dst_y; - const uint32_t tile_row = - (dy / tile_height * stride_tiles * tile_size + - (dy & (tile_height-1)) * tile_width); - const uint8_t *src_row = (const uint8_t *)src + src_stride * y; - uint32_t dx = dst_x, offset; - - x = width * cpp; - if (dx & (swizzle_pixels - 1)) { - const uint32_t swizzle_bound_pixels = ALIGN(dx + 1, swizzle_pixels); - const uint32_t length = min(dst_x + width, swizzle_bound_pixels) - dx; - offset = tile_row + - (dx >> tile_pixels) * tile_size + - (dx & tile_mask) * cpp; - offset ^= ((offset ^ (offset >> 2)) >> 3) & 64; - memcpy((char *)dst + offset, src_row, length * cpp); - - src_row += length * cpp; - x -= length * cpp; - dx += length; - } - while (x >= 64) { - offset = tile_row + - (dx >> tile_pixels) * tile_size + - (dx & tile_mask) * cpp; - offset ^= ((offset ^ (offset >> 2)) >> 3) & 64; - - memcpy((char *)dst + offset, src_row, 64); - - src_row += 64; - x -= 64; - dx += swizzle_pixels; - } - if (x) { - offset = tile_row + - (dx >> tile_pixels) * tile_size + - (dx & tile_mask) * cpp; - offset ^= ((offset ^ (offset >> 2)) >> 3) & 64; - memcpy((char *)dst + offset, src_row, x); - } - } -} - -fast_memcpy static void -memcpy_from_tiled_x__swizzle_9_11(const void *src, void *dst, int bpp, - int32_t src_stride, int32_t dst_stride, - int16_t src_x, int16_t src_y, - int16_t dst_x, int16_t dst_y, - uint16_t width, uint16_t height) -{ - const unsigned tile_width = 512; - const unsigned tile_height = 8; - const unsigned tile_size = 4096; - - const unsigned cpp = bpp / 8; - const unsigned stride_tiles = src_stride / tile_width; - const unsigned swizzle_pixels = 64 / cpp; - const unsigned tile_pixels = ffs(tile_width / cpp) - 1; - const unsigned tile_mask = (1 << tile_pixels) - 1; + if (dst_x | dst_y) + dst = (uint8_t *)dst + dst_y * dst_stride + dst_x * cpp; + assert(dst_stride >= width * cpp); + dst_stride -= width * cpp; - unsigned x, y; + while (height--) { + unsigned w = width * cpp; + const uint8_t *tile_row = src; - DBG(("%s(bpp=%d): src=(%d, %d), dst=(%d, %d), size=%dx%d, pitch=%d/%d\n", - __FUNCTION__, bpp, src_x, src_y, dst_x, dst_y, width, height, src_stride, dst_stride)); + tile_row += src_y / tile_height * src_stride * tile_height; + tile_row += (src_y & (tile_height-1)) * tile_width; + if (src_x) { + tile_row += (src_x >> tile_shift) * tile_size; + if (src_x & tile_mask) { + const unsigned x = (src_x & tile_mask) * cpp; + const unsigned len = min(tile_width - x, w); + memcpy(dst, tile_row + x, len); - dst = (uint8_t *)dst + dst_y * dst_stride + dst_x * cpp; - - for (y = 0; y < height; ++y) { - const uint32_t sy = y + src_y; - const uint32_t tile_row = - (sy / tile_height * stride_tiles * tile_size + - (sy & (tile_height-1)) * tile_width); - uint8_t *dst_row = (uint8_t *)dst + dst_stride * y; - uint32_t sx = src_x, offset; - - x = width * cpp; - if (sx & (swizzle_pixels - 1)) { - const uint32_t swizzle_bound_pixels = ALIGN(sx + 1, swizzle_pixels); - const uint32_t length = min(src_x + width, swizzle_bound_pixels) - sx; - offset = tile_row + - (sx >> tile_pixels) * tile_size + - (sx & tile_mask) * cpp; - offset ^= ((offset ^ (offset >> 2)) >> 3) & 64; - memcpy(dst_row, (const char *)src + offset, length * cpp); - - dst_row += length * cpp; - x -= length * cpp; - sx += length; + tile_row += tile_size; + dst = (uint8_t *)dst + len; + w -= len; + } } - while (x >= 64) { - offset = tile_row + - (sx >> tile_pixels) * tile_size + - (sx & tile_mask) * cpp; - offset ^= ((offset ^ (offset >> 2)) >> 3) & 64; - - memcpy(dst_row, (const char *)src + offset, 64); + while (w >= tile_width) { + memcpy(dst, tile_row, tile_width); - dst_row += 64; - x -= 64; - sx += swizzle_pixels; - } - if (x) { - offset = tile_row + - (sx >> tile_pixels) * tile_size + - (sx & tile_mask) * cpp; - offset ^= ((offset ^ (offset >> 2)) >> 3) & 64; - memcpy(dst_row, (const char *)src + offset, x); + tile_row += tile_size; + dst = (uint8_t *)dst + tile_width; + w -= tile_width; } + memcpy(dst, tile_row, w); + dst = (uint8_t *)dst + dst_stride + w; + src_y++; } } void choose_memcpy_tiled_x(struct kgem *kgem, int swizzling) { + if (kgem->gen < 030) { + if (swizzling == I915_BIT_6_SWIZZLE_NONE) { + DBG(("%s: gen2, no swizzling\n", __FUNCTION__)); + kgem->memcpy_to_tiled_x = memcpy_to_tiled_x__gen2; + kgem->memcpy_from_tiled_x = memcpy_from_tiled_x__gen2; + } else + DBG(("%s: no detiling with swizzle functions for gen2\n", __FUNCTION__)); + return; + } + switch (swizzling) { default: DBG(("%s: unknown swizzling, %d\n", __FUNCTION__, swizzling)); @@ -771,6 +622,11 @@ void choose_memcpy_tiled_x(struct kgem *kgem, int swizzling) kgem->memcpy_to_tiled_x = memcpy_to_tiled_x__swizzle_9_11; kgem->memcpy_from_tiled_x = memcpy_from_tiled_x__swizzle_9_11; break; + case I915_BIT_6_SWIZZLE_9_10_11: + DBG(("%s: 6^9^10^11 swizzling\n", __FUNCTION__)); + kgem->memcpy_to_tiled_x = memcpy_to_tiled_x__swizzle_9_10_11; + kgem->memcpy_from_tiled_x = memcpy_from_tiled_x__swizzle_9_10_11; + break; } } @@ -1118,3 +974,241 @@ memcpy_xor(const void *src, void *dst, int bpp, } } } + +#define BILINEAR_INTERPOLATION_BITS 4 +static inline int +bilinear_weight(pixman_fixed_t x) +{ + return (x >> (16 - BILINEAR_INTERPOLATION_BITS)) & + ((1 << BILINEAR_INTERPOLATION_BITS) - 1); +} + +#if BILINEAR_INTERPOLATION_BITS <= 4 +/* Inspired by Filter_32_opaque from Skia */ +static inline uint32_t +bilinear_interpolation(uint32_t tl, uint32_t tr, + uint32_t bl, uint32_t br, + int distx, int disty) +{ + int distxy, distxiy, distixy, distixiy; + uint32_t lo, hi; + + distx <<= (4 - BILINEAR_INTERPOLATION_BITS); + disty <<= (4 - BILINEAR_INTERPOLATION_BITS); + + distxy = distx * disty; + distxiy = (distx << 4) - distxy; /* distx * (16 - disty) */ + distixy = (disty << 4) - distxy; /* disty * (16 - distx) */ + distixiy = + 16 * 16 - (disty << 4) - + (distx << 4) + distxy; /* (16 - distx) * (16 - disty) */ + + lo = (tl & 0xff00ff) * distixiy; + hi = ((tl >> 8) & 0xff00ff) * distixiy; + + lo += (tr & 0xff00ff) * distxiy; + hi += ((tr >> 8) & 0xff00ff) * distxiy; + + lo += (bl & 0xff00ff) * distixy; + hi += ((bl >> 8) & 0xff00ff) * distixy; + + lo += (br & 0xff00ff) * distxy; + hi += ((br >> 8) & 0xff00ff) * distxy; + + return ((lo >> 8) & 0xff00ff) | (hi & ~0xff00ff); +} +#elif SIZEOF_LONG > 4 +static inline uint32_t +bilinear_interpolation(uint32_t tl, uint32_t tr, + uint32_t bl, uint32_t br, + int distx, int disty) +{ + uint64_t distxy, distxiy, distixy, distixiy; + uint64_t tl64, tr64, bl64, br64; + uint64_t f, r; + + distx <<= (8 - BILINEAR_INTERPOLATION_BITS); + disty <<= (8 - BILINEAR_INTERPOLATION_BITS); + + distxy = distx * disty; + distxiy = distx * (256 - disty); + distixy = (256 - distx) * disty; + distixiy = (256 - distx) * (256 - disty); + + /* Alpha and Blue */ + tl64 = tl & 0xff0000ff; + tr64 = tr & 0xff0000ff; + bl64 = bl & 0xff0000ff; + br64 = br & 0xff0000ff; + + f = tl64 * distixiy + tr64 * distxiy + bl64 * distixy + br64 * distxy; + r = f & 0x0000ff0000ff0000ull; + + /* Red and Green */ + tl64 = tl; + tl64 = ((tl64 << 16) & 0x000000ff00000000ull) | (tl64 & 0x0000ff00ull); + + tr64 = tr; + tr64 = ((tr64 << 16) & 0x000000ff00000000ull) | (tr64 & 0x0000ff00ull); + + bl64 = bl; + bl64 = ((bl64 << 16) & 0x000000ff00000000ull) | (bl64 & 0x0000ff00ull); + + br64 = br; + br64 = ((br64 << 16) & 0x000000ff00000000ull) | (br64 & 0x0000ff00ull); + + f = tl64 * distixiy + tr64 * distxiy + bl64 * distixy + br64 * distxy; + r |= ((f >> 16) & 0x000000ff00000000ull) | (f & 0xff000000ull); + + return (uint32_t)(r >> 16); +} +#else +static inline uint32_t +bilinear_interpolation(uint32_t tl, uint32_t tr, + uint32_t bl, uint32_t br, + int distx, int disty) +{ + int distxy, distxiy, distixy, distixiy; + uint32_t f, r; + + distx <<= (8 - BILINEAR_INTERPOLATION_BITS); + disty <<= (8 - BILINEAR_INTERPOLATION_BITS); + + distxy = distx * disty; + distxiy = (distx << 8) - distxy; /* distx * (256 - disty) */ + distixy = (disty << 8) - distxy; /* disty * (256 - distx) */ + distixiy = + 256 * 256 - (disty << 8) - + (distx << 8) + distxy; /* (256 - distx) * (256 - disty) */ + + /* Blue */ + r = ((tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy + + (bl & 0x000000ff) * distixy + (br & 0x000000ff) * distxy); + + /* Green */ + f = ((tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy + + (bl & 0x0000ff00) * distixy + (br & 0x0000ff00) * distxy); + r |= f & 0xff000000; + + tl >>= 16; + tr >>= 16; + bl >>= 16; + br >>= 16; + r >>= 16; + + /* Red */ + f = ((tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy + + (bl & 0x000000ff) * distixy + (br & 0x000000ff) * distxy); + r |= f & 0x00ff0000; + + /* Alpha */ + f = ((tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy + + (bl & 0x0000ff00) * distixy + (br & 0x0000ff00) * distxy); + r |= f & 0xff000000; + + return r; +} +#endif + +static inline uint32_t convert_pixel(const uint8_t *p, int x) +{ + return ((uint32_t *)p)[x]; +} + +fast void +affine_blt(const void *src, void *dst, int bpp, + int16_t src_x, int16_t src_y, + int16_t src_width, int16_t src_height, + int32_t src_stride, + int16_t dst_x, int16_t dst_y, + uint16_t dst_width, uint16_t dst_height, + int32_t dst_stride, + const struct pixman_f_transform *t) +{ + static const uint8_t zero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + const pixman_fixed_t ux = pixman_double_to_fixed(t->m[0][0]); + const pixman_fixed_t uy = pixman_double_to_fixed(t->m[1][0]); + int i, j; + + assert(bpp == 32); + + for (j = 0; j < dst_height; j++) { + pixman_fixed_t x, y; + struct pixman_f_vector v; + uint32_t *b; + + /* reference point is the center of the pixel */ + v.v[0] = dst_x + 0.5; + v.v[1] = dst_y + j + 0.5; + v.v[2] = 1.0; + + pixman_f_transform_point_3d(t, &v); + + x = pixman_double_to_fixed(v.v[0]); + x += pixman_int_to_fixed(src_x - dst_x); + y = pixman_double_to_fixed(v.v[1]); + y += pixman_int_to_fixed(src_y - dst_y); + + b = (uint32_t*)((uint8_t *)dst + (dst_y + j) * dst_stride + dst_x * bpp / 8); + for (i = 0; i < dst_width; i++) { + const uint8_t *row1; + const uint8_t *row2; + int x1, y1, x2, y2; + uint32_t tl, tr, bl, br; + int32_t fx, fy; + + x1 = x - pixman_fixed_1/2; + y1 = y - pixman_fixed_1/2; + + fx = bilinear_weight(x1); + fy = bilinear_weight(y1); + + x1 = pixman_fixed_to_int(x1); + x2 = x1 + 1; + y1 = pixman_fixed_to_int(y1); + y2 = y1 + 1; + + if (x1 >= src_width || x2 < 0 || + y1 >= src_height || y2 < 0) { + b[i] = 0; + goto next; + } + + if (y2 == 0) { + row1 = zero; + } else { + row1 = (uint8_t *)src + src_stride * y1; + row1 += bpp / 8 * x1; + } + + if (y1 == src_height - 1) { + row2 = zero; + } else { + row2 = (uint8_t *)src + src_stride * y2; + row2 += bpp / 8 * x1; + } + + if (x2 == 0) { + tl = 0; + bl = 0; + } else { + tl = convert_pixel(row1, 0); + bl = convert_pixel(row2, 0); + } + + if (x1 == src_width - 1) { + tr = 0; + br = 0; + } else { + tr = convert_pixel(row1, 1); + br = convert_pixel(row2, 1); + } + + b[i] = bilinear_interpolation(tl, tr, bl, br, fx, fy); + +next: + x += ux; + y += uy; + } + } +} diff --git a/src/sna/brw/brw_eu_emit.c b/src/sna/brw/brw_eu_emit.c index 00c984d..2f33022 100644 --- a/src/sna/brw/brw_eu_emit.c +++ b/src/sna/brw/brw_eu_emit.c @@ -700,7 +700,7 @@ push_if_stack(struct brw_compile *p, struct brw_instruction *inst) * * When the matching 'else' instruction is reached (presumably by * countdown of the instruction count patched in by our ELSE/ENDIF - * functions), the relevent flags are inverted. + * functions), the relevant flags are inverted. * * When the matching 'endif' instruction is reached, the flags are * popped off. If the stack is now empty, normal execution resumes. diff --git a/src/sna/compiler.h b/src/sna/compiler.h index ff41217..c723137 100644 --- a/src/sna/compiler.h +++ b/src/sna/compiler.h @@ -39,6 +39,7 @@ #define pure __attribute__((pure)) #define tightly_packed __attribute__((__packed__)) #define flatten __attribute__((flatten)) +#define nonnull __attribute__((nonnull)) #define page_aligned __attribute__((aligned(4096))) #else #define likely(expr) (expr) @@ -51,6 +52,7 @@ #define pure #define tighly_packed #define flatten +#define nonnull #define page_aligned #endif @@ -61,20 +63,18 @@ #define sse4_2 __attribute__((target("sse4.2,sse2,fpmath=sse"))) #endif -#if HAS_GCC(4, 7) -#define avx2 __attribute__((target("avx2,sse4.2,sse2,fpmath=sse"))) -#endif - #if HAS_GCC(4, 6) && defined(__OPTIMIZE__) #define fast __attribute__((optimize("Ofast"))) #else #define fast #endif -#if HAS_GCC(4, 6) && defined(__OPTIMIZE__) -#define fast_memcpy __attribute__((optimize("Ofast"))) __attribute__((target("inline-all-stringops"))) -#elif HAS_GCC(4, 5) && defined(__OPTIMIZE__) -#define fast_memcpy __attribute__((target("inline-all-stringops"))) +#if HAS_GCC(4, 7) +#define avx2 fast __attribute__((target("avx2,avx,sse4.2,sse2,fpmath=sse"))) +#endif + +#if HAS_GCC(4, 5) && defined(__OPTIMIZE__) +#define fast_memcpy fast __attribute__((target("inline-all-stringops"))) #else #define fast_memcpy #endif diff --git a/src/sna/fb/fb.h b/src/sna/fb/fb.h index 8bf9008..9043174 100644 --- a/src/sna/fb/fb.h +++ b/src/sna/fb/fb.h @@ -24,10 +24,6 @@ #ifndef FB_H #define FB_H -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - #include #include #include diff --git a/src/sna/fb/fbpict.h b/src/sna/fb/fbpict.h index 932032f..2087777 100644 --- a/src/sna/fb/fbpict.h +++ b/src/sna/fb/fbpict.h @@ -24,10 +24,6 @@ #ifndef FBPICT_H #define FBPICT_H -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - #include #include diff --git a/src/sna/gen2_render.c b/src/sna/gen2_render.c index 1104f46..12b741c 100644 --- a/src/sna/gen2_render.c +++ b/src/sna/gen2_render.c @@ -1572,12 +1572,12 @@ gen2_composite_picture(struct sna *sna, if (channel->repeat && (x >= 0 && y >= 0 && - x + w < pixmap->drawable.width && - y + h < pixmap->drawable.height)) { + x + w <= pixmap->drawable.width && + y + h <= pixmap->drawable.height)) { struct sna_pixmap *priv = sna_pixmap(pixmap); if (priv && priv->clear) { DBG(("%s: converting large pixmap source into solid [%08x]\n", __FUNCTION__, priv->clear_color)); - return gen2_composite_solid_init(sna, channel, priv->clear_color); + return gen2_composite_solid_init(sna, channel, solid_color(picture->format, priv->clear_color)); } } } else diff --git a/src/sna/gen3_render.c b/src/sna/gen3_render.c index 78289f0..2d3fb1e 100644 --- a/src/sna/gen3_render.c +++ b/src/sna/gen3_render.c @@ -531,6 +531,7 @@ gen3_emit_composite_primitive_affine_gradient(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 12; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = dst_x + r->width; v[1] = dst_y + r->height; @@ -596,6 +597,7 @@ gen3_emit_composite_primitive_identity_source(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 12; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[8] = v[4] = r->dst.x + op->dst.x; v[0] = v[4] + w; @@ -643,6 +645,7 @@ gen3_emit_composite_primitive_identity_source_no_offset(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 12; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[8] = v[4] = r->dst.x; v[9] = r->dst.y; @@ -693,6 +696,7 @@ gen3_emit_composite_primitive_affine_source(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 12; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = dst_x + r->width; v[5] = v[1] = dst_y + r->height; @@ -756,6 +760,7 @@ gen3_emit_composite_primitive_constant_identity_mask(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 12; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[8] = v[4] = r->dst.x + op->dst.x; v[0] = v[4] + w; @@ -781,6 +786,7 @@ gen3_emit_composite_primitive_constant_identity_mask_no_offset(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 12; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[8] = v[4] = r->dst.x; v[9] = r->dst.y; @@ -817,6 +823,7 @@ gen3_emit_composite_primitive_identity_source_mask(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 18; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = dst_x + w; v[1] = dst_y + h; @@ -862,6 +869,7 @@ gen3_emit_composite_primitive_affine_source_mask(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 18; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = dst_x + w; v[1] = dst_y + h; @@ -978,6 +986,7 @@ gen3_emit_composite_primitive_constant__sse2(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 6; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[4] = v[2] = r->dst.x + op->dst.x; v[5] = r->dst.y + op->dst.y; @@ -1013,6 +1022,7 @@ gen3_emit_composite_primitive_identity_gradient__sse2(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 12; + assert(sna->render.vertex_used <= sna->render.vertex_size); x = r->dst.x + op->dst.x; y = r->dst.y + op->dst.y; @@ -1067,6 +1077,7 @@ gen3_emit_composite_primitive_affine_gradient__sse2(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 12; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = dst_x + r->width; v[1] = dst_y + r->height; @@ -1132,6 +1143,7 @@ gen3_emit_composite_primitive_identity_source__sse2(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 12; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[8] = v[4] = r->dst.x + op->dst.x; v[0] = v[4] + w; @@ -1179,6 +1191,7 @@ gen3_emit_composite_primitive_identity_source_no_offset__sse2(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 12; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[8] = v[4] = r->dst.x; v[9] = r->dst.y; @@ -1229,6 +1242,7 @@ gen3_emit_composite_primitive_affine_source__sse2(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 12; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = dst_x + r->width; v[5] = v[1] = dst_y + r->height; @@ -1292,6 +1306,7 @@ gen3_emit_composite_primitive_constant_identity_mask__sse2(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 12; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[8] = v[4] = r->dst.x + op->dst.x; v[0] = v[4] + w; @@ -1317,6 +1332,7 @@ gen3_emit_composite_primitive_constant_identity_mask_no_offset__sse2(struct sna v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 12; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[8] = v[4] = r->dst.x; v[9] = r->dst.y; @@ -1353,6 +1369,7 @@ gen3_emit_composite_primitive_identity_source_mask__sse2(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 18; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = dst_x + w; v[1] = dst_y + h; @@ -1398,6 +1415,7 @@ gen3_emit_composite_primitive_affine_source_mask__sse2(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 18; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = dst_x + w; v[1] = dst_y + h; @@ -2233,6 +2251,7 @@ static void gen3_vertex_flush(struct sna *sna) static int gen3_vertex_finish(struct sna *sna) { struct kgem_bo *bo; + unsigned hint, size; DBG(("%s: used=%d/%d, vbo active? %d\n", __FUNCTION__, sna->render.vertex_used, sna->render.vertex_size, @@ -2243,6 +2262,7 @@ static int gen3_vertex_finish(struct sna *sna) sna_vertex_wait__locked(&sna->render); + hint = CREATE_GTT_MAP; bo = sna->render.vbo; if (bo) { DBG(("%s: reloc = %d\n", __FUNCTION__, @@ -2251,7 +2271,7 @@ static int gen3_vertex_finish(struct sna *sna) if (sna->render.vertex_reloc[0]) { sna->kgem.batch[sna->render.vertex_reloc[0]] = kgem_add_reloc(&sna->kgem, sna->render.vertex_reloc[0], - bo, I915_GEM_DOMAIN_VERTEX << 16, 0); + bo, I915_GEM_DOMAIN_VERTEX << 16 | KGEM_RELOC_FENCED, 0); sna->render.vertex_reloc[0] = 0; } @@ -2260,17 +2280,29 @@ static int gen3_vertex_finish(struct sna *sna) sna->render.vbo = NULL; kgem_bo_destroy(&sna->kgem, bo); + hint |= CREATE_CACHED | CREATE_NO_THROTTLE; } + size = 256*1024; sna->render.vertices = NULL; - sna->render.vbo = kgem_create_linear(&sna->kgem, - 256*1024, CREATE_GTT_MAP); - if (sna->render.vbo) + sna->render.vbo = kgem_create_linear(&sna->kgem, size, hint); + while (sna->render.vbo == NULL && size > sizeof(sna->render.vertex_data)) { + size /= 2; + sna->render.vbo = kgem_create_linear(&sna->kgem, size, hint); + } + if (sna->render.vbo == NULL) + sna->render.vbo = kgem_create_linear(&sna->kgem, + 256*1024, CREATE_GTT_MAP); + if (sna->render.vbo && + kgem_check_bo(&sna->kgem, sna->render.vbo, NULL)) sna->render.vertices = kgem_bo_map(&sna->kgem, sna->render.vbo); if (sna->render.vertices == NULL) { - if (sna->render.vbo) + if (sna->render.vbo) { kgem_bo_destroy(&sna->kgem, sna->render.vbo); - sna->render.vbo = NULL; + sna->render.vbo = NULL; + } + sna->render.vertices = sna->render.vertex_data; + sna->render.vertex_size = ARRAY_SIZE(sna->render.vertex_data); return 0; } assert(sna->render.vbo->snoop == false); @@ -2280,8 +2312,14 @@ static int gen3_vertex_finish(struct sna *sna) sna->render.vertex_data, sizeof(float)*sna->render.vertex_used); } - sna->render.vertex_size = 64 * 1024 - 1; - return sna->render.vertex_size - sna->render.vertex_used; + + size = __kgem_bo_size(sna->render.vbo)/4; + if (size >= UINT16_MAX) + size = UINT16_MAX - 1; + assert(size > sna->render.vertex_used); + + sna->render.vertex_size = size; + return size - sna->render.vertex_used; } static void gen3_vertex_close(struct sna *sna) @@ -2345,7 +2383,7 @@ static void gen3_vertex_close(struct sna *sna) DBG(("%s: reloc = %d\n", __FUNCTION__, sna->render.vertex_reloc[0])); sna->kgem.batch[sna->render.vertex_reloc[0]] = kgem_add_reloc(&sna->kgem, sna->render.vertex_reloc[0], - bo, I915_GEM_DOMAIN_VERTEX << 16, delta); + bo, I915_GEM_DOMAIN_VERTEX << 16 | KGEM_RELOC_FENCED, delta); sna->render.vertex_reloc[0] = 0; if (sna->render.vbo == NULL) { @@ -2580,6 +2618,7 @@ gen3_render_composite_boxes(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += nbox_this_time * op->floats_per_rect; + assert(sna->render.vertex_used <= sna->render.vertex_size); op->emit_boxes(op, box, nbox_this_time, v); box += nbox_this_time; @@ -2604,6 +2643,7 @@ gen3_render_composite_boxes__thread(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += nbox_this_time * op->floats_per_rect; + assert(sna->render.vertex_used <= sna->render.vertex_size); sna_vertex_acquire__locked(&sna->render); sna_vertex_unlock(&sna->render); @@ -3065,7 +3105,7 @@ gen3_composite_picture(struct sna *sna, if (sna_picture_is_clear(picture, x, y, w, h, &color)) { DBG(("%s: clear drawable [%08x]\n", __FUNCTION__, color)); - return gen3_init_solid(channel, color_convert(color, picture->format, PICT_a8r8g8b8)); + return gen3_init_solid(channel, solid_color(picture->format, color)); } if (!gen3_check_repeat(picture)) @@ -3097,12 +3137,12 @@ gen3_composite_picture(struct sna *sna, if (channel->repeat || (x >= 0 && y >= 0 && - x + w < pixmap->drawable.width && - y + h < pixmap->drawable.height)) { + x + w <= pixmap->drawable.width && + y + h <= pixmap->drawable.height)) { struct sna_pixmap *priv = sna_pixmap(pixmap); if (priv && priv->clear) { DBG(("%s: converting large pixmap source into solid [%08x]\n", __FUNCTION__, priv->clear_color)); - return gen3_init_solid(channel, priv->clear_color); + return gen3_init_solid(channel, solid_color(picture->format, priv->clear_color)); } } } else { @@ -3862,6 +3902,7 @@ gen3_emit_composite_spans_primitive_zero(struct sna *sna, { float *v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 6; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = op->base.dst.x + box->x2; v[1] = op->base.dst.y + box->y2; @@ -3901,6 +3942,7 @@ gen3_emit_composite_spans_primitive_zero_no_offset(struct sna *sna, { float *v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 6; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = box->x2; v[3] = v[1] = box->y2; @@ -3932,6 +3974,7 @@ gen3_emit_composite_spans_primitive_constant(struct sna *sna, { float *v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 9; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = op->base.dst.x + box->x2; v[6] = v[3] = op->base.dst.x + box->x1; @@ -3966,6 +4009,7 @@ gen3_emit_composite_spans_primitive_constant_no_offset(struct sna *sna, { float *v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 9; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = box->x2; v[6] = v[3] = box->x1; @@ -3999,6 +4043,7 @@ gen3_emit_composite_spans_primitive_identity_source(struct sna *sna, { float *v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 15; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = op->base.dst.x + box->x2; v[1] = op->base.dst.y + box->y2; @@ -4060,6 +4105,7 @@ gen3_emit_composite_spans_primitive_affine_source(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 15; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = op->base.dst.x + box->x2; v[6] = v[1] = op->base.dst.y + box->y2; @@ -4125,6 +4171,7 @@ gen3_emit_composite_spans_primitive_identity_gradient(struct sna *sna, { float *v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 15; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = op->base.dst.x + box->x2; v[1] = op->base.dst.y + box->y2; @@ -4184,6 +4231,7 @@ gen3_emit_composite_spans_primitive_constant__sse2(struct sna *sna, { float *v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 9; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = op->base.dst.x + box->x2; v[6] = v[3] = op->base.dst.x + box->x1; @@ -4229,6 +4277,7 @@ gen3_render_composite_spans_constant_box__sse2(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 9; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = box->x2; v[6] = v[3] = box->x1; @@ -4259,6 +4308,7 @@ gen3_render_composite_spans_constant_thread__sse2__boxes(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += nbox_this_time * 9; + assert(sna->render.vertex_used <= sna->render.vertex_size); sna_vertex_acquire__locked(&sna->render); sna_vertex_unlock(&sna->render); @@ -4287,6 +4337,7 @@ gen3_emit_composite_spans_primitive_constant__sse2__no_offset(struct sna *sna, { float *v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 9; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = box->x2; v[6] = v[3] = box->x1; @@ -4320,6 +4371,7 @@ gen3_emit_composite_spans_primitive_identity_source__sse2(struct sna *sna, { float *v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 15; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = op->base.dst.x + box->x2; v[1] = op->base.dst.y + box->y2; @@ -4380,6 +4432,7 @@ gen3_emit_composite_spans_primitive_affine_source__sse2(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 15; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = op->base.dst.x + box->x2; v[6] = v[1] = op->base.dst.y + box->y2; @@ -4445,6 +4498,7 @@ gen3_emit_composite_spans_primitive_identity_gradient__sse2(struct sna *sna, { float *v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 15; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = op->base.dst.x + box->x2; v[1] = op->base.dst.y + box->y2; @@ -4504,6 +4558,7 @@ gen3_emit_composite_spans_primitive_affine_gradient__sse2(struct sna *sna, PictTransform *transform = op->base.src.transform; float *v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 15; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = op->base.dst.x + box->x2; v[1] = op->base.dst.y + box->y2; @@ -4577,6 +4632,7 @@ gen3_emit_composite_spans_primitive_affine_gradient(struct sna *sna, PictTransform *transform = op->base.src.transform; float *v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 15; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = op->base.dst.x + box->x2; v[1] = op->base.dst.y + box->y2; @@ -4676,6 +4732,7 @@ gen3_render_composite_spans_constant_box(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += 9; + assert(sna->render.vertex_used <= sna->render.vertex_size); v[0] = box->x2; v[6] = v[3] = box->x1; @@ -4706,6 +4763,7 @@ gen3_render_composite_spans_constant_thread_boxes(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += nbox_this_time * 9; + assert(sna->render.vertex_used <= sna->render.vertex_size); sna_vertex_acquire__locked(&sna->render); sna_vertex_unlock(&sna->render); @@ -4795,6 +4853,7 @@ gen3_render_composite_spans_boxes__thread(struct sna *sna, v = sna->render.vertices + sna->render.vertex_used; sna->render.vertex_used += nbox_this_time * op->base.floats_per_rect; + assert(sna->render.vertex_used <= sna->render.vertex_size); sna_vertex_acquire__locked(&sna->render); sna_vertex_unlock(&sna->render); @@ -5436,17 +5495,7 @@ gen3_render_video(struct sna *sna, pix_yoff = -dstRegion->extents.y1; copy = 1; } else { - /* Set up the offset for translating from the given region - * (in screen coordinates) to the backing pixmap. - */ -#ifdef COMPOSITE - pix_xoff = -pixmap->screen_x + pixmap->drawable.x; - pix_yoff = -pixmap->screen_y + pixmap->drawable.y; -#else - pix_xoff = 0; - pix_yoff = 0; -#endif - + pix_xoff = pix_yoff = 0; dst_width = pixmap->drawable.width; dst_height = pixmap->drawable.height; } @@ -5502,16 +5551,9 @@ gen3_render_video(struct sna *sna, } while (nbox); if (copy) { -#ifdef COMPOSITE - pix_xoff = -pixmap->screen_x + pixmap->drawable.x; - pix_yoff = -pixmap->screen_y + pixmap->drawable.y; -#else - pix_xoff = 0; - pix_yoff = 0; -#endif sna_blt_copy_boxes(sna, GXcopy, dst_bo, -dstRegion->extents.x1, -dstRegion->extents.y1, - priv->gpu_bo, pix_xoff, pix_yoff, + priv->gpu_bo, 0, 0, pixmap->drawable.bitsPerPixel, region_rects(dstRegion), region_num_rects(dstRegion)); @@ -5519,21 +5561,8 @@ gen3_render_video(struct sna *sna, kgem_bo_destroy(&sna->kgem, dst_bo); } - if (!DAMAGE_IS_ALL(priv->gpu_damage)) { - if ((pix_xoff | pix_yoff) == 0) { - sna_damage_add(&priv->gpu_damage, dstRegion); - sna_damage_subtract(&priv->cpu_damage, dstRegion); - } else { - sna_damage_add_boxes(&priv->gpu_damage, - region_rects(dstRegion), - region_num_rects(dstRegion), - pix_xoff, pix_yoff); - sna_damage_subtract_boxes(&priv->cpu_damage, - region_rects(dstRegion), - region_num_rects(dstRegion), - pix_xoff, pix_yoff); - } - } + if (!DAMAGE_IS_ALL(priv->gpu_damage)) + sna_damage_add(&priv->gpu_damage, dstRegion); return true; } diff --git a/src/sna/gen4_render.c b/src/sna/gen4_render.c index 6c2d380..552da49 100644 --- a/src/sna/gen4_render.c +++ b/src/sna/gen4_render.c @@ -1405,8 +1405,8 @@ gen4_render_video(struct sna *sna, int src_height = frame->src.y2 - frame->src.y1; float src_offset_x, src_offset_y; float src_scale_x, src_scale_y; - int nbox, pix_xoff, pix_yoff; const BoxRec *box; + int nbox; DBG(("%s: %dx%d -> %dx%d\n", __FUNCTION__, src_width, src_height, dst_width, dst_height)); @@ -1445,17 +1445,6 @@ gen4_render_video(struct sna *sna, gen4_align_vertex(sna, &tmp); gen4_video_bind_surfaces(sna, &tmp); - /* Set up the offset for translating from the given region (in screen - * coordinates) to the backing pixmap. - */ -#ifdef COMPOSITE - pix_xoff = -pixmap->screen_x + pixmap->drawable.x; - pix_yoff = -pixmap->screen_y + pixmap->drawable.y; -#else - pix_xoff = 0; - pix_yoff = 0; -#endif - src_scale_x = (float)src_width / dst_width / frame->width; src_offset_x = (float)frame->src.x1 / frame->width - dstRegion->extents.x1 * src_scale_x; @@ -1473,34 +1462,26 @@ gen4_render_video(struct sna *sna, nbox -= n; do { - BoxRec r; - - r.x1 = box->x1 + pix_xoff; - r.x2 = box->x2 + pix_xoff; - r.y1 = box->y1 + pix_yoff; - r.y2 = box->y2 + pix_yoff; - - OUT_VERTEX(r.x2, r.y2); + OUT_VERTEX(box->x2, box->y2); OUT_VERTEX_F(box->x2 * src_scale_x + src_offset_x); OUT_VERTEX_F(box->y2 * src_scale_y + src_offset_y); - OUT_VERTEX(r.x1, r.y2); + OUT_VERTEX(box->x1, box->y2); OUT_VERTEX_F(box->x1 * src_scale_x + src_offset_x); OUT_VERTEX_F(box->y2 * src_scale_y + src_offset_y); - OUT_VERTEX(r.x1, r.y1); + OUT_VERTEX(box->x1, box->y1); OUT_VERTEX_F(box->x1 * src_scale_x + src_offset_x); OUT_VERTEX_F(box->y1 * src_scale_y + src_offset_y); - if (!DAMAGE_IS_ALL(priv->gpu_damage)) { - sna_damage_add_box(&priv->gpu_damage, &r); - sna_damage_subtract_box(&priv->cpu_damage, &r); - } box++; } while (--n); } while (nbox); gen4_vertex_flush(sna); + if (!DAMAGE_IS_ALL(priv->gpu_damage)) + sna_damage_add(&priv->gpu_damage, dstRegion); + return true; } @@ -1585,12 +1566,14 @@ gen4_composite_picture(struct sna *sna, if (channel->repeat && (x >= 0 && y >= 0 && - x + w < pixmap->drawable.width && - y + h < pixmap->drawable.height)) { + x + w <= pixmap->drawable.width && + y + h <= pixmap->drawable.height)) { struct sna_pixmap *priv = sna_pixmap(pixmap); if (priv && priv->clear) { DBG(("%s: converting large pixmap source into solid [%08x]\n", __FUNCTION__, priv->clear_color)); - return gen4_channel_init_solid(sna, channel, priv->clear_color); + return gen4_channel_init_solid(sna, channel, + solid_color(picture->format, + priv->clear_color)); } } } else @@ -2738,6 +2721,20 @@ gen4_render_fill_boxes(struct sna *sna, tmp.dst.format = format; tmp.dst.bo = dst_bo; + sna_render_composite_redirect_init(&tmp); + if (too_large(dst->width, dst->height)) { + BoxRec extents; + + boxes_extents(box, n, &extents); + if (!sna_render_composite_redirect(sna, &tmp, + extents.x1, extents.y1, + extents.x2 - extents.x1, + extents.y2 - extents.y1, + n > 1)) + return sna_tiling_fill_boxes(sna, op, format, color, + dst, dst_bo, box, n); + } + gen4_channel_init_solid(sna, &tmp.src, pixel); tmp.is_affine = true; @@ -2748,8 +2745,10 @@ gen4_render_fill_boxes(struct sna *sna, if (!kgem_check_bo(&sna->kgem, dst_bo, NULL)) { kgem_submit(&sna->kgem); - if (!kgem_check_bo(&sna->kgem, dst_bo, NULL)) + if (!kgem_check_bo(&sna->kgem, dst_bo, NULL)) { + kgem_bo_destroy(&sna->kgem, tmp.src.bo); return false; + } } gen4_align_vertex(sna, &tmp); @@ -2765,6 +2764,7 @@ gen4_render_fill_boxes(struct sna *sna, gen4_vertex_flush(sna); kgem_bo_destroy(&sna->kgem, tmp.src.bo); + sna_render_composite_redirect_done(sna, &tmp); return true; } diff --git a/src/sna/gen5_render.c b/src/sna/gen5_render.c index 37cf1ff..e162f9f 100644 --- a/src/sna/gen5_render.c +++ b/src/sna/gen5_render.c @@ -1355,8 +1355,8 @@ gen5_render_video(struct sna *sna, int src_height = frame->src.y2 - frame->src.y1; float src_offset_x, src_offset_y; float src_scale_x, src_scale_y; - int nbox, pix_xoff, pix_yoff; const BoxRec *box; + int nbox; DBG(("%s: %dx%d -> %dx%d\n", __FUNCTION__, src_width, src_height, dst_width, dst_height)); @@ -1395,17 +1395,6 @@ gen5_render_video(struct sna *sna, gen5_align_vertex(sna, &tmp); gen5_video_bind_surfaces(sna, &tmp); - /* Set up the offset for translating from the given region (in screen - * coordinates) to the backing pixmap. - */ -#ifdef COMPOSITE - pix_xoff = -pixmap->screen_x + pixmap->drawable.x; - pix_yoff = -pixmap->screen_y + pixmap->drawable.y; -#else - pix_xoff = 0; - pix_yoff = 0; -#endif - src_scale_x = (float)src_width / dst_width / frame->width; src_offset_x = (float)frame->src.x1 / frame->width - dstRegion->extents.x1 * src_scale_x; @@ -1415,35 +1404,27 @@ gen5_render_video(struct sna *sna, box = region_rects(dstRegion); nbox = region_num_rects(dstRegion); while (nbox--) { - BoxRec r; - - r.x1 = box->x1 + pix_xoff; - r.x2 = box->x2 + pix_xoff; - r.y1 = box->y1 + pix_yoff; - r.y2 = box->y2 + pix_yoff; - gen5_get_rectangles(sna, &tmp, 1, gen5_video_bind_surfaces); - OUT_VERTEX(r.x2, r.y2); + OUT_VERTEX(box->x2, box->y2); OUT_VERTEX_F(box->x2 * src_scale_x + src_offset_x); OUT_VERTEX_F(box->y2 * src_scale_y + src_offset_y); - OUT_VERTEX(r.x1, r.y2); + OUT_VERTEX(box->x1, box->y2); OUT_VERTEX_F(box->x1 * src_scale_x + src_offset_x); OUT_VERTEX_F(box->y2 * src_scale_y + src_offset_y); - OUT_VERTEX(r.x1, r.y1); + OUT_VERTEX(box->x1, box->y1); OUT_VERTEX_F(box->x1 * src_scale_x + src_offset_x); OUT_VERTEX_F(box->y1 * src_scale_y + src_offset_y); - if (!DAMAGE_IS_ALL(priv->gpu_damage)) { - sna_damage_add_box(&priv->gpu_damage, &r); - sna_damage_subtract_box(&priv->cpu_damage, &r); - } box++; } - gen4_vertex_flush(sna); + + if (!DAMAGE_IS_ALL(priv->gpu_damage)) + sna_damage_add(&priv->gpu_damage, dstRegion); + return true; } @@ -1524,12 +1505,12 @@ gen5_composite_picture(struct sna *sna, if (channel->repeat || (x >= 0 && y >= 0 && - x + w < pixmap->drawable.width && - y + h < pixmap->drawable.height)) { + x + w <= pixmap->drawable.width && + y + h <= pixmap->drawable.height)) { struct sna_pixmap *priv = sna_pixmap(pixmap); if (priv && priv->clear) { DBG(("%s: converting large pixmap source into solid [%08x]\n", __FUNCTION__, priv->clear_color)); - return gen4_channel_init_solid(sna, channel, priv->clear_color); + return gen4_channel_init_solid(sna, channel, solid_color(picture->format, priv->clear_color)); } } } else @@ -2734,6 +2715,19 @@ gen5_render_fill_boxes(struct sna *sna, tmp.dst.format = format; tmp.dst.bo = dst_bo; + if (too_large(dst->width, dst->height)) { + BoxRec extents; + + boxes_extents(box, n, &extents); + if (!sna_render_composite_redirect(sna, &tmp, + extents.x1, extents.y1, + extents.x2 - extents.x1, + extents.y2 - extents.y1, + n > 1)) + return sna_tiling_fill_boxes(sna, op, format, color, + dst, dst_bo, box, n); + } + tmp.src.bo = sna_render_get_solid(sna, pixel); tmp.src.filter = SAMPLER_FILTER_NEAREST; tmp.src.repeat = SAMPLER_EXTEND_REPEAT; @@ -2780,6 +2774,7 @@ gen5_render_fill_boxes(struct sna *sna, gen4_vertex_flush(sna); kgem_bo_destroy(&sna->kgem, tmp.src.bo); + sna_render_composite_redirect_done(sna, &tmp); return true; } diff --git a/src/sna/gen6_common.h b/src/sna/gen6_common.h index 6668620..898cda8 100644 --- a/src/sna/gen6_common.h +++ b/src/sna/gen6_common.h @@ -30,8 +30,8 @@ #include "sna.h" -#define NO_RING_SWITCH 0 -#define PREFER_RENDER 0 +#define NO_RING_SWITCH(sna) (!(sna)->kgem.has_semaphores) +#define PREFER_RENDER 0 /* -1 -> BLT, 1 -> RENDER */ static inline bool is_uncached(struct sna *sna, struct kgem_bo *bo) @@ -46,40 +46,25 @@ inline static bool can_switch_to_blt(struct sna *sna, if (sna->kgem.ring != KGEM_RENDER) return true; - if (NO_RING_SWITCH) + if (NO_RING_SWITCH(sna)) return false; - if (!sna->kgem.has_semaphores) - return false; - - if (flags & COPY_LAST) - return true; - if (bo && RQ_IS_BLT(bo->rq)) return true; - if (sna->render_state.gt < 2) - return true; - - return kgem_ring_is_idle(&sna->kgem, KGEM_BLT); -} + if (bo && bo->tiling == I915_TILING_Y) + return false; -inline static bool can_switch_to_render(struct sna *sna, - struct kgem_bo *bo) -{ - if (sna->kgem.ring == KGEM_RENDER) + if (sna->render_state.gt < 2) return true; - if (NO_RING_SWITCH) + if (bo && RQ_IS_RENDER(bo->rq)) return false; - if (!sna->kgem.has_semaphores) - return false; - - if (bo && !RQ_IS_BLT(bo->rq) && !is_uncached(sna, bo)) + if (flags & COPY_LAST) return true; - return !kgem_ring_is_idle(&sna->kgem, KGEM_RENDER); + return kgem_ring_is_idle(&sna->kgem, KGEM_BLT); } static inline bool untiled_tlb_miss(struct kgem_bo *bo) @@ -90,57 +75,95 @@ static inline bool untiled_tlb_miss(struct kgem_bo *bo) return bo->tiling == I915_TILING_NONE && bo->pitch >= 4096; } -static int prefer_blt_bo(struct sna *sna, struct kgem_bo *bo) +static int prefer_blt_bo(struct sna *sna, + struct kgem_bo *src, + struct kgem_bo *dst) { + assert(dst != NULL); + if (PREFER_RENDER) return PREFER_RENDER < 0; - if (bo->rq) - return RQ_IS_BLT(bo->rq); + if (dst->rq) + return RQ_IS_BLT(dst->rq); if (sna->flags & SNA_POWERSAVE) return true; - return bo->tiling == I915_TILING_NONE || is_uncached(sna, bo); -} + if (src) { + if (sna->render_state.gt > 1) + return false; -inline static bool force_blt_ring(struct sna *sna) -{ - if (sna->flags & SNA_POWERSAVE) + if (src->rq) + return RQ_IS_BLT(src->rq); + + if (src->tiling == I915_TILING_Y) + return false; + } else { + if (sna->render_state.gt > 2) + return false; + } + + if (sna->render_state.gt < 2) return true; + return dst->tiling == I915_TILING_NONE || is_uncached(sna, dst); +} + +inline static bool force_blt_ring(struct sna *sna, struct kgem_bo *bo) +{ if (sna->kgem.mode == KGEM_RENDER) return false; + if (NO_RING_SWITCH(sna)) + return sna->kgem.ring == KGEM_BLT; + + if (bo->tiling == I915_TILING_Y) + return false; + + if (sna->flags & SNA_POWERSAVE) + return true; + if (sna->render_state.gt < 2) return true; return false; } -inline static bool prefer_blt_ring(struct sna *sna, - struct kgem_bo *bo, - unsigned flags) +nonnull inline static bool +prefer_blt_ring(struct sna *sna, struct kgem_bo *bo, unsigned flags) { if (PREFER_RENDER) return PREFER_RENDER < 0; - assert(!force_blt_ring(sna)); - assert(!kgem_bo_is_render(bo)); + assert(!force_blt_ring(sna, bo)); + assert(!kgem_bo_is_render(bo) || NO_RING_SWITCH(sna)); + + if (kgem_bo_is_blt(bo)) + return true; return can_switch_to_blt(sna, bo, flags); } -inline static bool prefer_render_ring(struct sna *sna, - struct kgem_bo *bo) +nonnull inline static bool +prefer_render_ring(struct sna *sna, struct kgem_bo *bo) { + if (sna->kgem.ring == KGEM_RENDER) + return true; + + if (sna->kgem.ring != KGEM_NONE && NO_RING_SWITCH(sna)) + return false; + + if (kgem_bo_is_render(bo)) + return true; + if (sna->flags & SNA_POWERSAVE) return false; - if (sna->render_state.gt < 2) - return false; + if (!prefer_blt_bo(sna, NULL, bo)) + return true; - return can_switch_to_render(sna, bo); + return !kgem_ring_is_idle(&sna->kgem, KGEM_RENDER); } inline static bool @@ -153,25 +176,20 @@ prefer_blt_composite(struct sna *sna, struct sna_composite_op *tmp) untiled_tlb_miss(tmp->src.bo)) return true; - if (force_blt_ring(sna)) + if (force_blt_ring(sna, tmp->dst.bo)) return true; - if (kgem_bo_is_render(tmp->dst.bo) || - kgem_bo_is_render(tmp->src.bo)) - return false; - if (prefer_render_ring(sna, tmp->dst.bo)) return false; if (!prefer_blt_ring(sna, tmp->dst.bo, 0)) return false; - return prefer_blt_bo(sna, tmp->dst.bo) || prefer_blt_bo(sna, tmp->src.bo); + return prefer_blt_bo(sna, tmp->src.bo, tmp->dst.bo); } -static inline bool prefer_blt_fill(struct sna *sna, - struct kgem_bo *bo, - unsigned flags) +nonnull static inline bool +prefer_blt_fill(struct sna *sna, struct kgem_bo *bo, unsigned flags) { if (PREFER_RENDER) return PREFER_RENDER < 0; @@ -179,13 +197,10 @@ static inline bool prefer_blt_fill(struct sna *sna, if (untiled_tlb_miss(bo)) return true; - if (force_blt_ring(sna)) + if (force_blt_ring(sna, bo)) return true; if ((flags & (FILL_POINTS | FILL_SPANS)) == 0) { - if (kgem_bo_is_render(bo)) - return false; - if (prefer_render_ring(sna, bo)) return false; @@ -196,7 +211,7 @@ static inline bool prefer_blt_fill(struct sna *sna, return true; } - return prefer_blt_bo(sna, bo); + return prefer_blt_bo(sna, NULL, bo); } void gen6_render_context_switch(struct kgem *kgem, int new_mode); diff --git a/src/sna/gen6_render.c b/src/sna/gen6_render.c index 2504468..ee17593 100644 --- a/src/sna/gen6_render.c +++ b/src/sna/gen6_render.c @@ -1633,9 +1633,9 @@ gen6_render_video(struct sna *sna, int src_height = frame->src.y2 - frame->src.y1; float src_offset_x, src_offset_y; float src_scale_x, src_scale_y; - int nbox, pix_xoff, pix_yoff; unsigned filter; const BoxRec *box; + int nbox; DBG(("%s: src=(%d, %d), dst=(%d, %d), %dx[(%d, %d), (%d, %d)...]\n", __FUNCTION__, @@ -1686,17 +1686,6 @@ gen6_render_video(struct sna *sna, gen6_align_vertex(sna, &tmp); gen6_emit_video_state(sna, &tmp); - /* Set up the offset for translating from the given region (in screen - * coordinates) to the backing pixmap. - */ -#ifdef COMPOSITE - pix_xoff = -pixmap->screen_x + pixmap->drawable.x; - pix_yoff = -pixmap->screen_y + pixmap->drawable.y; -#else - pix_xoff = 0; - pix_yoff = 0; -#endif - src_scale_x = (float)src_width / dst_width / frame->width; src_offset_x = (float)frame->src.x1 / frame->width - dstRegion->extents.x1 * src_scale_x; @@ -1706,35 +1695,27 @@ gen6_render_video(struct sna *sna, box = region_rects(dstRegion); nbox = region_num_rects(dstRegion); while (nbox--) { - BoxRec r; - - r.x1 = box->x1 + pix_xoff; - r.x2 = box->x2 + pix_xoff; - r.y1 = box->y1 + pix_yoff; - r.y2 = box->y2 + pix_yoff; - gen6_get_rectangles(sna, &tmp, 1, gen6_emit_video_state); - OUT_VERTEX(r.x2, r.y2); + OUT_VERTEX(box->x2, box->y2); OUT_VERTEX_F(box->x2 * src_scale_x + src_offset_x); OUT_VERTEX_F(box->y2 * src_scale_y + src_offset_y); - OUT_VERTEX(r.x1, r.y2); + OUT_VERTEX(box->x1, box->y2); OUT_VERTEX_F(box->x1 * src_scale_x + src_offset_x); OUT_VERTEX_F(box->y2 * src_scale_y + src_offset_y); - OUT_VERTEX(r.x1, r.y1); + OUT_VERTEX(box->x1, box->y1); OUT_VERTEX_F(box->x1 * src_scale_x + src_offset_x); OUT_VERTEX_F(box->y1 * src_scale_y + src_offset_y); - if (!DAMAGE_IS_ALL(priv->gpu_damage)) { - sna_damage_add_box(&priv->gpu_damage, &r); - sna_damage_subtract_box(&priv->cpu_damage, &r); - } box++; } - gen4_vertex_flush(sna); + + if (!DAMAGE_IS_ALL(priv->gpu_damage)) + sna_damage_add(&priv->gpu_damage, dstRegion); + return true; } @@ -1815,12 +1796,12 @@ gen6_composite_picture(struct sna *sna, if (channel->repeat && (x >= 0 && y >= 0 && - x + w < pixmap->drawable.width && - y + h < pixmap->drawable.height)) { + x + w <= pixmap->drawable.width && + y + h <= pixmap->drawable.height)) { struct sna_pixmap *priv = sna_pixmap(pixmap); if (priv && priv->clear) { DBG(("%s: converting large pixmap source into solid [%08x]\n", __FUNCTION__, priv->clear_color)); - return gen4_channel_init_solid(sna, channel, priv->clear_color); + return gen4_channel_init_solid(sna, channel, solid_color(picture->format, priv->clear_color)); } } } else @@ -1965,46 +1946,77 @@ gen6_composite_set_target(struct sna *sna, static bool try_blt(struct sna *sna, - PicturePtr dst, PicturePtr src, - int width, int height) + uint8_t op, + PicturePtr src, + PicturePtr mask, + PicturePtr dst, + int16_t src_x, int16_t src_y, + int16_t msk_x, int16_t msk_y, + int16_t dst_x, int16_t dst_y, + int16_t width, int16_t height, + unsigned flags, + struct sna_composite_op *tmp) { struct kgem_bo *bo; if (sna->kgem.mode == KGEM_BLT) { DBG(("%s: already performing BLT\n", __FUNCTION__)); - return true; + goto execute; } if (too_large(width, height)) { DBG(("%s: operation too large for 3D pipe (%d, %d)\n", __FUNCTION__, width, height)); - return true; + goto execute; } bo = __sna_drawable_peek_bo(dst->pDrawable); if (bo == NULL) - return true; - if (bo->rq) - return RQ_IS_BLT(bo->rq); + goto execute; + + if (untiled_tlb_miss(bo)) + goto execute; + + if (bo->rq) { + if (RQ_IS_BLT(bo->rq)) + goto execute; + + return false; + } + + if (bo->tiling == I915_TILING_Y) + goto upload; + + if (src->pDrawable == dst->pDrawable && + can_switch_to_blt(sna, bo, 0)) + goto execute; if (sna_picture_is_solid(src, NULL) && can_switch_to_blt(sna, bo, 0)) - return true; + goto execute; if (src->pDrawable) { - bo = __sna_drawable_peek_bo(src->pDrawable); - if (bo == NULL) - return true; + struct kgem_bo *s = __sna_drawable_peek_bo(src->pDrawable); + if (s == NULL) + goto execute; - if (prefer_blt_bo(sna, bo)) - return true; + if (prefer_blt_bo(sna, s, bo)) + goto execute; } if (sna->kgem.ring == KGEM_BLT) { DBG(("%s: already performing BLT\n", __FUNCTION__)); - return true; + goto execute; } - return false; +upload: + flags |= COMPOSITE_UPLOAD; +execute: + return sna_blt_composite(sna, op, + src, dst, + src_x, src_y, + dst_x, dst_y, + width, height, + flags, tmp); } static bool @@ -2234,13 +2246,13 @@ gen6_render_composite(struct sna *sna, width, height, sna->kgem.ring)); if (mask == NULL && - try_blt(sna, dst, src, width, height) && - sna_blt_composite(sna, op, - src, dst, - src_x, src_y, - dst_x, dst_y, - width, height, - flags, tmp)) + try_blt(sna, op, + src, mask, dst, + src_x, src_y, + msk_x, msk_y, + dst_x, dst_y, + width, height, + flags, tmp)) return true; if (gen6_composite_fallback(sna, src, mask, dst)) @@ -2676,27 +2688,35 @@ static inline bool prefer_blt_copy(struct sna *sna, if (sna->kgem.ring == KGEM_BLT) return true; - if (src_bo == dst_bo && can_switch_to_blt(sna, dst_bo, flags)) + if (flags & COPY_DRI && !sna->kgem.has_semaphores) + return false; + + if ((flags & COPY_SMALL || src_bo == dst_bo) && + can_switch_to_blt(sna, dst_bo, flags)) return true; if (untiled_tlb_miss(src_bo) || untiled_tlb_miss(dst_bo)) return true; - if (force_blt_ring(sna)) + if (force_blt_ring(sna, dst_bo)) return true; if (kgem_bo_is_render(dst_bo) || kgem_bo_is_render(src_bo)) return false; + if (flags & COPY_LAST && + can_switch_to_blt(sna, dst_bo, flags)) + return true; + if (prefer_render_ring(sna, dst_bo)) return false; if (!prefer_blt_ring(sna, dst_bo, flags)) return false; - return prefer_blt_bo(sna, src_bo) || prefer_blt_bo(sna, dst_bo); + return prefer_blt_bo(sna, src_bo, dst_bo); } static bool @@ -2758,8 +2778,7 @@ fallback_blt: assert(src->depth == dst->depth); assert(src->width == dst->width); assert(src->height == dst->height); - return sna_render_copy_boxes__overlap(sna, alu, - src, src_bo, + return sna_render_copy_boxes__overlap(sna, alu, dst, dst_bo, src_dx, src_dy, dst_dx, dst_dy, box, n, &extents); diff --git a/src/sna/gen7_render.c b/src/sna/gen7_render.c index 2ecfd64..1c420a7 100644 --- a/src/sna/gen7_render.c +++ b/src/sna/gen7_render.c @@ -60,8 +60,6 @@ #define NO_FILL_ONE 0 #define NO_FILL_CLEAR 0 -#define NO_RING_SWITCH 0 - #define USE_8_PIXEL_DISPATCH 1 #define USE_16_PIXEL_DISPATCH 1 #define USE_32_PIXEL_DISPATCH 0 @@ -149,7 +147,7 @@ static const struct gt_info hsw_gt1_info = { .max_vs_threads = 70, .max_gs_threads = 70, .max_wm_threads = - (102 - 1) << HSW_PS_MAX_THREADS_SHIFT | + (70 - 1) << HSW_PS_MAX_THREADS_SHIFT | 1 << HSW_PS_SAMPLE_MASK_SHIFT, .urb = { 128, 640, 256, 8 }, .gt = 1, @@ -810,7 +808,7 @@ gen7_emit_cc(struct sna *sna, uint32_t blend_offset) DBG(("%s: blend = %x\n", __FUNCTION__, blend_offset)); - /* XXX can have upto 8 blend states preload, selectable via + /* XXX can have up to 8 blend states preload, selectable via * Render Target Index. What other side-effects of Render Target Index? */ @@ -1841,9 +1839,9 @@ gen7_render_video(struct sna *sna, int src_height = frame->src.y2 - frame->src.y1; float src_offset_x, src_offset_y; float src_scale_x, src_scale_y; - int nbox, pix_xoff, pix_yoff; unsigned filter; const BoxRec *box; + int nbox; DBG(("%s: src=(%d, %d), dst=(%d, %d), %dx[(%d, %d), (%d, %d)...]\n", __FUNCTION__, @@ -1896,17 +1894,6 @@ gen7_render_video(struct sna *sna, gen7_align_vertex(sna, &tmp); gen7_emit_video_state(sna, &tmp); - /* Set up the offset for translating from the given region (in screen - * coordinates) to the backing pixmap. - */ -#ifdef COMPOSITE - pix_xoff = -pixmap->screen_x + pixmap->drawable.x; - pix_yoff = -pixmap->screen_y + pixmap->drawable.y; -#else - pix_xoff = 0; - pix_yoff = 0; -#endif - DBG(("%s: src=(%d, %d)x(%d, %d); frame=(%dx%d), dst=(%dx%d)\n", __FUNCTION__, frame->src.x1, frame->src.y1, @@ -1928,45 +1915,36 @@ gen7_render_video(struct sna *sna, box = region_rects(dstRegion); nbox = region_num_rects(dstRegion); while (nbox--) { - BoxRec r; - - DBG(("%s: dst=(%d, %d), (%d, %d) + (%d, %d); src=(%f, %f), (%f, %f)\n", + DBG(("%s: dst=(%d, %d), (%d, %d); src=(%f, %f), (%f, %f)\n", __FUNCTION__, box->x1, box->y1, box->x2, box->y2, - pix_xoff, pix_yoff, box->x1 * src_scale_x + src_offset_x, box->y1 * src_scale_y + src_offset_y, box->x2 * src_scale_x + src_offset_x, box->y2 * src_scale_y + src_offset_y)); - r.x1 = box->x1 + pix_xoff; - r.x2 = box->x2 + pix_xoff; - r.y1 = box->y1 + pix_yoff; - r.y2 = box->y2 + pix_yoff; - gen7_get_rectangles(sna, &tmp, 1, gen7_emit_video_state); - OUT_VERTEX(r.x2, r.y2); + OUT_VERTEX(box->x2, box->y2); OUT_VERTEX_F(box->x2 * src_scale_x + src_offset_x); OUT_VERTEX_F(box->y2 * src_scale_y + src_offset_y); - OUT_VERTEX(r.x1, r.y2); + OUT_VERTEX(box->x1, box->y2); OUT_VERTEX_F(box->x1 * src_scale_x + src_offset_x); OUT_VERTEX_F(box->y2 * src_scale_y + src_offset_y); - OUT_VERTEX(r.x1, r.y1); + OUT_VERTEX(box->x1, box->y1); OUT_VERTEX_F(box->x1 * src_scale_x + src_offset_x); OUT_VERTEX_F(box->y1 * src_scale_y + src_offset_y); - if (!DAMAGE_IS_ALL(priv->gpu_damage)) { - sna_damage_add_box(&priv->gpu_damage, &r); - sna_damage_subtract_box(&priv->cpu_damage, &r); - } box++; } - gen4_vertex_flush(sna); + + if (!DAMAGE_IS_ALL(priv->gpu_damage)) + sna_damage_add(&priv->gpu_damage, dstRegion); + return true; } @@ -2048,12 +2026,13 @@ gen7_composite_picture(struct sna *sna, if (channel->repeat || (x >= 0 && y >= 0 && - x + w < pixmap->drawable.width && - y + h < pixmap->drawable.height)) { + x + w <= pixmap->drawable.width && + y + h <= pixmap->drawable.height)) { struct sna_pixmap *priv = sna_pixmap(pixmap); if (priv && priv->clear) { DBG(("%s: converting large pixmap source into solid [%08x]\n", __FUNCTION__, priv->clear_color)); - return gen4_channel_init_solid(sna, channel, priv->clear_color); + return gen4_channel_init_solid(sna, channel, + solid_color(picture->format, priv->clear_color)); } } } else @@ -2185,46 +2164,78 @@ gen7_composite_set_target(struct sna *sna, static bool try_blt(struct sna *sna, - PicturePtr dst, PicturePtr src, - int width, int height) + uint8_t op, + PicturePtr src, + PicturePtr mask, + PicturePtr dst, + int16_t src_x, int16_t src_y, + int16_t msk_x, int16_t msk_y, + int16_t dst_x, int16_t dst_y, + int16_t width, int16_t height, + unsigned flags, + struct sna_composite_op *tmp) { struct kgem_bo *bo; if (sna->kgem.mode == KGEM_BLT) { DBG(("%s: already performing BLT\n", __FUNCTION__)); - return true; + goto execute; } if (too_large(width, height)) { DBG(("%s: operation too large for 3D pipe (%d, %d)\n", __FUNCTION__, width, height)); - return true; + goto execute; } bo = __sna_drawable_peek_bo(dst->pDrawable); if (bo == NULL) - return true; - if (bo->rq) - return RQ_IS_BLT(bo->rq); + goto execute; + + if (untiled_tlb_miss(bo)) + goto execute; + + if (bo->rq) { + if (RQ_IS_BLT(bo->rq)) + goto execute; + + return false; + } + + if (bo->tiling == I915_TILING_Y) + goto upload; + + if (src->pDrawable == dst->pDrawable && + (sna->render_state.gt < 3 || width*height < 1024) && + can_switch_to_blt(sna, bo, 0)) + goto execute; if (sna_picture_is_solid(src, NULL) && can_switch_to_blt(sna, bo, 0)) - return true; + goto execute; if (src->pDrawable) { - bo = __sna_drawable_peek_bo(src->pDrawable); - if (bo == NULL) - return true; + struct kgem_bo *s = __sna_drawable_peek_bo(src->pDrawable); + if (s == NULL) + goto upload; - if (prefer_blt_bo(sna, bo)) - return true; + if (prefer_blt_bo(sna, s, bo)) + goto execute; } if (sna->kgem.ring == KGEM_BLT) { DBG(("%s: already performing BLT\n", __FUNCTION__)); - return true; + goto execute; } - return false; +upload: + flags |= COMPOSITE_UPLOAD; +execute: + return sna_blt_composite(sna, op, + src, dst, + src_x, src_y, + dst_x, dst_y, + width, height, + flags, tmp); } static bool @@ -2454,13 +2465,13 @@ gen7_render_composite(struct sna *sna, width, height, sna->kgem.mode, sna->kgem.ring)); if (mask == NULL && - try_blt(sna, dst, src, width, height) && - sna_blt_composite(sna, op, - src, dst, - src_x, src_y, - dst_x, dst_y, - width, height, - flags, tmp)) + try_blt(sna, op, + src, mask, dst, + src_x, src_y, + msk_x, msk_y, + dst_x, dst_y, + width, height, + flags, tmp)) return true; if (gen7_composite_fallback(sna, src, mask, dst)) @@ -2878,27 +2889,37 @@ prefer_blt_copy(struct sna *sna, assert((flags & COPY_SYNC) == 0); - if (src_bo == dst_bo && can_switch_to_blt(sna, dst_bo, flags)) - return true; - if (untiled_tlb_miss(src_bo) || untiled_tlb_miss(dst_bo)) return true; - if (force_blt_ring(sna)) + if (flags & COPY_DRI && !sna->kgem.has_semaphores) + return false; + + if (force_blt_ring(sna, dst_bo)) + return true; + + if ((flags & COPY_SMALL || + (sna->render_state.gt < 3 && src_bo == dst_bo)) && + can_switch_to_blt(sna, dst_bo, flags)) return true; if (kgem_bo_is_render(dst_bo) || kgem_bo_is_render(src_bo)) return false; + if (flags & COPY_LAST && + sna->render_state.gt < 3 && + can_switch_to_blt(sna, dst_bo, flags)) + return true; + if (prefer_render_ring(sna, dst_bo)) return false; if (!prefer_blt_ring(sna, dst_bo, flags)) return false; - return prefer_blt_bo(sna, src_bo) || prefer_blt_bo(sna, dst_bo); + return prefer_blt_bo(sna, src_bo, dst_bo); } static bool @@ -2946,7 +2967,7 @@ fallback_blt: &extents)) { bool big = too_large(extents.x2-extents.x1, extents.y2-extents.y1); - if ((big || can_switch_to_blt(sna, dst_bo, flags)) && + if ((big || !prefer_render_ring(sna, dst_bo)) && sna_blt_copy_boxes(sna, alu, src_bo, src_dx, src_dy, dst_bo, dst_dx, dst_dy, @@ -2961,8 +2982,7 @@ fallback_blt: assert(src->depth == dst->depth); assert(src->width == dst->width); assert(src->height == dst->height); - return sna_render_copy_boxes__overlap(sna, alu, - src, src_bo, + return sna_render_copy_boxes__overlap(sna, alu, dst, dst_bo, src_dx, src_dy, dst_dx, dst_dy, box, n, &extents); diff --git a/src/sna/gen8_render.c b/src/sna/gen8_render.c index 6eb1145..0631e0a 100644 --- a/src/sna/gen8_render.c +++ b/src/sna/gen8_render.c @@ -205,6 +205,33 @@ static const struct blendinfo { #define OUT_VERTEX(x,y) vertex_emit_2s(sna, x,y) #define OUT_VERTEX_F(v) vertex_emit(sna, v) +struct gt_info { + const char *name; + struct { + int max_vs_entries; + } urb; +}; + +static const struct gt_info bdw_gt_info = { + .name = "Broadwell (gen8)", + .urb = { .max_vs_entries = 960 }, +}; + +static bool is_bdw(struct sna *sna) +{ + return sna->kgem.gen == 0100; +} + +static const struct gt_info chv_gt_info = { + .name = "Cherryview (gen8)", + .urb = { .max_vs_entries = 640 }, +}; + +static bool is_chv(struct sna *sna) +{ + return sna->kgem.gen == 0101; +} + static inline bool too_large(int width, int height) { return width > GEN8_MAX_SIZE || height > GEN8_MAX_SIZE; @@ -462,7 +489,7 @@ gen8_emit_urb(struct sna *sna) { /* num of VS entries must be divisible by 8 if size < 9 */ OUT_BATCH(GEN8_3DSTATE_URB_VS | (2 - 2)); - OUT_BATCH(960 << URB_ENTRY_NUMBER_SHIFT | + OUT_BATCH(sna->render_state.gen8.info->urb.max_vs_entries << URB_ENTRY_NUMBER_SHIFT | (2 - 1) << URB_ENTRY_SIZE_SHIFT | 4 << URB_STARTING_ADDRESS_SHIFT); @@ -873,7 +900,7 @@ gen8_emit_cc(struct sna *sna, uint32_t blend) assert(blend / GEN8_BLENDFACTOR_COUNT > 0); assert(blend % GEN8_BLENDFACTOR_COUNT > 0); - /* XXX can have upto 8 blend states preload, selectable via + /* XXX can have up to 8 blend states preload, selectable via * Render Target Index. What other side-effects of Render Target Index? */ @@ -1876,12 +1903,12 @@ gen8_composite_picture(struct sna *sna, if (channel->repeat || (x >= 0 && y >= 0 && - x + w < pixmap->drawable.width && - y + h < pixmap->drawable.height)) { + x + w <= pixmap->drawable.width && + y + h <= pixmap->drawable.height)) { struct sna_pixmap *priv = sna_pixmap(pixmap); if (priv && priv->clear) { DBG(("%s: converting large pixmap source into solid [%08x]\n", __FUNCTION__, priv->clear_color)); - return gen4_channel_init_solid(sna, channel, priv->clear_color); + return gen4_channel_init_solid(sna, channel, solid_color(picture->format, priv->clear_color)); } } } else @@ -2002,46 +2029,78 @@ gen8_composite_set_target(struct sna *sna, static bool try_blt(struct sna *sna, - PicturePtr dst, PicturePtr src, - int width, int height) + uint8_t op, + PicturePtr src, + PicturePtr mask, + PicturePtr dst, + int16_t src_x, int16_t src_y, + int16_t msk_x, int16_t msk_y, + int16_t dst_x, int16_t dst_y, + int16_t width, int16_t height, + unsigned flags, + struct sna_composite_op *tmp) { struct kgem_bo *bo; if (sna->kgem.mode == KGEM_BLT) { DBG(("%s: already performing BLT\n", __FUNCTION__)); - return true; + goto execute; } if (too_large(width, height)) { DBG(("%s: operation too large for 3D pipe (%d, %d)\n", __FUNCTION__, width, height)); - return true; + goto execute; } bo = __sna_drawable_peek_bo(dst->pDrawable); if (bo == NULL) - return true; - if (bo->rq) - return RQ_IS_BLT(bo->rq); + goto execute; + + if (untiled_tlb_miss(bo)) + goto execute; + + if (bo->rq) { + if (RQ_IS_BLT(bo->rq)) + goto execute; + + return false; + } + + if (bo->tiling == I915_TILING_Y) + goto upload; if (sna_picture_is_solid(src, NULL) && can_switch_to_blt(sna, bo, 0)) - return true; + goto execute; + + if (src->pDrawable == dst->pDrawable && + (sna->render_state.gt < 3 || width*height < 1024) && + can_switch_to_blt(sna, bo, 0)) + goto execute; if (src->pDrawable) { - bo = __sna_drawable_peek_bo(src->pDrawable); - if (bo == NULL) - return true; + struct kgem_bo *s = __sna_drawable_peek_bo(src->pDrawable); + if (s == NULL) + goto upload; - if (prefer_blt_bo(sna, bo)) - return RQ_IS_BLT(bo->rq); + if (prefer_blt_bo(sna, s, bo)) + goto execute; } if (sna->kgem.ring == KGEM_BLT) { DBG(("%s: already performing BLT\n", __FUNCTION__)); - return true; + goto execute; } - return false; +upload: + flags |= COMPOSITE_UPLOAD; +execute: + return sna_blt_composite(sna, op, + src, dst, + src_x, src_y, + dst_x, dst_y, + width, height, + flags, tmp); } static bool @@ -2271,13 +2330,13 @@ gen8_render_composite(struct sna *sna, width, height, sna->kgem.mode, sna->kgem.ring)); if (mask == NULL && - try_blt(sna, dst, src, width, height) && - sna_blt_composite(sna, op, - src, dst, - src_x, src_y, - dst_x, dst_y, - width, height, - flags, tmp)) + try_blt(sna, op, + src, mask, dst, + src_x, src_y, + msk_x, msk_y, + dst_x, dst_y, + width, height, + flags, tmp)) return true; if (gen8_composite_fallback(sna, src, mask, dst)) @@ -2700,27 +2759,37 @@ prefer_blt_copy(struct sna *sna, assert((flags & COPY_SYNC) == 0); - if (src_bo == dst_bo && can_switch_to_blt(sna, dst_bo, flags)) - return true; - if (untiled_tlb_miss(src_bo) || untiled_tlb_miss(dst_bo)) return true; - if (force_blt_ring(sna)) + if (flags & COPY_DRI && !sna->kgem.has_semaphores) + return false; + + if (force_blt_ring(sna, dst_bo)) + return true; + + if ((flags & COPY_SMALL || + (sna->render_state.gt < 3 && src_bo == dst_bo)) && + can_switch_to_blt(sna, dst_bo, flags)) return true; if (kgem_bo_is_render(dst_bo) || kgem_bo_is_render(src_bo)) return false; + if (flags & COPY_LAST && + sna->render_state.gt < 3 && + can_switch_to_blt(sna, dst_bo, flags)) + return true; + if (prefer_render_ring(sna, dst_bo)) return false; if (!prefer_blt_ring(sna, dst_bo, flags)) return false; - return prefer_blt_bo(sna, src_bo) || prefer_blt_bo(sna, dst_bo); + return prefer_blt_bo(sna, src_bo, dst_bo); } static bool @@ -2770,7 +2839,7 @@ fallback_blt: &extents)) { bool big = too_large(extents.x2-extents.x1, extents.y2-extents.y1); - if ((big || can_switch_to_blt(sna, dst_bo, flags)) && + if ((big || !prefer_render_ring(sna, dst_bo)) && sna_blt_copy_boxes(sna, alu, src_bo, src_dx, src_dy, dst_bo, dst_dx, dst_dy, @@ -2785,8 +2854,7 @@ fallback_blt: assert(src->depth == dst->depth); assert(src->width == dst->width); assert(src->height == dst->height); - return sna_render_copy_boxes__overlap(sna, alu, - src, src_bo, + return sna_render_copy_boxes__overlap(sna, alu, dst, dst_bo, src_dx, src_dy, dst_dx, dst_dy, box, n, &extents); @@ -3712,9 +3780,9 @@ gen8_render_video(struct sna *sna, int src_height = frame->src.y2 - frame->src.y1; float src_offset_x, src_offset_y; float src_scale_x, src_scale_y; - int nbox, pix_xoff, pix_yoff; unsigned filter; const BoxRec *box; + int nbox; DBG(("%s: src=(%d, %d), dst=(%d, %d), %dx[(%d, %d), (%d, %d)...]\n", __FUNCTION__, @@ -3770,17 +3838,6 @@ gen8_render_video(struct sna *sna, gen8_align_vertex(sna, &tmp); gen8_emit_video_state(sna, &tmp); - /* Set up the offset for translating from the given region (in screen - * coordinates) to the backing pixmap. - */ -#ifdef COMPOSITE - pix_xoff = -pixmap->screen_x + pixmap->drawable.x; - pix_yoff = -pixmap->screen_y + pixmap->drawable.y; -#else - pix_xoff = 0; - pix_yoff = 0; -#endif - DBG(("%s: src=(%d, %d)x(%d, %d); frame=(%dx%d), dst=(%dx%d)\n", __FUNCTION__, frame->src.x1, frame->src.y1, @@ -3802,45 +3859,36 @@ gen8_render_video(struct sna *sna, box = region_rects(dstRegion); nbox = region_num_rects(dstRegion); while (nbox--) { - BoxRec r; - DBG(("%s: dst=(%d, %d), (%d, %d) + (%d, %d); src=(%f, %f), (%f, %f)\n", __FUNCTION__, box->x1, box->y1, box->x2, box->y2, - pix_xoff, pix_yoff, box->x1 * src_scale_x + src_offset_x, box->y1 * src_scale_y + src_offset_y, box->x2 * src_scale_x + src_offset_x, box->y2 * src_scale_y + src_offset_y)); - r.x1 = box->x1 + pix_xoff; - r.x2 = box->x2 + pix_xoff; - r.y1 = box->y1 + pix_yoff; - r.y2 = box->y2 + pix_yoff; - gen8_get_rectangles(sna, &tmp, 1, gen8_emit_video_state); - OUT_VERTEX(r.x2, r.y2); + OUT_VERTEX(box->x2, box->y2); OUT_VERTEX_F(box->x2 * src_scale_x + src_offset_x); OUT_VERTEX_F(box->y2 * src_scale_y + src_offset_y); - OUT_VERTEX(r.x1, r.y2); + OUT_VERTEX(box->x1, box->y2); OUT_VERTEX_F(box->x1 * src_scale_x + src_offset_x); OUT_VERTEX_F(box->y2 * src_scale_y + src_offset_y); - OUT_VERTEX(r.x1, r.y1); + OUT_VERTEX(box->x1, box->y1); OUT_VERTEX_F(box->x1 * src_scale_x + src_offset_x); OUT_VERTEX_F(box->y1 * src_scale_y + src_offset_y); - if (!DAMAGE_IS_ALL(priv->gpu_damage)) { - sna_damage_add_box(&priv->gpu_damage, &r); - sna_damage_subtract_box(&priv->cpu_damage, &r); - } box++; } - gen8_vertex_flush(sna); + + if (!DAMAGE_IS_ALL(priv->gpu_damage)) + sna_damage_add(&priv->gpu_damage, dstRegion); + return true; } #endif @@ -3896,6 +3944,13 @@ static bool gen8_render_setup(struct sna *sna) state->gt = ((devid >> 4) & 0xf) + 1; DBG(("%s: gt=%d\n", __FUNCTION__, state->gt)); + if (is_bdw(sna)) + state->info = &bdw_gt_info; + else if (is_chv(sna)) + state->info = &chv_gt_info; + else + return false; + sna_static_stream_init(&general); /* Zero pad the start. If you see an offset of 0x0 in the batchbuffer @@ -4007,5 +4062,5 @@ const char *gen8_render_init(struct sna *sna, const char *backend) sna->render.max_3d_size = GEN8_MAX_SIZE; sna->render.max_3d_pitch = 1 << 18; - return "Broadwell"; + return sna->render_state.gen8.info->name; } diff --git a/src/sna/kgem.c b/src/sna/kgem.c index 78ed540..fefa475 100644 --- a/src/sna/kgem.c +++ b/src/sna/kgem.c @@ -84,6 +84,9 @@ search_snoop_cache(struct kgem *kgem, unsigned int num_pages, unsigned flags); #define DBG_NO_HANDLE_LUT 0 #define DBG_NO_WT 0 #define DBG_NO_WC_MMAP 0 +#define DBG_NO_BLT_Y 0 +#define DBG_NO_SCANOUT_Y 0 +#define DBG_NO_DETILING 0 #define DBG_DUMP 0 #define DBG_NO_MALLOC_CACHE 0 @@ -96,11 +99,6 @@ search_snoop_cache(struct kgem *kgem, unsigned int num_pages, unsigned flags); #define SHOW_BATCH_BEFORE 0 #define SHOW_BATCH_AFTER 0 -#if !USE_WC_MMAP -#undef DBG_NO_WC_MMAP -#define DBG_NO_WC_MMAP 1 -#endif - #if 0 #define ASSERT_IDLE(kgem__, handle__) assert(!__kgem_busy(kgem__, handle__)) #define ASSERT_MAYBE_IDLE(kgem__, handle__, expect__) assert(!(expect__) || !__kgem_busy(kgem__, handle__)) @@ -187,6 +185,15 @@ struct local_i915_gem_caching { #define LOCAL_IOCTL_I915_GEM_SET_CACHING DRM_IOW(DRM_COMMAND_BASE + LOCAL_I915_GEM_SET_CACHING, struct local_i915_gem_caching) #define LOCAL_IOCTL_I915_GEM_GET_CACHING DRM_IOW(DRM_COMMAND_BASE + LOCAL_I915_GEM_GET_CACHING, struct local_i915_gem_caching) +struct local_i915_gem_mmap { + uint32_t handle; + uint32_t pad; + uint64_t offset; + uint64_t size; + uint64_t addr_ptr; +}; +#define LOCAL_IOCTL_I915_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP, struct local_i915_gem_mmap) + struct local_i915_gem_mmap2 { uint32_t handle; uint32_t pad; @@ -216,6 +223,12 @@ static struct kgem_bo *__kgem_freed_bo; static struct kgem_request *__kgem_freed_request; static struct drm_i915_gem_exec_object2 _kgem_dummy_exec; +static inline struct sna *__to_sna(struct kgem *kgem) +{ + /* minor layering violations */ + return container_of(kgem, struct sna, kgem); +} + static inline int bytes(struct kgem_bo *bo) { return __kgem_bo_size(bo); @@ -224,25 +237,31 @@ static inline int bytes(struct kgem_bo *bo) #define bucket(B) (B)->size.pages.bucket #define num_pages(B) (B)->size.pages.count -static int do_ioctl(int fd, unsigned long req, void *arg) +static int __do_ioctl(int fd, unsigned long req, void *arg) { - int err; - -restart: - if (ioctl(fd, req, arg) == 0) - return 0; + do { + int err; - err = errno; + switch ((err = errno)) { + case EAGAIN: + sched_yield(); + case EINTR: + break; + default: + return -err; + } - if (err == EINTR) - goto restart; + if (likely(ioctl(fd, req, arg) == 0)) + return 0; + } while (1); +} - if (err == EAGAIN) { - sched_yield(); - goto restart; - } +inline static int do_ioctl(int fd, unsigned long req, void *arg) +{ + if (likely(ioctl(fd, req, arg) == 0)) + return 0; - return -err; + return __do_ioctl(fd, req, arg); } #ifdef DEBUG_MEMORY @@ -266,6 +285,9 @@ static void assert_tiling(struct kgem *kgem, struct kgem_bo *bo) assert(bo); + if (!kgem->can_fence && kgem->gen >= 040 && bo->tiling) + return; /* lies */ + VG_CLEAR(tiling); tiling.handle = bo->handle; tiling.tiling_mode = bo->tiling; @@ -273,7 +295,7 @@ static void assert_tiling(struct kgem *kgem, struct kgem_bo *bo) assert(tiling.tiling_mode == bo->tiling); } -static void assert_cacheing(struct kgem *kgem, struct kgem_bo *bo) +static void assert_caching(struct kgem *kgem, struct kgem_bo *bo) { struct local_i915_gem_caching arg; int expect = kgem->has_llc ? SNOOPED : UNCACHED; @@ -294,24 +316,117 @@ static void assert_bo_retired(struct kgem_bo *bo) assert(bo->refcnt); assert(bo->rq == NULL); assert(bo->exec == NULL); + assert(!bo->needs_flush); assert(list_is_empty(&bo->request)); } #else #define assert_tiling(kgem, bo) -#define assert_cacheing(kgem, bo) +#define assert_caching(kgem, bo) #define assert_bo_retired(bo) #endif +static int __find_debugfs(struct kgem *kgem) +{ + int i; + + for (i = 0; i < DRM_MAX_MINOR; i++) { + char path[80]; + + sprintf(path, "/sys/kernel/debug/dri/%d/i915_wedged", i); + if (access(path, R_OK) == 0) + return i; + + sprintf(path, "/debug/dri/%d/i915_wedged", i); + if (access(path, R_OK) == 0) + return i; + } + + return -1; +} + +static int kgem_get_minor(struct kgem *kgem) +{ + struct stat st; + + if (fstat(kgem->fd, &st)) + return __find_debugfs(kgem); + + if (!S_ISCHR(st.st_mode)) + return __find_debugfs(kgem); + + return st.st_rdev & 0x63; +} + +static bool find_hang_state(struct kgem *kgem, char *path, int maxlen) +{ + int minor = kgem_get_minor(kgem); + + /* Search for our hang state in a few canonical locations. + * In the unlikely event of having multiple devices, we + * will need to check which minor actually corresponds to ours. + */ + + snprintf(path, maxlen, "/sys/class/drm/card%d/error", minor); + if (access(path, R_OK) == 0) + return true; + + snprintf(path, maxlen, "/sys/kernel/debug/dri/%d/i915_error_state", minor); + if (access(path, R_OK) == 0) + return true; + + snprintf(path, maxlen, "/debug/dri/%d/i915_error_state", minor); + if (access(path, R_OK) == 0) + return true; + + path[0] = '\0'; + return false; +} + +static bool has_error_state(struct kgem *kgem, char *path) +{ + bool ret = false; + char no; + int fd; + + fd = open(path, O_RDONLY); + if (fd >= 0) { + ret = read(fd, &no, 1) == 1 && no != 'N'; + close(fd); + } + + return ret; +} + +static int kgem_get_screen_index(struct kgem *kgem) +{ + return __to_sna(kgem)->scrn->scrnIndex; +} + static void __kgem_set_wedged(struct kgem *kgem) { + static int once; + char path[256]; + + if (kgem->wedged) + return; + + if (!once && + find_hang_state(kgem, path, sizeof(path)) && + has_error_state(kgem, path)) { + xf86DrvMsg(kgem_get_screen_index(kgem), X_ERROR, + "When reporting this, please include %s and the full dmesg.\n", + path); + once = 1; + } + kgem->wedged = true; - sna_render_mark_wedged(container_of(kgem, struct sna, kgem)); + sna_render_mark_wedged(__to_sna(kgem)); } static void kgem_sna_reset(struct kgem *kgem) { - struct sna *sna = container_of(kgem, struct sna, kgem); + struct sna *sna = __to_sna(kgem); sna->render.reset(sna); sna->blt_state.fill_bo = 0; @@ -319,7 +434,7 @@ static void kgem_sna_reset(struct kgem *kgem) static void kgem_sna_flush(struct kgem *kgem) { - struct sna *sna = container_of(kgem, struct sna, kgem); + struct sna *sna = __to_sna(kgem); sna->render.flush(sna); @@ -327,22 +442,40 @@ static void kgem_sna_flush(struct kgem *kgem) sna_render_flush_solid(sna); } -static bool gem_set_tiling(int fd, uint32_t handle, int tiling, int stride) +static bool kgem_set_tiling(struct kgem *kgem, struct kgem_bo *bo, + int tiling, int stride) { struct drm_i915_gem_set_tiling set_tiling; int err; + if (tiling == bo->tiling) { + if (tiling == I915_TILING_NONE) { + bo->pitch = stride; + return true; + } + if (stride == bo->pitch) + return true; + } + if (DBG_NO_TILING) return false; VG_CLEAR(set_tiling); restart: - set_tiling.handle = handle; + set_tiling.handle = bo->handle; set_tiling.tiling_mode = tiling; - set_tiling.stride = stride; + set_tiling.stride = tiling ? stride : 0; - if (ioctl(fd, DRM_IOCTL_I915_GEM_SET_TILING, &set_tiling) == 0) - return true; + if (ioctl(kgem->fd, DRM_IOCTL_I915_GEM_SET_TILING, &set_tiling) == 0) { + bo->tiling = set_tiling.tiling_mode; + bo->pitch = set_tiling.tiling_mode ? set_tiling.stride : stride; + DBG(("%s: handle=%d, tiling=%d [%d], pitch=%d [%d]: %d\n", + __FUNCTION__, bo->handle, + bo->tiling, tiling, + bo->pitch, stride, + set_tiling.tiling_mode == tiling)); + return set_tiling.tiling_mode == tiling; + } err = errno; if (err == EINTR) @@ -353,6 +486,8 @@ restart: goto restart; } + ERR(("%s: failed to set-tiling(tiling=%d, pitch=%d) for handle=%d: %d\n", + __FUNCTION__, tiling, stride, bo->handle, err)); return false; } @@ -437,10 +572,15 @@ static void *__kgem_bo_map__gtt(struct kgem *kgem, struct kgem_bo *bo) DBG(("%s(handle=%d, size=%d)\n", __FUNCTION__, bo->handle, bytes(bo))); + if (bo->tiling && !kgem->can_fence) + return NULL; + VG_CLEAR(gtt); retry_gtt: gtt.handle = bo->handle; if ((err = do_ioctl(kgem->fd, DRM_IOCTL_I915_GEM_MMAP_GTT, >t))) { + DBG(("%s: failed %d, throttling/cleaning caches\n", + __FUNCTION__, err)); assert(err != EINVAL); (void)__kgem_throttle_retire(kgem, 0); @@ -460,6 +600,8 @@ retry_mmap: kgem->fd, gtt.offset); if (ptr == MAP_FAILED) { err = errno; + DBG(("%s: failed %d, throttling/cleaning caches\n", + __FUNCTION__, err)); assert(err != EINVAL); if (__kgem_throttle_retire(kgem, 0)) @@ -498,6 +640,8 @@ retry_wc: wc.size = bytes(bo); wc.flags = I915_MMAP_WC; if ((err = do_ioctl(kgem->fd, LOCAL_IOCTL_I915_GEM_MMAP_v2, &wc))) { + DBG(("%s: failed %d, throttling/cleaning caches\n", + __FUNCTION__, err)); assert(err != EINVAL); if (__kgem_throttle_retire(kgem, 0)) @@ -519,16 +663,19 @@ retry_wc: static void *__kgem_bo_map__cpu(struct kgem *kgem, struct kgem_bo *bo) { - struct drm_i915_gem_mmap mmap_arg; + struct local_i915_gem_mmap arg; int err; + VG_CLEAR(arg); + arg.offset = 0; + retry: - VG_CLEAR(mmap_arg); - mmap_arg.handle = bo->handle; - mmap_arg.offset = 0; - mmap_arg.size = bytes(bo); - if ((err = do_ioctl(kgem->fd, DRM_IOCTL_I915_GEM_MMAP, &mmap_arg))) { - assert(err != EINVAL); + arg.handle = bo->handle; + arg.size = bytes(bo); + if ((err = do_ioctl(kgem->fd, LOCAL_IOCTL_I915_GEM_MMAP, &arg))) { + DBG(("%s: failed %d, throttling/cleaning caches\n", + __FUNCTION__, err)); + assert(err != -EINVAL || bo->prime); if (__kgem_throttle_retire(kgem, 0)) goto retry; @@ -536,15 +683,16 @@ retry: if (kgem_cleanup_cache(kgem)) goto retry; - ERR(("%s: failed to mmap handle=%d, %d bytes, into CPU domain: %d\n", - __FUNCTION__, bo->handle, bytes(bo), -err)); + ERR(("%s: failed to mmap handle=%d (prime? %d), %d bytes, into CPU domain: %d\n", + __FUNCTION__, bo->handle, bo->prime, bytes(bo), -err)); + bo->purged = 1; return NULL; } - VG(VALGRIND_MAKE_MEM_DEFINED(mmap_arg.addr_ptr, bytes(bo))); + VG(VALGRIND_MAKE_MEM_DEFINED(arg.addr_ptr, bytes(bo))); DBG(("%s: caching CPU vma for %d\n", __FUNCTION__, bo->handle)); - return bo->map__cpu = (void *)(uintptr_t)mmap_arg.addr_ptr; + return bo->map__cpu = (void *)(uintptr_t)arg.addr_ptr; } static int gem_write(int fd, uint32_t handle, @@ -634,16 +782,10 @@ static void kgem_bo_retire(struct kgem *kgem, struct kgem_bo *bo) assert(bo->exec == NULL); assert(list_is_empty(&bo->vma)); - if (bo->rq) { - __kgem_bo_clear_busy(bo); - kgem_retire(kgem); - assert_bo_retired(bo); - } else { - assert(bo->exec == NULL); - assert(list_is_empty(&bo->request)); - assert(!bo->needs_flush); - ASSERT_IDLE(kgem, bo->handle); - } + if (bo->rq) + __kgem_retire_requests_upto(kgem, bo); + ASSERT_IDLE(kgem, bo->handle); + assert_bo_retired(bo); } static void kgem_bo_maybe_retire(struct kgem *kgem, struct kgem_bo *bo) @@ -655,10 +797,8 @@ static void kgem_bo_maybe_retire(struct kgem *kgem, struct kgem_bo *bo) assert(list_is_empty(&bo->vma)); if (bo->rq) { - if (!__kgem_busy(kgem, bo->handle)) { - __kgem_bo_clear_busy(bo); - kgem_retire(kgem); - } + if (!__kgem_busy(kgem, bo->handle)) + __kgem_retire_requests_upto(kgem, bo); } else { assert(!bo->needs_flush); ASSERT_IDLE(kgem, bo->handle); @@ -694,6 +834,8 @@ retry: } if ((err = gem_write(kgem->fd, bo->handle, 0, length, data))) { + DBG(("%s: failed %d, throttling/cleaning caches\n", + __FUNCTION__, err)); assert(err != EINVAL); (void)__kgem_throttle_retire(kgem, 0); @@ -728,27 +870,21 @@ static uint32_t gem_create(int fd, int num_pages) return create.handle; } -static bool +static void kgem_bo_set_purgeable(struct kgem *kgem, struct kgem_bo *bo) { -#if DBG_NO_MADV - return true; -#else +#if !DBG_NO_MADV struct drm_i915_gem_madvise madv; assert(bo->exec == NULL); - assert(!bo->purged); VG_CLEAR(madv); madv.handle = bo->handle; madv.madv = I915_MADV_DONTNEED; if (do_ioctl(kgem->fd, DRM_IOCTL_I915_GEM_MADVISE, &madv) == 0) { - bo->purged = 1; - kgem->need_purge |= !madv.retained && bo->domain == DOMAIN_GPU; - return madv.retained; + bo->purged = true; + kgem->need_purge |= !madv.retained && bo->domain != DOMAIN_CPU; } - - return true; #endif } @@ -788,7 +924,7 @@ kgem_bo_clear_purgeable(struct kgem *kgem, struct kgem_bo *bo) madv.madv = I915_MADV_WILLNEED; if (do_ioctl(kgem->fd, DRM_IOCTL_I915_GEM_MADVISE, &madv) == 0) { bo->purged = !madv.retained; - kgem->need_purge |= !madv.retained && bo->domain == DOMAIN_GPU; + kgem->need_purge |= !madv.retained && bo->domain != DOMAIN_CPU; return madv.retained; } @@ -869,13 +1005,17 @@ static struct kgem_request *__kgem_request_alloc(struct kgem *kgem) { struct kgem_request *rq; - rq = __kgem_freed_request; - if (rq) { - __kgem_freed_request = *(struct kgem_request **)rq; + if (unlikely(kgem->wedged)) { + rq = &kgem->static_request; } else { - rq = malloc(sizeof(*rq)); - if (rq == NULL) - rq = &kgem->static_request; + rq = __kgem_freed_request; + if (rq) { + __kgem_freed_request = *(struct kgem_request **)rq; + } else { + rq = malloc(sizeof(*rq)); + if (rq == NULL) + rq = &kgem->static_request; + } } list_init(&rq->buffers); @@ -925,11 +1065,11 @@ total_ram_size(void) #ifdef HAVE_STRUCT_SYSINFO_TOTALRAM struct sysinfo info; if (sysinfo(&info) == 0) - return info.totalram * info.mem_unit; + return (size_t)info.totalram * info.mem_unit; #endif #ifdef _SC_PHYS_PAGES - return sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE); + return (size_t)sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE); #endif return 0; @@ -1187,7 +1327,7 @@ static bool test_has_caching(struct kgem *kgem) static bool test_has_userptr(struct kgem *kgem) { - uint32_t handle; + struct local_i915_gem_userptr arg; void *ptr; if (DBG_NO_USERPTR) @@ -1200,11 +1340,23 @@ static bool test_has_userptr(struct kgem *kgem) if (posix_memalign(&ptr, PAGE_SIZE, PAGE_SIZE)) return false; - handle = gem_userptr(kgem->fd, ptr, PAGE_SIZE, false); - gem_close(kgem->fd, handle); - free(ptr); + VG_CLEAR(arg); + arg.user_ptr = (uintptr_t)ptr; + arg.user_size = PAGE_SIZE; + arg.flags = I915_USERPTR_UNSYNCHRONIZED; - return handle != 0; + if (DBG_NO_UNSYNCHRONIZED_USERPTR || + do_ioctl(kgem->fd, LOCAL_IOCTL_I915_GEM_USERPTR, &arg)) { + arg.flags &= ~I915_USERPTR_UNSYNCHRONIZED; + if (do_ioctl(kgem->fd, LOCAL_IOCTL_I915_GEM_USERPTR, &arg)) + arg.handle = 0; + /* Leak the userptr bo to keep the mmu_notifier alive */ + } else { + gem_close(kgem->fd, arg.handle); + free(ptr); + } + + return arg.handle != 0; } static bool test_has_create2(struct kgem *kgem) @@ -1227,67 +1379,146 @@ static bool test_has_create2(struct kgem *kgem) #endif } -static bool test_has_secure_batches(struct kgem *kgem) +static bool test_can_blt_y(struct kgem *kgem) { - if (DBG_NO_SECURE_BATCHES) + struct drm_i915_gem_exec_object2 object; + uint32_t batch[] = { +#define MI_LOAD_REGISTER_IMM (0x22<<23 | (3-2)) +#define BCS_SWCTRL 0x22200 +#define BCS_SRC_Y (1 << 0) +#define BCS_DST_Y (1 << 1) + MI_LOAD_REGISTER_IMM, + BCS_SWCTRL, + (BCS_SRC_Y | BCS_DST_Y) << 16 | (BCS_SRC_Y | BCS_DST_Y), + + MI_LOAD_REGISTER_IMM, + BCS_SWCTRL, + (BCS_SRC_Y | BCS_DST_Y) << 16, + + MI_BATCH_BUFFER_END, + 0, + }; + int ret; + + if (DBG_NO_BLT_Y) return false; - return gem_param(kgem, LOCAL_I915_PARAM_HAS_SECURE_BATCHES) > 0; + if (kgem->gen < 060) + return false; + + memset(&object, 0, sizeof(object)); + object.handle = gem_create(kgem->fd, 1); + + ret = gem_write(kgem->fd, object.handle, 0, sizeof(batch), batch); + if (ret == 0) { + struct drm_i915_gem_execbuffer2 execbuf; + + memset(&execbuf, 0, sizeof(execbuf)); + execbuf.buffers_ptr = (uintptr_t)&object; + execbuf.buffer_count = 1; + execbuf.flags = KGEM_BLT; + + ret = do_ioctl(kgem->fd, + DRM_IOCTL_I915_GEM_EXECBUFFER2, + &execbuf); + } + gem_close(kgem->fd, object.handle); + + return ret == 0; } -static bool test_has_pinned_batches(struct kgem *kgem) +static bool gem_set_tiling(int fd, uint32_t handle, int tiling, int stride) { - if (DBG_NO_PINNED_BATCHES) + struct drm_i915_gem_set_tiling set_tiling; + + if (DBG_NO_TILING) return false; - return gem_param(kgem, LOCAL_I915_PARAM_HAS_PINNED_BATCHES) > 0; -} + VG_CLEAR(set_tiling); + set_tiling.handle = handle; + set_tiling.tiling_mode = tiling; + set_tiling.stride = stride; -static int kgem_get_screen_index(struct kgem *kgem) -{ - struct sna *sna = container_of(kgem, struct sna, kgem); - return sna->scrn->scrnIndex; + if (ioctl(fd, DRM_IOCTL_I915_GEM_SET_TILING, &set_tiling) == 0) + return set_tiling.tiling_mode == tiling; + + return false; } -static int __find_debugfs(struct kgem *kgem) +static bool test_can_scanout_y(struct kgem *kgem) { - int i; - - for (i = 0; i < DRM_MAX_MINOR; i++) { - char path[80]; + struct drm_mode_fb_cmd arg; + bool ret = false; - sprintf(path, "/sys/kernel/debug/dri/%d/i915_wedged", i); - if (access(path, R_OK) == 0) - return i; + if (DBG_NO_SCANOUT_Y) + return false; - sprintf(path, "/debug/dri/%d/i915_wedged", i); - if (access(path, R_OK) == 0) - return i; - } + VG_CLEAR(arg); + arg.width = 32; + arg.height = 32; + arg.pitch = 4*32; + arg.bpp = 32; + arg.depth = 24; + arg.handle = gem_create(kgem->fd, 1); + + if (gem_set_tiling(kgem->fd, arg.handle, I915_TILING_Y, arg.pitch)) + ret = do_ioctl(kgem->fd, DRM_IOCTL_MODE_ADDFB, &arg) == 0; + if (!ret) { + struct local_mode_fb_cmd2 { + uint32_t fb_id; + uint32_t width, height; + uint32_t pixel_format; + uint32_t flags; + + uint32_t handles[4]; + uint32_t pitches[4]; + uint32_t offsets[4]; + uint64_t modifiers[4]; + } f; +#define LOCAL_IOCTL_MODE_ADDFB2 DRM_IOWR(0xb8, struct local_mode_fb_cmd2) + memset(&f, 0, sizeof(f)); + f.width = arg.width; + f.height = arg.height; + f.handles[0] = arg.handle; + f.pitches[0] = arg.pitch; + f.modifiers[0] = (uint64_t)1 << 56 | 2; /* MOD_Y_TILED */ + f.pixel_format = 'X' | 'R' << 8 | '2' << 16 | '4' << 24; /* XRGB8888 */ + f.flags = 1 << 1; /* + modifier */ + if (drmIoctl(kgem->fd, LOCAL_IOCTL_MODE_ADDFB2, &f) == 0) { + ret = true; + arg.fb_id = f.fb_id; + } + } + do_ioctl(kgem->fd, DRM_IOCTL_MODE_RMFB, &arg.fb_id); + gem_close(kgem->fd, arg.handle); - return -1; + return ret; } -static int kgem_get_minor(struct kgem *kgem) +static bool test_has_secure_batches(struct kgem *kgem) { - struct stat st; + if (DBG_NO_SECURE_BATCHES) + return false; - if (fstat(kgem->fd, &st)) - return __find_debugfs(kgem); + return gem_param(kgem, LOCAL_I915_PARAM_HAS_SECURE_BATCHES) > 0; +} - if (!S_ISCHR(st.st_mode)) - return __find_debugfs(kgem); +static bool test_has_pinned_batches(struct kgem *kgem) +{ + if (DBG_NO_PINNED_BATCHES) + return false; - return st.st_rdev & 0x63; + return gem_param(kgem, LOCAL_I915_PARAM_HAS_PINNED_BATCHES) > 0; } static bool kgem_init_pinned_batches(struct kgem *kgem) { int count[2] = { 16, 4 }; int size[2] = { 1, 4 }; + int ret = 0; int n, i; - if (kgem->wedged) + if (unlikely(kgem->wedged)) return true; for (n = 0; n < ARRAY_SIZE(count); n++) { @@ -1311,7 +1542,8 @@ static bool kgem_init_pinned_batches(struct kgem *kgem) } pin.alignment = 0; - if (do_ioctl(kgem->fd, DRM_IOCTL_I915_GEM_PIN, &pin)) { + ret = do_ioctl(kgem->fd, DRM_IOCTL_I915_GEM_PIN, &pin); + if (ret) { gem_close(kgem->fd, pin.handle); free(bo); goto err; @@ -1333,6 +1565,16 @@ err: } } + /* If we fail to pin some memory for 830gm/845g, we need to disable + * acceleration as otherwise the machine will eventually fail. However, + * the kernel started arbitrarily rejecting PIN, so hope for the best + * if the ioctl no longer works. + */ + if (ret != -ENODEV && kgem->gen == 020) + return false; + + kgem->has_pinned_batches = false; + /* For simplicity populate the lists with a single unpinned bo */ for (n = 0; n < ARRAY_SIZE(count); n++) { struct kgem_bo *bo; @@ -1340,18 +1582,18 @@ err: handle = gem_create(kgem->fd, size[n]); if (handle == 0) - break; + return false; bo = __kgem_bo_alloc(handle, size[n]); if (bo == NULL) { gem_close(kgem->fd, handle); - break; + return false; } debug_alloc__bo(kgem, bo); list_add(&bo->list, &kgem->pinned_batches[n]); } - return false; + return true; } static void kgem_init_swizzling(struct kgem *kgem) @@ -1364,7 +1606,7 @@ static void kgem_init_swizzling(struct kgem *kgem) } tiling; #define LOCAL_IOCTL_I915_GEM_GET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_TILING, struct local_i915_gem_get_tiling_v2) - VG_CLEAR(tiling); + memset(&tiling, 0, sizeof(tiling)); tiling.handle = gem_create(kgem->fd, 1); if (!tiling.handle) return; @@ -1375,12 +1617,21 @@ static void kgem_init_swizzling(struct kgem *kgem) if (do_ioctl(kgem->fd, LOCAL_IOCTL_I915_GEM_GET_TILING, &tiling)) goto out; - if (kgem->gen < 50 && tiling.phys_swizzle_mode != tiling.swizzle_mode) + DBG(("%s: swizzle_mode=%d, phys_swizzle_mode=%d\n", + __FUNCTION__, tiling.swizzle_mode, tiling.phys_swizzle_mode)); + + kgem->can_fence = + !DBG_NO_TILING && + tiling.swizzle_mode != I915_BIT_6_SWIZZLE_UNKNOWN; + + if (kgem->gen < 050 && tiling.phys_swizzle_mode != tiling.swizzle_mode) goto out; - choose_memcpy_tiled_x(kgem, tiling.swizzle_mode); + if (!DBG_NO_DETILING) + choose_memcpy_tiled_x(kgem, tiling.swizzle_mode); out: gem_close(kgem->fd, tiling.handle); + DBG(("%s: can fence?=%d\n", __FUNCTION__, kgem->can_fence)); } static void kgem_fixup_relocs(struct kgem *kgem, struct kgem_bo *bo, int shrink) @@ -1399,6 +1650,7 @@ static void kgem_fixup_relocs(struct kgem *kgem, struct kgem_bo *bo, int shrink) bo->handle, (long long)bo->presumed_offset)); for (n = 0; n < kgem->nreloc__self; n++) { int i = kgem->reloc__self[n]; + uint64_t addr; assert(kgem->reloc[i].target_handle == ~0U); kgem->reloc[i].target_handle = bo->target_handle; @@ -1412,13 +1664,17 @@ static void kgem_fixup_relocs(struct kgem *kgem, struct kgem_bo *bo, int shrink) kgem->reloc[i].delta -= shrink; } - kgem->batch[kgem->reloc[i].offset/sizeof(uint32_t)] = - kgem->reloc[i].delta + bo->presumed_offset; + addr = (int)kgem->reloc[i].delta + bo->presumed_offset; + kgem->batch[kgem->reloc[i].offset/sizeof(uint32_t)] = addr; + if (kgem->gen >= 0100) + kgem->batch[kgem->reloc[i].offset/sizeof(uint32_t) + 1] = addr >> 32; } if (n == 256) { for (n = kgem->reloc__self[255]; n < kgem->nreloc; n++) { if (kgem->reloc[n].target_handle == ~0U) { + uint64_t addr; + kgem->reloc[n].target_handle = bo->target_handle; kgem->reloc[n].presumed_offset = bo->presumed_offset; @@ -1429,8 +1685,11 @@ static void kgem_fixup_relocs(struct kgem *kgem, struct kgem_bo *bo, int shrink) kgem->reloc[n].delta - shrink)); kgem->reloc[n].delta -= shrink; } - kgem->batch[kgem->reloc[n].offset/sizeof(uint32_t)] = - kgem->reloc[n].delta + bo->presumed_offset; + + addr = (int)kgem->reloc[n].delta + bo->presumed_offset; + kgem->batch[kgem->reloc[n].offset/sizeof(uint32_t)] = addr; + if (kgem->gen >= 0100) + kgem->batch[kgem->reloc[n].offset/sizeof(uint32_t) + 1] = addr >> 32; } } } @@ -1444,6 +1703,44 @@ static void kgem_fixup_relocs(struct kgem *kgem, struct kgem_bo *bo, int shrink) } } +static int kgem_bo_wait(struct kgem *kgem, struct kgem_bo *bo) +{ + struct local_i915_gem_wait { + uint32_t handle; + uint32_t flags; + int64_t timeout; + } wait; +#define LOCAL_I915_GEM_WAIT 0x2c +#define LOCAL_IOCTL_I915_GEM_WAIT DRM_IOWR(DRM_COMMAND_BASE + LOCAL_I915_GEM_WAIT, struct local_i915_gem_wait) + int ret; + + DBG(("%s: waiting for handle=%d\n", __FUNCTION__, bo->handle)); + if (bo->rq == NULL) + return 0; + + VG_CLEAR(wait); + wait.handle = bo->handle; + wait.flags = 0; + wait.timeout = -1; + ret = do_ioctl(kgem->fd, LOCAL_IOCTL_I915_GEM_WAIT, &wait); + if (ret) { + struct drm_i915_gem_set_domain set_domain; + + VG_CLEAR(set_domain); + set_domain.handle = bo->handle; + set_domain.read_domains = I915_GEM_DOMAIN_GTT; + set_domain.write_domain = I915_GEM_DOMAIN_GTT; + ret = do_ioctl(kgem->fd, + DRM_IOCTL_I915_GEM_SET_DOMAIN, + &set_domain); + } + + if (ret == 0) + __kgem_retire_requests_upto(kgem, bo); + + return ret; +} + static struct kgem_bo *kgem_new_batch(struct kgem *kgem) { struct kgem_bo *last; @@ -1464,20 +1761,41 @@ static struct kgem_bo *kgem_new_batch(struct kgem *kgem) if (!kgem->has_llc) flags |= CREATE_UNCACHED; +restart: kgem->batch_bo = kgem_create_linear(kgem, sizeof(uint32_t)*kgem->batch_size, flags); if (kgem->batch_bo) kgem->batch = kgem_bo_map__cpu(kgem, kgem->batch_bo); if (kgem->batch == NULL) { - DBG(("%s: unable to map batch bo, mallocing(size=%d)\n", - __FUNCTION__, - sizeof(uint32_t)*kgem->batch_size)); + int ring = kgem->ring == KGEM_BLT; + assert(ring < ARRAY_SIZE(kgem->requests)); + if (kgem->batch_bo) { kgem_bo_destroy(kgem, kgem->batch_bo); kgem->batch_bo = NULL; } + if (!list_is_empty(&kgem->requests[ring])) { + struct kgem_request *rq; + + rq = list_first_entry(&kgem->requests[ring], + struct kgem_request, list); + assert(rq->ring == ring); + assert(rq->bo); + assert(RQ(rq->bo->rq) == rq); + if (kgem_bo_wait(kgem, rq->bo) == 0) + goto restart; + } + + if (flags & CREATE_NO_THROTTLE) { + flags &= ~CREATE_NO_THROTTLE; + if (kgem_cleanup_cache(kgem)) + goto restart; + } + + DBG(("%s: unable to map batch bo, mallocing(size=%d)\n", + __FUNCTION__, sizeof(uint32_t)*kgem->batch_size)); if (posix_memalign((void **)&kgem->batch, PAGE_SIZE, ALIGN(sizeof(uint32_t) * kgem->batch_size, PAGE_SIZE))) { ERR(("%s: batch allocation failed, disabling acceleration\n", __FUNCTION__)); @@ -1495,18 +1813,79 @@ static struct kgem_bo *kgem_new_batch(struct kgem *kgem) return last; } -void kgem_init(struct kgem *kgem, int fd, struct pci_device *dev, unsigned gen) +static void +no_retire(struct kgem *kgem) +{ + (void)kgem; +} + +static void +no_expire(struct kgem *kgem) +{ + (void)kgem; +} + +static void +no_context_switch(struct kgem *kgem, int new_mode) +{ + (void)kgem; + (void)new_mode; +} + +static uint64_t get_gtt_size(int fd) { struct drm_i915_gem_get_aperture aperture; + struct local_i915_gem_context_param { + uint32_t context; + uint32_t size; + uint64_t param; +#define LOCAL_CONTEXT_PARAM_BAN_PERIOD 0x1 +#define LOCAL_CONTEXT_PARAM_NO_ZEROMAP 0x2 +#define LOCAL_CONTEXT_PARAM_GTT_SIZE 0x3 + uint64_t value; + } p; +#define LOCAL_I915_GEM_CONTEXT_GETPARAM 0x34 +#define LOCAL_IOCTL_I915_GEM_CONTEXT_GETPARAM DRM_IOWR (DRM_COMMAND_BASE + LOCAL_I915_GEM_CONTEXT_GETPARAM, struct local_i915_gem_context_param) + + memset(&aperture, 0, sizeof(aperture)); + + memset(&p, 0, sizeof(p)); + p.param = LOCAL_CONTEXT_PARAM_GTT_SIZE; + if (drmIoctl(fd, LOCAL_IOCTL_I915_GEM_CONTEXT_GETPARAM, &p) == 0) + aperture.aper_size = p.value; + if (aperture.aper_size == 0) + (void)drmIoctl(fd, DRM_IOCTL_I915_GEM_GET_APERTURE, &aperture); + if (aperture.aper_size == 0) + aperture.aper_size = 64*1024*1024; + + DBG(("%s: aperture size %lld, available now %lld\n", + __FUNCTION__, + (long long)aperture.aper_size, + (long long)aperture.aper_available_size)); + + /* clamp aperture to uint32_t for simplicity */ + if (aperture.aper_size > 0xc0000000) + aperture.aper_size = 0xc0000000; + + return aperture.aper_size; +} + +void kgem_init(struct kgem *kgem, int fd, struct pci_device *dev, unsigned gen) +{ size_t totalram; unsigned half_gpu_max; unsigned int i, j; + uint64_t gtt_size; DBG(("%s: fd=%d, gen=%d\n", __FUNCTION__, fd, gen)); kgem->fd = fd; kgem->gen = gen; + kgem->retire = no_retire; + kgem->expire = no_expire; + kgem->context_switch = no_context_switch; + list_init(&kgem->requests[0]); list_init(&kgem->requests[1]); list_init(&kgem->batch_buffers); @@ -1586,10 +1965,18 @@ void kgem_init(struct kgem *kgem, int fd, struct pci_device *dev, unsigned gen) DBG(("%s: can blt to cpu? %d\n", __FUNCTION__, kgem->can_blt_cpu)); + kgem->can_blt_y = test_can_blt_y(kgem); + DBG(("%s: can blit to Y-tiled surfaces? %d\n", __FUNCTION__, + kgem->can_blt_y)); + kgem->can_render_y = gen != 021 && (gen >> 3) != 4; DBG(("%s: can render to Y-tiled surfaces? %d\n", __FUNCTION__, kgem->can_render_y)); + kgem->can_scanout_y = test_can_scanout_y(kgem); + DBG(("%s: can scanout Y-tiled surfaces? %d\n", __FUNCTION__, + kgem->can_scanout_y)); + kgem->has_secure_batches = test_has_secure_batches(kgem); DBG(("%s: can use privileged batchbuffers? %d\n", __FUNCTION__, kgem->has_secure_batches)); @@ -1620,7 +2007,7 @@ void kgem_init(struct kgem *kgem, int fd, struct pci_device *dev, unsigned gen) if (!kgem->has_relaxed_delta && kgem->batch_size > 4*1024) kgem->batch_size = 4*1024; - if (!kgem_init_pinned_batches(kgem) && gen == 020) { + if (!kgem_init_pinned_batches(kgem)) { xf86DrvMsg(kgem_get_screen_index(kgem), X_WARNING, "Unable to reserve memory for GPU, disabling acceleration.\n"); __kgem_set_wedged(kgem); @@ -1640,35 +2027,24 @@ void kgem_init(struct kgem *kgem, int fd, struct pci_device *dev, unsigned gen) !DBG_NO_CPU && (kgem->has_llc | kgem->has_userptr | kgem->has_caching), kgem->has_llc, kgem->has_caching, kgem->has_userptr)); - VG_CLEAR(aperture); - aperture.aper_size = 0; - (void)do_ioctl(fd, DRM_IOCTL_I915_GEM_GET_APERTURE, &aperture); - if (aperture.aper_size == 0) - aperture.aper_size = 64*1024*1024; - - DBG(("%s: aperture size %lld, available now %lld\n", - __FUNCTION__, - (long long)aperture.aper_size, - (long long)aperture.aper_available_size)); - - kgem->aperture_total = aperture.aper_size; - kgem->aperture_high = aperture.aper_size * 3/4; - kgem->aperture_low = aperture.aper_size * 1/3; + gtt_size = get_gtt_size(fd); + kgem->aperture_total = gtt_size; + kgem->aperture_high = gtt_size * 3/4; + kgem->aperture_low = gtt_size * 1/3; if (gen < 033) { /* Severe alignment penalties */ kgem->aperture_high /= 2; kgem->aperture_low /= 2; } - DBG(("%s: aperture low=%d [%d], high=%d [%d]\n", __FUNCTION__, + DBG(("%s: aperture low=%u [%u], high=%u [%u]\n", __FUNCTION__, kgem->aperture_low, kgem->aperture_low / (1024*1024), kgem->aperture_high, kgem->aperture_high / (1024*1024))); kgem->aperture_mappable = 256 * 1024 * 1024; if (dev != NULL) kgem->aperture_mappable = agp_aperture_size(dev, gen); - if (kgem->aperture_mappable == 0 || - kgem->aperture_mappable > aperture.aper_size) - kgem->aperture_mappable = aperture.aper_size; + if (kgem->aperture_mappable == 0 || kgem->aperture_mappable > gtt_size) + kgem->aperture_mappable = gtt_size; DBG(("%s: aperture mappable=%d [%d MiB]\n", __FUNCTION__, kgem->aperture_mappable, kgem->aperture_mappable / (1024*1024))); @@ -1697,7 +2073,7 @@ void kgem_init(struct kgem *kgem, int fd, struct pci_device *dev, unsigned gen) __FUNCTION__)); totalram = kgem->aperture_total; } - DBG(("%s: total ram=%ld\n", __FUNCTION__, (long)totalram)); + DBG(("%s: total ram=%lld\n", __FUNCTION__, (long long)totalram)); if (kgem->max_object_size > totalram / 2) kgem->max_object_size = totalram / 2; if (kgem->max_gpu_size > totalram / 4) @@ -1749,11 +2125,11 @@ void kgem_init(struct kgem *kgem, int fd, struct pci_device *dev, unsigned gen) if (DBG_NO_CPU) kgem->max_cpu_size = 0; - DBG(("%s: maximum object size=%d\n", + DBG(("%s: maximum object size=%u\n", __FUNCTION__, kgem->max_object_size)); - DBG(("%s: large object thresold=%d\n", + DBG(("%s: large object thresold=%u\n", __FUNCTION__, kgem->large_object_size)); - DBG(("%s: max object sizes (gpu=%d, cpu=%d, tile upload=%d, copy=%d)\n", + DBG(("%s: max object sizes (gpu=%u, cpu=%u, tile upload=%u, copy=%u)\n", __FUNCTION__, kgem->max_gpu_size, kgem->max_cpu_size, kgem->max_upload_tile_size, kgem->max_copy_tile_size)); @@ -2043,8 +2419,34 @@ static void kgem_add_bo(struct kgem *kgem, struct kgem_bo *bo) kgem->flush |= bo->flush; } +static void kgem_clear_swctrl(struct kgem *kgem) +{ + uint32_t *b; + + if (kgem->bcs_state == 0) + return; + + DBG(("%s: clearin SWCTRL LRI from %x\n", + __FUNCTION__, kgem->bcs_state)); + + b = kgem->batch + kgem->nbatch; + kgem->nbatch += 7; + + *b++ = MI_FLUSH_DW; + *b++ = 0; + *b++ = 0; + *b++ = 0; + + *b++ = MI_LOAD_REGISTER_IMM; + *b++ = BCS_SWCTRL; + *b++ = (BCS_SRC_Y | BCS_DST_Y) << 16; + + kgem->bcs_state = 0; +} + static uint32_t kgem_end_batch(struct kgem *kgem) { + kgem_clear_swctrl(kgem); kgem->batch[kgem->nbatch++] = MI_BATCH_BUFFER_END; if (kgem->nbatch & 1) kgem->batch[kgem->nbatch++] = MI_NOOP; @@ -2150,13 +2552,16 @@ inline static void kgem_bo_move_to_inactive(struct kgem *kgem, assert(!bo->snoop); assert(!bo->flush); assert(!bo->needs_flush); + assert(!bo->delta); assert(list_is_empty(&bo->vma)); assert_tiling(kgem, bo); - assert_cacheing(kgem, bo); + assert_caching(kgem, bo); ASSERT_IDLE(kgem, bo->handle); if (bucket(bo) >= NUM_CACHE_BUCKETS) { if (bo->map__gtt) { + DBG(("%s: relinquishing large GTT mapping for handle=%d\n", + __FUNCTION__, bo->handle)); munmap(bo->map__gtt, bytes(bo)); bo->map__gtt = NULL; } @@ -2167,6 +2572,8 @@ inline static void kgem_bo_move_to_inactive(struct kgem *kgem, assert(list_is_empty(&bo->vma)); list_move(&bo->list, &kgem->inactive[bucket(bo)]); if (bo->map__gtt && !kgem_bo_can_map(kgem, bo)) { + DBG(("%s: relinquishing old GTT mapping for handle=%d\n", + __FUNCTION__, bo->handle)); munmap(bo->map__gtt, bytes(bo)); bo->map__gtt = NULL; } @@ -2191,6 +2598,10 @@ static struct kgem_bo *kgem_bo_replace_io(struct kgem_bo *bo) return bo; assert(!bo->snoop); + assert(!bo->purged); + assert(!bo->scanout); + assert(!bo->delta); + if (__kgem_freed_bo) { base = __kgem_freed_bo; __kgem_freed_bo = *(struct kgem_bo **)base; @@ -2221,6 +2632,7 @@ inline static void kgem_bo_remove_from_inactive(struct kgem *kgem, list_del(&bo->list); assert(bo->rq == NULL); assert(bo->exec == NULL); + assert(!bo->purged); if (!list_is_empty(&bo->vma)) { assert(bo->map__gtt || bo->map__wc || bo->map__cpu); list_del(&bo->vma); @@ -2305,7 +2717,6 @@ static void kgem_bo_move_to_scanout(struct kgem *kgem, struct kgem_bo *bo) list_move(&bo->list, &kgem->scanout); kgem->need_expire = true; - } static void kgem_bo_move_to_snoop(struct kgem *kgem, struct kgem_bo *bo) @@ -2316,6 +2727,8 @@ static void kgem_bo_move_to_snoop(struct kgem *kgem, struct kgem_bo *bo) assert(!bo->needs_flush); assert(bo->refcnt == 0); assert(bo->exec == NULL); + assert(!bo->purged); + assert(!bo->delta); if (DBG_NO_SNOOP_CACHE) { kgem_bo_free(kgem, bo); @@ -2351,8 +2764,7 @@ static bool kgem_bo_move_to_cache(struct kgem *kgem, struct kgem_bo *bo) kgem_bo_move_to_snoop(kgem, bo); } else if (bo->scanout) { kgem_bo_move_to_scanout(kgem, bo); - } else if ((bo = kgem_bo_replace_io(bo))->reusable && - kgem_bo_set_purgeable(kgem, bo)) { + } else if ((bo = kgem_bo_replace_io(bo))->reusable) { kgem_bo_move_to_inactive(kgem, bo); retired = true; } else @@ -2429,7 +2841,7 @@ void kgem_bo_undo(struct kgem *kgem, struct kgem_bo *bo) DBG(("%s: only handle in batch, discarding last operations for handle=%d\n", __FUNCTION__, bo->handle)); - assert(bo->exec == &kgem->exec[0]); + assert(bo->exec == &_kgem_dummy_exec || bo->exec == &kgem->exec[0]); assert(kgem->exec[0].handle == bo->handle); assert(RQ(bo->rq) == kgem->next_request); @@ -2457,16 +2869,23 @@ void kgem_bo_pair_undo(struct kgem *kgem, struct kgem_bo *a, struct kgem_bo *b) if (a == NULL || b == NULL) return; + assert(a != b); if (a->exec == NULL || b->exec == NULL) return; - DBG(("%s: only handles in batch, discarding last operations for handle=%d and handle=%d\n", - __FUNCTION__, a->handle, b->handle)); + DBG(("%s: only handles in batch, discarding last operations for handle=%d (index=%d) and handle=%d (index=%d)\n", + __FUNCTION__, + a->handle, a->proxy ? -1 : a->exec - kgem->exec, + b->handle, b->proxy ? -1 : b->exec - kgem->exec)); - assert(a->exec == &kgem->exec[0] || a->exec == &kgem->exec[1]); + assert(a->exec == &_kgem_dummy_exec || + a->exec == &kgem->exec[0] || + a->exec == &kgem->exec[1]); assert(a->handle == kgem->exec[0].handle || a->handle == kgem->exec[1].handle); assert(RQ(a->rq) == kgem->next_request); - assert(b->exec == &kgem->exec[0] || b->exec == &kgem->exec[1]); + assert(b->exec == &_kgem_dummy_exec || + b->exec == &kgem->exec[0] || + b->exec == &kgem->exec[1]); assert(b->handle == kgem->exec[0].handle || b->handle == kgem->exec[1].handle); assert(RQ(b->rq) == kgem->next_request); @@ -2532,7 +2951,7 @@ static void __kgem_bo_destroy(struct kgem *kgem, struct kgem_bo *bo) assert(bo->snoop == false); assert(bo->io == false); assert(bo->scanout == false); - assert_cacheing(kgem, bo); + assert_caching(kgem, bo); kgem_bo_undo(kgem, bo); assert(bo->refcnt == 0); @@ -2556,9 +2975,6 @@ static void __kgem_bo_destroy(struct kgem *kgem, struct kgem_bo *bo) assert(list_is_empty(&bo->request)); if (bo->map__cpu == NULL || bucket(bo) >= NUM_CACHE_BUCKETS) { - if (!kgem_bo_set_purgeable(kgem, bo)) - goto destroy; - if (!kgem->has_llc && bo->domain == DOMAIN_CPU) goto destroy; @@ -2647,7 +3063,7 @@ static bool kgem_retire__flushing(struct kgem *kgem) int count = 0; list_for_each_entry(bo, &kgem->flushing, request) count++; - DBG(("%s: %d bo on flushing list\n", __FUNCTION__, count)); + DBG(("%s: %d bo on flushing list, retired? %d\n", __FUNCTION__, count, retired)); } #endif @@ -2656,6 +3072,34 @@ static bool kgem_retire__flushing(struct kgem *kgem) return retired; } +static bool __kgem_bo_flush(struct kgem *kgem, struct kgem_bo *bo) +{ + struct drm_i915_gem_busy busy; + + if (!bo->needs_flush) + return false; + + bo->needs_flush = false; + + VG_CLEAR(busy); + busy.handle = bo->handle; + busy.busy = !kgem->wedged; + (void)do_ioctl(kgem->fd, DRM_IOCTL_I915_GEM_BUSY, &busy); + DBG(("%s: handle=%d, busy=%d, wedged=%d\n", + __FUNCTION__, bo->handle, busy.busy, kgem->wedged)); + + if (busy.busy == 0) + return false; + + DBG(("%s: moving %d to flushing\n", + __FUNCTION__, bo->handle)); + list_add(&bo->request, &kgem->flushing); + bo->rq = MAKE_REQUEST(kgem, !!(busy.busy & ~0x1ffff)); + bo->needs_flush = busy.busy & 0xffff; + kgem->need_retire = true; + return true; +} + static bool __kgem_retire_rq(struct kgem *kgem, struct kgem_request *rq) { bool retired = false; @@ -2663,6 +3107,8 @@ static bool __kgem_retire_rq(struct kgem *kgem, struct kgem_request *rq) DBG(("%s: request %d complete\n", __FUNCTION__, rq->bo->handle)); assert(RQ(rq->bo->rq) == rq); + assert(rq != (struct kgem_request *)kgem); + assert(rq != &kgem->static_request); if (rq == kgem->fence[rq->ring]) kgem->fence[rq->ring] = NULL; @@ -2680,19 +3126,14 @@ static bool __kgem_retire_rq(struct kgem *kgem, struct kgem_request *rq) list_del(&bo->request); - if (bo->needs_flush) - bo->needs_flush = __kgem_busy(kgem, bo->handle); - if (bo->needs_flush) { - DBG(("%s: moving %d to flushing\n", + if (unlikely(__kgem_bo_flush(kgem, bo))) { + assert(bo != rq->bo); + DBG(("%s: movied %d to flushing\n", __FUNCTION__, bo->handle)); - list_add(&bo->request, &kgem->flushing); - bo->rq = MAKE_REQUEST(kgem, RQ_RING(bo->rq)); - kgem->need_retire = true; continue; } bo->domain = DOMAIN_NONE; - bo->gtt_dirty = false; bo->rq = NULL; if (bo->refcnt) continue; @@ -2706,14 +3147,8 @@ static bool __kgem_retire_rq(struct kgem *kgem, struct kgem_request *rq) assert(rq->bo->refcnt > 0); if (--rq->bo->refcnt == 0) { - if (kgem_bo_set_purgeable(kgem, rq->bo)) { - kgem_bo_move_to_inactive(kgem, rq->bo); - retired = true; - } else { - DBG(("%s: closing %d\n", - __FUNCTION__, rq->bo->handle)); - kgem_bo_free(kgem, rq->bo); - } + kgem_bo_move_to_inactive(kgem, rq->bo); + retired = true; } __kgem_request_free(rq); @@ -2724,13 +3159,18 @@ static bool kgem_retire__requests_ring(struct kgem *kgem, int ring) { bool retired = false; + assert(ring < ARRAY_SIZE(kgem->requests)); while (!list_is_empty(&kgem->requests[ring])) { struct kgem_request *rq; + DBG(("%s: retiring ring %d\n", __FUNCTION__, ring)); + rq = list_first_entry(&kgem->requests[ring], struct kgem_request, list); assert(rq->ring == ring); + assert(rq->bo); + assert(RQ(rq->bo->rq) == rq); if (__kgem_busy(kgem, rq->bo->handle)) break; @@ -2751,8 +3191,8 @@ static bool kgem_retire__requests_ring(struct kgem *kgem, int ring) struct kgem_request, list)->bo; - DBG(("%s: ring=%d, %d outstanding requests, oldest=%d\n", - __FUNCTION__, ring, count, bo ? bo->handle : 0)); + DBG(("%s: ring=%d, %d outstanding requests, oldest=%d, retired? %d\n", + __FUNCTION__, ring, count, bo ? bo->handle : 0, retired)); } #endif @@ -2824,6 +3264,8 @@ bool __kgem_ring_is_idle(struct kgem *kgem, int ring) rq = list_last_entry(&kgem->requests[ring], struct kgem_request, list); assert(rq->ring == ring); + assert(rq->bo); + assert(RQ(rq->bo->rq) == rq); if (__kgem_busy(kgem, rq->bo->handle)) { DBG(("%s: last requests handle=%d still busy\n", __FUNCTION__, rq->bo->handle)); @@ -2845,23 +3287,30 @@ bool __kgem_ring_is_idle(struct kgem *kgem, int ring) return true; } -void __kgem_retire_requests_upto(struct kgem *kgem, struct kgem_bo *bo) +bool __kgem_retire_requests_upto(struct kgem *kgem, struct kgem_bo *bo) { - struct kgem_request *rq = bo->rq, *tmp; - struct list *requests = &kgem->requests[RQ_RING(rq) == I915_EXEC_BLT]; + struct kgem_request * const rq = RQ(bo->rq), *tmp; + struct list *requests = &kgem->requests[rq->ring]; + + DBG(("%s(handle=%d, ring=%d)\n", __FUNCTION__, bo->handle, rq->ring)); - rq = RQ(rq); assert(rq != &kgem->static_request); if (rq == (struct kgem_request *)kgem) { __kgem_bo_clear_busy(bo); - return; + return false; } + assert(rq->ring < ARRAY_SIZE(kgem->requests)); do { tmp = list_first_entry(requests, struct kgem_request, list); assert(tmp->ring == rq->ring); __kgem_retire_rq(kgem, tmp); } while (tmp != rq); + + assert(bo->needs_flush || bo->rq == NULL); + assert(bo->needs_flush || list_is_empty(&bo->request)); + assert(bo->needs_flush || bo->domain == DOMAIN_NONE); + return bo->rq; } #if 0 @@ -2932,6 +3381,7 @@ static void kgem_commit(struct kgem *kgem) bo->binding.offset = 0; bo->domain = DOMAIN_GPU; bo->gpu_dirty = false; + bo->gtt_dirty = false; if (bo->proxy) { /* proxies are not used for domain tracking */ @@ -2955,6 +3405,23 @@ static void kgem_commit(struct kgem *kgem) kgem_throttle(kgem); } + while (!list_is_empty(&rq->buffers)) { + bo = list_first_entry(&rq->buffers, + struct kgem_bo, + request); + + assert(RQ(bo->rq) == rq); + assert(bo->exec == NULL); + assert(bo->domain == DOMAIN_GPU); + + list_del(&bo->request); + bo->domain = DOMAIN_NONE; + bo->rq = NULL; + + if (bo->refcnt == 0) + _kgem_bo_destroy(kgem, bo); + } + kgem_retire(kgem); assert(list_is_empty(&rq->buffers)); @@ -2964,7 +3431,9 @@ static void kgem_commit(struct kgem *kgem) gem_close(kgem->fd, rq->bo->handle); kgem_cleanup_cache(kgem); } else { + assert(rq != (struct kgem_request *)kgem); assert(rq->ring < ARRAY_SIZE(kgem->requests)); + assert(rq->bo); list_add_tail(&rq->list, &kgem->requests[rq->ring]); kgem->need_throttle = kgem->need_retire = 1; @@ -2988,8 +3457,10 @@ static void kgem_close_inactive(struct kgem *kgem) { unsigned int i; - for (i = 0; i < ARRAY_SIZE(kgem->inactive); i++) + for (i = 0; i < ARRAY_SIZE(kgem->inactive); i++) { kgem_close_list(kgem, &kgem->inactive[i]); + assert(list_is_empty(&kgem->inactive[i])); + } } static void kgem_finish_buffers(struct kgem *kgem) @@ -3079,10 +3550,13 @@ static void kgem_finish_buffers(struct kgem *kgem) kgem->has_handle_lut ? bo->base.target_handle : shrink->handle; for (n = 0; n < kgem->nreloc; n++) { if (kgem->reloc[n].target_handle == bo->base.target_handle) { + uint64_t addr = (int)kgem->reloc[n].delta + shrink->presumed_offset; + kgem->batch[kgem->reloc[n].offset/sizeof(kgem->batch[0])] = addr; + if (kgem->gen >= 0100) + kgem->batch[kgem->reloc[n].offset/sizeof(kgem->batch[0]) + 1] = addr >> 32; + kgem->reloc[n].target_handle = shrink->target_handle; kgem->reloc[n].presumed_offset = shrink->presumed_offset; - kgem->batch[kgem->reloc[n].offset/sizeof(kgem->batch[0])] = - kgem->reloc[n].delta + shrink->presumed_offset; } } @@ -3124,10 +3598,13 @@ static void kgem_finish_buffers(struct kgem *kgem) kgem->has_handle_lut ? bo->base.target_handle : shrink->handle; for (n = 0; n < kgem->nreloc; n++) { if (kgem->reloc[n].target_handle == bo->base.target_handle) { + uint64_t addr = (int)kgem->reloc[n].delta + shrink->presumed_offset; + kgem->batch[kgem->reloc[n].offset/sizeof(kgem->batch[0])] = addr; + if (kgem->gen >= 0100) + kgem->batch[kgem->reloc[n].offset/sizeof(kgem->batch[0]) + 1] = addr >> 32; + kgem->reloc[n].target_handle = shrink->target_handle; kgem->reloc[n].presumed_offset = shrink->presumed_offset; - kgem->batch[kgem->reloc[n].offset/sizeof(kgem->batch[0])] = - kgem->reloc[n].delta + shrink->presumed_offset; } } @@ -3195,6 +3672,9 @@ static void kgem_cleanup(struct kgem *kgem) kgem_bo_free(kgem, bo); } + if (--rq->bo->refcnt == 0) + kgem_bo_free(kgem, rq->bo); + __kgem_request_free(rq); } } @@ -3210,7 +3690,9 @@ kgem_batch_write(struct kgem *kgem, char *ptr; int ret; - ASSERT_IDLE(kgem, bo->handle); + assert(bo->exec == NULL); + assert(bo->rq == NULL); + assert(!__kgem_busy(kgem, bo->handle)); #if DBG_NO_EXEC { @@ -3359,67 +3841,66 @@ static int compact_batch_surface(struct kgem *kgem, int *shrink) { int size, n; - if (!kgem->has_relaxed_delta) - return kgem->batch_size * sizeof(uint32_t); + if (!kgem->has_relaxed_delta) + return kgem->batch_size * sizeof(uint32_t); + + /* See if we can pack the contents into one or two pages */ + n = ALIGN(kgem->batch_size, 1024); + size = n - kgem->surface + kgem->nbatch; + size = ALIGN(size, 1024); + + *shrink = (n - size) * sizeof(uint32_t); + return size * sizeof(uint32_t); +} + +static struct kgem_bo *first_available(struct kgem *kgem, struct list *list) +{ + struct kgem_bo *bo; + + list_for_each_entry(bo, list, list) { + assert(bo->refcnt > 0); + + if (bo->rq) { + assert(RQ(bo->rq)->bo == bo); + if (__kgem_busy(kgem, bo->handle)) + break; + + __kgem_retire_rq(kgem, RQ(bo->rq)); + assert(bo->rq == NULL); + } + + if (bo->refcnt > 1) + continue; - /* See if we can pack the contents into one or two pages */ - n = ALIGN(kgem->batch_size, 1024); - size = n - kgem->surface + kgem->nbatch; - size = ALIGN(size, 1024); + list_move_tail(&bo->list, list); + return kgem_bo_reference(bo); + } - *shrink = (n - size) * sizeof(uint32_t); - return size * sizeof(uint32_t); + return NULL; } static struct kgem_bo * kgem_create_batch(struct kgem *kgem) { -#if !DBG_NO_SHRINK_BATCHES - struct drm_i915_gem_set_domain set_domain; struct kgem_bo *bo; - int shrink = 0; - int size; + int size, shrink = 0; +#if !DBG_NO_SHRINK_BATCHES if (kgem->surface != kgem->batch_size) size = compact_batch_surface(kgem, &shrink); else size = kgem->nbatch * sizeof(uint32_t); if (size <= 4096) { - bo = list_first_entry(&kgem->pinned_batches[0], - struct kgem_bo, - list); - if (!bo->rq) { -out_4096: - assert(bo->refcnt > 0); - list_move_tail(&bo->list, &kgem->pinned_batches[0]); - bo = kgem_bo_reference(bo); + bo = first_available(kgem, &kgem->pinned_batches[0]); + if (bo) goto write; - } - - if (!__kgem_busy(kgem, bo->handle)) { - assert(RQ(bo->rq)->bo == bo); - __kgem_retire_rq(kgem, RQ(bo->rq)); - goto out_4096; - } } if (size <= 16384) { - bo = list_first_entry(&kgem->pinned_batches[1], - struct kgem_bo, - list); - if (!bo->rq) { -out_16384: - assert(bo->refcnt > 0); - list_move_tail(&bo->list, &kgem->pinned_batches[1]); - bo = kgem_bo_reference(bo); + bo = first_available(kgem, &kgem->pinned_batches[1]); + if (bo) goto write; - } - - if (!__kgem_busy(kgem, bo->handle)) { - __kgem_retire_rq(kgem, RQ(bo->rq)); - goto out_16384; - } } if (kgem->gen == 020) { @@ -3443,16 +3924,8 @@ out_16384: list_move_tail(&bo->list, &kgem->pinned_batches[size > 4096]); DBG(("%s: syncing due to busy batches\n", __FUNCTION__)); - - VG_CLEAR(set_domain); - set_domain.handle = bo->handle; - set_domain.read_domains = I915_GEM_DOMAIN_GTT; - set_domain.write_domain = I915_GEM_DOMAIN_GTT; - if (do_ioctl(kgem->fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain)) { - DBG(("%s: sync: GPU hang detected\n", __FUNCTION__)); - kgem_throttle(kgem); + if (kgem_bo_wait(kgem, bo)) return NULL; - } kgem_retire(kgem); assert(bo->rq == NULL); @@ -3460,9 +3933,14 @@ out_16384: goto write; } } +#else + if (kgem->surface != kgem->batch_size) + size = kgem->batch_size * sizeof(uint32_t); + else + size = kgem->nbatch * sizeof(uint32_t); +#endif - bo = NULL; - if (!kgem->has_llc) { + if (!kgem->batch_bo || !kgem->has_llc) { bo = kgem_create_linear(kgem, size, CREATE_NO_THROTTLE); if (bo) { write: @@ -3471,14 +3949,11 @@ write: kgem_bo_destroy(kgem, bo); return NULL; } + return bo; } } - if (bo == NULL) - bo = kgem_new_batch(kgem); - return bo; -#else + return kgem_new_batch(kgem); -#endif } #if !NDEBUG @@ -3530,7 +4005,7 @@ static void dump_fence_regs(struct kgem *kgem) static int do_execbuf(struct kgem *kgem, struct drm_i915_gem_execbuffer2 *execbuf) { - int ret, err; + int ret; retry: ret = do_ioctl(kgem->fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, execbuf); @@ -3547,26 +4022,25 @@ retry: /* last gasp */ ret = do_ioctl(kgem->fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, execbuf); - if (ret == 0) - return 0; + if (ret != -ENOSPC) + return ret; + + /* One final trick up our sleeve for when we run out of space. + * We turn everything off to free up our pinned framebuffers, + * sprites and cursors, and try just one more time. + */ xf86DrvMsg(kgem_get_screen_index(kgem), X_WARNING, "Failed to submit rendering commands, trying again with outputs disabled.\n"); - /* One last trick up our sleeve for when we run out of space. - * We turn everything off to free up our pinned framebuffers, - * sprites and cursors, and try one last time. - */ - err = errno; - if (sna_mode_disable(container_of(kgem, struct sna, kgem))) { + if (sna_mode_disable(__to_sna(kgem))) { kgem_cleanup_cache(kgem); ret = do_ioctl(kgem->fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, execbuf); DBG(("%s: last_gasp ret=%d\n", __FUNCTION__, ret)); - sna_mode_enable(container_of(kgem, struct sna, kgem)); + sna_mode_enable(__to_sna(kgem)); } - errno = err; return ret; } @@ -3575,6 +4049,7 @@ void _kgem_submit(struct kgem *kgem) { struct kgem_request *rq; uint32_t batch_end; + int i, ret; assert(!DBG_NO_HW); assert(!kgem->wedged); @@ -3609,7 +4084,6 @@ void _kgem_submit(struct kgem *kgem) rq->bo = kgem_create_batch(kgem); if (rq->bo) { struct drm_i915_gem_execbuffer2 execbuf; - int i, ret; assert(!rq->bo->needs_flush); @@ -3619,7 +4093,8 @@ void _kgem_submit(struct kgem *kgem) kgem->exec[i].relocs_ptr = (uintptr_t)kgem->reloc; kgem->exec[i].alignment = 0; kgem->exec[i].offset = rq->bo->presumed_offset; - kgem->exec[i].flags = 0; + /* Make sure the kernel releases any fence, ignored if gen4+ */ + kgem->exec[i].flags = EXEC_OBJECT_NEEDS_FENCE; kgem->exec[i].rsvd1 = 0; kgem->exec[i].rsvd2 = 0; @@ -3631,7 +4106,8 @@ void _kgem_submit(struct kgem *kgem) memset(&execbuf, 0, sizeof(execbuf)); execbuf.buffers_ptr = (uintptr_t)kgem->exec; execbuf.buffer_count = kgem->nexec; - execbuf.batch_len = batch_end*sizeof(uint32_t); + if (kgem->gen < 030) + execbuf.batch_len = batch_end*sizeof(uint32_t); execbuf.flags = kgem->ring | kgem->batch_flags; if (DBG_DUMP) { @@ -3645,91 +4121,98 @@ void _kgem_submit(struct kgem *kgem) } ret = do_execbuf(kgem, &execbuf); - if (DEBUG_SYNC && ret == 0) { - struct drm_i915_gem_set_domain set_domain; - - VG_CLEAR(set_domain); - set_domain.handle = rq->bo->handle; - set_domain.read_domains = I915_GEM_DOMAIN_GTT; - set_domain.write_domain = I915_GEM_DOMAIN_GTT; + } else + ret = -ENOMEM; - ret = do_ioctl(kgem->fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain); + if (ret < 0) { + kgem_throttle(kgem); + if (!kgem->wedged) { + xf86DrvMsg(kgem_get_screen_index(kgem), X_ERROR, + "Failed to submit rendering commands (%s), disabling acceleration.\n", + strerror(-ret)); + __kgem_set_wedged(kgem); } - if (ret < 0) { - kgem_throttle(kgem); - if (!kgem->wedged) { - xf86DrvMsg(kgem_get_screen_index(kgem), X_ERROR, - "Failed to submit rendering commands, disabling acceleration.\n"); - __kgem_set_wedged(kgem); - } #if !NDEBUG - ErrorF("batch[%d/%d]: %d %d %d, nreloc=%d, nexec=%d, nfence=%d, aperture=%d, fenced=%d, high=%d,%d: errno=%d\n", - kgem->mode, kgem->ring, batch_end, kgem->nbatch, kgem->surface, - kgem->nreloc, kgem->nexec, kgem->nfence, kgem->aperture, kgem->aperture_fenced, kgem->aperture_high, kgem->aperture_total, -ret); + ErrorF("batch[%d/%d]: %d %d %d, nreloc=%d, nexec=%d, nfence=%d, aperture=%d, fenced=%d, high=%d,%d: errno=%d\n", + kgem->mode, kgem->ring, batch_end, kgem->nbatch, kgem->surface, + kgem->nreloc, kgem->nexec, kgem->nfence, kgem->aperture, kgem->aperture_fenced, kgem->aperture_high, kgem->aperture_total, -ret); - for (i = 0; i < kgem->nexec; i++) { - struct kgem_bo *bo, *found = NULL; + for (i = 0; i < kgem->nexec; i++) { + struct kgem_bo *bo, *found = NULL; - list_for_each_entry(bo, &kgem->next_request->buffers, request) { - if (bo->handle == kgem->exec[i].handle) { - found = bo; - break; - } + list_for_each_entry(bo, &kgem->next_request->buffers, request) { + if (bo->handle == kgem->exec[i].handle) { + found = bo; + break; } - ErrorF("exec[%d] = handle:%d, presumed offset: %x, size: %d, tiling %d, fenced %d, snooped %d, deleted %d\n", - i, - kgem->exec[i].handle, - (int)kgem->exec[i].offset, - found ? kgem_bo_size(found) : -1, - found ? found->tiling : -1, - (int)(kgem->exec[i].flags & EXEC_OBJECT_NEEDS_FENCE), - found ? found->snoop : -1, - found ? found->purged : -1); } - for (i = 0; i < kgem->nreloc; i++) { - ErrorF("reloc[%d] = pos:%d, target:%d, delta:%d, read:%x, write:%x, offset:%x\n", - i, - (int)kgem->reloc[i].offset, - kgem->reloc[i].target_handle, - kgem->reloc[i].delta, - kgem->reloc[i].read_domains, - kgem->reloc[i].write_domain, - (int)kgem->reloc[i].presumed_offset); + ErrorF("exec[%d] = handle:%d, presumed offset: %x, size: %d, tiling %d, fenced %d, snooped %d, deleted %d\n", + i, + kgem->exec[i].handle, + (int)kgem->exec[i].offset, + found ? kgem_bo_size(found) : -1, + found ? found->tiling : -1, + (int)(kgem->exec[i].flags & EXEC_OBJECT_NEEDS_FENCE), + found ? found->snoop : -1, + found ? found->purged : -1); + } + for (i = 0; i < kgem->nreloc; i++) { + ErrorF("reloc[%d] = pos:%d, target:%d, delta:%d, read:%x, write:%x, offset:%x\n", + i, + (int)kgem->reloc[i].offset, + kgem->reloc[i].target_handle, + kgem->reloc[i].delta, + kgem->reloc[i].read_domains, + kgem->reloc[i].write_domain, + (int)kgem->reloc[i].presumed_offset); + } + + { + struct drm_i915_gem_get_aperture aperture; + if (do_ioctl(kgem->fd, DRM_IOCTL_I915_GEM_GET_APERTURE, &aperture) == 0) + ErrorF("Aperture size %lld, available %lld\n", + (long long)aperture.aper_size, + (long long)aperture.aper_available_size); + } + + if (ret == -ENOSPC) + dump_gtt_info(kgem); + if (ret == -EDEADLK) + dump_fence_regs(kgem); + + if (DEBUG_SYNC) { + int fd = open("/tmp/batchbuffer", O_WRONLY | O_CREAT | O_APPEND, 0666); + if (fd != -1) { + int ignored = write(fd, kgem->batch, batch_end*sizeof(uint32_t)); + assert(ignored == batch_end*sizeof(uint32_t)); + close(fd); } - { - struct drm_i915_gem_get_aperture aperture; - if (do_ioctl(kgem->fd, DRM_IOCTL_I915_GEM_GET_APERTURE, &aperture) == 0) - ErrorF("Aperture size %lld, available %lld\n", - (long long)aperture.aper_size, - (long long)aperture.aper_available_size); - } + FatalError("SNA: failed to submit batchbuffer, errno=%d\n", -ret); + } +#endif + } else { + if (DEBUG_SYNC) { + struct drm_i915_gem_set_domain set_domain; - if (ret == -ENOSPC) - dump_gtt_info(kgem); - if (ret == -EDEADLK) - dump_fence_regs(kgem); - - if (DEBUG_SYNC) { - int fd = open("/tmp/batchbuffer", O_WRONLY | O_CREAT | O_APPEND, 0666); - if (fd != -1) { - int ignored = write(fd, kgem->batch, batch_end*sizeof(uint32_t)); - assert(ignored == batch_end*sizeof(uint32_t)); - close(fd); - } + VG_CLEAR(set_domain); + set_domain.handle = rq->bo->handle; + set_domain.read_domains = I915_GEM_DOMAIN_GTT; + set_domain.write_domain = I915_GEM_DOMAIN_GTT; - FatalError("SNA: failed to submit batchbuffer, errno=%d\n", -ret); - } -#endif + ret = do_ioctl(kgem->fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain); } - } + #if SHOW_BATCH_AFTER - if (gem_read(kgem->fd, rq->bo->handle, kgem->batch, 0, batch_end*sizeof(uint32_t)) == 0) - __kgem_batch_debug(kgem, batch_end); + if (gem_read(kgem->fd, rq->bo->handle, kgem->batch, 0, batch_end*sizeof(uint32_t)) == 0) + __kgem_batch_debug(kgem, batch_end); #endif - kgem_commit(kgem); - if (kgem->wedged) + + kgem_commit(kgem); + } + + if (unlikely(kgem->wedged)) kgem_cleanup(kgem); kgem_reset(kgem); @@ -3737,49 +4220,14 @@ void _kgem_submit(struct kgem *kgem) assert(kgem->next_request != NULL); } -static bool find_hang_state(struct kgem *kgem, char *path, int maxlen) -{ - int minor = kgem_get_minor(kgem); - - /* Search for our hang state in a few canonical locations. - * In the unlikely event of having multiple devices, we - * will need to check which minor actually corresponds to ours. - */ - - snprintf(path, maxlen, "/sys/class/drm/card%d/error", minor); - if (access(path, R_OK) == 0) - return true; - - snprintf(path, maxlen, "/sys/kernel/debug/dri/%d/i915_error_state", minor); - if (access(path, R_OK) == 0) - return true; - - snprintf(path, maxlen, "/debug/dri/%d/i915_error_state", minor); - if (access(path, R_OK) == 0) - return true; - - path[0] = '\0'; - return false; -} - void kgem_throttle(struct kgem *kgem) { - if (kgem->wedged) + if (unlikely(kgem->wedged)) return; if (__kgem_throttle(kgem, true)) { - static int once; - char path[128]; - xf86DrvMsg(kgem_get_screen_index(kgem), X_ERROR, "Detected a hung GPU, disabling acceleration.\n"); - if (!once && find_hang_state(kgem, path, sizeof(path))) { - xf86DrvMsg(kgem_get_screen_index(kgem), X_ERROR, - "When reporting this, please include %s and the full dmesg.\n", - path); - once = 1; - } - __kgem_set_wedged(kgem); kgem->need_throttle = false; } @@ -3860,7 +4308,8 @@ bool kgem_expire_cache(struct kgem *kgem) bool idle; unsigned int i; - time(&now); + if (!time(&now)) + return false; while (__kgem_freed_bo) { bo = __kgem_freed_bo; @@ -3875,7 +4324,7 @@ bool kgem_expire_cache(struct kgem *kgem) } kgem_clean_large_cache(kgem); - if (container_of(kgem, struct sna, kgem)->scrn->vtSema) + if (__to_sna(kgem)->scrn->vtSema) kgem_clean_scanout_cache(kgem); expire = 0; @@ -3885,6 +4334,7 @@ bool kgem_expire_cache(struct kgem *kgem) break; } + assert(now); bo->delta = now; } if (expire) { @@ -3909,7 +4359,7 @@ bool kgem_expire_cache(struct kgem *kgem) #endif kgem_retire(kgem); - if (kgem->wedged) + if (unlikely(kgem->wedged)) kgem_cleanup(kgem); kgem->expire(kgem); @@ -3930,6 +4380,8 @@ bool kgem_expire_cache(struct kgem *kgem) break; } + assert(now); + kgem_bo_set_purgeable(kgem, bo); bo->delta = now; } } @@ -3960,16 +4412,11 @@ bool kgem_expire_cache(struct kgem *kgem) count++; size += bytes(bo); kgem_bo_free(kgem, bo); - DBG(("%s: expiring %d\n", + DBG(("%s: expiring handle=%d\n", __FUNCTION__, bo->handle)); } } - if (!list_is_empty(&preserve)) { - preserve.prev->next = kgem->inactive[i].next; - kgem->inactive[i].next->prev = preserve.prev; - kgem->inactive[i].next = preserve.next; - preserve.next->prev = &kgem->inactive[i]; - } + list_splice_tail(&preserve, &kgem->inactive[i]); } #ifdef DEBUG_MEMORY @@ -3998,31 +4445,30 @@ bool kgem_cleanup_cache(struct kgem *kgem) unsigned int i; int n; + DBG(("%s\n", __FUNCTION__)); + /* sync to the most recent request */ for (n = 0; n < ARRAY_SIZE(kgem->requests); n++) { if (!list_is_empty(&kgem->requests[n])) { struct kgem_request *rq; - struct drm_i915_gem_set_domain set_domain; - rq = list_first_entry(&kgem->requests[n], - struct kgem_request, - list); + rq = list_last_entry(&kgem->requests[n], + struct kgem_request, + list); DBG(("%s: sync on cleanup\n", __FUNCTION__)); - - VG_CLEAR(set_domain); - set_domain.handle = rq->bo->handle; - set_domain.read_domains = I915_GEM_DOMAIN_GTT; - set_domain.write_domain = I915_GEM_DOMAIN_GTT; - (void)do_ioctl(kgem->fd, - DRM_IOCTL_I915_GEM_SET_DOMAIN, - &set_domain); + assert(rq->ring == n); + assert(rq->bo); + assert(RQ(rq->bo->rq) == rq); + kgem_bo_wait(kgem, rq->bo); } + assert(list_is_empty(&kgem->requests[n])); } kgem_retire(kgem); kgem_cleanup(kgem); + DBG(("%s: need_expire?=%d\n", __FUNCTION__, kgem->need_expire)); if (!kgem->need_expire) return false; @@ -4049,6 +4495,8 @@ bool kgem_cleanup_cache(struct kgem *kgem) kgem->need_purge = false; kgem->need_expire = false; + + DBG(("%s: complete\n", __FUNCTION__)); return true; } @@ -4079,16 +4527,15 @@ retry_large: goto discard; if (bo->tiling != I915_TILING_NONE) { - if (use_active) + if (use_active && kgem->gen < 040) goto discard; - if (!gem_set_tiling(kgem->fd, bo->handle, + if (!kgem_set_tiling(kgem, bo, I915_TILING_NONE, 0)) goto discard; - - bo->tiling = I915_TILING_NONE; - bo->pitch = 0; } + assert(bo->tiling == I915_TILING_NONE); + bo->pitch = 0; if (bo->purged && !kgem_bo_clear_purgeable(kgem, bo)) goto discard; @@ -4169,17 +4616,17 @@ discard: break; } - if (I915_TILING_NONE != bo->tiling && - !gem_set_tiling(kgem->fd, bo->handle, - I915_TILING_NONE, 0)) - continue; + if (!kgem_set_tiling(kgem, bo, I915_TILING_NONE, 0)) { + kgem_bo_free(kgem, bo); + break; + } kgem_bo_remove_from_inactive(kgem, bo); assert(list_is_empty(&bo->vma)); assert(list_is_empty(&bo->list)); - bo->tiling = I915_TILING_NONE; - bo->pitch = 0; + assert(bo->tiling == I915_TILING_NONE); + assert(bo->pitch == 0); bo->delta = 0; DBG((" %s: found handle=%d (num_pages=%d) in linear vma cache\n", __FUNCTION__, bo->handle, num_pages(bo))); @@ -4225,13 +4672,13 @@ discard: if (first) continue; - if (!gem_set_tiling(kgem->fd, bo->handle, - I915_TILING_NONE, 0)) - continue; - - bo->tiling = I915_TILING_NONE; - bo->pitch = 0; + if (!kgem_set_tiling(kgem, bo, I915_TILING_NONE, 0)) { + kgem_bo_free(kgem, bo); + break; + } } + assert(bo->tiling == I915_TILING_NONE); + bo->pitch = 0; if (bo->map__gtt || bo->map__wc || bo->map__cpu) { if (flags & (CREATE_CPU_MAP | CREATE_GTT_MAP)) { @@ -4269,7 +4716,7 @@ discard: kgem_bo_remove_from_inactive(kgem, bo); assert(bo->tiling == I915_TILING_NONE); - bo->pitch = 0; + assert(bo->pitch == 0); bo->delta = 0; DBG((" %s: found handle=%d (num_pages=%d) in linear %s cache\n", __FUNCTION__, bo->handle, num_pages(bo), @@ -4342,7 +4789,6 @@ struct kgem_bo *kgem_create_for_name(struct kgem *kgem, uint32_t name) bo->tiling = tiling.tiling_mode; bo->reusable = false; bo->prime = true; - bo->purged = true; /* no coherency guarantees */ debug_alloc__bo(kgem, bo); return bo; @@ -4479,6 +4925,8 @@ struct kgem_bo *kgem_create_linear(struct kgem *kgem, int size, unsigned flags) if ((flags & CREATE_UNCACHED) == 0) { bo = search_linear_cache(kgem, size, CREATE_INACTIVE | flags); if (bo) { + assert(!bo->purged); + assert(!bo->delta); assert(bo->domain != DOMAIN_GPU); ASSERT_IDLE(kgem, bo->handle); bo->refcnt = 1; @@ -4760,8 +5208,7 @@ static void __kgem_bo_make_scanout(struct kgem *kgem, struct kgem_bo *bo, int width, int height) { - ScrnInfoPtr scrn = - container_of(kgem, struct sna, kgem)->scrn; + ScrnInfoPtr scrn = __to_sna(kgem)->scrn; struct drm_mode_fb_cmd arg; assert(bo->proxy == NULL); @@ -4809,6 +5256,36 @@ static void __kgem_bo_make_scanout(struct kgem *kgem, } } +static bool tiling_changed(struct kgem_bo *bo, int tiling, int pitch) +{ + if (tiling != bo->tiling) + return true; + + return tiling != I915_TILING_NONE && pitch != bo->pitch; +} + +static void set_gpu_tiling(struct kgem *kgem, + struct kgem_bo *bo, + int tiling, int pitch) +{ + DBG(("%s: handle=%d, tiling=%d, pitch=%d\n", + __FUNCTION__, bo->handle, tiling, pitch)); + + assert(!kgem->can_fence); + + if (tiling_changed(bo, tiling, pitch) && bo->map__gtt) { + if (!list_is_empty(&bo->vma)) { + list_del(&bo->vma); + kgem->vma[0].count--; + } + munmap(bo->map__gtt, bytes(bo)); + bo->map__gtt = NULL; + } + + bo->tiling = tiling; + bo->pitch = pitch; +} + struct kgem_bo *kgem_create_2d(struct kgem *kgem, int width, int height, @@ -4892,8 +5369,8 @@ struct kgem_bo *kgem_create_2d(struct kgem *kgem, return last; } - if (container_of(kgem, struct sna, kgem)->scrn->vtSema) { - ScrnInfoPtr scrn = container_of(kgem, struct sna, kgem)->scrn; + if (__to_sna(kgem)->scrn->vtSema) { + ScrnInfoPtr scrn = __to_sna(kgem)->scrn; list_for_each_entry_reverse(bo, &kgem->scanout, list) { struct drm_mode_fb_cmd arg; @@ -4915,11 +5392,8 @@ struct kgem_bo *kgem_create_2d(struct kgem *kgem, bo->delta = 0; } - if (gem_set_tiling(kgem->fd, bo->handle, - tiling, pitch)) { - bo->tiling = tiling; - bo->pitch = pitch; - } else { + if (!kgem_set_tiling(kgem, bo, + tiling, pitch)) { kgem_bo_free(kgem, bo); break; } @@ -4950,6 +5424,9 @@ struct kgem_bo *kgem_create_2d(struct kgem *kgem, } } + if (flags & CREATE_CACHED) + return NULL; + bo = __kgem_bo_create_as_display(kgem, size, tiling, pitch); if (bo) return bo; @@ -4987,14 +5464,9 @@ struct kgem_bo *kgem_create_2d(struct kgem *kgem, if (num_pages(bo) < size) continue; - if (bo->pitch != pitch || bo->tiling != tiling) { - if (!gem_set_tiling(kgem->fd, bo->handle, - tiling, pitch)) - continue; - - bo->pitch = pitch; - bo->tiling = tiling; - } + if (!kgem_set_tiling(kgem, bo, tiling, pitch) && + !exact) + set_gpu_tiling(kgem, bo, tiling, pitch); } kgem_bo_remove_from_active(kgem, bo); @@ -5020,14 +5492,11 @@ large_inactive: if (size > num_pages(bo)) continue; - if (bo->tiling != tiling || - (tiling != I915_TILING_NONE && bo->pitch != pitch)) { - if (!gem_set_tiling(kgem->fd, bo->handle, - tiling, pitch)) + if (!kgem_set_tiling(kgem, bo, tiling, pitch)) { + if (kgem->gen >= 040 && !exact) + set_gpu_tiling(kgem, bo, tiling, pitch); + else continue; - - bo->tiling = tiling; - bo->pitch = pitch; } if (bo->purged && !kgem_bo_clear_purgeable(kgem, bo)) { @@ -5039,7 +5508,6 @@ large_inactive: assert(bo->domain != DOMAIN_GPU); bo->unique_id = kgem_get_unique_id(kgem); - bo->pitch = pitch; bo->delta = 0; DBG((" 1:from large inactive: pitch=%d, tiling=%d, handle=%d, id=%d\n", bo->pitch, bo->tiling, bo->handle, bo->unique_id)); @@ -5088,14 +5556,13 @@ large_inactive: if (bo->tiling != tiling || (tiling != I915_TILING_NONE && bo->pitch != pitch)) { if (bo->map__gtt || - !gem_set_tiling(kgem->fd, bo->handle, - tiling, pitch)) { + !kgem_set_tiling(kgem, bo, + tiling, pitch)) { DBG(("inactive GTT vma with wrong tiling: %d < %d\n", bo->tiling, tiling)); - continue; + kgem_bo_free(kgem, bo); + break; } - bo->tiling = tiling; - bo->pitch = pitch; } if (bo->purged && !kgem_bo_clear_purgeable(kgem, bo)) { @@ -5103,8 +5570,11 @@ large_inactive: break; } + if (tiling == I915_TILING_NONE) + bo->pitch = pitch; + assert(bo->tiling == tiling); - bo->pitch = pitch; + assert(bo->pitch >= pitch); bo->delta = 0; bo->unique_id = kgem_get_unique_id(kgem); @@ -5170,15 +5640,12 @@ search_active: if (num_pages(bo) < size) continue; - if (bo->pitch != pitch) { - if (!gem_set_tiling(kgem->fd, - bo->handle, - tiling, pitch)) - continue; - - bo->pitch = pitch; - } + if (!kgem_set_tiling(kgem, bo, tiling, pitch) && + !exact) + set_gpu_tiling(kgem, bo, tiling, pitch); } + assert(bo->tiling == tiling); + assert(bo->pitch >= pitch); kgem_bo_remove_from_active(kgem, bo); @@ -5233,19 +5700,21 @@ search_active: if (num_pages(bo) < size) continue; - if (bo->tiling != tiling || - (tiling != I915_TILING_NONE && bo->pitch != pitch)) { - if (!gem_set_tiling(kgem->fd, - bo->handle, - tiling, pitch)) - continue; + if (!kgem_set_tiling(kgem, bo, tiling, pitch)) { + if (kgem->gen >= 040 && !exact) { + set_gpu_tiling(kgem, bo, + tiling, pitch); + } else { + kgem_bo_free(kgem, bo); + break; + } } + assert(bo->tiling == tiling); + assert(bo->pitch >= pitch); kgem_bo_remove_from_active(kgem, bo); bo->unique_id = kgem_get_unique_id(kgem); - bo->pitch = pitch; - bo->tiling = tiling; bo->delta = 0; DBG((" 1:from active: pitch=%d, tiling=%d, handle=%d, id=%d\n", bo->pitch, bo->tiling, bo->handle, bo->unique_id)); @@ -5323,11 +5792,13 @@ search_inactive: continue; } - if (bo->tiling != tiling || - (tiling != I915_TILING_NONE && bo->pitch != pitch)) { - if (!gem_set_tiling(kgem->fd, bo->handle, - tiling, pitch)) - continue; + if (!kgem_set_tiling(kgem, bo, tiling, pitch)) { + if (kgem->gen >= 040 && !exact) { + set_gpu_tiling(kgem, bo, tiling, pitch); + } else { + kgem_bo_free(kgem, bo); + break; + } } if (bo->purged && !kgem_bo_clear_purgeable(kgem, bo)) { @@ -5338,9 +5809,8 @@ search_inactive: kgem_bo_remove_from_inactive(kgem, bo); assert(list_is_empty(&bo->list)); assert(list_is_empty(&bo->vma)); - - bo->pitch = pitch; - bo->tiling = tiling; + assert(bo->tiling == tiling); + assert(bo->pitch >= pitch); bo->delta = 0; bo->unique_id = kgem_get_unique_id(kgem); @@ -5388,14 +5858,17 @@ search_inactive: kgem_bo_remove_from_active(kgem, bo); __kgem_bo_clear_busy(bo); - if (tiling != I915_TILING_NONE && bo->pitch != pitch) { - if (!gem_set_tiling(kgem->fd, bo->handle, tiling, pitch)) { + if (!kgem_set_tiling(kgem, bo, tiling, pitch)) { + if (kgem->gen >= 040 && !exact) { + set_gpu_tiling(kgem, bo, tiling, pitch); + } else { kgem_bo_free(kgem, bo); goto no_retire; } } + assert(bo->tiling == tiling); + assert(bo->pitch >= pitch); - bo->pitch = pitch; bo->unique_id = kgem_get_unique_id(kgem); bo->delta = 0; DBG((" 2:from active: pitch=%d, tiling=%d, handle=%d, id=%d\n", @@ -5440,18 +5913,21 @@ create: } bo->unique_id = kgem_get_unique_id(kgem); - if (tiling == I915_TILING_NONE || - gem_set_tiling(kgem->fd, handle, tiling, pitch)) { - bo->tiling = tiling; - bo->pitch = pitch; + if (kgem_set_tiling(kgem, bo, tiling, pitch)) { if (flags & CREATE_SCANOUT) __kgem_bo_make_scanout(kgem, bo, width, height); } else { - if (flags & CREATE_EXACT) { - DBG(("%s: failed to set exact tiling (gem_set_tiling)\n", __FUNCTION__)); - gem_close(kgem->fd, handle); - free(bo); - return NULL; + if (kgem->gen >= 040) { + assert(!kgem->can_fence); + bo->tiling = tiling; + bo->pitch = pitch; + } else { + if (flags & CREATE_EXACT) { + DBG(("%s: failed to set exact tiling (gem_set_tiling)\n", __FUNCTION__)); + gem_close(kgem->fd, handle); + free(bo); + return NULL; + } } } @@ -5633,6 +6109,7 @@ inline static bool nearly_idle(struct kgem *kgem) { int ring = kgem->ring == KGEM_BLT; + assert(ring < ARRAY_SIZE(kgem->requests)); if (list_is_singular(&kgem->requests[ring])) return true; @@ -5720,7 +6197,7 @@ static inline bool kgem_flush(struct kgem *kgem, bool flush) if (kgem->nreloc == 0) return true; - if (container_of(kgem, struct sna, kgem)->flags & SNA_POWERSAVE) + if (__to_sna(kgem)->flags & SNA_POWERSAVE) return true; if (kgem->flush == flush && kgem->aperture < kgem->aperture_low) @@ -5982,6 +6459,55 @@ bool kgem_check_many_bo_fenced(struct kgem *kgem, ...) return kgem_flush(kgem, flush); } +void __kgem_bcs_set_tiling(struct kgem *kgem, + struct kgem_bo *src, + struct kgem_bo *dst) +{ + uint32_t state, *b; + + DBG(("%s: src handle=%d:tiling=%d, dst handle=%d:tiling=%d\n", + __FUNCTION__, + src ? src->handle : 0, src ? src->tiling : 0, + dst ? dst->handle : 0, dst ? dst->tiling : 0)); + assert(kgem->mode == KGEM_BLT); + assert(dst == NULL || kgem_bo_can_blt(kgem, dst)); + assert(src == NULL || kgem_bo_can_blt(kgem, src)); + + state = 0; + if (dst && dst->tiling == I915_TILING_Y) + state |= BCS_DST_Y; + if (src && src->tiling == I915_TILING_Y) + state |= BCS_SRC_Y; + + if (kgem->bcs_state == state) + return; + + DBG(("%s: updating SWCTRL %x -> %x\n", __FUNCTION__, + kgem->bcs_state, state)); + + /* Over-estimate space in case we need to re-emit the cmd packet */ + if (!kgem_check_batch(kgem, 24)) { + _kgem_submit(kgem); + _kgem_set_mode(kgem, KGEM_BLT); + if (state == 0) + return; + } + + b = kgem->batch + kgem->nbatch; + if (kgem->nbatch) { + *b++ = MI_FLUSH_DW; + *b++ = 0; + *b++ = 0; + *b++ = 0; + } + *b++ = MI_LOAD_REGISTER_IMM; + *b++ = BCS_SWCTRL; + *b++ = (BCS_SRC_Y | BCS_DST_Y) << 16 | state; + kgem->nbatch = b - kgem->batch; + + kgem->bcs_state = state; +} + uint32_t kgem_add_reloc(struct kgem *kgem, uint32_t pos, struct kgem_bo *bo, @@ -6195,12 +6721,6 @@ static void kgem_trim_vma_cache(struct kgem *kgem, int type, int bucket) list_del(&bo->vma); kgem->vma[type].count--; - - if (!bo->purged && !kgem_bo_set_purgeable(kgem, bo)) { - DBG(("%s: freeing unpurgeable old mapping\n", - __FUNCTION__)); - kgem_bo_free(kgem, bo); - } } } @@ -6216,8 +6736,8 @@ static void *__kgem_bo_map__gtt_or_wc(struct kgem *kgem, struct kgem_bo *bo) kgem_trim_vma_cache(kgem, MAP_GTT, bucket(bo)); if (bo->tiling || !kgem->has_wc_mmap) { - assert(num_pages(bo) <= kgem->aperture_mappable / 2); assert(kgem->gen != 021 || bo->tiling != I915_TILING_Y); + warn_unless(num_pages(bo) <= kgem->aperture_mappable / 2); ptr = bo->map__gtt; if (ptr == NULL) @@ -6291,6 +6811,7 @@ void *kgem_bo_map(struct kgem *kgem, struct kgem_bo *bo) DBG(("%s: sync: GPU hang detected\n", __FUNCTION__)); kgem_throttle(kgem); } + bo->needs_flush = false; kgem_bo_retire(kgem, bo); bo->domain = DOMAIN_GTT; bo->gtt_dirty = true; @@ -6319,14 +6840,16 @@ void *kgem_bo_map__wc(struct kgem *kgem, struct kgem_bo *bo) bo->handle, (long)bo->presumed_offset, bo->tiling, bo->map__gtt, bo->map__cpu, bo->domain)); assert(bo->proxy == NULL); - assert(bo->exec == NULL); assert(list_is_empty(&bo->list)); assert_tiling(kgem, bo); assert(!bo->purged || bo->reusable); if (bo->map__wc) return bo->map__wc; + if (!kgem->has_wc_mmap) + return NULL; + kgem_trim_vma_cache(kgem, MAP_GTT, bucket(bo)); return __kgem_bo_map__wc(kgem, bo); } @@ -6411,16 +6934,34 @@ struct kgem_bo *kgem_create_map(struct kgem *kgem, first_page = (uintptr_t)ptr; last_page = first_page + size + PAGE_SIZE - 1; - first_page &= ~(PAGE_SIZE-1); - last_page &= ~(PAGE_SIZE-1); + first_page &= ~(uintptr_t)(PAGE_SIZE-1); + last_page &= ~(uintptr_t)(PAGE_SIZE-1); assert(last_page > first_page); handle = gem_userptr(kgem->fd, (void *)first_page, last_page-first_page, read_only); if (handle == 0) { - DBG(("%s: import failed, errno=%d\n", __FUNCTION__, errno)); - return NULL; + if (read_only && kgem->has_wc_mmap) { + struct drm_i915_gem_set_domain set_domain; + + handle = gem_userptr(kgem->fd, + (void *)first_page, last_page-first_page, + false); + + VG_CLEAR(set_domain); + set_domain.handle = handle; + set_domain.read_domains = I915_GEM_DOMAIN_GTT; + set_domain.write_domain = 0; + if (do_ioctl(kgem->fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain)) { + gem_close(kgem->fd, handle); + handle = 0; + } + } + if (handle == 0) { + DBG(("%s: import failed, errno=%d\n", __FUNCTION__, errno)); + return NULL; + } } bo = __kgem_bo_alloc(handle, (last_page - first_page) / PAGE_SIZE); @@ -6483,6 +7024,7 @@ void kgem_bo_sync__cpu(struct kgem *kgem, struct kgem_bo *bo) DBG(("%s: sync: GPU hang detected\n", __FUNCTION__)); kgem_throttle(kgem); } + bo->needs_flush = false; kgem_bo_retire(kgem, bo); bo->domain = DOMAIN_CPU; } @@ -6523,6 +7065,7 @@ void kgem_bo_sync__cpu_full(struct kgem *kgem, struct kgem_bo *bo, bool write) kgem_throttle(kgem); } if (write) { + bo->needs_flush = false; kgem_bo_retire(kgem, bo); bo->domain = DOMAIN_CPU; } else { @@ -6559,6 +7102,7 @@ void kgem_bo_sync__gtt(struct kgem *kgem, struct kgem_bo *bo) DBG(("%s: sync: GPU hang detected\n", __FUNCTION__)); kgem_throttle(kgem); } + bo->needs_flush = false; kgem_bo_retire(kgem, bo); bo->domain = DOMAIN_GTT; bo->gtt_dirty = true; @@ -7485,6 +8029,7 @@ kgem_replace_bo(struct kgem *kgem, } _kgem_set_mode(kgem, KGEM_BLT); } + kgem_bcs_set_tiling(kgem, src, dst); br00 = XY_SRC_COPY_BLT_CMD; br13 = pitch; @@ -7553,6 +8098,9 @@ bool kgem_bo_convert_to_gpu(struct kgem *kgem, __FUNCTION__, bo->handle, flags, __kgem_bo_is_busy(kgem, bo))); assert(bo->tiling == I915_TILING_NONE); + if (flags & (__MOVE_PRIME | __MOVE_SCANOUT)) + return false; + if (kgem->has_llc) return true; diff --git a/src/sna/kgem.h b/src/sna/kgem.h index 2267bac..6cf877c 100644 --- a/src/sna/kgem.h +++ b/src/sna/kgem.h @@ -42,6 +42,7 @@ struct kgem_bo { #define RQ(rq) ((struct kgem_request *)((uintptr_t)(rq) & ~3)) #define RQ_RING(rq) ((uintptr_t)(rq) & 3) #define RQ_IS_BLT(rq) (RQ_RING(rq) == KGEM_BLT) +#define RQ_IS_RENDER(rq) (RQ_RING(rq) == KGEM_RENDER) #define MAKE_REQUEST(rq, ring) ((struct kgem_request *)((uintptr_t)(rq) | (ring))) struct drm_i915_gem_exec_object2 *exec; @@ -103,7 +104,7 @@ struct kgem_request { struct list list; struct kgem_bo *bo; struct list buffers; - int ring; + unsigned ring; }; enum { @@ -157,6 +158,8 @@ struct kgem { int16_t count; } vma[NUM_MAP_TYPES]; + uint32_t bcs_state; + uint32_t batch_flags; uint32_t batch_flags_base; #define I915_EXEC_SECURE (1<<9) @@ -187,8 +190,11 @@ struct kgem { uint32_t has_handle_lut :1; uint32_t has_wc_mmap :1; + uint32_t can_fence :1; uint32_t can_blt_cpu :1; + uint32_t can_blt_y :1; uint32_t can_render_y :1; + uint32_t can_scanout_y :1; uint16_t fence_max; uint16_t half_cpu_cache_pages; @@ -230,7 +236,7 @@ struct kgem { #define KGEM_MAX_DEFERRED_VBO 16 -#define KGEM_BATCH_RESERVED 1 +#define KGEM_BATCH_RESERVED 8 /* LRI(SWCTRL) + END */ #define KGEM_RELOC_RESERVED (KGEM_MAX_DEFERRED_VBO) #define KGEM_EXEC_RESERVED (1+KGEM_MAX_DEFERRED_VBO) @@ -342,6 +348,11 @@ static inline bool kgem_ring_is_idle(struct kgem *kgem, int ring) { ring = ring == KGEM_BLT; + if (kgem->needs_semaphore && + !list_is_empty(&kgem->requests[!ring]) && + !__kgem_ring_is_idle(kgem, !ring)) + return false; + if (list_is_empty(&kgem->requests[ring])) return true; @@ -400,7 +411,7 @@ static inline void kgem_set_mode(struct kgem *kgem, enum kgem_mode mode, struct kgem_bo *bo) { - assert(!kgem->wedged); + warn_unless(!kgem->wedged); #if DEBUG_FLUSH_BATCH kgem_submit(kgem); @@ -422,7 +433,7 @@ static inline void _kgem_set_mode(struct kgem *kgem, enum kgem_mode mode) { assert(kgem->mode == KGEM_NONE); assert(kgem->nbatch == 0); - assert(!kgem->wedged); + warn_unless(!kgem->wedged); kgem->context_switch(kgem, mode); kgem->mode = mode; } @@ -566,7 +577,7 @@ static inline bool kgem_bo_can_blt(struct kgem *kgem, { assert(bo->refcnt); - if (bo->tiling == I915_TILING_Y) { + if (bo->tiling == I915_TILING_Y && !kgem->can_blt_y) { DBG(("%s: can not blt to handle=%d, tiling=Y\n", __FUNCTION__, bo->handle)); return false; @@ -581,6 +592,22 @@ static inline bool kgem_bo_can_blt(struct kgem *kgem, return kgem_bo_blt_pitch_is_ok(kgem, bo); } +void __kgem_bcs_set_tiling(struct kgem *kgem, + struct kgem_bo *src, + struct kgem_bo *dst); + +inline static void kgem_bcs_set_tiling(struct kgem *kgem, + struct kgem_bo *src, + struct kgem_bo *dst) +{ + assert(kgem->mode == KGEM_BLT); + + if (!kgem->can_blt_y) + return; + + __kgem_bcs_set_tiling(kgem, src, dst); +} + static inline bool kgem_bo_is_snoop(struct kgem_bo *bo) { assert(bo->refcnt); @@ -626,7 +653,7 @@ static inline bool kgem_bo_is_busy(struct kgem_bo *bo) return bo->rq; } -void __kgem_retire_requests_upto(struct kgem *kgem, struct kgem_bo *bo); +bool __kgem_retire_requests_upto(struct kgem *kgem, struct kgem_bo *bo); static inline bool __kgem_bo_is_busy(struct kgem *kgem, struct kgem_bo *bo) { DBG(("%s: handle=%d, domain: %d exec? %d, rq? %d\n", __FUNCTION__, @@ -636,14 +663,13 @@ static inline bool __kgem_bo_is_busy(struct kgem *kgem, struct kgem_bo *bo) if (bo->exec) return true; - if (bo->rq && !__kgem_busy(kgem, bo->handle)) { - __kgem_retire_requests_upto(kgem, bo); - assert(list_is_empty(&bo->request)); - assert(bo->rq == NULL); - assert(bo->domain == DOMAIN_NONE); - } + if (bo->rq == NULL) + return false; + + if (__kgem_busy(kgem, bo->handle)) + return true; - return kgem_bo_is_busy(bo); + return __kgem_retire_requests_upto(kgem, bo); } static inline bool kgem_bo_is_render(struct kgem_bo *bo) @@ -651,7 +677,15 @@ static inline bool kgem_bo_is_render(struct kgem_bo *bo) DBG(("%s: handle=%d, rq? %d [%d]\n", __FUNCTION__, bo->handle, bo->rq != NULL, (int)RQ_RING(bo->rq))); assert(bo->refcnt); - return bo->rq && RQ_RING(bo->rq) == I915_EXEC_RENDER; + return bo->rq && RQ_RING(bo->rq) != KGEM_BLT; +} + +static inline bool kgem_bo_is_blt(struct kgem_bo *bo) +{ + DBG(("%s: handle=%d, rq? %d\n", __FUNCTION__, + bo->handle, bo->rq != NULL, (int)RQ_RING(bo->rq))); + assert(bo->refcnt); + return RQ_RING(bo->rq) == KGEM_BLT; } static inline void kgem_bo_mark_unreusable(struct kgem_bo *bo) diff --git a/src/sna/kgem_debug_gen4.c b/src/sna/kgem_debug_gen4.c index 9b80dc8..8e6e47b 100644 --- a/src/sna/kgem_debug_gen4.c +++ b/src/sna/kgem_debug_gen4.c @@ -598,7 +598,7 @@ int kgem_gen4_decode_3d(struct kgem *kgem, uint32_t offset) assert(len == 7); kgem_debug_print(data, offset, 0, "3DSTATE_DEPTH_BUFFER\n"); - kgem_debug_print(data, offset, 1, "%s, %s, pitch = %d bytes, %stiled, HiZ %d, Seperate Stencil %d\n", + kgem_debug_print(data, offset, 1, "%s, %s, pitch = %d bytes, %stiled, HiZ %d, Separate Stencil %d\n", get_965_surfacetype(data[1] >> 29), get_965_depthformat((data[1] >> 18) & 0x7), (data[1] & 0x0001ffff) + 1, diff --git a/src/sna/kgem_debug_gen5.c b/src/sna/kgem_debug_gen5.c index 8b55dd9..f1b1275 100644 --- a/src/sna/kgem_debug_gen5.c +++ b/src/sna/kgem_debug_gen5.c @@ -573,7 +573,7 @@ int kgem_gen5_decode_3d(struct kgem *kgem, uint32_t offset) assert(len == 7); kgem_debug_print(data, offset, 0, "3DSTATE_DEPTH_BUFFER\n"); - kgem_debug_print(data, offset, 1, "%s, %s, pitch = %d bytes, %stiled, HiZ %d, Seperate Stencil %d\n", + kgem_debug_print(data, offset, 1, "%s, %s, pitch = %d bytes, %stiled, HiZ %d, Separate Stencil %d\n", get_965_surfacetype(data[1] >> 29), get_965_depthformat((data[1] >> 18) & 0x7), (data[1] & 0x0001ffff) + 1, diff --git a/src/sna/kgem_debug_gen6.c b/src/sna/kgem_debug_gen6.c index 7ef55d3..579c5d5 100644 --- a/src/sna/kgem_debug_gen6.c +++ b/src/sna/kgem_debug_gen6.c @@ -985,7 +985,7 @@ int kgem_gen6_decode_3d(struct kgem *kgem, uint32_t offset) assert(len == 7); kgem_debug_print(data, offset, 0, "3DSTATE_DEPTH_BUFFER\n"); - kgem_debug_print(data, offset, 1, "%s, %s, pitch = %d bytes, %stiled, HiZ %d, Seperate Stencil %d\n", + kgem_debug_print(data, offset, 1, "%s, %s, pitch = %d bytes, %stiled, HiZ %d, Separate Stencil %d\n", get_965_surfacetype(data[1] >> 29), get_965_depthformat((data[1] >> 18) & 0x7), (data[1] & 0x0001ffff) + 1, diff --git a/src/sna/sna.h b/src/sna/sna.h index 18425e3..3c19aa5 100644 --- a/src/sna/sna.h +++ b/src/sna/sna.h @@ -154,6 +154,8 @@ struct sna_pixmap { #define MAPPED_GTT 1 #define MAPPED_CPU 2 uint8_t flush :2; +#define FLUSH_READ 1 +#define FLUSH_WRITE 2 uint8_t shm :1; uint8_t clear :1; uint8_t header :1; @@ -179,18 +181,31 @@ static inline WindowPtr get_root_window(ScreenPtr screen) #endif } +#if !NDEBUG +static PixmapPtr check_pixmap(PixmapPtr pixmap) +{ + if (pixmap != NULL) { + assert(pixmap->refcnt >= 1); + assert(pixmap->devKind != 0xdeadbeef); + } + return pixmap; +} +#else +#define check_pixmap(p) p +#endif + static inline PixmapPtr get_window_pixmap(WindowPtr window) { assert(window); assert(window->drawable.type != DRAWABLE_PIXMAP); - return fbGetWindowPixmap(window); + return check_pixmap(fbGetWindowPixmap(window)); } static inline PixmapPtr get_drawable_pixmap(DrawablePtr drawable) { assert(drawable); if (drawable->type == DRAWABLE_PIXMAP) - return (PixmapPtr)drawable; + return check_pixmap((PixmapPtr)drawable); else return get_window_pixmap((WindowPtr)drawable); } @@ -248,7 +263,6 @@ struct sna { #define SNA_FLUSH_GTT 0x400 #define SNA_PERFORMANCE 0x1000 #define SNA_POWERSAVE 0x2000 -#define SNA_REMOVE_OUTPUTS 0x4000 #define SNA_HAS_FLIP 0x10000 #define SNA_HAS_ASYNC_FLIP 0x20000 #define SNA_LINEAR_FB 0x40000 @@ -265,6 +279,8 @@ struct sna { #define AVX 0x80 #define AVX2 0x100 + bool ignore_copy_area : 1; + unsigned watch_flush; struct timeval timer_tv; @@ -284,7 +300,11 @@ struct sna { struct kgem_bo *shadow; unsigned front_active; unsigned shadow_active; + unsigned rr_active; unsigned flip_active; + unsigned hidden; + bool shadow_enabled; + bool shadow_wait; bool dirty; int max_crtc_width, max_crtc_height; @@ -318,7 +338,8 @@ struct sna { uint32_t fg, bg; int size; - int active; + bool disable; + bool active; int last_x; int last_y; @@ -353,6 +374,8 @@ struct sna { bool available; bool open; #if HAVE_PRESENT + struct list vblank_queue; + uint64_t unflip; #endif } present; @@ -420,7 +443,7 @@ bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna); bool sna_mode_fake_init(struct sna *sna, int num_fake); bool sna_mode_wants_tear_free(struct sna *sna); void sna_mode_adjust_frame(struct sna *sna, int x, int y); -extern void sna_mode_discover(struct sna *sna); +extern void sna_mode_discover(struct sna *sna, bool tell); extern void sna_mode_check(struct sna *sna); extern bool sna_mode_disable(struct sna *sna); extern void sna_mode_enable(struct sna *sna); @@ -461,6 +484,11 @@ to_sna_from_screen(ScreenPtr screen) return to_sna(xf86ScreenToScrn(screen)); } +pure static inline ScreenPtr to_screen_from_sna(struct sna *sna) +{ + return xf86ScrnToScreen(sna->scrn); +} + pure static inline struct sna * to_sna_from_pixmap(PixmapPtr pixmap) { @@ -498,12 +526,11 @@ to_sna_from_kgem(struct kgem *kgem) extern xf86CrtcPtr sna_covering_crtc(struct sna *sna, const BoxRec *box, xf86CrtcPtr desired); +extern xf86CrtcPtr sna_primary_crtc(struct sna *sna); extern bool sna_wait_for_scanline(struct sna *sna, PixmapPtr pixmap, xf86CrtcPtr crtc, const BoxRec *clip); -xf86CrtcPtr sna_mode_first_crtc(struct sna *sna); - const struct ust_msc { uint64_t msc; int tv_sec; @@ -536,6 +563,11 @@ static inline uint64_t ust64(int tv_sec, int tv_usec) return (uint64_t)tv_sec * 1000000 + tv_usec; } +static inline uint64_t swap_ust(const struct ust_msc *swap) +{ + return ust64(swap->tv_sec, swap->tv_usec); +} + #if HAVE_DRI2 bool sna_dri2_open(struct sna *sna, ScreenPtr pScreen); void sna_dri2_page_flip_handler(struct sna *sna, struct drm_event_vblank *event); @@ -575,12 +607,51 @@ static inline void sna_present_vblank_handler(struct drm_event_vblank *event) { #endif extern bool sna_crtc_set_sprite_rotation(xf86CrtcPtr crtc, uint32_t rotation); -extern int sna_crtc_to_pipe(xf86CrtcPtr crtc); extern uint32_t sna_crtc_to_sprite(xf86CrtcPtr crtc); -extern uint32_t sna_crtc_id(xf86CrtcPtr crtc); -extern bool sna_crtc_is_on(xf86CrtcPtr crtc); extern bool sna_crtc_is_transformed(xf86CrtcPtr crtc); +#define CRTC_VBLANK 0x3 +#define CRTC_ON 0x80000000 + +static inline unsigned long *sna_crtc_flags(xf86CrtcPtr crtc) +{ + unsigned long *flags = crtc->driver_private; + assert(flags); + return flags; +} + +static inline unsigned sna_crtc_pipe(xf86CrtcPtr crtc) +{ + return *sna_crtc_flags(crtc) >> 8 & 0xff; +} + +static inline unsigned sna_crtc_id(xf86CrtcPtr crtc) +{ + return *sna_crtc_flags(crtc) >> 16 & 0xff; +} + +static inline bool sna_crtc_is_on(xf86CrtcPtr crtc) +{ + return *sna_crtc_flags(crtc) & CRTC_ON; +} + +static inline void sna_crtc_set_vblank(xf86CrtcPtr crtc) +{ + assert((*sna_crtc_flags(crtc) & CRTC_VBLANK) < 3); + ++*sna_crtc_flags(crtc); +} + +static inline void sna_crtc_clear_vblank(xf86CrtcPtr crtc) +{ + assert(*sna_crtc_flags(crtc) & CRTC_VBLANK); + --*sna_crtc_flags(crtc); +} + +static inline bool sna_crtc_has_vblank(xf86CrtcPtr crtc) +{ + return *sna_crtc_flags(crtc) & CRTC_VBLANK; +} + CARD32 sna_format_for_depth(int depth); CARD32 sna_render_format_for_depth(int depth); @@ -998,8 +1069,7 @@ static inline uint32_t pixmap_size(PixmapPtr pixmap) bool sna_accel_init(ScreenPtr sreen, struct sna *sna); void sna_accel_create(struct sna *sna); -void sna_accel_block_handler(struct sna *sna, struct timeval **tv); -void sna_accel_wakeup_handler(struct sna *sna); +void sna_accel_block(struct sna *sna, struct timeval **tv); void sna_accel_watch_flush(struct sna *sna, int enable); void sna_accel_flush(struct sna *sna); void sna_accel_enter(struct sna *sna); @@ -1127,6 +1197,16 @@ memcpy_blt(const void *src, void *dst, int bpp, uint16_t width, uint16_t height); void +affine_blt(const void *src, void *dst, int bpp, + int16_t src_x, int16_t src_y, + int16_t src_width, int16_t src_height, + int32_t src_stride, + int16_t dst_x, int16_t dst_y, + uint16_t dst_width, uint16_t dst_height, + int32_t dst_stride, + const struct pixman_f_transform *t); + +void memmove_box(const void *src, void *dst, int bpp, int32_t stride, const BoxRec *box, @@ -1182,6 +1262,31 @@ box_intersect(BoxPtr a, const BoxRec *b) return true; } +const BoxRec * +__find_clip_box_for_y(const BoxRec *begin, const BoxRec *end, int16_t y); +inline static const BoxRec * +find_clip_box_for_y(const BoxRec *begin, const BoxRec *end, int16_t y) +{ + /* Special case for incremental trapezoid clipping */ + if (begin == end) + return end; + + /* Quick test if scanline is within range of clip boxes */ + if (begin->y2 > y) { + assert(end == begin + 1 || + __find_clip_box_for_y(begin, end, y) == begin); + return begin; + } + if (y >= end[-1].y2) { + assert(end == begin + 1 || + __find_clip_box_for_y(begin, end, y) == end); + return end; + } + + /* Otherwise bisect to find the first box crossing y */ + return __find_clip_box_for_y(begin, end, y); +} + unsigned sna_cpu_detect(void); char *sna_cpu_features_to_string(unsigned features, char *line); diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c index baf5f60..71a6207 100644 --- a/src/sna/sna_accel.c +++ b/src/sna/sna_accel.c @@ -52,6 +52,7 @@ #include #include +#include #include #ifdef HAVE_VALGRIND @@ -66,7 +67,8 @@ #define FORCE_FLUSH 0 #define FORCE_FULL_SYNC 0 /* https://bugs.freedesktop.org/show_bug.cgi?id=61628 */ -#define DEFAULT_TILING I915_TILING_X +#define DEFAULT_PIXMAP_TILING I915_TILING_X +#define DEFAULT_SCANOUT_TILING I915_TILING_X #define USE_INPLACE 1 #define USE_SPANS 0 /* -1 force CPU, 1 force GPU */ @@ -527,10 +529,10 @@ sna_pixmap_alloc_cpu(struct sna *sna, DBG(("%s: allocating CPU buffer (%dx%d)\n", __FUNCTION__, pixmap->drawable.width, pixmap->drawable.height)); - hint = 0; - if ((flags & MOVE_ASYNC_HINT) == 0 && - ((flags & MOVE_READ) == 0 || (priv->gpu_damage && !priv->clear && !sna->kgem.has_llc))) - hint = CREATE_CPU_MAP | CREATE_INACTIVE | CREATE_NO_THROTTLE; + hint = CREATE_CPU_MAP | CREATE_INACTIVE | CREATE_NO_THROTTLE; + if ((flags & MOVE_ASYNC_HINT) || + (priv->gpu_damage && !priv->clear && kgem_bo_is_busy(priv->gpu_bo) && sna->kgem.can_blt_cpu)) + hint = 0; priv->cpu_bo = kgem_create_cpu_2d(&sna->kgem, pixmap->drawable.width, @@ -612,9 +614,9 @@ static bool sna_pixmap_free_cpu(struct sna *sna, struct sna_pixmap *priv, bool a static inline uint32_t default_tiling(struct sna *sna, PixmapPtr pixmap) { -#if DEFAULT_TILING == I915_TILING_NONE +#if DEFAULT_PIXMAP_TILING == I915_TILING_NONE return I915_TILING_NONE; -#elif DEFAULT_TILING == I915_TILING_X +#elif DEFAULT_PIXMAP_TILING == I915_TILING_X return I915_TILING_X; #else /* Try to avoid hitting the Y-tiling GTT mapping bug on 855GM */ @@ -630,15 +632,6 @@ static inline uint32_t default_tiling(struct sna *sna, PixmapPtr pixmap) pixmap->drawable.height > sna->render.max_3d_size)) return I915_TILING_X; - if (sna_damage_is_all(&sna_pixmap(pixmap)->cpu_damage, - pixmap->drawable.width, - pixmap->drawable.height)) { - DBG(("%s: entire source is damaged, using Y-tiling\n", - __FUNCTION__)); - sna_damage_destroy(&sna_pixmap(priv)->gpu_damage); - return I915_TILING_Y; - } - return I915_TILING_Y; #endif } @@ -666,6 +659,7 @@ struct kgem_bo *sna_pixmap_change_tiling(PixmapPtr pixmap, uint32_t tiling) __FUNCTION__, priv->gpu_bo->tiling, tiling, pixmap->drawable.width, pixmap->drawable.height)); assert(priv->gpu_damage == NULL || priv->gpu_bo); + assert(priv->gpu_bo->tiling != tiling); if (priv->pinned) { DBG(("%s: can't convert pinned bo\n", __FUNCTION__)); @@ -690,6 +684,12 @@ struct kgem_bo *sna_pixmap_change_tiling(PixmapPtr pixmap, uint32_t tiling) return NULL; } + if (bo->tiling == priv->gpu_bo->tiling) { + DBG(("%s: tiling request failed\n", __FUNCTION__)); + kgem_bo_destroy(&sna->kgem, bo); + return NULL; + } + box.x1 = box.y1 = 0; box.x2 = pixmap->drawable.width; box.y2 = pixmap->drawable.height; @@ -824,8 +824,8 @@ create_pixmap(struct sna *sna, ScreenPtr screen, datasize += adjust; } - DBG(("%s: allocating pixmap %dx%d, depth=%d, size=%ld\n", - __FUNCTION__, width, height, depth, (long)datasize)); + DBG(("%s: allocating pixmap %dx%d, depth=%d/%d, size=%ld\n", + __FUNCTION__, width, height, depth, bpp, (long)datasize)); pixmap = AllocatePixmap(screen, datasize); if (!pixmap) return NullPixmap; @@ -878,7 +878,11 @@ __pop_freed_pixmap(struct sna *sna) pixmap = sna->freed_pixmap; sna->freed_pixmap = pixmap->devPrivate.ptr; + DBG(("%s: reusing freed pixmap=%ld header\n", + __FUNCTION__, pixmap->drawable.serialNumber)); + assert(pixmap->refcnt == 0); + assert(pixmap->devKind = 0xdeadbeef); assert(sna_pixmap(pixmap)); assert(sna_pixmap(pixmap)->header); @@ -1081,6 +1085,18 @@ sna_pixmap_create_scratch(ScreenPtr screen, return pixmap; } +static unsigned small_copy(const RegionRec *region) +{ + if ((region->extents.x2 - region->extents.x1)*(region->extents.y2 - region->extents.y1) < 1024) { + DBG(("%s: region:%dx%d\n", __FUNCTION__, + (region->extents.x2 - region->extents.x1), + (region->extents.y2 - region->extents.y1))); + return COPY_SMALL; + } + + return 0; +} + #ifdef CREATE_PIXMAP_USAGE_SHARED static Bool sna_share_pixmap_backing(PixmapPtr pixmap, ScreenPtr slave, void **fd_handle) @@ -1124,7 +1140,7 @@ sna_share_pixmap_backing(PixmapPtr pixmap, ScreenPtr slave, void **fd_handle) pixmap->drawable.height, pixmap->drawable.bitsPerPixel, I915_TILING_NONE, - CREATE_GTT_MAP | CREATE_PRIME | CREATE_EXACT); + CREATE_GTT_MAP | CREATE_SCANOUT | CREATE_PRIME | CREATE_EXACT); if (bo == NULL) { DBG(("%s: allocation failed\n", __FUNCTION__)); return FALSE; @@ -1243,7 +1259,7 @@ sna_create_pixmap_shared(struct sna *sna, ScreenPtr screen, width, height, pixmap->drawable.bitsPerPixel, I915_TILING_NONE, - CREATE_GTT_MAP | CREATE_PRIME | CREATE_EXACT); + CREATE_GTT_MAP | CREATE_SCANOUT | CREATE_PRIME | CREATE_EXACT); if (priv->gpu_bo == NULL) { free(priv); FreePixmap(pixmap); @@ -1311,7 +1327,7 @@ static PixmapPtr sna_create_pixmap(ScreenPtr screen, if (unlikely((sna->render.prefer_gpu & PREFER_GPU_RENDER) == 0)) flags &= ~KGEM_CAN_CREATE_GPU; - if (wedged(sna)) + if (wedged(sna) && usage != SNA_CREATE_FB) flags &= ~KGEM_CAN_CREATE_GTT; DBG(("%s: usage=%d, flags=%x\n", __FUNCTION__, usage, flags)); @@ -1419,8 +1435,11 @@ static void __sna_free_pixmap(struct sna *sna, if (priv->flush) sna_accel_watch_flush(sna, -1); +#if !NDEBUG + pixmap->devKind = 0xdeadbeef; +#endif if (priv->header) { - assert(pixmap->drawable.pScreen == sna->scrn->pScreen); + assert(pixmap->drawable.pScreen == to_screen_from_sna(sna)); assert(!priv->shm); pixmap->devPrivate.ptr = sna->freed_pixmap; sna->freed_pixmap = pixmap; @@ -1529,7 +1548,7 @@ static inline bool has_coherent_ptr(struct sna *sna, struct sna_pixmap *priv, un if (!priv->cpu_bo) return true; - assert(!priv->cpu_bo->needs_flush); + assert(!priv->cpu_bo->needs_flush || (flags & MOVE_WRITE) == 0); assert(priv->pixmap->devKind == priv->cpu_bo->pitch); return priv->pixmap->devPrivate.ptr == MAP(priv->cpu_bo->map__cpu); } @@ -1557,6 +1576,11 @@ static inline bool has_coherent_ptr(struct sna *sna, struct sna_pixmap *priv, un return true; } + if (priv->pixmap->devPrivate.ptr == MAP(priv->gpu_bo->map__wc)) { + assert(priv->mapped == MAPPED_GTT); + return true; + } + return false; } @@ -1577,6 +1601,16 @@ static inline bool pixmap_inplace(struct sna *sna, return false; if (priv->gpu_bo && kgem_bo_is_busy(priv->gpu_bo)) { + if (priv->clear) { + DBG(("%s: no, clear GPU bo is busy\n", __FUNCTION__)); + return false; + } + + if (flags & MOVE_ASYNC_HINT) { + DBG(("%s: no, async hint and GPU bo is busy\n", __FUNCTION__)); + return false; + } + if ((flags & (MOVE_WRITE | MOVE_READ)) == (MOVE_WRITE | MOVE_READ)) { DBG(("%s: no, GPU bo is busy\n", __FUNCTION__)); return false; @@ -1624,7 +1658,7 @@ static bool sna_pixmap_alloc_gpu(struct sna *sna, if (pixmap->usage_hint == SNA_CREATE_FB && (sna->flags & SNA_LINEAR_FB) == 0) { flags |= CREATE_SCANOUT; tiling = kgem_choose_tiling(&sna->kgem, - -I915_TILING_X, + -DEFAULT_SCANOUT_TILING, pixmap->drawable.width, pixmap->drawable.height, pixmap->drawable.bitsPerPixel); @@ -1861,7 +1895,9 @@ sna_pixmap_undo_cow(struct sna *sna, struct sna_pixmap *priv, unsigned flags) assert(priv->gpu_bo == cow->bo); assert(cow->refcnt); - if (flags && (flags & MOVE_WRITE) == 0 && IS_COW_OWNER(priv->cow)) + if (flags && /* flags == 0 => force decouple */ + (flags & MOVE_WRITE) == 0 && + (((flags & __MOVE_FORCE) == 0) || IS_COW_OWNER(priv->cow))) return true; if (!IS_COW_OWNER(priv->cow)) @@ -1933,7 +1969,7 @@ sna_pixmap_undo_cow(struct sna *sna, struct sna_pixmap *priv, unsigned flags) box.y2 = pixmap->drawable.height; if (flags & __MOVE_PRIME) { - create = CREATE_GTT_MAP | CREATE_PRIME | CREATE_EXACT; + create = CREATE_GTT_MAP | CREATE_SCANOUT | CREATE_PRIME | CREATE_EXACT; tiling = I915_TILING_NONE; } else { create = 0; @@ -2021,6 +2057,10 @@ sna_pixmap_make_cow(struct sna *sna, cow->bo->handle)); src_priv->cow = MAKE_COW_OWNER(cow); + if (src_priv->flush & FLUSH_WRITE) { + assert(!src_priv->shm); + sna_add_flush_pixmap(sna, src_priv, src_priv->gpu_bo); + } } if (cow == COW(dst_priv->cow)) { @@ -2267,6 +2307,7 @@ skip_inplace_map: (flags & MOVE_WRITE ? (void *)priv->gpu_bo : (void *)priv->gpu_damage) && priv->cpu_damage == NULL && priv->gpu_bo->tiling == I915_TILING_NONE && (flags & MOVE_READ || kgem_bo_can_map__cpu(&sna->kgem, priv->gpu_bo, flags & MOVE_WRITE)) && + (!priv->clear || !kgem_bo_is_busy(priv->gpu_bo)) && ((flags & (MOVE_WRITE | MOVE_ASYNC_HINT)) == 0 || (!priv->cow && !priv->move_to_gpu && !__kgem_bo_is_busy(&sna->kgem, priv->gpu_bo)))) { void *ptr; @@ -2330,7 +2371,9 @@ skip_inplace_map: pixmap->devKind, pixmap->devKind * pixmap->drawable.height)); if (priv->cpu_bo) { + kgem_bo_undo(&sna->kgem, priv->cpu_bo); if ((flags & MOVE_ASYNC_HINT || priv->cpu_bo->exec) && + sna->kgem.can_blt_cpu && sna->render.fill_one(sna, pixmap, priv->cpu_bo, priv->clear_color, 0, 0, @@ -2344,21 +2387,26 @@ skip_inplace_map: assert(pixmap->devPrivate.ptr == MAP(priv->cpu_bo->map__cpu)); } - assert(pixmap->devKind); - if (priv->clear_color == 0 || - pixmap->drawable.bitsPerPixel == 8 || - priv->clear_color == (1 << pixmap->drawable.depth) - 1) { - memset(pixmap->devPrivate.ptr, priv->clear_color, - (size_t)pixmap->devKind * pixmap->drawable.height); - } else { - pixman_fill(pixmap->devPrivate.ptr, - pixmap->devKind/sizeof(uint32_t), - pixmap->drawable.bitsPerPixel, - 0, 0, - pixmap->drawable.width, - pixmap->drawable.height, - priv->clear_color); - } + if (sigtrap_get() == 0) { + assert(pixmap->devKind); + sigtrap_assert_active(); + if (priv->clear_color == 0 || + pixmap->drawable.bitsPerPixel == 8 || + priv->clear_color == (1 << pixmap->drawable.depth) - 1) { + memset(pixmap->devPrivate.ptr, priv->clear_color, + (size_t)pixmap->devKind * pixmap->drawable.height); + } else { + pixman_fill(pixmap->devPrivate.ptr, + pixmap->devKind/sizeof(uint32_t), + pixmap->drawable.bitsPerPixel, + 0, 0, + pixmap->drawable.width, + pixmap->drawable.height, + priv->clear_color); + } + sigtrap_put(); + } else + return false; clear_done: sna_damage_all(&priv->cpu_damage, pixmap); @@ -2531,6 +2579,9 @@ static bool cpu_clear_boxes(struct sna *sna, { struct sna_fill_op fill; + if (!sna->kgem.can_blt_cpu) + return false; + if (!sna_fill_init_blt(&fill, sna, pixmap, priv->cpu_bo, GXcopy, priv->clear_color, @@ -2904,17 +2955,22 @@ move_to_cpu: assert(pixmap->devPrivate.ptr == MAP(priv->cpu_bo->map__cpu)); } - assert(pixmap->devKind); - do { - pixman_fill(pixmap->devPrivate.ptr, - pixmap->devKind/sizeof(uint32_t), - pixmap->drawable.bitsPerPixel, - box->x1, box->y1, - box->x2 - box->x1, - box->y2 - box->y1, - priv->clear_color); - box++; - } while (--n); + if (sigtrap_get() == 0) { + assert(pixmap->devKind); + sigtrap_assert_active(); + do { + pixman_fill(pixmap->devPrivate.ptr, + pixmap->devKind/sizeof(uint32_t), + pixmap->drawable.bitsPerPixel, + box->x1, box->y1, + box->x2 - box->x1, + box->y2 - box->y1, + priv->clear_color); + box++; + } while (--n); + sigtrap_put(); + } else + return false; clear_done: if (flags & MOVE_WRITE || @@ -3209,13 +3265,14 @@ __sna_pixmap_for_gpu(struct sna *sna, PixmapPtr pixmap, unsigned flags) { struct sna_pixmap *priv; + assert(flags & (MOVE_READ | MOVE_WRITE | __MOVE_FORCE)); if ((flags & __MOVE_FORCE) == 0 && wedged(sna)) return NULL; priv = sna_pixmap(pixmap); if (priv == NULL) { DBG(("%s: not attached\n", __FUNCTION__)); - if ((flags & __MOVE_DRI) == 0) + if ((flags & (__MOVE_DRI | __MOVE_SCANOUT)) == 0) return NULL; if (pixmap->usage_hint == -1) { @@ -3238,6 +3295,44 @@ __sna_pixmap_for_gpu(struct sna *sna, PixmapPtr pixmap, unsigned flags) return priv; } +inline static void sna_pixmap_unclean(struct sna *sna, + struct sna_pixmap *priv, + unsigned flags) +{ + struct drm_i915_gem_busy busy; + + assert(DAMAGE_IS_ALL(priv->gpu_damage)); + assert(priv->gpu_bo); + assert(priv->gpu_bo->proxy == NULL); + assert_pixmap_map(priv->pixmap, priv); + + sna_damage_destroy(&priv->cpu_damage); + list_del(&priv->flush_list); + + if (flags & (__MOVE_DRI | __MOVE_SCANOUT)) + return; + + if (!priv->flush || priv->gpu_bo->exec) + return; + + busy.handle = priv->gpu_bo->handle; + busy.busy = 0; + ioctl(sna->kgem.fd, DRM_IOCTL_I915_GEM_BUSY, &busy); + + DBG(("%s(pixmap=%ld): cleaning foreign bo handle=%u, busy=%x [ring=%d]\n", + __FUNCTION__, + priv->pixmap->drawable.serialNumber, + busy.handle, busy.busy, !!(busy.busy & (0xfffe << 16)))); + + if (busy.busy) { + unsigned mode = KGEM_RENDER; + if (busy.busy & (0xfffe << 16)) + mode = KGEM_BLT; + kgem_bo_mark_busy(&sna->kgem, priv->gpu_bo, mode); + } else + __kgem_bo_clear_busy(priv->gpu_bo); +} + struct sna_pixmap * sna_pixmap_move_area_to_gpu(PixmapPtr pixmap, const BoxRec *box, unsigned int flags) { @@ -3287,12 +3382,14 @@ sna_pixmap_move_area_to_gpu(PixmapPtr pixmap, const BoxRec *box, unsigned int fl if (priv->cow) { unsigned cow = flags & (MOVE_READ | MOVE_WRITE | __MOVE_FORCE); + assert(cow); + if ((flags & MOVE_READ) == 0) { if (priv->gpu_damage) { r.extents = *box; r.data = NULL; if (!region_subsumes_damage(&r, priv->gpu_damage)) - cow |= MOVE_READ; + cow |= MOVE_READ | __MOVE_FORCE; } } else { if (priv->cpu_damage) { @@ -3303,22 +3400,18 @@ sna_pixmap_move_area_to_gpu(PixmapPtr pixmap, const BoxRec *box, unsigned int fl } } - if (cow) { - if (!sna_pixmap_undo_cow(sna, priv, cow)) - return NULL; + if (!sna_pixmap_undo_cow(sna, priv, cow)) + return NULL; - if (priv->gpu_bo == NULL) - sna_damage_destroy(&priv->gpu_damage); - } + if (priv->gpu_bo == NULL) + sna_damage_destroy(&priv->gpu_damage); } if (sna_damage_is_all(&priv->gpu_damage, pixmap->drawable.width, pixmap->drawable.height)) { - assert(priv->gpu_bo); - assert(priv->gpu_bo->proxy == NULL); - sna_damage_destroy(&priv->cpu_damage); - list_del(&priv->flush_list); + DBG(("%s: already all-damaged\n", __FUNCTION__)); + sna_pixmap_unclean(sna, priv, flags); goto done; } @@ -3527,7 +3620,8 @@ sna_drawable_use_bo(DrawablePtr drawable, unsigned flags, const BoxRec *box, } if (priv->cow) { - unsigned cow = MOVE_WRITE | MOVE_READ; + unsigned cow = MOVE_WRITE | MOVE_READ | __MOVE_FORCE; + assert(cow); if (flags & IGNORE_DAMAGE) { if (priv->gpu_damage) { @@ -3717,8 +3811,11 @@ create_gpu_bo: else move = MOVE_WRITE | MOVE_READ | MOVE_ASYNC_HINT; - if (sna_pixmap_move_to_gpu(pixmap, move)) + if (sna_pixmap_move_to_gpu(pixmap, move)) { + sna_damage_all(&priv->gpu_damage, + pixmap); goto use_gpu_bo; + } } if (DAMAGE_IS_ALL(priv->gpu_damage) || @@ -4019,7 +4116,7 @@ sna_pixmap_create_upload(ScreenPtr screen, assert(width); assert(height); - if (depth == 1) + if (depth < 8) return create_pixmap(sna, screen, width, height, depth, CREATE_PIXMAP_USAGE_SCRATCH); @@ -4121,27 +4218,21 @@ sna_pixmap_move_to_gpu(PixmapPtr pixmap, unsigned flags) if (priv->cow) { unsigned cow = flags & (MOVE_READ | MOVE_WRITE | __MOVE_FORCE); + assert(cow); if (flags & MOVE_READ && priv->cpu_damage) cow |= MOVE_WRITE; - if (cow) { - if (!sna_pixmap_undo_cow(sna, priv, cow)) - return NULL; + if (!sna_pixmap_undo_cow(sna, priv, cow)) + return NULL; - if (priv->gpu_bo == NULL) - sna_damage_destroy(&priv->gpu_damage); - } + if (priv->gpu_bo == NULL) + sna_damage_destroy(&priv->gpu_damage); } if (sna_damage_is_all(&priv->gpu_damage, pixmap->drawable.width, pixmap->drawable.height)) { DBG(("%s: already all-damaged\n", __FUNCTION__)); - assert(DAMAGE_IS_ALL(priv->gpu_damage)); - assert(priv->gpu_bo); - assert(priv->gpu_bo->proxy == NULL); - assert_pixmap_map(pixmap, priv); - sna_damage_destroy(&priv->cpu_damage); - list_del(&priv->flush_list); + sna_pixmap_unclean(sna, priv, flags); goto active; } @@ -4206,7 +4297,7 @@ sna_pixmap_move_to_gpu(PixmapPtr pixmap, unsigned flags) if (flags & MOVE_INPLACE_HINT || (priv->cpu_damage && priv->cpu_bo == NULL)) create = CREATE_GTT_MAP | CREATE_INACTIVE; if (flags & __MOVE_PRIME) - create |= CREATE_GTT_MAP | CREATE_PRIME | CREATE_EXACT; + create |= CREATE_GTT_MAP | CREATE_SCANOUT | CREATE_PRIME | CREATE_EXACT; sna_pixmap_alloc_gpu(sna, pixmap, priv, create); } @@ -4534,7 +4625,7 @@ static inline bool box32_trim_and_translate(Box32Rec *box, DrawablePtr d, GCPtr return box32_clip(box, gc); } -static inline void box_add_pt(BoxPtr box, int16_t x, int16_t y) +static inline void box_add_xy(BoxPtr box, int16_t x, int16_t y) { if (box->x1 > x) box->x1 = x; @@ -4547,6 +4638,11 @@ static inline void box_add_pt(BoxPtr box, int16_t x, int16_t y) box->y2 = y; } +static inline void box_add_pt(BoxPtr box, const DDXPointRec *pt) +{ + box_add_xy(box, pt->x, pt->y); +} + static inline bool box32_to_box16(const Box32Rec *b32, BoxRec *b16) { b16->x1 = b32->x1; @@ -4864,6 +4960,7 @@ try_upload__inplace(PixmapPtr pixmap, RegionRec *region, pixmap->devPrivate.ptr = dst; pixmap->devKind = priv->gpu_bo->pitch; priv->mapped = dst == MAP(priv->gpu_bo->map__cpu) ? MAPPED_CPU : MAPPED_GTT; + priv->cpu &= priv->mapped == MAPPED_CPU; assert(has_coherent_ptr(sna, priv, MOVE_WRITE)); box = region_rects(region); @@ -5172,6 +5269,16 @@ static inline uint8_t blt_depth(int depth) } } +inline static void blt_done(struct sna *sna) +{ + sna->blt_state.fill_bo = 0; + if (sna->kgem.nbatch && __kgem_ring_empty(&sna->kgem)) { + DBG(("%s: flushing BLT operation on empty ring\n", + __FUNCTION__)); + _kgem_submit(&sna->kgem); + } +} + static bool sna_put_xybitmap_blt(DrawablePtr drawable, GCPtr gc, RegionPtr region, int x, int y, int w, int h, char *bits) @@ -5217,6 +5324,7 @@ sna_put_xybitmap_blt(DrawablePtr drawable, GCPtr gc, RegionPtr region, kgem_set_mode(&sna->kgem, KGEM_BLT, bo); assert(kgem_bo_can_blt(&sna->kgem, bo)); + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); /* Region is pre-clipped and translated into pixmap space */ box = region_rects(region); @@ -5238,6 +5346,7 @@ sna_put_xybitmap_blt(DrawablePtr drawable, GCPtr gc, RegionPtr region, return false; _kgem_set_mode(&sna->kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); upload = kgem_create_buffer(&sna->kgem, bstride*bh, @@ -5331,7 +5440,7 @@ sna_put_xybitmap_blt(DrawablePtr drawable, GCPtr gc, RegionPtr region, box++; } while (--n); - sna->blt_state.fill_bo = 0; + blt_done(sna); return true; } @@ -5381,6 +5490,7 @@ sna_put_xypixmap_blt(DrawablePtr drawable, GCPtr gc, RegionPtr region, kgem_set_mode(&sna->kgem, KGEM_BLT, bo); assert(kgem_bo_can_blt(&sna->kgem, bo)); + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); skip = h * BitmapBytePad(w + left); for (i = 1 << (gc->depth-1); i; i >>= 1, bits += skip) { @@ -5408,6 +5518,7 @@ sna_put_xypixmap_blt(DrawablePtr drawable, GCPtr gc, RegionPtr region, return false; _kgem_set_mode(&sna->kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); upload = kgem_create_buffer(&sna->kgem, bstride*bh, @@ -5509,7 +5620,7 @@ sna_put_xypixmap_blt(DrawablePtr drawable, GCPtr gc, RegionPtr region, } while (--n); } - sna->blt_state.fill_bo = 0; + blt_done(sna); return true; } @@ -5837,7 +5948,7 @@ sna_self_copy_boxes(DrawablePtr src, DrawablePtr dst, GCPtr gc, if (!sna->render.copy_boxes(sna, alu, &pixmap->drawable, priv->gpu_bo, sx, sy, &pixmap->drawable, priv->gpu_bo, tx, ty, - box, n, 0)) { + box, n, small_copy(region))) { DBG(("%s: fallback - accelerated copy boxes failed\n", __FUNCTION__)); goto fallback; @@ -6098,6 +6209,9 @@ sna_copy_boxes__inplace(struct sna *sna, RegionPtr region, int alu, kgem_bo_sync__cpu_full(&sna->kgem, src_priv->gpu_bo, FORCE_FULL_SYNC); + if (sigtrap_get()) + return false; + box = region_rects(region); n = region_num_rects(region); if (src_priv->gpu_bo->tiling) { @@ -6137,6 +6251,8 @@ sna_copy_boxes__inplace(struct sna *sna, RegionPtr region, int alu, } } + sigtrap_put(); + return true; upload_inplace: @@ -6234,6 +6350,9 @@ upload_inplace: assert(has_coherent_ptr(sna, src_priv, MOVE_READ)); + if (sigtrap_get()) + return false; + box = region_rects(region); n = region_num_rects(region); if (dst_priv->gpu_bo->tiling) { @@ -6265,15 +6384,19 @@ upload_inplace: } while (--n); if (!dst_priv->shm) { - assert(ptr == MAP(dst_priv->gpu_bo->map__cpu)); dst_pixmap->devPrivate.ptr = ptr; dst_pixmap->devKind = dst_priv->gpu_bo->pitch; - dst_priv->mapped = MAPPED_CPU; + if (ptr == MAP(dst_priv->gpu_bo->map__cpu)) { + dst_priv->mapped = MAPPED_CPU; + dst_priv->cpu = true; + } else + dst_priv->mapped = MAPPED_GTT; assert_pixmap_map(dst_pixmap, dst_priv); - dst_priv->cpu = true; } } + sigtrap_put(); + return true; } @@ -6326,6 +6449,16 @@ sna_copy_boxes(DrawablePtr src, DrawablePtr dst, GCPtr gc, assert(region_num_rects(region)); + if (src_priv && + src_priv->gpu_bo == NULL && + src_priv->cpu_bo == NULL && + src_priv->ptr == NULL) { + /* Rare but still happens, nothing to copy */ + DBG(("%s: src pixmap=%ld is empty\n", + __FUNCTION__, src_pixmap->drawable.serialNumber)); + return; + } + if (src_pixmap == dst_pixmap) return sna_self_copy_boxes(src, dst, gc, region, dx, dy, @@ -6499,7 +6632,7 @@ discard_cow: if (!sna->render.copy_boxes(sna, alu, &src_pixmap->drawable, src_priv->gpu_bo, src_dx, src_dy, &dst_pixmap->drawable, bo, 0, 0, - box, n, 0)) { + box, n, small_copy(region))) { DBG(("%s: fallback - accelerated copy boxes failed\n", __FUNCTION__)); goto fallback; @@ -6536,7 +6669,7 @@ discard_cow: if (!sna->render.copy_boxes(sna, alu, &src_pixmap->drawable, src_priv->gpu_bo, src_dx, src_dy, &dst_pixmap->drawable, bo, 0, 0, - box, n, 0)) { + box, n, small_copy(region))) { DBG(("%s: fallback - accelerated copy boxes failed\n", __FUNCTION__)); goto fallback; @@ -6579,7 +6712,7 @@ discard_cow: if (!sna->render.copy_boxes(sna, alu, &src_pixmap->drawable, src_priv->cpu_bo, src_dx, src_dy, &dst_pixmap->drawable, bo, 0, 0, - box, n, src_priv->shm ? COPY_LAST : 0)) { + box, n, small_copy(region) | (src_priv->shm ? COPY_LAST : 0))) { DBG(("%s: fallback - accelerated copy boxes failed\n", __FUNCTION__)); goto fallback; @@ -6631,8 +6764,7 @@ discard_cow: ok = sna->render.copy_boxes(sna, alu, &src_pixmap->drawable, src_bo, src_dx, src_dy, &dst_pixmap->drawable, bo, 0, 0, - box, n, COPY_LAST); - + box, n, small_copy(region) | COPY_LAST); kgem_bo_sync__cpu(&sna->kgem, src_bo); assert(src_bo->rq == NULL); kgem_bo_destroy(&sna->kgem, src_bo); @@ -6780,18 +6912,22 @@ fallback: return; } - assert(dst_pixmap->devPrivate.ptr); - assert(dst_pixmap->devKind); - do { - pixman_fill(dst_pixmap->devPrivate.ptr, - dst_pixmap->devKind/sizeof(uint32_t), - dst_pixmap->drawable.bitsPerPixel, - box->x1, box->y1, - box->x2 - box->x1, - box->y2 - box->y1, - src_priv->clear_color); - box++; - } while (--n); + if (sigtrap_get() == 0) { + assert(dst_pixmap->devPrivate.ptr); + assert(dst_pixmap->devKind); + sigtrap_assert_active(); + do { + pixman_fill(dst_pixmap->devPrivate.ptr, + dst_pixmap->devKind/sizeof(uint32_t), + dst_pixmap->drawable.bitsPerPixel, + box->x1, box->y1, + box->x2 - box->x1, + box->y2 - box->y1, + src_priv->clear_color); + box++; + } while (--n); + sigtrap_put(); + } } else if (!sna_copy_boxes__inplace(sna, region, alu, src_pixmap, src_priv, src_dx, src_dy, @@ -6848,36 +6984,39 @@ fallback: ((char *)src_pixmap->devPrivate.ptr + src_dy * src_stride + src_dx * bpp / 8); - do { - DBG(("%s: memcpy_blt(box=(%d, %d), (%d, %d), src=(%d, %d), pitches=(%d, %d))\n", - __FUNCTION__, - box->x1, box->y1, - box->x2 - box->x1, - box->y2 - box->y1, - src_dx, src_dy, - src_stride, dst_stride)); - - assert(box->x1 >= 0); - assert(box->y1 >= 0); - assert(box->x2 <= dst_pixmap->drawable.width); - assert(box->y2 <= dst_pixmap->drawable.height); - - assert(box->x1 + src_dx >= 0); - assert(box->y1 + src_dy >= 0); - assert(box->x2 + src_dx <= src_pixmap->drawable.width); - assert(box->y2 + src_dy <= src_pixmap->drawable.height); - assert(has_coherent_ptr(sna, src_priv, MOVE_READ)); - assert(has_coherent_ptr(sna, dst_priv, MOVE_WRITE)); - assert(src_stride); - assert(dst_stride); - memcpy_blt(src_bits, dst_bits, bpp, - src_stride, dst_stride, - box->x1, box->y1, - box->x1, box->y1, - box->x2 - box->x1, - box->y2 - box->y1); - box++; - } while (--n); + if (sigtrap_get() == 0) { + do { + DBG(("%s: memcpy_blt(box=(%d, %d), (%d, %d), src=(%d, %d), pitches=(%d, %d))\n", + __FUNCTION__, + box->x1, box->y1, + box->x2 - box->x1, + box->y2 - box->y1, + src_dx, src_dy, + src_stride, dst_stride)); + + assert(box->x1 >= 0); + assert(box->y1 >= 0); + assert(box->x2 <= dst_pixmap->drawable.width); + assert(box->y2 <= dst_pixmap->drawable.height); + + assert(box->x1 + src_dx >= 0); + assert(box->y1 + src_dy >= 0); + assert(box->x2 + src_dx <= src_pixmap->drawable.width); + assert(box->y2 + src_dy <= src_pixmap->drawable.height); + assert(has_coherent_ptr(sna, src_priv, MOVE_READ)); + assert(has_coherent_ptr(sna, dst_priv, MOVE_WRITE)); + assert(src_stride); + assert(dst_stride); + memcpy_blt(src_bits, dst_bits, bpp, + src_stride, dst_stride, + box->x1, box->y1, + box->x1, box->y1, + box->x2 - box->x1, + box->y2 - box->y1); + box++; + } while (--n); + sigtrap_put(); + } } else { DBG(("%s: fallback -- miCopyRegion\n", __FUNCTION__)); @@ -6931,7 +7070,8 @@ sna_do_copy(DrawablePtr src, DrawablePtr dst, GCPtr gc, /* Short cut for unmapped windows */ if (dst->type == DRAWABLE_WINDOW && !((WindowPtr)dst)->realized) { - DBG(("%s: unmapped\n", __FUNCTION__)); + DBG(("%s: unmapped/unrealized dst (pixmap=%ld)\n", + __FUNCTION__, get_window_pixmap((WindowPtr)dst))); return NULL; } @@ -7115,19 +7255,28 @@ sna_copy_area(DrawablePtr src, DrawablePtr dst, GCPtr gc, if (gc->planemask == 0) return NULL; - DBG(("%s: src=(%d, %d)x(%d, %d)+(%d, %d) -> dst=(%d, %d)+(%d, %d); alu=%d, pm=%lx, depth=%d\n", + if (sna->ignore_copy_area) + return NULL; + + DBG(("%s: src=pixmap=%ld:(%d, %d)x(%d, %d)+(%d, %d) -> dst=pixmap=%ld:(%d, %d)+(%d, %d); alu=%d, pm=%lx, depth=%d\n", __FUNCTION__, + get_drawable_pixmap(src)->drawable.serialNumber, src_x, src_y, width, height, src->x, src->y, + get_drawable_pixmap(dst)->drawable.serialNumber, dst_x, dst_y, dst->x, dst->y, gc->alu, gc->planemask, gc->depth)); if (FORCE_FALLBACK || !ACCEL_COPY_AREA || wedged(sna) || - !PM_IS_SOLID(dst, gc->planemask) || gc->depth < 8) + !PM_IS_SOLID(dst, gc->planemask) || gc->depth < 8) { + DBG(("%s: fallback copy\n", __FUNCTION__)); copy = sna_fallback_copy_boxes; - else if (src == dst) + } else if (src == dst) { + DBG(("%s: self copy\n", __FUNCTION__)); copy = sna_self_copy_boxes; - else + } else { + DBG(("%s: normal copy\n", __FUNCTION__)); copy = sna_copy_boxes; + } return sna_do_copy(src, dst, gc, src_x, src_y, @@ -7136,30 +7285,21 @@ sna_copy_area(DrawablePtr src, DrawablePtr dst, GCPtr gc, copy, 0, NULL); } -static const BoxRec * -find_clip_box_for_y(const BoxRec *begin, const BoxRec *end, int16_t y) +const BoxRec * +__find_clip_box_for_y(const BoxRec *begin, const BoxRec *end, int16_t y) { - const BoxRec *mid; - - if (end == begin) - return end; - - if (end - begin == 1) { + assert(end - begin > 1); + do { + const BoxRec *mid = begin + (end - begin) / 2; + if (mid->y2 > y) + end = mid; + else + begin = mid; + } while (end > begin + 1); if (begin->y2 > y) - return begin; + return begin; else - return end; - } - - mid = begin + (end - begin) / 2; - if (mid->y2 > y) - /* If no box is found in [begin, mid], the function - * will return @mid, which is then known to be the - * correct answer. - */ - return find_clip_box_for_y(begin, mid, y); - else - return find_clip_box_for_y(mid, end, y); + return end; } struct sna_fill_spans { @@ -8223,6 +8363,8 @@ sna_copy_bitmap_blt(DrawablePtr _bitmap, DrawablePtr drawable, GCPtr gc, } br13 |= blt_depth(drawable->depth) << 24; br13 |= copy_ROP[gc->alu] << 16; + DBG(("%s: target-depth=%d, alu=%d, bg=%08x, fg=%08x\n", + __FUNCTION__, drawable->depth, gc->alu, gc->bgPixel, gc->fgPixel)); kgem_set_mode(&sna->kgem, KGEM_BLT, arg->bo); assert(kgem_bo_can_blt(&sna->kgem, arg->bo)); @@ -8255,6 +8397,7 @@ sna_copy_bitmap_blt(DrawablePtr _bitmap, DrawablePtr drawable, GCPtr gc, return; /* XXX fallback? */ _kgem_set_mode(&sna->kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, NULL, arg->bo); assert(sna->kgem.mode == KGEM_BLT); if (sna->kgem.gen >= 0100) { @@ -8270,8 +8413,8 @@ sna_copy_bitmap_blt(DrawablePtr _bitmap, DrawablePtr drawable, GCPtr gc, I915_GEM_DOMAIN_RENDER | KGEM_RELOC_FENCED, 0); - b[5] = gc->bgPixel; - b[6] = gc->fgPixel; + b[6] = gc->bgPixel; + b[7] = gc->fgPixel; dst = (uint8_t *)&b[8]; sna->kgem.nbatch += 8 + src_stride; @@ -8322,6 +8465,7 @@ sna_copy_bitmap_blt(DrawablePtr _bitmap, DrawablePtr drawable, GCPtr gc, return; /* XXX fallback? */ _kgem_set_mode(&sna->kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, NULL, arg->bo); upload = kgem_create_buffer(&sna->kgem, bstride*bh, @@ -8408,7 +8552,7 @@ sna_copy_bitmap_blt(DrawablePtr _bitmap, DrawablePtr drawable, GCPtr gc, sna_damage_add_to_pixmap(arg->damage, region, pixmap); } assert_pixmap_damage(pixmap); - sna->blt_state.fill_bo = 0; + blt_done(sna); } static void @@ -8472,6 +8616,7 @@ sna_copy_plane_blt(DrawablePtr source, DrawablePtr drawable, GCPtr gc, return; /* XXX fallback? */ _kgem_set_mode(&sna->kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, NULL, arg->bo); upload = kgem_create_buffer(&sna->kgem, bstride*bh, @@ -8588,6 +8733,8 @@ sna_copy_plane_blt(DrawablePtr source, DrawablePtr drawable, GCPtr gc, } } + kgem_bcs_set_tiling(&sna->kgem, upload, arg->bo); + assert(sna->kgem.mode == KGEM_BLT); b = sna->kgem.batch + sna->kgem.nbatch; if (sna->kgem.gen >= 0100) { @@ -8641,7 +8788,7 @@ sna_copy_plane_blt(DrawablePtr source, DrawablePtr drawable, GCPtr gc, sna_damage_add_to_pixmap(arg->damage, region, dst_pixmap); } assert_pixmap_damage(dst_pixmap); - sna->blt_state.fill_bo = 0; + blt_done(sna); } static RegionPtr @@ -8895,36 +9042,11 @@ sna_poly_point_extents(DrawablePtr drawable, GCPtr gc, last.x += pt->x; last.y += pt->y; pt++; - box_add_pt(&box, last.x, last.y); + box_add_xy(&box, last.x, last.y); } } else { - --n; ++pt; - while (n >= 8) { - box_add_pt(&box, pt[0].x, pt[0].y); - box_add_pt(&box, pt[1].x, pt[1].y); - box_add_pt(&box, pt[2].x, pt[2].y); - box_add_pt(&box, pt[3].x, pt[3].y); - box_add_pt(&box, pt[4].x, pt[4].y); - box_add_pt(&box, pt[5].x, pt[5].y); - box_add_pt(&box, pt[6].x, pt[6].y); - box_add_pt(&box, pt[7].x, pt[7].y); - pt += 8; - n -= 8; - } - if (n & 4) { - box_add_pt(&box, pt[0].x, pt[0].y); - box_add_pt(&box, pt[1].x, pt[1].y); - box_add_pt(&box, pt[2].x, pt[2].y); - box_add_pt(&box, pt[3].x, pt[3].y); - pt += 4; - } - if (n & 2) { - box_add_pt(&box, pt[0].x, pt[0].y); - box_add_pt(&box, pt[1].x, pt[1].y); - pt += 2; - } - if (n & 1) - box_add_pt(&box, pt[0].x, pt[0].y); + while (--n) + box_add_pt(&box, ++pt); } box.x2++; box.y2++; @@ -9636,7 +9758,7 @@ sna_poly_line_extents(DrawablePtr drawable, GCPtr gc, y += pt->y; if (blt) blt &= pt->x == 0 || pt->y == 0; - box_add_pt(&box, x, y); + box_add_xy(&box, x, y); } } else { int x = box.x1; @@ -9648,7 +9770,7 @@ sna_poly_line_extents(DrawablePtr drawable, GCPtr gc, x = pt->x; y = pt->y; } - box_add_pt(&box, pt->x, pt->y); + box_add_pt(&box, pt); } } box.x2++; @@ -11785,14 +11907,29 @@ sna_poly_fill_rect_blt(DrawablePtr drawable, if (nbox > ARRAY_SIZE(boxes)) nbox = ARRAY_SIZE(boxes); n -= nbox; - do { + while (nbox >= 2) { + b[0].x1 = rect[0].x + dx; + b[0].y1 = rect[0].y + dy; + b[0].x2 = b[0].x1 + rect[0].width; + b[0].y2 = b[0].y1 + rect[0].height; + + b[1].x1 = rect[1].x + dx; + b[1].y1 = rect[1].y + dy; + b[1].x2 = b[1].x1 + rect[1].width; + b[1].y2 = b[1].y1 + rect[1].height; + + b += 2; + rect += 2; + nbox -= 2; + } + if (nbox) { b->x1 = rect->x + dx; b->y1 = rect->y + dy; b->x2 = b->x1 + rect->width; b->y2 = b->y1 + rect->height; b++; rect++; - } while (--nbox); + } fill.boxes(sna, &fill, boxes, b-boxes); b = boxes; } while (n); @@ -11802,14 +11939,29 @@ sna_poly_fill_rect_blt(DrawablePtr drawable, if (nbox > ARRAY_SIZE(boxes)) nbox = ARRAY_SIZE(boxes); n -= nbox; - do { + while (nbox >= 2) { + b[0].x1 = rect[0].x; + b[0].y1 = rect[0].y; + b[0].x2 = b[0].x1 + rect[0].width; + b[0].y2 = b[0].y1 + rect[0].height; + + b[1].x1 = rect[1].x; + b[1].y1 = rect[1].y; + b[1].x2 = b[1].x1 + rect[1].width; + b[1].y2 = b[1].y1 + rect[1].height; + + b += 2; + rect += 2; + nbox -= 2; + } + if (nbox) { b->x1 = rect->x; b->y1 = rect->y; b->x2 = b->x1 + rect->width; b->y2 = b->y1 + rect->height; b++; rect++; - } while (--nbox); + } fill.boxes(sna, &fill, boxes, b-boxes); b = boxes; } while (n); @@ -12192,6 +12344,7 @@ sna_poly_fill_rect_tiled_8x8_blt(DrawablePtr drawable, return false; _kgem_set_mode(&sna->kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, tile_bo, bo); get_drawable_deltas(drawable, pixmap, &dx, &dy); assert(extents->x1 + dx >= 0); @@ -12335,6 +12488,7 @@ sna_poly_fill_rect_tiled_8x8_blt(DrawablePtr drawable, _kgem_submit(&sna->kgem); _kgem_set_mode(&sna->kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, tile_bo, bo); } while (1); } else { RegionRec clip; @@ -12403,6 +12557,7 @@ sna_poly_fill_rect_tiled_8x8_blt(DrawablePtr drawable, if (!kgem_check_batch(&sna->kgem, 3)) { _kgem_submit(&sna->kgem); _kgem_set_mode(&sna->kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, tile_bo, bo); unwind_batch = sna->kgem.nbatch; unwind_reloc = sna->kgem.nreloc; @@ -12499,6 +12654,7 @@ sna_poly_fill_rect_tiled_8x8_blt(DrawablePtr drawable, DBG(("%s: emitting split batch\n", __FUNCTION__)); _kgem_submit(&sna->kgem); _kgem_set_mode(&sna->kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, tile_bo, bo); unwind_batch = sna->kgem.nbatch; unwind_reloc = sna->kgem.nreloc; @@ -12572,7 +12728,7 @@ sna_poly_fill_rect_tiled_8x8_blt(DrawablePtr drawable, } done: assert_pixmap_damage(pixmap); - sna->blt_state.fill_bo = 0; + blt_done(sna); return true; } @@ -13128,6 +13284,7 @@ sna_poly_fill_rect_stippled_8x8_blt(DrawablePtr drawable, return false; _kgem_set_mode(&sna->kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); if (!clipped) { dx += drawable->x; @@ -13240,6 +13397,7 @@ sna_poly_fill_rect_stippled_8x8_blt(DrawablePtr drawable, _kgem_submit(&sna->kgem); _kgem_set_mode(&sna->kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); } while (1); } else { RegionRec clip; @@ -13297,6 +13455,7 @@ sna_poly_fill_rect_stippled_8x8_blt(DrawablePtr drawable, if (!kgem_check_batch(&sna->kgem, 3)) { _kgem_submit(&sna->kgem); _kgem_set_mode(&sna->kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); assert(sna->kgem.mode == KGEM_BLT); b = sna->kgem.batch + sna->kgem.nbatch; @@ -13369,6 +13528,7 @@ sna_poly_fill_rect_stippled_8x8_blt(DrawablePtr drawable, if (!kgem_check_batch(&sna->kgem, 3)) { _kgem_submit(&sna->kgem); _kgem_set_mode(&sna->kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); assert(sna->kgem.mode == KGEM_BLT); b = sna->kgem.batch + sna->kgem.nbatch; @@ -13419,7 +13579,7 @@ sna_poly_fill_rect_stippled_8x8_blt(DrawablePtr drawable, } assert_pixmap_damage(pixmap); - sna->blt_state.fill_bo = 0; + blt_done(sna); return true; } @@ -13499,6 +13659,7 @@ sna_poly_fill_rect_stippled_1_blt(DrawablePtr drawable, get_drawable_deltas(drawable, pixmap, &dx, &dy); kgem_set_mode(&sna->kgem, KGEM_BLT, bo); assert(kgem_bo_can_blt(&sna->kgem, bo)); + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); br00 = 3 << 20; br13 = bo->pitch; @@ -13543,6 +13704,7 @@ sna_poly_fill_rect_stippled_1_blt(DrawablePtr drawable, return false; _kgem_set_mode(&sna->kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); assert(sna->kgem.mode == KGEM_BLT); b = sna->kgem.batch + sna->kgem.nbatch; @@ -13606,6 +13768,7 @@ sna_poly_fill_rect_stippled_1_blt(DrawablePtr drawable, return false; _kgem_set_mode(&sna->kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); upload = kgem_create_buffer(&sna->kgem, bstride*bh, @@ -13736,6 +13899,7 @@ sna_poly_fill_rect_stippled_1_blt(DrawablePtr drawable, return false; _kgem_set_mode(&sna->kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); assert(sna->kgem.mode == KGEM_BLT); b = sna->kgem.batch + sna->kgem.nbatch; @@ -13797,6 +13961,7 @@ sna_poly_fill_rect_stippled_1_blt(DrawablePtr drawable, return false; _kgem_set_mode(&sna->kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); upload = kgem_create_buffer(&sna->kgem, bstride*bh, @@ -13927,6 +14092,7 @@ sna_poly_fill_rect_stippled_1_blt(DrawablePtr drawable, return false; _kgem_set_mode(&sna->kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); assert(sna->kgem.mode == KGEM_BLT); b = sna->kgem.batch + sna->kgem.nbatch; @@ -13987,6 +14153,7 @@ sna_poly_fill_rect_stippled_1_blt(DrawablePtr drawable, return false; _kgem_set_mode(&sna->kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); upload = kgem_create_buffer(&sna->kgem, bstride*bh, @@ -14064,7 +14231,7 @@ sna_poly_fill_rect_stippled_1_blt(DrawablePtr drawable, } } - sna->blt_state.fill_bo = 0; + blt_done(sna); return true; } @@ -14126,6 +14293,7 @@ sna_poly_fill_rect_stippled_n_box__imm(struct sna *sna, return; /* XXX fallback? */ _kgem_set_mode(&sna->kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); assert(sna->kgem.mode == KGEM_BLT); b = sna->kgem.batch + sna->kgem.nbatch; @@ -14251,6 +14419,7 @@ sna_poly_fill_rect_stippled_n_box(struct sna *sna, return; /* XXX fallback? */ _kgem_set_mode(&sna->kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); assert(sna->kgem.mode == KGEM_BLT); b = sna->kgem.batch + sna->kgem.nbatch; @@ -14414,6 +14583,7 @@ sna_poly_fill_rect_stippled_n_blt__imm(DrawablePtr drawable, get_drawable_deltas(drawable, pixmap, &dx, &dy); kgem_set_mode(&sna->kgem, KGEM_BLT, bo); assert(kgem_bo_can_blt(&sna->kgem, bo)); + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); br00 = XY_MONO_SRC_COPY_IMM | 3 << 20; br13 = bo->pitch; @@ -14526,7 +14696,7 @@ sna_poly_fill_rect_stippled_n_blt__imm(DrawablePtr drawable, } assert_pixmap_damage(pixmap); - sna->blt_state.fill_bo = 0; + blt_done(sna); return true; } @@ -14559,6 +14729,7 @@ sna_poly_fill_rect_stippled_n_blt(DrawablePtr drawable, get_drawable_deltas(drawable, pixmap, &dx, &dy); kgem_set_mode(&sna->kgem, KGEM_BLT, bo); assert(kgem_bo_can_blt(&sna->kgem, bo)); + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); br00 = XY_MONO_SRC_COPY | 3 << 20; br13 = bo->pitch; @@ -14673,7 +14844,7 @@ sna_poly_fill_rect_stippled_n_blt(DrawablePtr drawable, assert_pixmap_damage(pixmap); if (tile) kgem_bo_destroy(&sna->kgem, tile); - sna->blt_state.fill_bo = 0; + blt_done(sna); return true; } @@ -15281,6 +15452,7 @@ sna_glyph_blt(DrawablePtr drawable, GCPtr gc, } _kgem_set_mode(&sna->kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); DBG(("%s: glyph clip box (%d, %d), (%d, %d)\n", __FUNCTION__, @@ -15368,6 +15540,7 @@ sna_glyph_blt(DrawablePtr drawable, GCPtr gc, if (!kgem_check_batch(&sna->kgem, 3+len)) { _kgem_submit(&sna->kgem); _kgem_set_mode(&sna->kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); DBG(("%s: new batch, glyph clip box (%d, %d), (%d, %d)\n", __FUNCTION__, @@ -15479,7 +15652,7 @@ skip: } assert_pixmap_damage(pixmap); - sna->blt_state.fill_bo = 0; + blt_done(sna); return true; } @@ -16002,6 +16175,7 @@ sna_reversed_glyph_blt(DrawablePtr drawable, GCPtr gc, } _kgem_set_mode(&sna->kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); unwind_batch = sna->kgem.nbatch; unwind_reloc = sna->kgem.nreloc; @@ -16111,6 +16285,7 @@ sna_reversed_glyph_blt(DrawablePtr drawable, GCPtr gc, if (!kgem_check_batch(&sna->kgem, 3+len)) { _kgem_submit(&sna->kgem); _kgem_set_mode(&sna->kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); unwind_batch = sna->kgem.nbatch; unwind_reloc = sna->kgem.nreloc; @@ -16229,7 +16404,7 @@ skip: } assert_pixmap_damage(pixmap); - sna->blt_state.fill_bo = 0; + blt_done(sna); return true; } @@ -16450,6 +16625,7 @@ sna_push_pixels_solid_blt(GCPtr gc, kgem_set_mode(&sna->kgem, KGEM_BLT, bo); assert(kgem_bo_can_blt(&sna->kgem, bo)); + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); /* Region is pre-clipped and translated into pixmap space */ box = region_rects(region); @@ -16471,6 +16647,7 @@ sna_push_pixels_solid_blt(GCPtr gc, return false; _kgem_set_mode(&sna->kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); upload = kgem_create_buffer(&sna->kgem, bstride*bh, @@ -16564,7 +16741,7 @@ sna_push_pixels_solid_blt(GCPtr gc, box++; } while (--n); - sna->blt_state.fill_bo = 0; + blt_done(sna); return true; } @@ -16789,7 +16966,8 @@ sna_get_image__inplace(PixmapPtr pixmap, break; } - if (!kgem_bo_can_map__cpu(&sna->kgem, priv->gpu_bo, FORCE_FULL_SYNC)) + if ((flags & MOVE_INPLACE_HINT) == 0 && + !kgem_bo_can_map__cpu(&sna->kgem, priv->gpu_bo, FORCE_FULL_SYNC)) return false; if (idle && __kgem_bo_is_busy(&sna->kgem, priv->gpu_bo)) @@ -16801,11 +16979,19 @@ sna_get_image__inplace(PixmapPtr pixmap, assert(sna_damage_contains_box(&priv->gpu_damage, ®ion->extents) == PIXMAN_REGION_IN); assert(sna_damage_contains_box(&priv->cpu_damage, ®ion->extents) == PIXMAN_REGION_OUT); - src = kgem_bo_map__cpu(&sna->kgem, priv->gpu_bo); - if (src == NULL) - return false; + if (kgem_bo_can_map__cpu(&sna->kgem, priv->gpu_bo, FORCE_FULL_SYNC)) { + src = kgem_bo_map__cpu(&sna->kgem, priv->gpu_bo); + if (src == NULL) + return false; - kgem_bo_sync__cpu_full(&sna->kgem, priv->gpu_bo, FORCE_FULL_SYNC); + kgem_bo_sync__cpu_full(&sna->kgem, priv->gpu_bo, FORCE_FULL_SYNC); + } else { + src = kgem_bo_map__wc(&sna->kgem, priv->gpu_bo); + if (src == NULL) + return false; + + kgem_bo_sync__gtt(&sna->kgem, priv->gpu_bo); + } if (sigtrap_get()) return false; @@ -16833,12 +17019,11 @@ sna_get_image__inplace(PixmapPtr pixmap, region->extents.x2 - region->extents.x1, region->extents.y2 - region->extents.y1); if (!priv->shm) { - assert(src == MAP(priv->gpu_bo->map__cpu)); pixmap->devPrivate.ptr = src; pixmap->devKind = priv->gpu_bo->pitch; - priv->mapped = MAPPED_CPU; + priv->mapped = src == MAP(priv->gpu_bo->map__cpu) ? MAPPED_CPU : MAPPED_GTT; assert_pixmap_map(pixmap, priv); - priv->cpu = true; + priv->cpu &= priv->mapped == MAPPED_CPU; } } @@ -16930,7 +17115,7 @@ sna_get_image__fast(PixmapPtr pixmap, if (priv == NULL || priv->gpu_damage == NULL) return false; - if (priv->clear) { + if (priv->clear && sigtrap_get() == 0) { int w = region->extents.x2 - region->extents.x1; int h = region->extents.y2 - region->extents.y1; int pitch = PixmapBytePad(w, pixmap->drawable.depth); @@ -16939,6 +17124,7 @@ sna_get_image__fast(PixmapPtr pixmap, __FUNCTION__, priv->clear_color)); assert(DAMAGE_IS_ALL(priv->gpu_damage)); assert(priv->cpu_damage == NULL); + sigtrap_assert_active(); if (priv->clear_color == 0 || pixmap->drawable.bitsPerPixel == 8 || @@ -16955,6 +17141,7 @@ sna_get_image__fast(PixmapPtr pixmap, priv->clear_color); } + sigtrap_put(); return true; } @@ -17162,17 +17349,19 @@ void sna_accel_flush(struct sna *sna) __sna_free_pixmap(sna, priv->pixmap, priv); } } else { + unsigned hints; DBG(("%s: flushing DRI pixmap=%ld\n", __FUNCTION__, priv->pixmap->drawable.serialNumber)); assert(priv->flush); - if (sna_pixmap_move_to_gpu(priv->pixmap, - MOVE_READ | __MOVE_FORCE)) { - if (priv->flush & IS_CLIPPED) { + hints = MOVE_READ | __MOVE_FORCE; + if (priv->flush & FLUSH_WRITE) + hints |= MOVE_WRITE; + if (sna_pixmap_move_to_gpu(priv->pixmap, hints)) { + if (priv->flush & FLUSH_WRITE) { kgem_bo_unclean(&sna->kgem, priv->gpu_bo); sna_damage_all(&priv->gpu_damage, priv->pixmap); assert(priv->cpu_damage == NULL); - priv->clear = false; - priv->cpu = false; + assert(priv->clear == false); } } } @@ -17199,6 +17388,7 @@ static struct sna_pixmap *sna_accel_scanout(struct sna *sna) assert(sna->vblank_interval); assert(sna->front); + assert(!sna->mode.hidden); priv = sna_pixmap(sna->front); if (priv->gpu_bo == NULL) @@ -17217,7 +17407,7 @@ static void sna_accel_disarm_timer(struct sna *sna, int id) static bool has_offload_slaves(struct sna *sna) { #if HAS_PIXMAP_SHARING - ScreenPtr screen = sna->scrn->pScreen; + ScreenPtr screen = to_screen_from_sna(sna); PixmapDirtyUpdatePtr dirty; xorg_list_for_each_entry(dirty, &screen->pixmap_dirty_list, ent) { @@ -17231,11 +17421,14 @@ static bool has_offload_slaves(struct sna *sna) static bool has_shadow(struct sna *sna) { - DamagePtr damage = sna->mode.shadow_damage; + DamagePtr damage; - if (damage == NULL) + if (!sna->mode.shadow_enabled) return false; + damage = sna->mode.shadow_damage; + assert(damage); + DBG(("%s: has pending damage? %d, outstanding flips: %d\n", __FUNCTION__, RegionNotEmpty(DamageRegion(damage)), @@ -17365,9 +17558,8 @@ static bool sna_accel_do_expire(struct sna *sna) static void sna_accel_post_damage(struct sna *sna) { #if HAS_PIXMAP_SHARING - ScreenPtr screen = sna->scrn->pScreen; + ScreenPtr screen = to_screen_from_sna(sna); PixmapDirtyUpdatePtr dirty; - bool flush = false; xorg_list_for_each_entry(dirty, &screen->pixmap_dirty_list, ent) { RegionRec region, *damage; @@ -17477,7 +17669,14 @@ fallback: box, n, COPY_LAST)) goto fallback; - flush = true; + /* Before signalling the slave via ProcessPending, + * ensure not only the batch is submitted as the + * slave may be using the Damage callback to perform + * its copy, but also that the memory must be coherent + * - we need to treat it as uncached for the PCI slave + * will bypass LLC. + */ + kgem_bo_sync__gtt(&sna->kgem, __sna_pixmap_get_bo(dst)); } DamageRegionProcessPending(&dirty->slave_dst->drawable); @@ -17485,8 +17684,6 @@ skip: RegionUninit(®ion); DamageEmpty(dirty->damage); } - if (flush) - kgem_submit(&sna->kgem); #endif } @@ -17689,6 +17886,7 @@ sna_set_screen_pixmap(PixmapPtr pixmap) static Bool sna_create_window(WindowPtr win) { + DBG(("%s: window=%ld\n", __FUNCTION__, win->drawable.id)); sna_set_window_pixmap(win, win->drawable.pScreen->devPrivate); return TRUE; } @@ -17714,6 +17912,7 @@ sna_unmap_window(WindowPtr win) static Bool sna_destroy_window(WindowPtr win) { + DBG(("%s: window=%ld\n", __FUNCTION__, win->drawable.id)); sna_video_destroy_window(win); sna_dri2_destroy_window(win); return TRUE; @@ -17790,20 +17989,35 @@ static bool sna_option_accel_none(struct sna *sna) if (wedged(sna)) return true; - if (xf86ReturnOptValBool(sna->Options, OPTION_ACCEL_DISABLE, FALSE)) + if (!xf86ReturnOptValBool(sna->Options, OPTION_ACCEL_ENABLE, TRUE)) return true; + if (sna->kgem.gen >= 0120) + return true; + + if (!intel_option_cast_to_bool(sna->Options, + OPTION_ACCEL_METHOD, + !IS_DEFAULT_ACCEL_METHOD(NOACCEL))) + return false; + +#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,7,99,901,0) s = xf86GetOptValString(sna->Options, OPTION_ACCEL_METHOD); if (s == NULL) return IS_DEFAULT_ACCEL_METHOD(NOACCEL); return strcasecmp(s, "none") == 0; +#else + return IS_DEFAULT_ACCEL_METHOD(NOACCEL); +#endif } static bool sna_option_accel_blt(struct sna *sna) { const char *s; + if (sna->kgem.gen >= 0110) + return true; + s = xf86GetOptValString(sna->Options, OPTION_ACCEL_METHOD); if (s == NULL) return false; @@ -17892,21 +18106,21 @@ bool sna_accel_init(ScreenPtr screen, struct sna *sna) backend = "disabled"; sna->kgem.wedged = true; sna_render_mark_wedged(sna); - } else if (sna_option_accel_blt(sna) || sna->info->gen >= 0110) + } else if (sna_option_accel_blt(sna)) (void)backend; - else if (sna->info->gen >= 0100) + else if (sna->kgem.gen >= 0100) backend = gen8_render_init(sna, backend); - else if (sna->info->gen >= 070) + else if (sna->kgem.gen >= 070) backend = gen7_render_init(sna, backend); - else if (sna->info->gen >= 060) + else if (sna->kgem.gen >= 060) backend = gen6_render_init(sna, backend); - else if (sna->info->gen >= 050) + else if (sna->kgem.gen >= 050) backend = gen5_render_init(sna, backend); - else if (sna->info->gen >= 040) + else if (sna->kgem.gen >= 040) backend = gen4_render_init(sna, backend); - else if (sna->info->gen >= 030) + else if (sna->kgem.gen >= 030) backend = gen3_render_init(sna, backend); - else if (sna->info->gen >= 020) + else if (sna->kgem.gen >= 020) backend = gen2_render_init(sna, backend); DBG(("%s(backend=%s, prefer_gpu=%x)\n", @@ -18003,7 +18217,7 @@ void sna_accel_close(struct sna *sna) kgem_cleanup_cache(&sna->kgem); } -void sna_accel_block_handler(struct sna *sna, struct timeval **tv) +void sna_accel_block(struct sna *sna, struct timeval **tv) { sigtrap_assert_inactive(); @@ -18083,22 +18297,6 @@ set_tv: } } -void sna_accel_wakeup_handler(struct sna *sna) -{ - DBG(("%s: nbatch=%d, need_retire=%d, need_purge=%d\n", __FUNCTION__, - sna->kgem.nbatch, sna->kgem.need_retire, sna->kgem.need_purge)); - - if (!sna->kgem.nbatch) - return; - - if (kgem_is_idle(&sna->kgem)) { - DBG(("%s: GPU idle, flushing\n", __FUNCTION__)); - _kgem_submit(&sna->kgem); - } - - sigtrap_assert_inactive(); -} - void sna_accel_free(struct sna *sna) { DBG(("%s\n", __FUNCTION__)); diff --git a/src/sna/sna_blt.c b/src/sna/sna_blt.c index de8f6ec..ff2bdc8 100644 --- a/src/sna/sna_blt.c +++ b/src/sna/sna_blt.c @@ -86,6 +86,11 @@ static const uint8_t fill_ROP[] = { ROP_1 }; +static void sig_done(struct sna *sna, const struct sna_composite_op *op) +{ + sigtrap_put(); +} + static void nop_done(struct sna *sna, const struct sna_composite_op *op) { assert(sna->kgem.nbatch <= KGEM_BATCH_SIZE(&sna->kgem)); @@ -129,7 +134,6 @@ static bool sna_blt_fill_init(struct sna *sna, struct kgem *kgem = &sna->kgem; assert(kgem_bo_can_blt (kgem, bo)); - assert(bo->tiling != I915_TILING_Y); blt->bo[0] = bo; blt->br13 = bo->pitch; @@ -183,6 +187,7 @@ static bool sna_blt_fill_init(struct sna *sna, return false; _kgem_set_mode(kgem, KGEM_BLT); } + kgem_bcs_set_tiling(kgem, NULL, bo); assert(sna->kgem.mode == KGEM_BLT); b = kgem->batch + kgem->nbatch; @@ -237,17 +242,13 @@ static bool sna_blt_fill_init(struct sna *sna, return true; } -noinline static void sna_blt_fill_begin(struct sna *sna, - const struct sna_blt_state *blt) +noinline static void __sna_blt_fill_begin(struct sna *sna, + const struct sna_blt_state *blt) { struct kgem *kgem = &sna->kgem; uint32_t *b; - if (kgem->nreloc) { - _kgem_submit(kgem); - _kgem_set_mode(kgem, KGEM_BLT); - assert(kgem->nbatch == 0); - } + kgem_bcs_set_tiling(&sna->kgem, NULL, blt->bo[0]); assert(kgem->mode == KGEM_BLT); b = kgem->batch + kgem->nbatch; @@ -293,6 +294,21 @@ noinline static void sna_blt_fill_begin(struct sna *sna, } } +inline static void sna_blt_fill_begin(struct sna *sna, + const struct sna_blt_state *blt) +{ + struct kgem *kgem = &sna->kgem; + + if (kgem->nreloc) { + _kgem_submit(kgem); + _kgem_set_mode(kgem, KGEM_BLT); + kgem_bcs_set_tiling(kgem, NULL, blt->bo[0]); + assert(kgem->nbatch == 0); + } + + __sna_blt_fill_begin(sna, blt); +} + inline static void sna_blt_fill_one(struct sna *sna, const struct sna_blt_state *blt, int16_t x, int16_t y, @@ -330,8 +346,8 @@ static bool sna_blt_copy_init(struct sna *sna, { struct kgem *kgem = &sna->kgem; - assert(kgem_bo_can_blt (kgem, src)); - assert(kgem_bo_can_blt (kgem, dst)); + assert(kgem_bo_can_blt(kgem, src)); + assert(kgem_bo_can_blt(kgem, dst)); blt->bo[0] = src; blt->bo[1] = dst; @@ -370,6 +386,7 @@ static bool sna_blt_copy_init(struct sna *sna, return false; _kgem_set_mode(kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, src, dst); sna->blt_state.fill_bo = 0; return true; @@ -424,6 +441,7 @@ static bool sna_blt_alpha_fixup_init(struct sna *sna, return false; _kgem_set_mode(kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, src, dst); sna->blt_state.fill_bo = 0; return true; @@ -454,6 +472,7 @@ static void sna_blt_alpha_fixup_one(struct sna *sna, !kgem_check_reloc(kgem, 2)) { _kgem_submit(kgem); _kgem_set_mode(kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, blt->bo[0], blt->bo[1]); } assert(sna->kgem.mode == KGEM_BLT); @@ -582,6 +601,7 @@ static void sna_blt_copy_one(struct sna *sna, !kgem_check_reloc(kgem, 2)) { _kgem_submit(kgem); _kgem_set_mode(kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, blt->bo[0], blt->bo[1]); } assert(sna->kgem.mode == KGEM_BLT); @@ -912,8 +932,27 @@ sna_composite_mask_is_opaque(PicturePtr mask) return is_solid(mask) && is_white(mask); else if (!PICT_FORMAT_A(mask->format)) return true; - else - return is_solid(mask) && is_opaque_solid(mask); + else if (mask->pSourcePict) { + PictSolidFill *fill = (PictSolidFill *) mask->pSourcePict; + return (fill->color >> 24) == 0xff; + } else { + struct sna_pixmap *priv; + assert(mask->pDrawable); + + if (mask->pDrawable->width == 1 && + mask->pDrawable->height == 1 && + mask->repeat) + return pixel_is_opaque(get_pixel(mask), mask->format); + + if (mask->transform) + return false; + + priv = sna_pixmap_from_drawable(mask->pDrawable); + if (priv == NULL || !priv->clear) + return false; + + return pixel_is_opaque(priv->clear_color, mask->format); + } } fastcall @@ -971,6 +1010,7 @@ static void blt_composite_fill__cpu(struct sna *sna, assert(op->dst.pixmap->devPrivate.ptr); assert(op->dst.pixmap->devKind); + sigtrap_assert_active(); pixman_fill(op->dst.pixmap->devPrivate.ptr, op->dst.pixmap->devKind / sizeof(uint32_t), op->dst.pixmap->drawable.bitsPerPixel, @@ -990,6 +1030,7 @@ blt_composite_fill_box_no_offset__cpu(struct sna *sna, assert(op->dst.pixmap->devPrivate.ptr); assert(op->dst.pixmap->devKind); + sigtrap_assert_active(); pixman_fill(op->dst.pixmap->devPrivate.ptr, op->dst.pixmap->devKind / sizeof(uint32_t), op->dst.pixmap->drawable.bitsPerPixel, @@ -1010,6 +1051,7 @@ blt_composite_fill_boxes_no_offset__cpu(struct sna *sna, assert(op->dst.pixmap->devPrivate.ptr); assert(op->dst.pixmap->devKind); + sigtrap_assert_active(); pixman_fill(op->dst.pixmap->devPrivate.ptr, op->dst.pixmap->devKind / sizeof(uint32_t), op->dst.pixmap->drawable.bitsPerPixel, @@ -1031,6 +1073,7 @@ blt_composite_fill_box__cpu(struct sna *sna, assert(op->dst.pixmap->devPrivate.ptr); assert(op->dst.pixmap->devKind); + sigtrap_assert_active(); pixman_fill(op->dst.pixmap->devPrivate.ptr, op->dst.pixmap->devKind / sizeof(uint32_t), op->dst.pixmap->drawable.bitsPerPixel, @@ -1052,6 +1095,7 @@ blt_composite_fill_boxes__cpu(struct sna *sna, assert(op->dst.pixmap->devPrivate.ptr); assert(op->dst.pixmap->devKind); + sigtrap_assert_active(); pixman_fill(op->dst.pixmap->devPrivate.ptr, op->dst.pixmap->devKind / sizeof(uint32_t), op->dst.pixmap->drawable.bitsPerPixel, @@ -1159,6 +1203,7 @@ static inline void _sna_blt_maybe_clear(const struct sna_composite_op *op, const box->y2 - box->y1 >= op->dst.height) { struct sna_pixmap *priv = sna_pixmap(op->dst.pixmap); if (op->dst.bo == priv->gpu_bo) { + sna_damage_all(&priv->gpu_damage, op->dst.pixmap); priv->clear = true; priv->clear_color = op->u.blt.pixel; DBG(("%s: pixmap=%ld marking clear [%08x]\n", @@ -1404,6 +1449,7 @@ begin_blt(struct sna *sna, return false; _kgem_set_mode(&sna->kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, NULL, op->dst.bo); } return true; @@ -1429,6 +1475,7 @@ prepare_blt_clear(struct sna *sna, DBG(("%s\n", __FUNCTION__)); if (op->dst.bo == NULL) { + op->u.blt.pixel = 0; op->blt = blt_composite_fill__cpu; if (op->dst.x|op->dst.y) { op->box = blt_composite_fill_box__cpu; @@ -1439,9 +1486,8 @@ prepare_blt_clear(struct sna *sna, op->boxes = blt_composite_fill_boxes_no_offset__cpu; op->thread_boxes = blt_composite_fill_boxes_no_offset__cpu; } - op->done = nop_done; - op->u.blt.pixel = 0; - return true; + op->done = sig_done; + return sigtrap_get() == 0; } op->blt = blt_composite_fill; @@ -1484,8 +1530,8 @@ prepare_blt_fill(struct sna *sna, op->boxes = blt_composite_fill_boxes_no_offset__cpu; op->thread_boxes = blt_composite_fill_boxes_no_offset__cpu; } - op->done = nop_done; - return true; + op->done = sig_done; + return sigtrap_get() == 0; } op->blt = blt_composite_fill; @@ -1668,6 +1714,7 @@ static void blt_composite_copy_boxes__thread(struct sna *sna, _kgem_submit(kgem); _kgem_set_mode(kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, src_bo, dst_bo); } while (1); } else { do { @@ -1724,6 +1771,7 @@ static void blt_composite_copy_boxes__thread(struct sna *sna, _kgem_submit(kgem); _kgem_set_mode(kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, src_bo, dst_bo); } while (1); } sna_vertex_unlock(&sna->render); @@ -1806,6 +1854,7 @@ static void blt_composite_copy_boxes__thread64(struct sna *sna, _kgem_submit(kgem); _kgem_set_mode(kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, src_bo, dst_bo); } while (1); } else { do { @@ -1864,6 +1913,7 @@ static void blt_composite_copy_boxes__thread64(struct sna *sna, _kgem_submit(kgem); _kgem_set_mode(kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, src_bo, dst_bo); } while (1); } sna_vertex_unlock(&sna->render); @@ -1973,6 +2023,7 @@ prepare_blt_copy(struct sna *sna, } _kgem_set_mode(&sna->kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, bo, op->dst.bo); DBG(("%s\n", __FUNCTION__)); @@ -2396,6 +2447,9 @@ prepare_blt_put(struct sna *sna, op->box = blt_put_composite_box; op->boxes = blt_put_composite_boxes; } + + op->done = nop_done; + return true; } else { if (alpha_fixup) { op->u.blt.pixel = alpha_fixup; @@ -2407,10 +2461,10 @@ prepare_blt_put(struct sna *sna, op->box = blt_put_composite_box__cpu; op->boxes = blt_put_composite_boxes__cpu; } - } - op->done = nop_done; - return true; + op->done = sig_done; + return sigtrap_get() == 0; + } } static bool @@ -2567,6 +2621,8 @@ clear: } if (hint & REPLACES) kgem_bo_undo(&sna->kgem, tmp->dst.bo); + if (flags & COMPOSITE_UPLOAD) + return false; } else { RegionRec region; @@ -2597,24 +2653,20 @@ clear: op = PictOpSrc; if (op == PictOpOver) { color = over(get_solid_color(src, PICT_a8r8g8b8), - color_convert(sna_pixmap(tmp->dst.pixmap)->clear_color, - dst->format, PICT_a8r8g8b8)); + solid_color(dst->format, sna_pixmap(tmp->dst.pixmap)->clear_color)); op = PictOpSrc; DBG(("%s: precomputing solid OVER (%08x, %08x) -> %08x\n", __FUNCTION__, get_solid_color(src, PICT_a8r8g8b8), - color_convert(sna_pixmap(tmp->dst.pixmap)->clear_color, - dst->format, PICT_a8r8g8b8), + solid_color(dst->format, sna_pixmap(tmp->dst.pixmap)->clear_color), color)); } if (op == PictOpAdd) { color = add(get_solid_color(src, PICT_a8r8g8b8), - color_convert(sna_pixmap(tmp->dst.pixmap)->clear_color, - dst->format, PICT_a8r8g8b8)); + solid_color(dst->format, sna_pixmap(tmp->dst.pixmap)->clear_color)); op = PictOpSrc; DBG(("%s: precomputing solid ADD (%08x, %08x) -> %08x\n", __FUNCTION__, get_solid_color(src, PICT_a8r8g8b8), - color_convert(sna_pixmap(tmp->dst.pixmap)->clear_color, - dst->format, PICT_a8r8g8b8), + solid_color(dst->format, sna_pixmap(tmp->dst.pixmap)->clear_color), color)); } } @@ -2657,6 +2709,8 @@ fill: } if (hint & REPLACES) kgem_bo_undo(&sna->kgem, tmp->dst.bo); + if (flags & COMPOSITE_UPLOAD) + return false; } else { RegionRec region; @@ -2720,8 +2774,8 @@ fill: if (is_clear(src_pixmap)) { if (src->repeat || (x >= 0 && y >= 0 && - x + width < src_pixmap->drawable.width && - y + height < src_pixmap->drawable.height)) { + x + width <= src_pixmap->drawable.width && + y + height <= src_pixmap->drawable.height)) { color = color_convert(sna_pixmap(src_pixmap)->clear_color, src->format, tmp->dst.format); goto fill; @@ -2795,7 +2849,7 @@ fill: if (src_pixmap->drawable.width <= sna->render.max_3d_size && src_pixmap->drawable.height <= sna->render.max_3d_size && bo->pitch <= sna->render.max_3d_pitch && - (flags & COMPOSITE_FALLBACK) == 0) + (flags & (COMPOSITE_UPLOAD | COMPOSITE_FALLBACK)) == 0) { return false; } @@ -2846,7 +2900,7 @@ fallback: DBG(("%s: fallback -- unaccelerated upload\n", __FUNCTION__)); goto fallback; - } else { + } else if ((flags & COMPOSITE_UPLOAD) == 0) { ret = prepare_blt_copy(sna, tmp, bo, alpha_fixup); if (!ret) goto fallback; @@ -3023,6 +3077,7 @@ sna_blt_composite__convert(struct sna *sna, } _kgem_set_mode(&sna->kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, tmp->src.bo, tmp->dst.bo); if (alpha_fixup) { tmp->blt = blt_composite_copy_with_alpha; @@ -3062,7 +3117,7 @@ static void sna_blt_fill_op_blt(struct sna *sna, if (sna->blt_state.fill_bo != op->base.u.blt.bo[0]->unique_id) { const struct sna_blt_state *blt = &op->base.u.blt; - sna_blt_fill_begin(sna, blt); + __sna_blt_fill_begin(sna, blt); sna->blt_state.fill_bo = blt->bo[0]->unique_id; sna->blt_state.fill_pixel = blt->pixel; @@ -3079,7 +3134,7 @@ fastcall static void sna_blt_fill_op_box(struct sna *sna, if (sna->blt_state.fill_bo != op->base.u.blt.bo[0]->unique_id) { const struct sna_blt_state *blt = &op->base.u.blt; - sna_blt_fill_begin(sna, blt); + __sna_blt_fill_begin(sna, blt); sna->blt_state.fill_bo = blt->bo[0]->unique_id; sna->blt_state.fill_pixel = blt->pixel; @@ -3097,7 +3152,7 @@ fastcall static void sna_blt_fill_op_boxes(struct sna *sna, if (sna->blt_state.fill_bo != op->base.u.blt.bo[0]->unique_id) { const struct sna_blt_state *blt = &op->base.u.blt; - sna_blt_fill_begin(sna, blt); + __sna_blt_fill_begin(sna, blt); sna->blt_state.fill_bo = blt->bo[0]->unique_id; sna->blt_state.fill_pixel = blt->pixel; @@ -3132,7 +3187,7 @@ fastcall static void sna_blt_fill_op_points(struct sna *sna, DBG(("%s: %08x x %d\n", __FUNCTION__, blt->pixel, n)); if (sna->blt_state.fill_bo != op->base.u.blt.bo[0]->unique_id) { - sna_blt_fill_begin(sna, blt); + __sna_blt_fill_begin(sna, blt); sna->blt_state.fill_bo = blt->bo[0]->unique_id; sna->blt_state.fill_pixel = blt->pixel; @@ -3162,65 +3217,15 @@ fastcall static void sna_blt_fill_op_points(struct sna *sna, assert(kgem->nbatch < kgem->surface); if ((dx|dy) == 0) { - while (n_this_time >= 8) { - *((uint64_t *)b + 0) = pt_add(cmd, p+0, 0, 0); - *((uint64_t *)b + 1) = pt_add(cmd, p+1, 0, 0); - *((uint64_t *)b + 2) = pt_add(cmd, p+2, 0, 0); - *((uint64_t *)b + 3) = pt_add(cmd, p+3, 0, 0); - *((uint64_t *)b + 4) = pt_add(cmd, p+4, 0, 0); - *((uint64_t *)b + 5) = pt_add(cmd, p+5, 0, 0); - *((uint64_t *)b + 6) = pt_add(cmd, p+6, 0, 0); - *((uint64_t *)b + 7) = pt_add(cmd, p+7, 0, 0); - b += 16; - n_this_time -= 8; - p += 8; - } - if (n_this_time & 4) { - *((uint64_t *)b + 0) = pt_add(cmd, p+0, 0, 0); - *((uint64_t *)b + 1) = pt_add(cmd, p+1, 0, 0); - *((uint64_t *)b + 2) = pt_add(cmd, p+2, 0, 0); - *((uint64_t *)b + 3) = pt_add(cmd, p+3, 0, 0); - b += 8; - p += 4; - } - if (n_this_time & 2) { - *((uint64_t *)b + 0) = pt_add(cmd, p+0, 0, 0); - *((uint64_t *)b + 1) = pt_add(cmd, p+1, 0, 0); - b += 4; - p += 2; - } - if (n_this_time & 1) - *((uint64_t *)b + 0) = pt_add(cmd, p++, 0, 0); + do { + *(uint64_t *)b = pt_add(cmd, p++, 0, 0); + b += 2; + } while (--n_this_time); } else { - while (n_this_time >= 8) { - *((uint64_t *)b + 0) = pt_add(cmd, p+0, dx, dy); - *((uint64_t *)b + 1) = pt_add(cmd, p+1, dx, dy); - *((uint64_t *)b + 2) = pt_add(cmd, p+2, dx, dy); - *((uint64_t *)b + 3) = pt_add(cmd, p+3, dx, dy); - *((uint64_t *)b + 4) = pt_add(cmd, p+4, dx, dy); - *((uint64_t *)b + 5) = pt_add(cmd, p+5, dx, dy); - *((uint64_t *)b + 6) = pt_add(cmd, p+6, dx, dy); - *((uint64_t *)b + 7) = pt_add(cmd, p+7, dx, dy); - b += 16; - n_this_time -= 8; - p += 8; - } - if (n_this_time & 4) { - *((uint64_t *)b + 0) = pt_add(cmd, p+0, dx, dy); - *((uint64_t *)b + 1) = pt_add(cmd, p+1, dx, dy); - *((uint64_t *)b + 2) = pt_add(cmd, p+2, dx, dy); - *((uint64_t *)b + 3) = pt_add(cmd, p+3, dx, dy); - b += 8; - p += 8; - } - if (n_this_time & 2) { - *((uint64_t *)b + 0) = pt_add(cmd, p+0, dx, dy); - *((uint64_t *)b + 1) = pt_add(cmd, p+1, dx, dy); - b += 4; - p += 2; - } - if (n_this_time & 1) - *((uint64_t *)b + 0) = pt_add(cmd, p++, dx, dy); + do { + *(uint64_t *)b = pt_add(cmd, p++, dx, dy); + b += 2; + } while (--n_this_time); } if (!n) @@ -3414,6 +3419,7 @@ static bool sna_blt_fill_box(struct sna *sna, uint8_t alu, _kgem_set_mode(kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); assert(kgem_check_batch(kgem, 6)); assert(kgem_check_reloc(kgem, 1)); @@ -3520,6 +3526,8 @@ bool sna_blt_fill_boxes(struct sna *sna, uint8_t alu, _kgem_set_mode(kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); + assert(sna->kgem.mode == KGEM_BLT); b = kgem->batch + kgem->nbatch; if (kgem->gen >= 0100) { @@ -3608,6 +3616,7 @@ bool sna_blt_fill_boxes(struct sna *sna, uint8_t alu, _kgem_submit(kgem); _kgem_set_mode(kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, NULL, bo); assert(sna->kgem.mode == KGEM_BLT); b = kgem->batch + kgem->nbatch; @@ -3754,6 +3763,7 @@ bool sna_blt_copy_boxes(struct sna *sna, uint8_t alu, } _kgem_set_mode(kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, src_bo, dst_bo); if ((dst_dx | dst_dy) == 0) { if (kgem->gen >= 0100) { @@ -3814,6 +3824,7 @@ bool sna_blt_copy_boxes(struct sna *sna, uint8_t alu, _kgem_submit(kgem); _kgem_set_mode(kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, src_bo, dst_bo); } while (1); } else { uint64_t hdr = (uint64_t)br13 << 32 | cmd | 6; @@ -3871,6 +3882,7 @@ bool sna_blt_copy_boxes(struct sna *sna, uint8_t alu, _kgem_submit(kgem); _kgem_set_mode(kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, src_bo, dst_bo); } while (1); } } else { @@ -3932,6 +3944,7 @@ bool sna_blt_copy_boxes(struct sna *sna, uint8_t alu, _kgem_submit(kgem); _kgem_set_mode(kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, src_bo, dst_bo); } while (1); } else { cmd |= 6; @@ -3989,6 +4002,7 @@ bool sna_blt_copy_boxes(struct sna *sna, uint8_t alu, _kgem_submit(kgem); _kgem_set_mode(kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, src_bo, dst_bo); } while (1); } } @@ -4095,6 +4109,7 @@ bool sna_blt_copy_boxes__with_alpha(struct sna *sna, uint8_t alu, !kgem_check_reloc(kgem, 2)) { _kgem_submit(kgem); _kgem_set_mode(kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, src_bo, dst_bo); } assert(sna->kgem.mode == KGEM_BLT); @@ -4190,6 +4205,7 @@ bool sna_blt_copy_boxes_fallback(struct sna *sna, uint8_t alu, DBG(("%s: dst == src\n", __FUNCTION__)); if (src_bo->tiling == I915_TILING_Y && + !sna->kgem.can_blt_y && kgem_bo_blt_pitch_is_ok(&sna->kgem, src_bo)) { struct kgem_bo *bo; @@ -4237,6 +4253,7 @@ bool sna_blt_copy_boxes_fallback(struct sna *sna, uint8_t alu, } } else { if (src_bo->tiling == I915_TILING_Y && + !sna->kgem.can_blt_y && kgem_bo_blt_pitch_is_ok(&sna->kgem, src_bo)) { DBG(("%s: src is y-tiled\n", __FUNCTION__)); if (src->type != DRAWABLE_PIXMAP) @@ -4251,6 +4268,7 @@ bool sna_blt_copy_boxes_fallback(struct sna *sna, uint8_t alu, } if (dst_bo->tiling == I915_TILING_Y && + !sna->kgem.can_blt_y && kgem_bo_blt_pitch_is_ok(&sna->kgem, dst_bo)) { DBG(("%s: dst is y-tiled\n", __FUNCTION__)); if (dst->type != DRAWABLE_PIXMAP) diff --git a/src/sna/sna_composite.c b/src/sna/sna_composite.c index f01f020..c6de9d5 100644 --- a/src/sna/sna_composite.c +++ b/src/sna/sna_composite.c @@ -653,8 +653,9 @@ sna_composite(CARD8 op, RegionRec region; int dx, dy; - DBG(("%s(%d src=%ld+(%d, %d), mask=%ld+(%d, %d), dst=%ld+(%d, %d)+(%d, %d), size=(%d, %d)\n", - __FUNCTION__, op, + DBG(("%s(pixmap=%ld, op=%d, src=%ld+(%d, %d), mask=%ld+(%d, %d), dst=%ld+(%d, %d)+(%d, %d), size=(%d, %d)\n", + __FUNCTION__, + pixmap->drawable.serialNumber, op, get_picture_id(src), src_x, src_y, get_picture_id(mask), mask_x, mask_y, get_picture_id(dst), dst_x, dst_y, @@ -673,13 +674,6 @@ sna_composite(CARD8 op, src = sna->clear; } - if (mask && sna_composite_mask_is_opaque(mask)) { - DBG(("%s: removing opaque %smask\n", - __FUNCTION__, - mask->componentAlpha && PICT_FORMAT_RGB(mask->format) ? "CA " : "")); - mask = NULL; - } - if (!sna_compute_composite_region(®ion, src, mask, dst, src_x, src_y, @@ -688,6 +682,13 @@ sna_composite(CARD8 op, width, height)) return; + if (mask && sna_composite_mask_is_opaque(mask)) { + DBG(("%s: removing opaque %smask\n", + __FUNCTION__, + mask->componentAlpha && PICT_FORMAT_RGB(mask->format) ? "CA " : "")); + mask = NULL; + } + if (NO_COMPOSITE) goto fallback; @@ -797,8 +798,10 @@ sna_composite_rectangles(CARD8 op, int i, num_boxes; unsigned hint; - DBG(("%s(op=%d, %08x x %d [(%d, %d)x(%d, %d) ...])\n", - __FUNCTION__, op, + DBG(("%s(pixmap=%ld, op=%d, %08x x %d [(%d, %d)x(%d, %d) ...])\n", + __FUNCTION__, + get_drawable_pixmap(dst->pDrawable)->drawable.serialNumber, + op, (color->alpha >> 8 << 24) | (color->red >> 8 << 16) | (color->green >> 8 << 8) | @@ -814,38 +817,40 @@ sna_composite_rectangles(CARD8 op, return; } - if ((color->red|color->green|color->blue|color->alpha) <= 0x00ff) { - switch (op) { - case PictOpOver: - case PictOpOutReverse: - case PictOpAdd: - return; - case PictOpInReverse: - case PictOpSrc: - op = PictOpClear; - break; - case PictOpAtopReverse: - op = PictOpOut; - break; - case PictOpXor: - op = PictOpOverReverse; - break; - } - } if (color->alpha <= 0x00ff) { - switch (op) { - case PictOpOver: - case PictOpOutReverse: - return; - case PictOpInReverse: - op = PictOpClear; - break; - case PictOpAtopReverse: - op = PictOpOut; - break; - case PictOpXor: - op = PictOpOverReverse; - break; + if (PICT_FORMAT_TYPE(dst->format) == PICT_TYPE_A || + (color->red|color->green|color->blue) <= 0x00ff) { + switch (op) { + case PictOpOver: + case PictOpOutReverse: + case PictOpAdd: + return; + case PictOpInReverse: + case PictOpSrc: + op = PictOpClear; + break; + case PictOpAtopReverse: + op = PictOpOut; + break; + case PictOpXor: + op = PictOpOverReverse; + break; + } + } else { + switch (op) { + case PictOpOver: + case PictOpOutReverse: + return; + case PictOpInReverse: + op = PictOpClear; + break; + case PictOpAtopReverse: + op = PictOpOut; + break; + case PictOpXor: + op = PictOpOverReverse; + break; + } } } else if (color->alpha >= 0xff00) { switch (op) { @@ -863,11 +868,16 @@ sna_composite_rectangles(CARD8 op, case PictOpXor: op = PictOpOut; break; + case PictOpAdd: + if (PICT_FORMAT_TYPE(dst->format) == PICT_TYPE_A || + (color->red&color->green&color->blue) >= 0xff00) + op = PictOpSrc; + break; } } /* Avoid reducing overlapping translucent rectangles */ - if (op == PictOpOver && + if ((op == PictOpOver || op == PictOpAdd) && num_rects == 1 && sna_drawable_is_clear(dst->pDrawable)) op = PictOpSrc; @@ -979,6 +989,9 @@ sna_composite_rectangles(CARD8 op, bool ok; if (op == PictOpClear) { + if (priv->clear_color == 0) + goto done; + ok = sna_get_pixel_from_rgba(&pixel, 0, 0, 0, 0, dst->format); @@ -990,8 +1003,11 @@ sna_composite_rectangles(CARD8 op, color->alpha, dst->format); } - if (ok && priv->clear_color == pixel) + if (ok && priv->clear_color == pixel) { + DBG(("%s: matches current clear, skipping\n", + __FUNCTION__)); goto done; + } } if (region.data == NULL) { diff --git a/src/sna/sna_display.c b/src/sna/sna_display.c index 4b218b7..1b39f20 100644 --- a/src/sna/sna_display.c +++ b/src/sna/sna_display.c @@ -40,6 +40,24 @@ #include #include +#if HAVE_ALLOCA_H +#include +#elif defined __GNUC__ +#define alloca __builtin_alloca +#elif defined _AIX +#define alloca __alloca +#elif defined _MSC_VER +#include +#define alloca _alloca +#else +void *alloca(size_t); +#endif + +#define _PARSE_EDID_ +/* Jump through a few hoops in order to fixup EDIDs */ +#undef VERSION +#undef REVISION + #include "sna.h" #include "sna_reg.h" #include "fb/fbpict.h" @@ -72,6 +90,8 @@ #include #endif +#define COLDPLUG_DELAY_MS 2000 + /* Minor discrepancy between 32-bit/64-bit ABI in old kernels */ union compat_mode_get_connector{ struct drm_mode_get_connector conn; @@ -88,6 +108,8 @@ union compat_mode_get_connector{ #define DEFAULT_DPI 96 #endif +#define OUTPUT_STATUS_CACHE_MS 15000 + #define DRM_MODE_PAGE_FLIP_ASYNC 0x02 #define DRM_CLIENT_CAP_UNIVERSAL_PLANES 2 @@ -106,30 +128,84 @@ struct local_mode_obj_get_properties { }; #define LOCAL_MODE_OBJECT_PLANE 0xeeeeeeee +struct local_mode_set_plane { + uint32_t plane_id; + uint32_t crtc_id; + uint32_t fb_id; /* fb object contains surface format type */ + uint32_t flags; + + /* Signed dest location allows it to be partially off screen */ + int32_t crtc_x, crtc_y; + uint32_t crtc_w, crtc_h; + + /* Source values are 16.16 fixed point */ + uint32_t src_x, src_y; + uint32_t src_h, src_w; +}; +#define LOCAL_IOCTL_MODE_SETPLANE DRM_IOWR(0xB7, struct local_mode_set_plane) + +struct local_mode_get_plane { + uint32_t plane_id; + + uint32_t crtc_id; + uint32_t fb_id; + + uint32_t possible_crtcs; + uint32_t gamma_size; + + uint32_t count_format_types; + uint64_t format_type_ptr; +}; +#define LOCAL_IOCTL_MODE_GETPLANE DRM_IOWR(0xb6, struct local_mode_get_plane) + +struct local_mode_get_plane_res { + uint64_t plane_id_ptr; + uint64_t count_planes; +}; +#define LOCAL_IOCTL_MODE_GETPLANERESOURCES DRM_IOWR(0xb5, struct local_mode_get_plane_res) + #if 0 #define __DBG DBG #else #define __DBG(x) #endif +#define DBG_NATIVE_ROTATION ~0 /* minimum RR_Rotate_0 */ + extern XF86ConfigPtr xf86configptr; +struct sna_cursor { + struct sna_cursor *next; + uint32_t *image; + bool transformed; + Rotation rotation; + int ref; + int size; + int last_width; + int last_height; + unsigned handle; + unsigned serial; + unsigned alloc; +}; + struct sna_crtc { + unsigned long flags; xf86CrtcPtr base; struct drm_mode_modeinfo kmode; - int dpms_mode; PixmapPtr slave_pixmap; DamagePtr slave_damage; - struct kgem_bo *bo, *shadow_bo, *client_bo; + struct kgem_bo *bo, *shadow_bo, *client_bo, *cache_bo; struct sna_cursor *cursor; unsigned int last_cursor_size; uint32_t offset; bool shadow; bool fallback_shadow; bool transform; + bool cursor_transform; + bool hwcursor; bool flip_pending; - uint8_t id; - uint8_t pipe; + + struct pict_f_transform cursor_to_fb, fb_to_cursor; RegionRec client_damage; /* XXX overlap with shadow damage? */ @@ -178,16 +254,22 @@ struct sna_output { uint32_t edid_blob_id; uint32_t edid_len; void *edid_raw; + xf86MonPtr fake_edid_mon; + void *fake_edid_raw; bool has_panel_limits; int panel_hdisplay; int panel_vdisplay; uint32_t dpms_id; - int dpms_mode; + uint8_t dpms_mode; struct backlight backlight; int backlight_active_level; + uint32_t last_detect; + uint32_t status; + bool update_properties; + int num_modes; struct drm_mode_modeinfo *modes; @@ -218,6 +300,7 @@ enum { /* XXX copied from hw/xfree86/modes/xf86Crtc.c */ OPTION_DEFAULT_MODES, }; +static void __sna_output_dpms(xf86OutputPtr output, int dpms, int fixup); static void sna_crtc_disable_cursor(struct sna *sna, struct sna_crtc *crtc); static bool is_zaphod(ScrnInfoPtr scrn) @@ -225,6 +308,81 @@ static bool is_zaphod(ScrnInfoPtr scrn) return xf86IsEntityShared(scrn->entityList[0]); } +static bool +sna_zaphod_match(struct sna *sna, const char *output) +{ + const char *s, *colon; + char t[20]; + unsigned int i = 0; + + s = xf86GetOptValString(sna->Options, OPTION_ZAPHOD); + if (s == NULL) + return false; + + colon = strchr(s, ':'); + if (colon) /* Skip over the ZaphodPipes */ + s = colon + 1; + + do { + /* match any outputs in a comma list, stopping at whitespace */ + switch (*s) { + case '\0': + t[i] = '\0'; + return strcmp(t, output) == 0; + + case ',': + t[i] ='\0'; + if (strcmp(t, output) == 0) + return TRUE; + i = 0; + break; + + case ' ': + case '\t': + case '\n': + case '\r': + break; + + default: + t[i++] = *s; + break; + } + + s++; + } while (i < sizeof(t)); + + return false; +} + +static unsigned +get_zaphod_crtcs(struct sna *sna) +{ + const char *str, *colon; + unsigned crtcs = 0; + + str = xf86GetOptValString(sna->Options, OPTION_ZAPHOD); + if (str == NULL || (colon = strchr(str, ':')) == NULL) { + DBG(("%s: no zaphod pipes, using screen number: %x\n", + __FUNCTION__, + sna->scrn->confScreen->device->screen)); + return 1 << sna->scrn->confScreen->device->screen; + } + + DBG(("%s: ZaphodHeads='%s'\n", __FUNCTION__, str)); + while (str < colon) { + char *end; + unsigned crtc = strtoul(str, &end, 0); + if (end == str) + break; + DBG(("%s: adding CRTC %d to zaphod pipes\n", + __FUNCTION__, crtc)); + crtcs |= 1 << crtc; + str = end + 1; + } + DBG(("%s: ZaphodPipes=%x\n", __FUNCTION__, crtcs)); + return crtcs; +} + inline static unsigned count_to_mask(int x) { return (1 << x) - 1; @@ -247,6 +405,16 @@ static inline struct sna_crtc *to_sna_crtc(xf86CrtcPtr crtc) return crtc->driver_private; } +static inline unsigned __sna_crtc_pipe(struct sna_crtc *crtc) +{ + return crtc->flags >> 8 & 0xff; +} + +static inline unsigned __sna_crtc_id(struct sna_crtc *crtc) +{ + return crtc->flags >> 16 & 0xff; +} + static inline bool event_pending(int fd) { struct pollfd pfd; @@ -268,65 +436,60 @@ static inline uint32_t fb_id(struct kgem_bo *bo) return bo->delta; } -uint32_t sna_crtc_id(xf86CrtcPtr crtc) -{ - if (to_sna_crtc(crtc) == NULL) - return 0; - return to_sna_crtc(crtc)->id; -} - -int sna_crtc_to_pipe(xf86CrtcPtr crtc) -{ - assert(to_sna_crtc(crtc)); - return to_sna_crtc(crtc)->pipe; -} - uint32_t sna_crtc_to_sprite(xf86CrtcPtr crtc) { assert(to_sna_crtc(crtc)); return to_sna_crtc(crtc)->sprite.id; } -bool sna_crtc_is_on(xf86CrtcPtr crtc) -{ - assert(to_sna_crtc(crtc)); - return to_sna_crtc(crtc)->bo != NULL; -} - bool sna_crtc_is_transformed(xf86CrtcPtr crtc) { assert(to_sna_crtc(crtc)); return to_sna_crtc(crtc)->transform; } -static inline uint64_t msc64(struct sna_crtc *sna_crtc, uint32_t seq) +static inline bool msc64(struct sna_crtc *sna_crtc, uint32_t seq, uint64_t *msc) { + bool record = true; if (seq < sna_crtc->last_seq) { if (sna_crtc->last_seq - seq > 0x40000000) { sna_crtc->wrap_seq++; DBG(("%s: pipe=%d wrapped; was %u, now %u, wraps=%u\n", - __FUNCTION__, sna_crtc->pipe, + __FUNCTION__, __sna_crtc_pipe(sna_crtc), sna_crtc->last_seq, seq, sna_crtc->wrap_seq)); - } else { - ERR(("%s: pipe=%d msc went backwards; was %u, now %u\n", - __FUNCTION__, sna_crtc->pipe, sna_crtc->last_seq, seq)); - seq = sna_crtc->last_seq; + } else { + DBG(("%s: pipe=%d msc went backwards; was %u, now %u; ignoring for last_swap\n", + __FUNCTION__, __sna_crtc_pipe(sna_crtc), sna_crtc->last_seq, seq)); + + record = false; } } - sna_crtc->last_seq = seq; - return (uint64_t)sna_crtc->wrap_seq << 32 | seq; + *msc = (uint64_t)sna_crtc->wrap_seq << 32 | seq; + return record; } uint64_t sna_crtc_record_swap(xf86CrtcPtr crtc, int tv_sec, int tv_usec, unsigned seq) { struct sna_crtc *sna_crtc = to_sna_crtc(crtc); + uint64_t msc; + assert(sna_crtc); - DBG(("%s: recording last swap on pipe=%d, frame %d, time %d.%06d\n", - __FUNCTION__, sna_crtc->pipe, seq, tv_sec, tv_usec)); - sna_crtc->swap.tv_sec = tv_sec; - sna_crtc->swap.tv_usec = tv_usec; - return sna_crtc->swap.msc = msc64(sna_crtc, seq); + + if (msc64(sna_crtc, seq, &msc)) { + DBG(("%s: recording last swap on pipe=%d, frame %d [%08llx], time %d.%06d\n", + __FUNCTION__, __sna_crtc_pipe(sna_crtc), seq, (long long)msc, + tv_sec, tv_usec)); + sna_crtc->swap.tv_sec = tv_sec; + sna_crtc->swap.tv_usec = tv_usec; + sna_crtc->swap.msc = msc; + } else { + DBG(("%s: swap event on pipe=%d, frame %d [%08llx], time %d.%06d\n", + __FUNCTION__, __sna_crtc_pipe(sna_crtc), seq, (long long)msc, + tv_sec, tv_usec)); + } + + return msc; } const struct ust_msc *sna_crtc_last_swap(xf86CrtcPtr crtc) @@ -342,15 +505,6 @@ const struct ust_msc *sna_crtc_last_swap(xf86CrtcPtr crtc) } } -xf86CrtcPtr sna_mode_first_crtc(struct sna *sna) -{ - xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); - if (sna->mode.num_real_crtc) - return config->crtc[0]; - else - return NULL; -} - #ifndef NDEBUG static void gem_close(int fd, uint32_t handle); static void assert_scanout(struct kgem *kgem, struct kgem_bo *bo, @@ -393,8 +547,9 @@ static unsigned get_fb(struct sna *sna, struct kgem_bo *bo, DBG(("%s: create fb %dx%d@%d/%d\n", __FUNCTION__, width, height, scrn->depth, scrn->bitsPerPixel)); - assert(bo->tiling != I915_TILING_Y); + assert(bo->tiling != I915_TILING_Y || sna->kgem.can_scanout_y); assert((bo->pitch & 63) == 0); + assert(scrn->vtSema); /* must be master */ VG_CLEAR(arg); arg.width = width; @@ -404,21 +559,83 @@ static unsigned get_fb(struct sna *sna, struct kgem_bo *bo, arg.depth = scrn->depth; arg.handle = bo->handle; - assert(sna->scrn->vtSema); /* must be master */ if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_ADDFB, &arg)) { - xf86DrvMsg(scrn->scrnIndex, X_ERROR, - "%s: failed to add fb: %dx%d depth=%d, bpp=%d, pitch=%d: %d\n", - __FUNCTION__, width, height, - scrn->depth, scrn->bitsPerPixel, bo->pitch, errno); - return 0; + /* Try again with the fancy version */ + struct local_mode_fb_cmd2 { + uint32_t fb_id; + uint32_t width, height; + uint32_t pixel_format; + uint32_t flags; + + uint32_t handles[4]; + uint32_t pitches[4]; /* pitch for each plane */ + uint32_t offsets[4]; /* offset of each plane */ + uint64_t modifiers[4]; + } f; +#define LOCAL_IOCTL_MODE_ADDFB2 DRM_IOWR(0xb8, struct local_mode_fb_cmd2) + memset(&f, 0, sizeof(f)); + f.width = width; + f.height = height; + /* XXX interlaced */ + f.flags = 1 << 1; /* +modifiers */ + f.handles[0] = bo->handle; + f.pitches[0] = bo->pitch; + + switch (bo->tiling) { + case I915_TILING_NONE: + break; + case I915_TILING_X: + /* I915_FORMAT_MOD_X_TILED */ + f.modifiers[0] = (uint64_t)1 << 56 | 1; + break; + case I915_TILING_Y: + /* I915_FORMAT_MOD_X_TILED */ + f.modifiers[0] = (uint64_t)1 << 56 | 2; + break; + } + +#define fourcc(a,b,c,d) ((a) | (b) << 8 | (c) << 16 | (d) << 24) + switch (scrn->depth) { + default: + ERR(("%s: unhandled screen format, depth=%d\n", + __FUNCTION__, scrn->depth)); + goto fail; + case 8: + f.pixel_format = fourcc('C', '8', ' ', ' '); + break; + case 15: + f.pixel_format = fourcc('X', 'R', '1', '5'); + break; + case 16: + f.pixel_format = fourcc('R', 'G', '1', '6'); + break; + case 24: + f.pixel_format = fourcc('X', 'R', '2', '4'); + break; + case 30: + f.pixel_format = fourcc('X', 'R', '3', '0'); + break; + } +#undef fourcc + + if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_ADDFB2, &f)) { +fail: + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "%s: failed to add fb: %dx%d depth=%d, bpp=%d, pitch=%d: %d\n", + __FUNCTION__, width, height, + scrn->depth, scrn->bitsPerPixel, bo->pitch, errno); + return 0; + } + + arg.fb_id = f.fb_id; } assert(arg.fb_id != 0); - + bo->delta = arg.fb_id; DBG(("%s: attached fb=%d to handle=%d\n", - __FUNCTION__, arg.fb_id, arg.handle)); + __FUNCTION__, bo->delta, arg.handle)); bo->scanout = true; - return bo->delta = arg.fb_id; + return bo->delta; } static uint32_t gem_create(int fd, int size) @@ -497,8 +714,6 @@ sna_backlight_uevent(int fd, void *closure) if (sna_output->dpms_mode != DPMSModeOn) continue; - assert(output->randr_output); - val = backlight_get(&sna_output->backlight); if (val < 0) continue; @@ -523,6 +738,7 @@ sna_backlight_uevent(int fd, void *closure) TRUE, FALSE); } } + DBG(("%s: complete\n", __FUNCTION__)); } static void sna_backlight_pre_init(struct sna *sna) @@ -570,6 +786,7 @@ static void sna_backlight_drain_uevents(struct sna *sna) if (sna->mode.backlight_monitor == NULL) return; + DBG(("%s()\n", __FUNCTION__)); sna_backlight_uevent(udev_monitor_get_fd(sna->mode.backlight_monitor), sna); } @@ -632,9 +849,22 @@ sna_output_backlight_set(struct sna_output *sna_output, int level) return ret; } +static bool +has_native_backlight(struct sna_output *sna_output) +{ + return sna_output->backlight.type == BL_RAW; +} + static void sna_output_backlight_off(struct sna_output *sna_output) { + /* Trust the kernel to turn the native backlight off. However, we + * do explicitly turn the backlight back on (when we wake the output) + * just in case a third party turns it off! + */ + if (has_native_backlight(sna_output)) + return; + DBG(("%s(%s)\n", __FUNCTION__, sna_output->base->name)); backlight_off(&sna_output->backlight); sna_output_backlight_set(sna_output, 0); @@ -674,7 +904,7 @@ has_user_backlight_override(xf86OutputPtr output) if (*str == '\0') return (char *)str; - if (backlight_exists(str) == BL_NONE) { + if (!backlight_exists(str)) { xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, "Unrecognised backlight control interface '%s'\n", str); @@ -728,6 +958,29 @@ done: sna_output->backlight.iface, best_iface, output->name); } +#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,12,99,901,0) +static inline int sigio_block(void) +{ + OsBlockSIGIO(); + return 0; +} +static inline void sigio_unblock(int was_blocked) +{ + OsReleaseSIGIO(); + (void)was_blocked; +} +#else +#include +static inline int sigio_block(void) +{ + return xf86BlockSIGIO(); +} +static inline void sigio_unblock(int was_blocked) +{ + xf86UnblockSIGIO(was_blocked); +} +#endif + static char *canonical_kmode_name(const struct drm_mode_modeinfo *kmode) { char tmp[32], *buf; @@ -781,6 +1034,7 @@ mode_from_kmode(ScrnInfoPtr scrn, mode->VTotal = kmode->vtotal; mode->VScan = kmode->vscan; + mode->VRefresh = kmode->vrefresh; mode->Flags = kmode->flags; mode->name = get_kmode_name(kmode); @@ -814,6 +1068,7 @@ mode_to_kmode(struct drm_mode_modeinfo *kmode, DisplayModePtr mode) kmode->vtotal = mode->VTotal; kmode->vscan = mode->VScan; + kmode->vrefresh = mode->VRefresh; kmode->flags = mode->Flags; if (mode->name) strncpy(kmode->name, mode->name, DRM_DISPLAY_MODE_LEN); @@ -824,11 +1079,12 @@ static void sna_crtc_force_outputs_on(xf86CrtcPtr crtc) { xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(crtc->scrn); + /* All attached outputs are valid, so update our timestamps */ + unsigned now = GetTimeInMillis(); int i; assert(to_sna_crtc(crtc)); - DBG(("%s(pipe=%d), currently? %d\n", __FUNCTION__, - to_sna_crtc(crtc)->pipe, to_sna_crtc(crtc)->dpms_mode)); + DBG(("%s(pipe=%d)\n", __FUNCTION__, sna_crtc_pipe(crtc))); /* DPMS handling by the kernel is inconsistent, so after setting a * mode on an output presume that we intend for it to be on, or that @@ -843,10 +1099,11 @@ sna_crtc_force_outputs_on(xf86CrtcPtr crtc) if (output->crtc != crtc) continue; - output->funcs->dpms(output, DPMSModeOn); + __sna_output_dpms(output, DPMSModeOn, false); + if (to_sna_output(output)->last_detect) + to_sna_output(output)->last_detect = now; } - to_sna_crtc(crtc)->dpms_mode = DPMSModeOn; #if XF86_CRTC_VERSION >= 3 crtc->active = TRUE; #endif @@ -859,8 +1116,7 @@ sna_crtc_force_outputs_off(xf86CrtcPtr crtc) int i; assert(to_sna_crtc(crtc)); - DBG(("%s(pipe=%d), currently? %d\n", __FUNCTION__, - to_sna_crtc(crtc)->pipe, to_sna_crtc(crtc)->dpms_mode)); + DBG(("%s(pipe=%d)\n", __FUNCTION__, sna_crtc_pipe(crtc))); /* DPMS handling by the kernel is inconsistent, so after setting a * mode on an output presume that we intend for it to be on, or that @@ -875,35 +1131,45 @@ sna_crtc_force_outputs_off(xf86CrtcPtr crtc) if (output->crtc != crtc) continue; - output->funcs->dpms(output, DPMSModeOff); + __sna_output_dpms(output, DPMSModeOff, false); } - - to_sna_crtc(crtc)->dpms_mode = DPMSModeOff; } static unsigned -rotation_reduce(struct plane *p, unsigned rotation) +rotation_reflect(unsigned rotation) { - unsigned unsupported_rotations = rotation & ~p->rotation.supported; + unsigned other_bits; - if (unsupported_rotations == 0) - return rotation; + /* paranoia for future extensions */ + other_bits = rotation & ~RR_Rotate_All; -#define RR_Reflect_XY (RR_Reflect_X | RR_Reflect_Y) + /* flip the reflection to compensate for reflecting the rotation */ + other_bits ^= RR_Reflect_X | RR_Reflect_Y; - if ((unsupported_rotations & RR_Reflect_XY) == RR_Reflect_XY && - p->rotation.supported& RR_Rotate_180) { - rotation &= ~RR_Reflect_XY; - rotation ^= RR_Rotate_180; - } + /* Reflect the screen by rotating the rotation bit, + * which has to have at least RR_Rotate_0 set. This allows + * us to reflect any of the rotation bits, not just 0. + */ + rotation &= RR_Rotate_All; + assert(rotation); + rotation <<= 2; /* RR_Rotate_0 -> RR_Rotate_180 etc */ + rotation |= rotation >> 4; /* RR_Rotate_270' to RR_Rotate_90 */ + + return rotation | other_bits; +} - if ((unsupported_rotations & RR_Rotate_180) && - (p->rotation.supported& RR_Reflect_XY) == RR_Reflect_XY) { - rotation ^= RR_Reflect_XY; - rotation &= ~RR_Rotate_180; +static unsigned +rotation_reduce(struct plane *p, unsigned rotation) +{ + /* If unsupported try exchanging rotation for a reflection */ + if (rotation & ~p->rotation.supported) { + unsigned new_rotation = rotation_reflect(rotation); + if ((new_rotation & p->rotation.supported) == new_rotation) + rotation = new_rotation; } -#undef RR_Reflect_XY + /* Only one rotation bit should be set */ + assert(is_power_of_two(rotation & RR_Rotate_All)); return rotation; } @@ -923,7 +1189,7 @@ rotation_set(struct sna *sna, struct plane *p, uint32_t desired) if (desired == p->rotation.current) return true; - if ((desired & p->rotation.supported) == 0) { + if ((desired & p->rotation.supported) != desired) { errno = EINVAL; return false; } @@ -961,14 +1227,89 @@ bool sna_crtc_set_sprite_rotation(xf86CrtcPtr crtc, uint32_t rotation) assert(to_sna_crtc(crtc)); DBG(("%s: CRTC:%d [pipe=%d], sprite=%u set-rotation=%x\n", __FUNCTION__, - to_sna_crtc(crtc)->id, to_sna_crtc(crtc)->pipe, to_sna_crtc(crtc)->sprite.id, - rotation)); + sna_crtc_id(crtc), sna_crtc_pipe(crtc), + to_sna_crtc(crtc)->sprite.id, rotation)); return rotation_set(to_sna(crtc->scrn), &to_sna_crtc(crtc)->sprite, rotation_reduce(&to_sna_crtc(crtc)->sprite, rotation)); } +#if HAS_DEBUG_FULL +struct kmsg { + int fd; + int saved_loglevel; +}; + +static int kmsg_get_debug(void) +{ + FILE *file; + int v = -1; + + file = fopen("/sys/module/drm/parameters/debug", "r"); + if (file) { + fscanf(file, "%d", &v); + fclose(file); + } + + return v; +} + +static void kmsg_set_debug(int v) +{ + FILE *file; + + file = fopen("/sys/module/drm/parameters/debug", "w"); + if (file) { + fprintf(file, "%d\n", v); + fclose(file); + } +} + +static void kmsg_open(struct kmsg *k) +{ + k->saved_loglevel = kmsg_get_debug(); + if (k->saved_loglevel != -1) + kmsg_set_debug(0xff); + + k->fd = open("/dev/kmsg", O_RDONLY | O_NONBLOCK); + if (k->fd != -1) + lseek(k->fd, 0, SEEK_END); +} + +static void kmsg_close(struct kmsg *k, int dump) +{ + FILE *file; + + file = NULL; + if (k->fd != -1 && dump) + file = fdopen(k->fd, "r"); + if (file) { + size_t len = 0; + char *line = NULL; + + while (getline(&line, &len, file) != -1) { + char *start = strchr(line, ';'); + if (start) + LogF("KMSG: %s", start + 1); + } + + free(line); + fclose(file); + } + + if (k->fd != -1) + close(k->fd); + + if (k->saved_loglevel != -1) + kmsg_set_debug(k->saved_loglevel); +} +#else +struct kmsg { int unused; }; +static void kmsg_open(struct kmsg *k) {} +static void kmsg_close(struct kmsg *k, int dump) {} +#endif + static bool sna_crtc_apply(xf86CrtcPtr crtc) { @@ -978,26 +1319,33 @@ sna_crtc_apply(xf86CrtcPtr crtc) struct drm_mode_crtc arg; uint32_t output_ids[32]; int output_count = 0; - int i; + int sigio, i; + struct kmsg kmsg; + bool ret = false; - DBG(("%s CRTC:%d [pipe=%d], handle=%d\n", __FUNCTION__, sna_crtc->id, sna_crtc->pipe, sna_crtc->bo->handle)); + DBG(("%s CRTC:%d [pipe=%d], handle=%d\n", __FUNCTION__, + __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc), + sna_crtc->bo->handle)); if (!sna_crtc->kmode.clock) { ERR(("%s(CRTC:%d [pipe=%d]): attempted to set an invalid mode\n", - __FUNCTION__, sna_crtc->id, sna_crtc->pipe)); + __FUNCTION__, __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc))); return false; } + sigio = sigio_block(); + kmsg_open(&kmsg); + assert(sna->mode.num_real_output < ARRAY_SIZE(output_ids)); sna_crtc_disable_cursor(sna, sna_crtc); if (!rotation_set(sna, &sna_crtc->primary, sna_crtc->rotation)) { ERR(("%s: set-primary-rotation failed (rotation-id=%d, rotation=%d) on CRTC:%d [pipe=%d], errno=%d\n", - __FUNCTION__, sna_crtc->primary.rotation.prop, sna_crtc->rotation, sna_crtc->id, sna_crtc->pipe, errno)); + __FUNCTION__, sna_crtc->primary.rotation.prop, sna_crtc->rotation, __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc), errno)); sna_crtc->primary.rotation.supported &= ~sna_crtc->rotation; - return false; + goto unblock; } DBG(("%s: CRTC:%d [pipe=%d] primary rotation set to %x\n", - __FUNCTION__, sna_crtc->id, sna_crtc->pipe, sna_crtc->rotation)); + __FUNCTION__, __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc), sna_crtc->rotation)); for (i = 0; i < sna->mode.num_real_output; i++) { xf86OutputPtr output = config->output[i]; @@ -1008,7 +1356,7 @@ sna_crtc_apply(xf86CrtcPtr crtc) * and we lose track of the user settings. */ if (output->crtc == NULL) - output->funcs->dpms(output, DPMSModeOff); + __sna_output_dpms(output, DPMSModeOff, false); if (output->crtc != crtc) continue; @@ -1022,11 +1370,11 @@ sna_crtc_apply(xf86CrtcPtr crtc) DBG(("%s: attaching output '%s' %d [%d] to crtc:%d (pipe %d) (possible crtc:%x, possible clones:%x)\n", __FUNCTION__, output->name, i, to_connector_id(output), - sna_crtc->id, sna_crtc->pipe, + __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc), (uint32_t)output->possible_crtcs, (uint32_t)output->possible_clones)); - assert(output->possible_crtcs & (1 << sna_crtc->pipe) || + assert(output->possible_crtcs & (1 << __sna_crtc_pipe(sna_crtc)) || is_zaphod(crtc->scrn)); output_ids[output_count] = to_connector_id(output); @@ -1034,17 +1382,17 @@ sna_crtc_apply(xf86CrtcPtr crtc) DBG(("%s: too many outputs (%d) for me!\n", __FUNCTION__, output_count)); errno = EINVAL; - return false; + goto unblock; } } if (output_count == 0) { DBG(("%s: no outputs\n", __FUNCTION__)); errno = EINVAL; - return false; + goto unblock; } VG_CLEAR(arg); - arg.crtc_id = sna_crtc->id; + arg.crtc_id = __sna_crtc_id(sna_crtc); arg.fb_id = fb_id(sna_crtc->bo); if (sna_crtc->transform || sna_crtc->slave_pixmap) { arg.x = 0; @@ -1061,7 +1409,7 @@ sna_crtc_apply(xf86CrtcPtr crtc) arg.mode_valid = 1; DBG(("%s: applying crtc [%d, pipe=%d] mode=%dx%d+%d+%d@%d, fb=%d%s%s update to %d outputs [%d...]\n", - __FUNCTION__, sna_crtc->id, sna_crtc->pipe, + __FUNCTION__, __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc), arg.mode.hdisplay, arg.mode.vdisplay, arg.x, arg.y, @@ -1071,12 +1419,15 @@ sna_crtc_apply(xf86CrtcPtr crtc) sna_crtc->transform ? " [transformed]" : "", output_count, output_count ? output_ids[0] : 0)); - if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_SETCRTC, &arg)) - return false; - - sna_crtc->mode_serial++; - sna_crtc_force_outputs_on(crtc); - return true; + ret = drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_SETCRTC, &arg) == 0; + if (ret) { + sna_crtc->mode_serial++; + sna_crtc_force_outputs_on(crtc); + } +unblock: + kmsg_close(&kmsg, !ret); + sigio_unblock(sigio); + return ret; } static bool overlap(const BoxRec *a, const BoxRec *b) @@ -1099,7 +1450,6 @@ static bool wait_for_shadow(struct sna *sna, unsigned flags) { PixmapPtr pixmap = priv->pixmap; - DamagePtr damage; struct kgem_bo *bo, *tmp; int flip_active; bool ret = true; @@ -1111,9 +1461,11 @@ static bool wait_for_shadow(struct sna *sna, assert(priv->move_to_gpu_data == sna); assert(sna->mode.shadow != priv->gpu_bo); - if (flags == 0 || pixmap != sna->front || !sna->mode.shadow_damage) + if (flags == 0 || pixmap != sna->front || !sna->mode.shadow_enabled) goto done; + assert(sna->mode.shadow_damage); + if ((flags & MOVE_WRITE) == 0) { if ((flags & __MOVE_SCANOUT) == 0) { struct sna_crtc *crtc; @@ -1154,9 +1506,8 @@ static bool wait_for_shadow(struct sna *sna, } assert(sna->mode.shadow_active); - - damage = sna->mode.shadow_damage; - sna->mode.shadow_damage = NULL; + sna->mode.shadow_wait = true; + sna->mode.shadow_enabled = false; flip_active = sna->mode.flip_active; if (flip_active) { @@ -1208,6 +1559,10 @@ static bool wait_for_shadow(struct sna *sna, bo = sna->mode.shadow; } } + assert(!sna->mode.shadow_enabled); + assert(sna->mode.shadow_wait); + sna->mode.shadow_wait = false; + sna->mode.shadow_enabled = true; if (bo->refcnt > 1) { bo = kgem_create_2d(&sna->kgem, @@ -1230,8 +1585,6 @@ static bool wait_for_shadow(struct sna *sna, bo = sna->mode.shadow; } - sna->mode.shadow_damage = damage; - RegionSubtract(&sna->mode.shadow_region, &sna->mode.shadow_region, &sna->mode.shadow_cancel); @@ -1311,6 +1664,8 @@ done: priv->move_to_gpu_data = NULL; priv->move_to_gpu = NULL; + assert(!sna->mode.shadow_wait); + return ret; } @@ -1358,22 +1713,38 @@ bool sna_pixmap_discard_shadow_damage(struct sna_pixmap *priv, return RegionNil(&sna->mode.shadow_region); } +static void sna_mode_damage(DamagePtr damage, RegionPtr region, void *closure) +{ + /* Throw away the rectangles if the region grows too big */ + region = DamageRegion(damage); + if (region->data) { + RegionRec dup; + + dup = *region; + RegionUninit(&dup); + + region->data = NULL; + } +} + static bool sna_mode_enable_shadow(struct sna *sna) { - ScreenPtr screen = sna->scrn->pScreen; + ScreenPtr screen = to_screen_from_sna(sna); DBG(("%s\n", __FUNCTION__)); assert(sna->mode.shadow == NULL); assert(sna->mode.shadow_damage == NULL); assert(sna->mode.shadow_active == 0); + assert(!sna->mode.shadow_enabled); - sna->mode.shadow_damage = DamageCreate(NULL, NULL, - DamageReportNone, TRUE, - screen, screen); + sna->mode.shadow_damage = DamageCreate(sna_mode_damage, NULL, + DamageReportRawRegion, + TRUE, screen, sna); if (!sna->mode.shadow_damage) return false; DamageRegister(&sna->front->drawable, sna->mode.shadow_damage); + sna->mode.shadow_enabled = true; return true; } @@ -1381,8 +1752,10 @@ static void sna_mode_disable_shadow(struct sna *sna) { struct sna_pixmap *priv; - if (!sna->mode.shadow_damage) + if (!sna->mode.shadow_damage) { + assert(!sna->mode.shadow_enabled); return; + } DBG(("%s\n", __FUNCTION__)); @@ -1393,6 +1766,7 @@ static void sna_mode_disable_shadow(struct sna *sna) DamageUnregister(&sna->front->drawable, sna->mode.shadow_damage); DamageDestroy(sna->mode.shadow_damage); sna->mode.shadow_damage = NULL; + sna->mode.shadow_enabled = false; if (sna->mode.shadow) { kgem_bo_destroy(&sna->kgem, sna->mode.shadow); @@ -1413,7 +1787,7 @@ static void sna_crtc_slave_damage(DamagePtr damage, RegionPtr region, void *clos __FUNCTION__, region->extents.x1, region->extents.y1, region->extents.x2, region->extents.y2, region_num_rects(region), - crtc->pipe, crtc->base->x, crtc->base->y)); + __sna_crtc_pipe(crtc), crtc->base->x, crtc->base->y)); assert(crtc->slave_damage == damage); assert(sna->mode.shadow_damage); @@ -1431,7 +1805,7 @@ static bool sna_crtc_enable_shadow(struct sna *sna, struct sna_crtc *crtc) return true; } - DBG(("%s: enabling for crtc %d\n", __FUNCTION__, crtc->id)); + DBG(("%s: enabling for crtc %d\n", __FUNCTION__, __sna_crtc_id(crtc))); if (!sna->mode.shadow_active) { if (!sna_mode_enable_shadow(sna)) @@ -1443,9 +1817,12 @@ static bool sna_crtc_enable_shadow(struct sna *sna, struct sna_crtc *crtc) if (crtc->slave_pixmap) { assert(crtc->slave_damage == NULL); + DBG(("%s: enabling PRIME slave tracking on CRTC %d [pipe=%d], pixmap=%ld\n", + __FUNCTION__, __sna_crtc_id(crtc), __sna_crtc_pipe(crtc), crtc->slave_pixmap->drawable.serialNumber)); crtc->slave_damage = DamageCreate(sna_crtc_slave_damage, NULL, DamageReportRawRegion, TRUE, - sna->scrn->pScreen, crtc); + to_screen_from_sna(sna), + crtc); if (crtc->slave_damage == NULL) { if (!--sna->mode.shadow_active) sna_mode_disable_shadow(sna); @@ -1465,6 +1842,8 @@ static void sna_crtc_disable_override(struct sna *sna, struct sna_crtc *crtc) if (crtc->client_bo == NULL) return; + assert(crtc->client_bo->refcnt > crtc->client_bo->active_scanout); + if (!crtc->transform) { DrawableRec tmp; @@ -1489,7 +1868,7 @@ static void sna_crtc_disable_shadow(struct sna *sna, struct sna_crtc *crtc) if (!crtc->shadow) return; - DBG(("%s: disabling for crtc %d\n", __FUNCTION__, crtc->id)); + DBG(("%s: disabling for crtc %d\n", __FUNCTION__, __sna_crtc_id(crtc))); assert(sna->mode.shadow_active > 0); if (crtc->slave_damage) { @@ -1517,14 +1896,24 @@ __sna_crtc_disable(struct sna *sna, struct sna_crtc *sna_crtc) sna_crtc_disable_shadow(sna, sna_crtc); if (sna_crtc->bo) { + DBG(("%s: releasing handle=%d from scanout, active=%d\n", + __FUNCTION__,sna_crtc->bo->handle, sna_crtc->bo->active_scanout-1)); + assert(sna_crtc->flags & CRTC_ON); assert(sna_crtc->bo->active_scanout); assert(sna_crtc->bo->refcnt >= sna_crtc->bo->active_scanout); sna_crtc->bo->active_scanout--; kgem_bo_destroy(&sna->kgem, sna_crtc->bo); sna_crtc->bo = NULL; + sna_crtc->flags &= ~CRTC_ON; - assert(sna->mode.front_active); - sna->mode.front_active--; + if (sna->mode.hidden) { + sna->mode.hidden--; + assert(sna->mode.hidden); + assert(sna->mode.front_active == 0); + } else { + assert(sna->mode.front_active); + sna->mode.front_active--; + } sna->mode.dirty = true; } @@ -1532,13 +1921,19 @@ __sna_crtc_disable(struct sna *sna, struct sna_crtc *sna_crtc) kgem_bo_destroy(&sna->kgem, sna_crtc->shadow_bo); sna_crtc->shadow_bo = NULL; } - sna_crtc->transform = false; + if (sna_crtc->transform) { + assert(sna->mode.rr_active); + sna->mode.rr_active--; + sna_crtc->transform = false; + } + sna_crtc->cursor_transform = false; + sna_crtc->hwcursor = true; assert(!sna_crtc->shadow); } static void -sna_crtc_disable(xf86CrtcPtr crtc) +sna_crtc_disable(xf86CrtcPtr crtc, bool force) { struct sna *sna = to_sna(crtc->scrn); struct sna_crtc *sna_crtc = to_sna_crtc(crtc); @@ -1547,14 +1942,16 @@ sna_crtc_disable(xf86CrtcPtr crtc) if (sna_crtc == NULL) return; - DBG(("%s: disabling crtc [%d, pipe=%d]\n", __FUNCTION__, - sna_crtc->id, sna_crtc->pipe)); + if (!force && sna_crtc->bo == NULL) + return; + + DBG(("%s: disabling crtc [%d, pipe=%d], force?=%d\n", __FUNCTION__, + __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc), force)); sna_crtc_force_outputs_off(crtc); - assert(sna_crtc->dpms_mode == DPMSModeOff); memset(&arg, 0, sizeof(arg)); - arg.crtc_id = sna_crtc->id; + arg.crtc_id = __sna_crtc_id(sna_crtc); (void)drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_SETCRTC, &arg); __sna_crtc_disable(sna, sna_crtc); @@ -1574,19 +1971,19 @@ static void update_flush_interval(struct sna *sna) if (!crtc->enabled) { DBG(("%s: CRTC:%d (pipe %d) disabled\n", - __FUNCTION__,i, to_sna_crtc(crtc)->pipe)); + __FUNCTION__,i, sna_crtc_pipe(crtc))); assert(to_sna_crtc(crtc)->bo == NULL); continue; } - if (to_sna_crtc(crtc)->dpms_mode != DPMSModeOn) { + if (to_sna_crtc(crtc)->bo == NULL) { DBG(("%s: CRTC:%d (pipe %d) turned off\n", - __FUNCTION__,i, to_sna_crtc(crtc)->pipe)); + __FUNCTION__,i, sna_crtc_pipe(crtc))); continue; } DBG(("%s: CRTC:%d (pipe %d) vrefresh=%f\n", - __FUNCTION__, i, to_sna_crtc(crtc)->pipe, + __FUNCTION__, i, sna_crtc_pipe(crtc), xf86ModeVRefresh(&crtc->mode))); max_vrefresh = max(max_vrefresh, xf86ModeVRefresh(&crtc->mode)); } @@ -1642,7 +2039,7 @@ void sna_copy_fbcon(struct sna *sna) int dx, dy; int i; - if (wedged(sna)) + if (wedged(sna) || isGPU(sna->scrn)) return; DBG(("%s\n", __FUNCTION__)); @@ -1662,7 +2059,7 @@ void sna_copy_fbcon(struct sna *sna) assert(crtc != NULL); VG_CLEAR(mode); - mode.crtc_id = crtc->id; + mode.crtc_id = __sna_crtc_id(crtc); if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCRTC, &mode)) continue; if (!mode.fb_id) @@ -1726,7 +2123,7 @@ void sna_copy_fbcon(struct sna *sna) kgem_bo_destroy(&sna->kgem, bo); #if ABI_VIDEODRV_VERSION >= SET_ABI_VERSION(10, 0) - sna->scrn->pScreen->canDoBGNoneRoot = ok; + to_screen_from_sna(sna)->canDoBGNoneRoot = ok; #endif } @@ -1736,7 +2133,6 @@ static bool use_shadow(struct sna *sna, xf86CrtcPtr crtc) PictTransform crtc_to_fb; struct pict_f_transform f_crtc_to_fb, f_fb_to_crtc; unsigned pitch_limit; - struct sna_pixmap *priv; BoxRec b; assert(sna->scrn->virtualX && sna->scrn->virtualY); @@ -1765,44 +2161,48 @@ static bool use_shadow(struct sna *sna, xf86CrtcPtr crtc) return true; } - priv = sna_pixmap_force_to_gpu(sna->front, MOVE_READ | __MOVE_SCANOUT); - if (priv == NULL) - return true; /* maybe we can create a bo for the scanout? */ - - if (sna->kgem.gen == 071) - pitch_limit = priv->gpu_bo->tiling ? 16 * 1024 : 32 * 1024; - else if ((sna->kgem.gen >> 3) > 4) - pitch_limit = 32 * 1024; - else if ((sna->kgem.gen >> 3) == 4) - pitch_limit = priv->gpu_bo->tiling ? 16 * 1024 : 32 * 1024; - else if ((sna->kgem.gen >> 3) == 3) - pitch_limit = priv->gpu_bo->tiling ? 8 * 1024 : 16 * 1024; - else - pitch_limit = 8 * 1024; - DBG(("%s: gpu bo handle=%d tiling=%d pitch=%d, limit=%d\n", __FUNCTION__, priv->gpu_bo->handle, priv->gpu_bo->tiling, priv->gpu_bo->pitch, pitch_limit)); - if (priv->gpu_bo->pitch > pitch_limit) - return true; + if (!isGPU(sna->scrn)) { + struct sna_pixmap *priv; - if (priv->gpu_bo->tiling && sna->flags & SNA_LINEAR_FB) { - DBG(("%s: gpu bo is tiled, need linear, forcing shadow\n", __FUNCTION__)); - return true; - } + priv = sna_pixmap_force_to_gpu(sna->front, MOVE_READ | __MOVE_SCANOUT); + if (priv == NULL) + return true; /* maybe we can create a bo for the scanout? */ - transform = NULL; - if (crtc->transformPresent) - transform = &crtc->transform; - if (RRTransformCompute(crtc->x, crtc->y, - crtc->mode.HDisplay, crtc->mode.VDisplay, - crtc->rotation, transform, + if (sna->kgem.gen == 071) + pitch_limit = priv->gpu_bo->tiling ? 16 * 1024 : 32 * 1024; + else if ((sna->kgem.gen >> 3) > 4) + pitch_limit = 32 * 1024; + else if ((sna->kgem.gen >> 3) == 4) + pitch_limit = priv->gpu_bo->tiling ? 16 * 1024 : 32 * 1024; + else if ((sna->kgem.gen >> 3) == 3) + pitch_limit = priv->gpu_bo->tiling ? 8 * 1024 : 16 * 1024; + else + pitch_limit = 8 * 1024; + DBG(("%s: gpu bo handle=%d tiling=%d pitch=%d, limit=%d\n", __FUNCTION__, priv->gpu_bo->handle, priv->gpu_bo->tiling, priv->gpu_bo->pitch, pitch_limit)); + if (priv->gpu_bo->pitch > pitch_limit) + return true; + + if (priv->gpu_bo->tiling && sna->flags & SNA_LINEAR_FB) { + DBG(("%s: gpu bo is tiled, need linear, forcing shadow\n", __FUNCTION__)); + return true; + } + } + + transform = NULL; + if (crtc->transformPresent) + transform = &crtc->transform; + if (RRTransformCompute(crtc->x, crtc->y, + crtc->mode.HDisplay, crtc->mode.VDisplay, + crtc->rotation, transform, &crtc_to_fb, &f_crtc_to_fb, &f_fb_to_crtc)) { bool needs_transform = true; unsigned rotation = rotation_reduce(&to_sna_crtc(crtc)->primary, crtc->rotation); DBG(("%s: natively supported rotation? rotation=%x & supported=%x == %d\n", - __FUNCTION__, crtc->rotation, to_sna_crtc(crtc)->primary.rotation.supported, - !!(crtc->rotation & to_sna_crtc(crtc)->primary.rotation.supported))); - if (to_sna_crtc(crtc)->primary.rotation.supported & rotation) + __FUNCTION__, rotation, to_sna_crtc(crtc)->primary.rotation.supported, + rotation == (rotation & to_sna_crtc(crtc)->primary.rotation.supported))); + if ((to_sna_crtc(crtc)->primary.rotation.supported & rotation) == rotation) needs_transform = RRTransformCompute(crtc->x, crtc->y, crtc->mode.HDisplay, crtc->mode.VDisplay, RR_Rotate_0, transform, @@ -1912,6 +2312,28 @@ get_scanout_bo(struct sna *sna, PixmapPtr pixmap) return priv->gpu_bo; } +static void shadow_clear(struct sna *sna, + PixmapPtr front, struct kgem_bo *bo, + xf86CrtcPtr crtc) +{ + bool ok = false; + if (!wedged(sna)) + ok = sna->render.fill_one(sna, front, bo, 0, + 0, 0, crtc->mode.HDisplay, crtc->mode.VDisplay, + GXclear); + if (!ok) { + void *ptr = kgem_bo_map__gtt(&sna->kgem, bo); + if (ptr) + memset(ptr, 0, bo->pitch * crtc->mode.HDisplay); + } + sna->mode.shadow_dirty = true; +} + +static bool rr_active(xf86CrtcPtr crtc) +{ + return crtc->transformPresent || crtc->rotation != RR_Rotate_0; +} + static struct kgem_bo *sna_crtc_attach(xf86CrtcPtr crtc) { struct sna_crtc *sna_crtc = to_sna_crtc(crtc); @@ -1919,10 +2341,15 @@ static struct kgem_bo *sna_crtc_attach(xf86CrtcPtr crtc) struct sna *sna = to_sna(scrn); struct kgem_bo *bo; - sna_crtc->transform = false; + if (sna_crtc->transform) { + assert(sna->mode.rr_active); + sna_crtc->transform = false; + sna->mode.rr_active--; + } sna_crtc->rotation = RR_Rotate_0; if (use_shadow(sna, crtc)) { + PixmapPtr front; unsigned long tiled_limit; int tiling; @@ -1977,8 +2404,8 @@ force_shadow: return NULL; } - if (__sna_pixmap_get_bo(sna->front) && !crtc->transformPresent) { - DrawableRec tmp; + front = sna_crtc->slave_pixmap ?: sna->front; + if (__sna_pixmap_get_bo(front) && !rr_active(crtc)) { BoxRec b; b.x1 = crtc->x; @@ -1986,28 +2413,48 @@ force_shadow: b.x2 = crtc->x + crtc->mode.HDisplay; b.y2 = crtc->y + crtc->mode.VDisplay; - DBG(("%s: copying onto shadow CRTC: (%d, %d)x(%d, %d), handle=%d\n", - __FUNCTION__, - b.x1, b.y1, - b.x2, b.y2, - bo->handle)); - - tmp.width = crtc->mode.HDisplay; - tmp.height = crtc->mode.VDisplay; - tmp.depth = sna->front->drawable.depth; - tmp.bitsPerPixel = sna->front->drawable.bitsPerPixel; - - (void)sna->render.copy_boxes(sna, GXcopy, - &sna->front->drawable, __sna_pixmap_get_bo(sna->front), 0, 0, - &tmp, bo, -b.x1, -b.y1, - &b, 1, 0); - } + if (b.x1 < 0) + b.x1 = 0; + if (b.y1 < 0) + b.y1 = 0; + if (b.x2 > scrn->virtualX) + b.x2 = scrn->virtualX; + if (b.y2 > scrn->virtualY) + b.y2 = scrn->virtualY; + if (b.x2 - b.x1 < crtc->mode.HDisplay || + b.y2 - b.y1 < crtc->mode.VDisplay) + shadow_clear(sna, front, bo, crtc); + + if (b.y2 > b.y1 && b.x2 > b.x1) { + DrawableRec tmp; + + DBG(("%s: copying onto shadow CRTC: (%d, %d)x(%d, %d) [fb=%dx%d], handle=%d\n", + __FUNCTION__, + b.x1, b.y1, + b.x2-b.x1, b.y2-b.y1, + scrn->virtualX, scrn->virtualY, + bo->handle)); + + tmp.width = crtc->mode.HDisplay; + tmp.height = crtc->mode.VDisplay; + tmp.depth = front->drawable.depth; + tmp.bitsPerPixel = front->drawable.bitsPerPixel; + + if (!sna->render.copy_boxes(sna, GXcopy, + &front->drawable, __sna_pixmap_get_bo(front), 0, 0, + &tmp, bo, -crtc->x, -crtc->y, + &b, 1, COPY_LAST)) + shadow_clear(sna, front, bo, crtc); + } + } else + shadow_clear(sna, front, bo, crtc); sna_crtc->shadow_bo_width = crtc->mode.HDisplay; sna_crtc->shadow_bo_height = crtc->mode.VDisplay; sna_crtc->shadow_bo = bo; out_shadow: sna_crtc->transform = true; + sna->mode.rr_active++; return kgem_bo_reference(bo); } else { if (sna_crtc->shadow_bo) { @@ -2048,26 +2495,26 @@ out_shadow: } if (sna->flags & SNA_TEAR_FREE) { + RegionRec region; + assert(sna_crtc->slave_pixmap == NULL); DBG(("%s: enabling TearFree shadow\n", __FUNCTION__)); + region.extents.x1 = 0; + region.extents.y1 = 0; + region.extents.x2 = sna->scrn->virtualX; + region.extents.y2 = sna->scrn->virtualY; + region.data = NULL; + if (!sna_crtc_enable_shadow(sna, sna_crtc)) { DBG(("%s: failed to enable crtc shadow\n", __FUNCTION__)); return NULL; } if (sna->mode.shadow == NULL && !wedged(sna)) { - RegionRec region; struct kgem_bo *shadow; DBG(("%s: creating TearFree shadow bo\n", __FUNCTION__)); - - region.extents.x1 = 0; - region.extents.y1 = 0; - region.extents.x2 = sna->scrn->virtualX; - region.extents.y2 = sna->scrn->virtualY; - region.data = NULL; - shadow = kgem_create_2d(&sna->kgem, region.extents.x2, region.extents.y2, @@ -2093,9 +2540,11 @@ out_shadow: goto force_shadow; } + assert(__sna_pixmap_get_bo(sna->front) == NULL || + __sna_pixmap_get_bo(sna->front)->pitch == shadow->pitch); sna->mode.shadow = shadow; - set_shadow(sna, ®ion); } + set_shadow(sna, ®ion); sna_crtc_disable_override(sna, sna_crtc); } else @@ -2107,6 +2556,37 @@ out_shadow: } } +#define SCALING_EPSILON (1./256) + +static bool +is_affine(const struct pixman_f_transform *t) +{ + return (fabs(t->m[2][0]) < SCALING_EPSILON && + fabs(t->m[2][1]) < SCALING_EPSILON); +} + +static double determinant(const struct pixman_f_transform *t) +{ + return t->m[0][0]*t->m[1][1] - t->m[1][0]*t->m[0][1]; +} + +static bool +affine_is_pixel_exact(const struct pixman_f_transform *t) +{ + double det = t->m[2][2] * determinant(t); + if (fabs (det * det - 1.0) < SCALING_EPSILON) { + if (fabs(t->m[0][1]) < SCALING_EPSILON && + fabs(t->m[1][0]) < SCALING_EPSILON) + return true; + + if (fabs(t->m[0][0]) < SCALING_EPSILON && + fabs(t->m[1][1]) < SCALING_EPSILON) + return true; + } + + return false; +} + static void sna_crtc_randr(xf86CrtcPtr crtc) { struct sna_crtc *sna_crtc = to_sna_crtc(crtc); @@ -2152,6 +2632,18 @@ static void sna_crtc_randr(xf86CrtcPtr crtc) } else crtc->transform_in_use = sna_crtc->rotation != RR_Rotate_0; + if (needs_transform) { + sna_crtc->hwcursor = is_affine(&f_fb_to_crtc); + sna_crtc->cursor_transform = + sna_crtc->hwcursor && + !affine_is_pixel_exact(&f_fb_to_crtc); + } else { + sna_crtc->hwcursor = true; + sna_crtc->cursor_transform = false; + } + DBG(("%s: hwcursor?=%d, cursor_transform?=%d\n", + __FUNCTION__, sna_crtc->hwcursor, sna_crtc->cursor_transform)); + crtc->crtc_to_framebuffer = crtc_to_fb; crtc->f_crtc_to_framebuffer = f_crtc_to_fb; crtc->f_framebuffer_to_crtc = f_fb_to_crtc; @@ -2184,7 +2676,7 @@ static void sna_crtc_randr(xf86CrtcPtr crtc) static void sna_crtc_damage(xf86CrtcPtr crtc) { - ScreenPtr screen = crtc->scrn->pScreen; + ScreenPtr screen = xf86ScrnToScreen(crtc->scrn); struct sna *sna = to_sna(crtc->scrn); RegionRec region, *damage; @@ -2200,8 +2692,14 @@ sna_crtc_damage(xf86CrtcPtr crtc) if (region.extents.y2 > screen->height) region.extents.y2 = screen->height; + if (region.extents.x2 <= region.extents.x1 || + region.extents.y2 <= region.extents.y1) { + DBG(("%s: crtc not damaged, all-clipped\n", __FUNCTION__)); + return; + } + DBG(("%s: marking crtc %d as completely damaged (%d, %d), (%d, %d)\n", - __FUNCTION__, to_sna_crtc(crtc)->id, + __FUNCTION__, sna_crtc_id(crtc), region.extents.x1, region.extents.y1, region.extents.x2, region.extents.y2)); to_sna_crtc(crtc)->client_damage = region; @@ -2268,11 +2766,18 @@ __sna_crtc_set_mode(xf86CrtcPtr crtc) struct kgem_bo *saved_bo, *bo; uint32_t saved_offset; bool saved_transform; + bool saved_hwcursor; + bool saved_cursor_transform; - DBG(("%s\n", __FUNCTION__)); + DBG(("%s: CRTC=%d, pipe=%d, hidden?=%d\n", __FUNCTION__, + __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc), sna->mode.hidden)); + if (sna->mode.hidden) + return TRUE; saved_bo = sna_crtc->bo; saved_transform = sna_crtc->transform; + saved_cursor_transform = sna_crtc->cursor_transform; + saved_hwcursor = sna_crtc->hwcursor; saved_offset = sna_crtc->offset; sna_crtc->fallback_shadow = false; @@ -2304,7 +2809,11 @@ retry: /* Attach per-crtc pixmap or direct */ goto error; } + sna_crtc->flags |= CRTC_ON; bo->active_scanout++; + DBG(("%s: marking handle=%d as active=%d (removing %d from scanout, active=%d)\n", + __FUNCTION__, bo->handle, bo->active_scanout, + saved_bo ? saved_bo->handle : 0, saved_bo ? saved_bo->active_scanout - 1: -1)); if (saved_bo) { assert(saved_bo->active_scanout); assert(saved_bo->refcnt >= saved_bo->active_scanout); @@ -2315,17 +2824,33 @@ retry: /* Attach per-crtc pixmap or direct */ sna_crtc_randr(crtc); if (sna_crtc->transform) sna_crtc_damage(crtc); + if (sna_crtc->cursor && /* Reload cursor if RandR maybe changed */ + (!sna_crtc->hwcursor || + saved_cursor_transform || sna_crtc->cursor_transform || + sna_crtc->cursor->rotation != crtc->rotation)) + sna_crtc_disable_cursor(sna, sna_crtc); + + assert(!sna->mode.hidden); sna->mode.front_active += saved_bo == NULL; sna->mode.dirty = true; - DBG(("%s: front_active=%d\n", __FUNCTION__, sna->mode.front_active)); + DBG(("%s: handle=%d, scanout_active=%d, front_active=%d\n", + __FUNCTION__, bo->handle, bo->active_scanout, sna->mode.front_active)); return TRUE; error: sna_crtc->offset = saved_offset; + if (sna_crtc->transform) { + assert(sna->mode.rr_active); + sna->mode.rr_active--; + } + if (saved_transform) + sna->mode.rr_active++; sna_crtc->transform = saved_transform; + sna_crtc->cursor_transform = saved_cursor_transform; + sna_crtc->hwcursor = saved_hwcursor; sna_crtc->bo = saved_bo; - sna_mode_discover(sna); + sna_mode_discover(sna, true); return FALSE; } @@ -2346,14 +2871,14 @@ sna_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, xf86DrvMsg(crtc->scrn->scrnIndex, X_INFO, "switch to mode %dx%d@%.1f on %s using pipe %d, position (%d, %d), rotation %s, reflection %s\n", mode->HDisplay, mode->VDisplay, xf86ModeVRefresh(mode), - outputs_for_crtc(crtc, outputs, sizeof(outputs)), sna_crtc->pipe, + outputs_for_crtc(crtc, outputs, sizeof(outputs)), __sna_crtc_pipe(sna_crtc), x, y, rotation_to_str(rotation), reflection_to_str(rotation)); assert(mode->HDisplay <= sna->mode.max_crtc_width && mode->VDisplay <= sna->mode.max_crtc_height); #if HAS_GAMMA - drmModeCrtcSetGamma(sna->kgem.fd, sna_crtc->id, + drmModeCrtcSetGamma(sna->kgem.fd, __sna_crtc_id(sna_crtc), crtc->gamma_size, crtc->gamma_red, crtc->gamma_green, @@ -2372,17 +2897,10 @@ sna_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, static void sna_crtc_dpms(xf86CrtcPtr crtc, int mode) { - struct sna_crtc *priv = to_sna_crtc(crtc); - DBG(("%s(pipe %d, dpms mode -> %d):= active=%d\n", - __FUNCTION__, priv->pipe, mode, mode == DPMSModeOn)); - if (priv->dpms_mode == mode) - return; + __FUNCTION__, sna_crtc_pipe(crtc), mode, mode == DPMSModeOn)); - assert(priv); - priv->dpms_mode = mode; - - if (mode == DPMSModeOn && crtc->enabled && priv->bo == NULL) { + if (mode == DPMSModeOn && crtc->enabled) { if (__sna_crtc_set_mode(crtc)) update_flush_interval(to_sna(crtc->scrn)); else @@ -2390,7 +2908,7 @@ sna_crtc_dpms(xf86CrtcPtr crtc, int mode) } if (mode != DPMSModeOn) - sna_crtc_disable(crtc); + sna_crtc_disable(crtc, false); } void sna_mode_adjust_frame(struct sna *sna, int x, int y) @@ -2426,7 +2944,7 @@ sna_crtc_gamma_set(xf86CrtcPtr crtc, { assert(to_sna_crtc(crtc)); drmModeCrtcSetGamma(to_sna(crtc->scrn)->kgem.fd, - to_sna_crtc(crtc)->id, + sna_crtc_id(crtc), size, red, green, blue); } @@ -2455,7 +2973,7 @@ sna_crtc_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr pixmap) return TRUE; DBG(("%s: CRTC:%d, pipe=%d setting scanout pixmap=%ld\n", - __FUNCTION__, sna_crtc->id, sna_crtc->pipe, + __FUNCTION__, __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc), pixmap ? pixmap->drawable.serialNumber : 0)); /* Disable first so that we can unregister the damage tracking */ @@ -2576,6 +3094,10 @@ static int plane_details(struct sna *sna, struct plane *p) } } + p->rotation.supported &= DBG_NATIVE_ROTATION; + if (!xf86ReturnOptValBool(sna->Options, OPTION_ROTATION, TRUE)) + p->rotation.supported = RR_Rotate_0; + if (props != (uint32_t *)stack_props) free(props); @@ -2587,16 +3109,11 @@ static void sna_crtc_find_planes(struct sna *sna, struct sna_crtc *crtc) { #define LOCAL_IOCTL_SET_CAP DRM_IOWR(0x0d, struct local_set_cap) -#define LOCAL_IOCTL_MODE_GETPLANERESOURCES DRM_IOWR(0xb5, struct local_mode_get_plane_res) -#define LOCAL_IOCTL_MODE_GETPLANE DRM_IOWR(0xb6, struct local_mode_get_plane) struct local_set_cap { uint64_t name; uint64_t value; } cap; - struct local_mode_get_plane_res { - uint64_t plane_id_ptr; - uint64_t count_planes; - } r; + struct local_mode_get_plane_res r; uint32_t stack_planes[32]; uint32_t *planes = stack_planes; int i; @@ -2629,18 +3146,7 @@ sna_crtc_find_planes(struct sna *sna, struct sna_crtc *crtc) VG(VALGRIND_MAKE_MEM_DEFINED(planes, sizeof(uint32_t)*r.count_planes)); for (i = 0; i < r.count_planes; i++) { - struct local_mode_get_plane { - uint32_t plane_id; - - uint32_t crtc_id; - uint32_t fb_id; - - uint32_t possible_crtcs; - uint32_t gamma_size; - - uint32_t count_format_types; - uint64_t format_type_ptr; - } p; + struct local_mode_get_plane p; struct plane details; VG_CLEAR(p); @@ -2649,11 +3155,11 @@ sna_crtc_find_planes(struct sna *sna, struct sna_crtc *crtc) if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_GETPLANE, &p)) continue; - if ((p.possible_crtcs & (1 << crtc->pipe)) == 0) + if ((p.possible_crtcs & (1 << __sna_crtc_pipe(crtc))) == 0) continue; DBG(("%s: plane %d is attached to our pipe=%d\n", - __FUNCTION__, planes[i], crtc->pipe)); + __FUNCTION__, planes[i], __sna_crtc_pipe(crtc))); details.id = p.plane_id; details.rotation.prop = 0; @@ -2698,43 +3204,45 @@ sna_crtc_init__cursor(struct sna *sna, struct sna_crtc *crtc) VG_CLEAR(arg); arg.flags = DRM_MODE_CURSOR_BO; - arg.crtc_id = crtc->id; + arg.crtc_id = __sna_crtc_id(crtc); arg.width = arg.height = 0; arg.handle = 0; (void)drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_CURSOR, &arg); + crtc->hwcursor = true; } static bool -sna_crtc_add(ScrnInfoPtr scrn, int id) +sna_crtc_add(ScrnInfoPtr scrn, unsigned id) { struct sna *sna = to_sna(scrn); xf86CrtcPtr crtc; struct sna_crtc *sna_crtc; struct drm_i915_get_pipe_from_crtc_id get_pipe; - DBG(("%s(%d)\n", __FUNCTION__, id)); + DBG(("%s(%d): is-zaphod? %d\n", __FUNCTION__, id, is_zaphod(scrn))); sna_crtc = calloc(sizeof(struct sna_crtc), 1); if (sna_crtc == NULL) return false; - sna_crtc->id = id; - sna_crtc->dpms_mode = -1; + assert(id < 256); + sna_crtc->flags = id << 16; VG_CLEAR(get_pipe); get_pipe.pipe = 0; - get_pipe.crtc_id = sna_crtc->id; + get_pipe.crtc_id = id; if (drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID, &get_pipe)) { free(sna_crtc); return false; } - sna_crtc->pipe = get_pipe.pipe; + assert((unsigned)get_pipe.pipe < 256); + sna_crtc->flags |= get_pipe.pipe << 8; if (is_zaphod(scrn) && - scrn->confScreen->device->screen != sna_crtc->pipe) { + (get_zaphod_crtcs(sna) & (1 << get_pipe.pipe)) == 0) { free(sna_crtc); return true; } @@ -2744,7 +3252,7 @@ sna_crtc_add(ScrnInfoPtr scrn, int id) sna_crtc_find_planes(sna, sna_crtc); DBG(("%s: CRTC:%d [pipe=%d], primary id=%x: supported-rotations=%x, current-rotation=%x, sprite id=%x: supported-rotations=%x, current-rotation=%x\n", - __FUNCTION__, sna_crtc->id, sna_crtc->pipe, + __FUNCTION__, id, get_pipe.pipe, sna_crtc->primary.id, sna_crtc->primary.rotation.supported, sna_crtc->primary.rotation.current, sna_crtc->sprite.id, sna_crtc->sprite.rotation.supported, sna_crtc->sprite.rotation.current)); @@ -2761,7 +3269,7 @@ sna_crtc_add(ScrnInfoPtr scrn, int id) crtc->driver_private = sna_crtc; sna_crtc->base = crtc; DBG(("%s: attached crtc[%d] pipe=%d\n", - __FUNCTION__, id, sna_crtc->pipe)); + __FUNCTION__, id, __sna_crtc_pipe(sna_crtc))); return true; } @@ -2798,20 +3306,56 @@ find_property(struct sna *sna, struct sna_output *output, const char *name) return -1; } +static void update_properties(struct sna *sna, struct sna_output *output) +{ + union compat_mode_get_connector compat_conn; + struct drm_mode_modeinfo dummy; + + VG_CLEAR(compat_conn); + + compat_conn.conn.connector_id = output->id; + compat_conn.conn.count_props = output->num_props; + compat_conn.conn.props_ptr = (uintptr_t)output->prop_ids; + compat_conn.conn.prop_values_ptr = (uintptr_t)output->prop_values; + compat_conn.conn.count_modes = 1; /* skip detect */ + compat_conn.conn.modes_ptr = (uintptr_t)&dummy; + compat_conn.conn.count_encoders = 0; + + (void)drmIoctl(sna->kgem.fd, + DRM_IOCTL_MODE_GETCONNECTOR, + &compat_conn.conn); + + assert(compat_conn.conn.count_props == output->num_props); + output->update_properties = false; +} + static xf86OutputStatus sna_output_detect(xf86OutputPtr output) { struct sna *sna = to_sna(output->scrn); struct sna_output *sna_output = output->driver_private; union compat_mode_get_connector compat_conn; + uint32_t now; DBG(("%s(%s:%d)\n", __FUNCTION__, output->name, sna_output->id)); + sna_output->update_properties = false; if (!sna_output->id) { DBG(("%s(%s) hiding due to lost connection\n", __FUNCTION__, output->name)); return XF86OutputStatusDisconnected; } + /* Cache detections for 15s or hotplug event */ + now = GetTimeInMillis(); + if (sna_output->last_detect != 0 && + (int32_t)(now - sna_output->last_detect) <= OUTPUT_STATUS_CACHE_MS) { + DBG(("%s(%s) reporting cached status (since %dms): %d\n", + __FUNCTION__, output->name, now - sna_output->last_detect, + sna_output->status)); + sna_output->update_properties = true; + return sna_output->status; + } + VG_CLEAR(compat_conn); compat_conn.conn.connector_id = sna_output->id; sna_output->num_modes = compat_conn.conn.count_modes = 0; /* reprobe */ @@ -2854,15 +3398,20 @@ sna_output_detect(xf86OutputPtr output) DBG(("%s(%s): found %d modes, connection status=%d\n", __FUNCTION__, output->name, sna_output->num_modes, compat_conn.conn.connection)); + sna_output->last_detect = now; switch (compat_conn.conn.connection) { case DRM_MODE_CONNECTED: - return XF86OutputStatusConnected; + sna_output->status = XF86OutputStatusConnected; + break; case DRM_MODE_DISCONNECTED: - return XF86OutputStatusDisconnected; + sna_output->status = XF86OutputStatusDisconnected; + break; default: case DRM_MODE_UNKNOWNCONNECTION: - return XF86OutputStatusUnknown; + sna_output->status = XF86OutputStatusUnknown; + break; } + return sna_output->status; } static Bool @@ -2907,6 +3456,13 @@ sna_output_attach_edid(xf86OutputPtr output) if (sna_output->edid_idx == -1) return; + /* Always refresh the blob as the kernel may randomly update the + * id even if the contents of the blob doesn't change, and a + * request for the stale id will return nothing. + */ + if (sna_output->update_properties) + update_properties(sna, sna_output); + raw = sna_output->edid_raw; blob.length = sna_output->edid_len; @@ -2917,8 +3473,12 @@ sna_output_attach_edid(xf86OutputPtr output) old = NULL; blob.blob_id = sna_output->prop_values[sna_output->edid_idx]; - DBG(("%s: attaching EDID id=%d, current=%d\n", - __FUNCTION__, blob.blob_id, sna_output->edid_blob_id)); + if (!blob.blob_id) + goto done; + + DBG(("%s(%s): attaching EDID id=%d, current=%d\n", + __FUNCTION__, output->name, + blob.blob_id, sna_output->edid_blob_id)); if (blob.blob_id == sna_output->edid_blob_id && 0) { /* sigh */ if (output->MonInfo) { /* XXX the property keeps on disappearing... */ @@ -2936,11 +3496,14 @@ sna_output_attach_edid(xf86OutputPtr output) } blob.data = (uintptr_t)raw; - if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob)) - goto done; + if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob)) { + DBG(("%s(%s): failed to read blob, reusing previous\n", + __FUNCTION__, output->name)); + goto skip_read; + } - DBG(("%s: retrieving blob id=%d, length=%d\n", - __FUNCTION__, blob.blob_id, blob.length)); + DBG(("%s(%s): retrieving blob id=%d, length=%d\n", + __FUNCTION__, output->name, blob.blob_id, blob.length)); if (blob.length > sna_output->edid_len) { raw = realloc(raw, blob.length); @@ -2949,13 +3512,26 @@ sna_output_attach_edid(xf86OutputPtr output) VG(memset(raw, 0, blob.length)); blob.data = (uintptr_t)raw; - if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob)) - goto done; + } + + if (blob.length != sna_output->edid_len && + drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob)) + goto done; + + if (blob.length < 128) + goto done; + + if (blob.length & 127) { + /* Truncated EDID! Make sure no one reads too far */ + *SECTION(NO_EDID, (uint8_t*)raw) = blob.length/128 - 1; + blob.length &= -128; } if (old && blob.length == sna_output->edid_len && memcmp(old, raw, blob.length) == 0) { + DBG(("%s(%s): EDID + MonInfo is unchanged\n", + __FUNCTION__, output->name)); assert(sna_output->edid_raw == raw); sna_output->edid_blob_id = blob.blob_id; RRChangeOutputProperty(output->randr_output, @@ -2983,18 +3559,173 @@ done: } } +static void +sna_output_attach_tile(xf86OutputPtr output) +{ +#if XF86_OUTPUT_VERSION >= 3 + struct sna *sna = to_sna(output->scrn); + struct sna_output *sna_output = output->driver_private; + struct drm_mode_get_blob blob; + struct xf86CrtcTileInfo tile_info, *set = NULL; + char *tile; + int id; + + id = find_property(sna, sna_output, "TILE"); + DBG(("%s: found? TILE=%d\n", __FUNCTION__, id)); + if (id == -1) + goto out; + + if (sna_output->update_properties) + update_properties(sna, sna_output); + + VG_CLEAR(blob); + blob.blob_id = sna_output->prop_values[id]; + blob.length = 0; + if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob)) + goto out; + + do { + id = blob.length; + tile = alloca(id + 1); + blob.data = (uintptr_t)tile; + VG(memset(tile, 0, id)); + DBG(("%s: reading %d bytes for TILE blob\n", __FUNCTION__, id)); + if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob)) + goto out; + } while (id != blob.length); + + tile[blob.length] = '\0'; /* paranoia */ + DBG(("%s: TILE='%s'\n", __FUNCTION__, tile)); + if (xf86OutputParseKMSTile(tile, blob.length, &tile_info)) + set = &tile_info; +out: + xf86OutputSetTile(output, set); +#endif +} + +static bool duplicate_mode(DisplayModePtr modes, DisplayModePtr m) +{ + if (m == NULL) + return false; + + while (modes) { + if (xf86ModesEqual(modes, m)) + return true; + + modes = modes->next; + } + + return false; +} + +static struct pixel_count { + int16_t width, height; +} common_16_9[] = { + { 640, 360 }, + { 720, 405 }, + { 864, 486 }, + { 960, 540 }, + { 1024, 576 }, + { 1280, 720 }, + { 1366, 768 }, + { 1600, 900 }, + { 1920, 1080 }, + { 2048, 1152 }, + { 2560, 1440 }, + { 2880, 1620 }, + { 3200, 1800 }, + { 3840, 2160 }, + { 4096, 2304 }, + { 5120, 2880 }, + { 7680, 4320 }, + { 15360, 8640 }, +}, common_16_10[] = { + { 1280, 800 }, + { 1400, 900 }, + { 1680, 1050 }, + { 1920, 1200 }, + { 2560, 1600 }, +}; + static DisplayModePtr -default_modes(void) +default_modes(DisplayModePtr preferred) { + DisplayModePtr modes; + int n; + #if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,6,99,900,0) - return xf86GetDefaultModes(); + modes = xf86GetDefaultModes(); #else - return xf86GetDefaultModes(0, 0); + modes = xf86GetDefaultModes(0, 0); +#endif + + /* XXX O(n^2) mode list generation :( */ + +#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,4,99,901,0) + if (preferred) { + DisplayModePtr m; + + /* Add a half-resolution mode useful for large panels */ + m = xf86GTFMode(preferred->HDisplay/2, + preferred->VDisplay/2, + xf86ModeVRefresh(preferred), + FALSE, FALSE); + if (!duplicate_mode(modes, m)) + modes = xf86ModesAdd(modes, m); + else + free(m); + + if (preferred->VDisplay * 16 > preferred->HDisplay*9 - preferred->HDisplay/32 && + preferred->VDisplay * 16 < preferred->HDisplay*9 + preferred->HDisplay/32) { + DBG(("Adding 16:9 modes -- %d < %d > %d\n", + preferred->HDisplay*9 - preferred->HDisplay/32, + preferred->VDisplay * 16, + preferred->HDisplay*9 + preferred->HDisplay/32)); + for (n = 0; n < ARRAY_SIZE(common_16_9); n++) { + if (preferred->HDisplay <= common_16_9[n].width || + preferred->VDisplay <= common_16_9[n].height) + break; + + m = xf86GTFMode(common_16_9[n].width, + common_16_9[n].height, + xf86ModeVRefresh(preferred), + FALSE, FALSE); + if (!duplicate_mode(modes, m)) + modes = xf86ModesAdd(modes, m); + else + free(m); + } + } + + if (preferred->VDisplay * 16 > preferred->HDisplay*10 - preferred->HDisplay/32 && + preferred->VDisplay * 16 < preferred->HDisplay*10 + preferred->HDisplay/32) { + DBG(("Adding 16:10 modes -- %d < %d > %d\n", + preferred->HDisplay*10 - preferred->HDisplay/32, + preferred->VDisplay * 16, + preferred->HDisplay*10 + preferred->HDisplay/32)); + for (n = 0; n < ARRAY_SIZE(common_16_10); n++) { + if (preferred->HDisplay <= common_16_10[n].width || + preferred->VDisplay <= common_16_10[n].height) + break; + + m = xf86GTFMode(common_16_10[n].width, + common_16_10[n].height, + xf86ModeVRefresh(preferred), + FALSE, FALSE); + if (!duplicate_mode(modes, m)) + modes = xf86ModesAdd(modes, m); + else + free(m); + } + } + } #endif + + return modes; } static DisplayModePtr -sna_output_panel_edid(xf86OutputPtr output, DisplayModePtr modes) +sna_output_add_default_modes(xf86OutputPtr output, DisplayModePtr modes) { xf86MonPtr mon = output->MonInfo; DisplayModePtr i, m, preferred = NULL; @@ -3015,7 +3746,7 @@ sna_output_panel_edid(xf86OutputPtr output, DisplayModePtr modes) max_vrefresh = max(max_vrefresh, 60.0); max_vrefresh *= (1 + SYNC_TOLERANCE); - m = default_modes(); + m = default_modes(preferred); xf86ValidateModesSize(output->scrn, m, max_x, max_y, 0); for (i = m; i; i = i->next) { @@ -3034,28 +3765,47 @@ sna_output_panel_edid(xf86OutputPtr output, DisplayModePtr modes) } static DisplayModePtr +sna_output_override_edid(xf86OutputPtr output) +{ + struct sna_output *sna_output = output->driver_private; + + if (sna_output->fake_edid_mon == NULL) + return NULL; + + xf86OutputSetEDID(output, sna_output->fake_edid_mon); + return xf86DDCGetModes(output->scrn->scrnIndex, + sna_output->fake_edid_mon); +} + +static DisplayModePtr sna_output_get_modes(xf86OutputPtr output) { struct sna_output *sna_output = output->driver_private; - DisplayModePtr Modes = NULL, current = NULL; + DisplayModePtr Modes, current; int i; DBG(("%s(%s:%d)\n", __FUNCTION__, output->name, sna_output->id)); assert(sna_output->id); + Modes = sna_output_override_edid(output); + if (Modes) + return Modes; + sna_output_attach_edid(output); + sna_output_attach_tile(output); + current = NULL; if (output->crtc) { struct drm_mode_crtc mode; VG_CLEAR(mode); assert(to_sna_crtc(output->crtc)); - mode.crtc_id = to_sna_crtc(output->crtc)->id; + mode.crtc_id = sna_crtc_id(output->crtc); if (drmIoctl(to_sna(output->scrn)->kgem.fd, DRM_IOCTL_MODE_GETCRTC, &mode) == 0) { DBG(("%s: CRTC:%d, pipe=%d: has mode?=%d\n", __FUNCTION__, - to_sna_crtc(output->crtc)->id, - to_sna_crtc(output->crtc)->pipe, + sna_crtc_id(output->crtc), + sna_crtc_pipe(output->crtc), mode.mode_valid && mode.mode.clock)); if (mode.mode_valid && mode.mode.clock) { @@ -3117,7 +3867,7 @@ sna_output_get_modes(xf86OutputPtr output) } if (sna_output->add_default_modes) - Modes = sna_output_panel_edid(output, Modes); + Modes = sna_output_add_default_modes(output, Modes); return Modes; } @@ -3132,6 +3882,8 @@ sna_output_destroy(xf86OutputPtr output) return; free(sna_output->edid_raw); + free(sna_output->fake_edid_raw); + for (i = 0; i < sna_output->num_props; i++) { if (sna_output->props[i].kprop == NULL) continue; @@ -3155,7 +3907,7 @@ sna_output_destroy(xf86OutputPtr output) } static void -sna_output_dpms(xf86OutputPtr output, int dpms) +__sna_output_dpms(xf86OutputPtr output, int dpms, int fixup) { struct sna *sna = to_sna(output->scrn); struct sna_output *sna_output = output->driver_private; @@ -3182,8 +3934,9 @@ sna_output_dpms(xf86OutputPtr output, int dpms) if (sna_output->backlight.iface && dpms != DPMSModeOn) { if (old_dpms == DPMSModeOn) { sna_output->backlight_active_level = sna_output_backlight_get(output); - DBG(("%s: saving current backlight %d\n", - __FUNCTION__, sna_output->backlight_active_level)); + DBG(("%s(%s:%d): saving current backlight %d\n", + __FUNCTION__, output->name, sna_output->id, + sna_output->backlight_active_level)); } sna_output->dpms_mode = dpms; sna_output_backlight_off(sna_output); @@ -3193,18 +3946,31 @@ sna_output_dpms(xf86OutputPtr output, int dpms) drmModeConnectorSetProperty(sna->kgem.fd, sna_output->id, sna_output->dpms_id, - dpms)) - dpms = old_dpms; + dpms)) { + DBG(("%s(%s:%d): failed to set DPMS to %d (fixup? %d)\n", + __FUNCTION__, output->name, sna_output->id, dpms, fixup)); + if (fixup) { + sna_crtc_disable(output->crtc, false); + return; + } + } if (sna_output->backlight.iface && dpms == DPMSModeOn) { - DBG(("%s: restoring previous backlight %d\n", - __FUNCTION__, sna_output->backlight_active_level)); + DBG(("%s(%d:%d: restoring previous backlight %d\n", + __FUNCTION__, output->name, sna_output->id, + sna_output->backlight_active_level)); sna_output_backlight_on(sna_output); } sna_output->dpms_mode = dpms; } +static void +sna_output_dpms(xf86OutputPtr output, int dpms) +{ + __sna_output_dpms(output, dpms, true); +} + static bool sna_property_ignore(drmModePropertyPtr prop) { @@ -3239,14 +4005,14 @@ sna_output_create_ranged_atom(xf86OutputPtr output, Atom *atom, err = RRConfigureOutputProperty(output->randr_output, *atom, FALSE, TRUE, immutable, 2, atom_range); if (err != 0) - xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + xf86DrvMsg(output->scrn->scrnIndex, X_WARNING, "RRConfigureOutputProperty error, %d\n", err); err = RRChangeOutputProperty(output->randr_output, *atom, XA_INTEGER, 32, PropModeReplace, 1, &value, FALSE, FALSE); if (err != 0) - xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + xf86DrvMsg(output->scrn->scrnIndex, X_WARNING, "RRChangeOutputProperty error, %d\n", err); } @@ -3303,7 +4069,7 @@ sna_output_create_resources(xf86OutputPtr output) p->kprop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE, p->num_atoms - 1, (INT32 *)&p->atoms[1]); if (err != 0) { - xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + xf86DrvMsg(output->scrn->scrnIndex, X_WARNING, "RRConfigureOutputProperty error, %d\n", err); } @@ -3315,7 +4081,7 @@ sna_output_create_resources(xf86OutputPtr output) XA_ATOM, 32, PropModeReplace, 1, &p->atoms[j+1], FALSE, FALSE); if (err != 0) { - xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + xf86DrvMsg(output->scrn->scrnIndex, X_WARNING, "RRChangeOutputProperty error, %d\n", err); } } @@ -3385,18 +4151,19 @@ sna_output_set_property(xf86OutputPtr output, Atom property, if (value->type != XA_INTEGER || value->format != 32 || value->size != 1) return FALSE; - val = *(uint32_t *)value->data; + val = *(uint32_t *)value->data; drmModeConnectorSetProperty(sna->kgem.fd, sna_output->id, p->kprop->prop_id, (uint64_t)val); return TRUE; } else if (p->kprop->flags & DRM_MODE_PROP_ENUM) { - Atom atom; - const char *name; - int j; + Atom atom; + const char *name; + int j; if (value->type != XA_ATOM || value->format != 32 || value->size != 1) return FALSE; + memcpy(&atom, value->data, 4); name = NameForAtom(atom); if (name == NULL) @@ -3425,7 +4192,7 @@ static Bool sna_output_get_property(xf86OutputPtr output, Atom property) { struct sna_output *sna_output = output->driver_private; - int err; + int err, i, j; if (property == backlight_atom || property == backlight_deprecated_atom) { INT32 val; @@ -3449,7 +4216,7 @@ sna_output_get_property(xf86OutputPtr output, Atom property) XA_INTEGER, 32, PropModeReplace, 1, &val, FALSE, FALSE); if (err != 0) { - xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + xf86DrvMsg(output->scrn->scrnIndex, X_WARNING, "RRChangeOutputProperty error, %d\n", err); return FALSE; } @@ -3457,6 +4224,40 @@ sna_output_get_property(xf86OutputPtr output, Atom property) return TRUE; } + for (i = 0; i < sna_output->num_props; i++) { + struct sna_property *p = &sna_output->props[i]; + + if (p->atoms == NULL || p->atoms[0] != property) + continue; + + if (sna_output->update_properties && output->scrn->vtSema) + update_properties(to_sna(output->scrn), sna_output); + + err = 0; + if (p->kprop->flags & DRM_MODE_PROP_RANGE) { + err = RRChangeOutputProperty(output->randr_output, + property, XA_INTEGER, 32, + PropModeReplace, 1, + &sna_output->prop_values[i], + FALSE, FALSE); + } else if (p->kprop->flags & DRM_MODE_PROP_ENUM) { + for (j = 0; j < p->kprop->count_enums; j++) { + if (p->kprop->enums[j].value == sna_output->prop_values[i]) + break; + } + err = RRChangeOutputProperty(output->randr_output, + property, XA_ATOM, 32, + PropModeReplace, 1, + &p->atoms[j+1], + FALSE, FALSE); + } + + if (err != 0) + xf86DrvMsg(output->scrn->scrnIndex, X_WARNING, + "RRChangeOutputProperty error, %d\n", err); + return TRUE; + } + return FALSE; } @@ -3504,43 +4305,6 @@ static const char * const output_names[] = { }; static bool -sna_zaphod_match(const char *s, const char *output) -{ - char t[20]; - unsigned int i = 0; - - do { - /* match any outputs in a comma list, stopping at whitespace */ - switch (*s) { - case '\0': - t[i] = '\0'; - return strcmp(t, output) == 0; - - case ',': - t[i] ='\0'; - if (strcmp(t, output) == 0) - return TRUE; - i = 0; - break; - - case ' ': - case '\t': - case '\n': - case '\r': - break; - - default: - t[i++] = *s; - break; - } - - s++; - } while (i < sizeof(t)); - - return false; -} - -static bool output_ignored(ScrnInfoPtr scrn, const char *name) { char monitor_name[64]; @@ -3572,14 +4336,20 @@ gather_encoders(struct sna *sna, uint32_t id, int count, struct drm_mode_get_encoder enc; uint32_t *ids = NULL; + DBG(("%s(%d): expected count=%d\n", __FUNCTION__, id, count)); + VG_CLEAR(compat_conn); memset(out, 0, sizeof(*out)); do { - free(ids); - ids = malloc(sizeof(*ids) * count); - if (ids == 0) + uint32_t *nids; + + nids = realloc(ids, sizeof(*ids) * count); + if (nids == NULL) { + free(ids); return false; + } + ids = nids; compat_conn.conn.connector_id = id; compat_conn.conn.count_props = 0; @@ -3599,6 +4369,7 @@ gather_encoders(struct sna *sna, uint32_t id, int count, count = compat_conn.conn.count_encoders; } while (1); + DBG(("%s(%d): gathering %d encoders\n", __FUNCTION__, id, count)); for (count = 0; count < compat_conn.conn.count_encoders; count++) { enc.encoder_id = ids[count]; if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETENCODER, &enc)) { @@ -3606,6 +4377,8 @@ gather_encoders(struct sna *sna, uint32_t id, int count, count = 0; break; } + DBG(("%s(%d): encoder=%d, possible_crtcs=%x, possible_clones=%x\n", + __FUNCTION__, id, enc.encoder_id, enc.possible_crtcs, enc.possible_clones)); out->possible_crtcs |= enc.possible_crtcs; out->possible_clones |= enc.possible_clones; @@ -3731,6 +4504,116 @@ static int name_from_path(struct sna *sna, return 0; } +static char *fake_edid_name(xf86OutputPtr output) +{ + struct sna *sna = to_sna(output->scrn); + const char *str, *colon; + +#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,7,99,901,0) + str = xf86GetOptValString(sna->Options, OPTION_EDID); +#else + str = NULL; +#endif + if (str == NULL) + return NULL; + + do { + colon = strchr(str, ':'); + if (colon == NULL) + return NULL; + + if (strncmp(str, output->name, colon-str) == 0 && + output->name[colon-str] == '\0') { + char *path; + int len; + + str = colon + 1; + colon = strchr(str, ','); + if (colon) + len = colon - str; + else + len = strlen(str); + + path = malloc(len + 1); + if (path == NULL) + return NULL; + + memcpy(path, str, len); + path[len] = '\0'; + return path; + } + + str = strchr(colon + 1, ','); + if (str == NULL) + return NULL; + + str++; + } while (1); +} + +static void +sna_output_load_fake_edid(xf86OutputPtr output) +{ + struct sna_output *sna_output = output->driver_private; + const char *filename; + FILE *file; + void *raw; + int size; + xf86MonPtr mon; + + filename = fake_edid_name(output); + if (filename == NULL) + return; + + file = fopen(filename, "rb"); + if (file == NULL) + goto err; + + fseek(file, 0, SEEK_END); + size = ftell(file); + if (size % 128) { + fclose(file); + goto err; + } + + raw = malloc(size); + if (raw == NULL) { + fclose(file); + free(raw); + goto err; + } + + fseek(file, 0, SEEK_SET); + if (fread(raw, size, 1, file) != 1) { + fclose(file); + free(raw); + goto err; + } + fclose(file); + + mon = xf86InterpretEDID(output->scrn->scrnIndex, raw); + if (mon == NULL) { + free(raw); + goto err; + } + + if (mon && size > 128) + mon->flags |= MONITOR_EDID_COMPLETE_RAWDATA; + + sna_output->fake_edid_mon = mon; + sna_output->fake_edid_raw = raw; + + xf86DrvMsg(output->scrn->scrnIndex, X_CONFIG, + "Loading EDID from \"%s\" for output %s\n", + filename, output->name); + return; + +err: + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "Could not read EDID file \"%s\" for output %s\n", + filename, output->name); +} + static int sna_output_add(struct sna *sna, unsigned id, unsigned serial) { @@ -3765,6 +4648,7 @@ sna_output_add(struct sna *sna, unsigned id, unsigned serial) return -1; } assert(compat_conn.conn.connector_id == id); + DBG(("%s(%d): has %d associated encoders\n", __FUNCTION__, id, compat_conn.conn.count_encoders)); if (compat_conn.conn.connector_type < ARRAY_SIZE(output_names)) output_name = output_names[compat_conn.conn.connector_type]; @@ -3813,25 +4697,26 @@ sna_output_add(struct sna *sna, unsigned id, unsigned serial) } if (is_zaphod(scrn)) { - const char *str; + unsigned zaphod_crtcs; - str = xf86GetOptValString(sna->Options, OPTION_ZAPHOD); - if (str && !sna_zaphod_match(str, name)) { - DBG(("%s: zaphod mismatch, want %s, have %s\n", __FUNCTION__, str, name)); + if (!sna_zaphod_match(sna, name)) { + DBG(("%s: zaphod mismatch, want %s, have %s\n", + __FUNCTION__, + xf86GetOptValString(sna->Options, OPTION_ZAPHOD) ?: "???", + name)); return 0; } - if ((possible_crtcs & (1 << scrn->confScreen->device->screen)) == 0) { - if (str) { - xf86DrvMsg(scrn->scrnIndex, X_ERROR, - "%s is an invalid output for screen (pipe) %d\n", - name, scrn->confScreen->device->screen); - return -1; - } else - return 0; + zaphod_crtcs = get_zaphod_crtcs(sna); + possible_crtcs &= zaphod_crtcs; + if (possible_crtcs == 0) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "%s is an invalid output for screen %d\n", + name, scrn->confScreen->device->screen); + return -1; } - possible_crtcs = 1; + possible_crtcs >>= ffs(zaphod_crtcs) - 1; } sna_output = calloc(sizeof(struct sna_output), 1); @@ -3841,6 +4726,12 @@ sna_output_add(struct sna *sna, unsigned id, unsigned serial) sna_output->num_props = compat_conn.conn.count_props; sna_output->prop_ids = malloc(sizeof(uint32_t)*compat_conn.conn.count_props); sna_output->prop_values = malloc(sizeof(uint64_t)*compat_conn.conn.count_props); + if (sna_output->prop_ids == NULL || sna_output->prop_values == NULL) { + free(sna_output->prop_ids); + free(sna_output->prop_values); + free(sna_output); + return -1; + } compat_conn.conn.count_encoders = 0; @@ -3865,16 +4756,16 @@ sna_output_add(struct sna *sna, unsigned id, unsigned serial) /* Construct name from topology, and recheck if output is acceptable */ path = name_from_path(sna, sna_output, name); if (path) { - const char *str; - if (output_ignored(scrn, name)) { len = 0; goto skip; } - str = xf86GetOptValString(sna->Options, OPTION_ZAPHOD); - if (str && !sna_zaphod_match(str, name)) { - DBG(("%s: zaphod mismatch, want %s, have %s\n", __FUNCTION__, str, name)); + if (is_zaphod(scrn) && !sna_zaphod_match(sna, name)) { + DBG(("%s: zaphod mismatch, want %s, have %s\n", + __FUNCTION__, + xf86GetOptValString(sna->Options, OPTION_ZAPHOD) ?: "???", + name)); len = 0; goto skip; } @@ -3945,10 +4836,8 @@ reset: sna_output->dpms_mode = sna_output->prop_values[i]; DBG(("%s: found 'DPMS' (idx=%d, id=%d), initial value=%d\n", __FUNCTION__, i, sna_output->dpms_id, sna_output->dpms_mode)); - } else { - sna_output->dpms_id = -1; + } else sna_output->dpms_mode = DPMSModeOff; - } sna_output->possible_encoders = possible_encoders; sna_output->attached_encoders = attached_encoders; @@ -3969,6 +4858,8 @@ reset: output->possible_crtcs = possible_crtcs & count_to_mask(sna->mode.num_real_crtc); output->interlaceAllowed = TRUE; + sna_output_load_fake_edid(output); + if (serial) { if (output->randr_output == NULL) { output->randr_output = RROutputCreate(xf86ScrnToScreen(scrn), name, len, output); @@ -3976,6 +4867,7 @@ reset: goto cleanup; } + RROutputChanged(output->randr_output, TRUE); sna_output_create_resources(output); RRPostPendingProperties(output->randr_output); @@ -4009,38 +4901,6 @@ skip: return len; } -static void sna_output_del(xf86OutputPtr output) -{ - ScrnInfoPtr scrn = output->scrn; - xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); - int i; - - DBG(("%s(%s)\n", __FUNCTION__, output->name)); - assert(to_sna_output(output)); - - RROutputDestroy(output->randr_output); - sna_output_destroy(output); - - while (output->probed_modes) - xf86DeleteMode(&output->probed_modes, output->probed_modes); - - free(output); - - for (i = 0; i < config->num_output; i++) - if (config->output[i] == output) - break; - assert(i < to_sna(scrn)->mode.num_real_output); - DBG(("%s: removing output #%d of %d\n", - __FUNCTION__, i, to_sna(scrn)->mode.num_real_output)); - - for (; i < config->num_output; i++) { - config->output[i] = config->output[i+1]; - config->output[i]->possible_clones >>= 1; - } - config->num_output--; - to_sna(scrn)->mode.num_real_output--; -} - static int output_rank(const void *A, const void *B) { const xf86OutputPtr *a = A; @@ -4058,6 +4918,7 @@ static void sort_config_outputs(struct sna *sna) { xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); qsort(config->output, sna->mode.num_real_output, sizeof(*config->output), output_rank); + config->compat_output = 0; /* make sure it is a sane value */ sna_mode_compute_possible_outputs(sna); } @@ -4080,6 +4941,8 @@ static bool disable_unused_crtc(struct sna *sna) bool update = false; int o, c; + DBG(("%s\n", __FUNCTION__)); + for (c = 0; c < sna->mode.num_real_crtc; c++) { xf86CrtcPtr crtc = config->crtc[c]; @@ -4094,7 +4957,7 @@ static bool disable_unused_crtc(struct sna *sna) if (o == sna->mode.num_real_output) { DBG(("%s: CRTC:%d was enabled with no outputs\n", - __FUNCTION__, to_sna_crtc(crtc)->id)); + __FUNCTION__, sna_crtc_id(crtc))); crtc->enabled = false; update = true; } @@ -4108,17 +4971,53 @@ static bool disable_unused_crtc(struct sna *sna) return update; } -void sna_mode_discover(struct sna *sna) +static bool +output_check_status(struct sna *sna, struct sna_output *output) +{ + union compat_mode_get_connector compat_conn; + struct drm_mode_modeinfo dummy; + xf86OutputStatus status; + + VG_CLEAR(compat_conn); + + compat_conn.conn.connector_id = output->id; + compat_conn.conn.count_modes = 1; /* skip detect */ + compat_conn.conn.modes_ptr = (uintptr_t)&dummy; + compat_conn.conn.count_encoders = 0; + compat_conn.conn.count_props = 0; + + (void)drmIoctl(sna->kgem.fd, + DRM_IOCTL_MODE_GETCONNECTOR, + &compat_conn.conn); + + switch (compat_conn.conn.connection) { + case DRM_MODE_CONNECTED: + status = XF86OutputStatusConnected; + break; + case DRM_MODE_DISCONNECTED: + status = XF86OutputStatusDisconnected; + break; + default: + case DRM_MODE_UNKNOWNCONNECTION: + status = XF86OutputStatusUnknown; + break; + } + return output->status == status; +} + +void sna_mode_discover(struct sna *sna, bool tell) { ScreenPtr screen = xf86ScrnToScreen(sna->scrn); xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); struct drm_mode_card_res res; - uint32_t connectors[32]; + uint32_t connectors[32], now; unsigned changed = 0; unsigned serial; int i, j; DBG(("%s()\n", __FUNCTION__)); + sna->flags &= ~SNA_REPROBE; + VG_CLEAR(connectors); memset(&res, 0, sizeof(res)); @@ -4128,8 +5027,9 @@ void sna_mode_discover(struct sna *sna) if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETRESOURCES, &res)) return; - DBG(("%s: now %d (was %d) connectors\n", __FUNCTION__, - res.count_connectors, sna->mode.num_real_output)); + DBG(("%s: now %d (was %d) connectors, %d encoders, %d crtc\n", __FUNCTION__, + res.count_connectors, sna->mode.num_real_output, + res.count_encoders, res.count_crtcs)); if (res.count_connectors > 32) return; @@ -4142,6 +5042,7 @@ void sna_mode_discover(struct sna *sna) if (serial == 0) serial = ++sna->mode.serial; + now = GetTimeInMillis(); for (i = 0; i < res.count_connectors; i++) { DBG(("%s: connector[%d] = %d\n", __FUNCTION__, i, connectors[i])); for (j = 0; j < sna->mode.num_real_output; j++) { @@ -4161,32 +5062,41 @@ void sna_mode_discover(struct sna *sna) for (i = 0; i < sna->mode.num_real_output; i++) { xf86OutputPtr output = config->output[i]; + struct sna_output *sna_output = to_sna_output(output); - if (to_sna_output(output)->id == 0) + if (sna_output->id == 0) continue; - if (to_sna_output(output)->serial == serial) + if (sna_output->serial == serial) { + if (output_check_status(sna, sna_output)) { + DBG(("%s: output %s (id=%d), retained state\n", + __FUNCTION__, output->name, sna_output->id)); + sna_output->last_detect = now; + } else { + DBG(("%s: output %s (id=%d), changed state, reprobing\n", + __FUNCTION__, output->name, sna_output->id)); + sna_output->last_detect = 0; + changed |= 4; + } continue; + } DBG(("%s: removing output %s (id=%d), serial=%u [now %u]\n", - __FUNCTION__, output->name, to_sna_output(output)->id, - to_sna_output(output)->serial, serial)); + __FUNCTION__, output->name, sna_output->id, + sna_output->serial, serial)); xf86DrvMsg(sna->scrn->scrnIndex, X_INFO, - "%s output %s\n", - sna->flags & SNA_REMOVE_OUTPUTS ? "Removed" : "Disabled", + "Disabled output %s\n", output->name); - if (sna->flags & SNA_REMOVE_OUTPUTS) { - sna_output_del(output); - i--; - } else { - to_sna_output(output)->id = 0; - output->crtc = NULL; - } + sna_output->id = 0; + sna_output->last_detect = 0; + output->crtc = NULL; + RROutputChanged(output->randr_output, TRUE); changed |= 2; } - if (changed) { + /* Have the list of available outputs been updated? */ + if (changed & 3) { DBG(("%s: outputs changed, broadcasting\n", __FUNCTION__)); sna_mode_set_primary(sna); @@ -4200,6 +5110,51 @@ void sna_mode_discover(struct sna *sna) xf86RandR12TellChanged(screen); } + + /* If anything has changed, refresh the RandR information. + * Note this could recurse once from udevless RRGetInfo() probes, + * but only once. + */ + if (changed && tell) + RRGetInfo(screen, TRUE); +} + +/* Since we only probe the current mode on startup, we may not have the full + * list of modes available until the user explicitly requests them. Fake a + * hotplug event after a second after starting to fill in any missing modes. + */ +static CARD32 sna_mode_coldplug(OsTimerPtr timer, CARD32 now, void *data) +{ + struct sna *sna = data; + ScreenPtr screen = xf86ScrnToScreen(sna->scrn); + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); + bool reprobe = false; + int i; + + DBG(("%s()\n", __FUNCTION__)); + + for (i = 0; i < sna->mode.num_real_output; i++) { + xf86OutputPtr output = config->output[i]; + struct sna_output *sna_output = to_sna_output(output); + + if (sna_output->id == 0) + continue; + if (sna_output->last_detect) + continue; + if (output->status == XF86OutputStatusDisconnected) + continue; + + DBG(("%s: output %s connected, needs reprobe\n", + __FUNCTION__, output->name)); + reprobe = true; + } + + if (reprobe) { + RRGetInfo(screen, TRUE); + RRTellChanged(screen); + } + free(timer); + return 0; } static void copy_front(struct sna *sna, PixmapPtr old, PixmapPtr new) @@ -4208,7 +5163,7 @@ static void copy_front(struct sna *sna, PixmapPtr old, PixmapPtr new) DBG(("%s\n", __FUNCTION__)); - if (wedged(sna)) + if (wedged(sna) || isGPU(sna->scrn)) return; old_priv = sna_pixmap_force_to_gpu(old, MOVE_READ); @@ -4220,12 +5175,19 @@ static void copy_front(struct sna *sna, PixmapPtr old, PixmapPtr new) return; if (old_priv->clear) { - (void)sna->render.fill_one(sna, new, new_priv->gpu_bo, - old_priv->clear_color, - 0, 0, - new->drawable.width, - new->drawable.height, - GXcopy); + bool ok = false; + if (!wedged(sna)) + ok = sna->render.fill_one(sna, new, new_priv->gpu_bo, + old_priv->clear_color, + 0, 0, + new->drawable.width, + new->drawable.height, + GXcopy); + if (!ok) { + void *ptr = kgem_bo_map__gtt(&sna->kgem, new_priv->gpu_bo); + if (ptr) + memset(ptr, 0, new_priv->gpu_bo->pitch*new->drawable.height); + } new_priv->clear = true; new_priv->clear_color = old_priv->clear_color; } else { @@ -4281,11 +5243,18 @@ static void copy_front(struct sna *sna, PixmapPtr old, PixmapPtr new) __FUNCTION__, box.x2, box.y2, sx, sy, dx, dy)); if (box.x2 != new->drawable.width || box.y2 != new->drawable.height) { - (void)sna->render.fill_one(sna, new, new_priv->gpu_bo, 0, - 0, 0, - new->drawable.width, - new->drawable.height, - GXclear); + bool ok = false; + if (!wedged(sna)) + ok = sna->render.fill_one(sna, new, new_priv->gpu_bo, 0, + 0, 0, + new->drawable.width, + new->drawable.height, + GXclear); + if (!ok) { + void *ptr = kgem_bo_map__gtt(&sna->kgem, new_priv->gpu_bo); + if (ptr) + memset(ptr, 0, new_priv->gpu_bo->pitch*new->drawable.height); + } } (void)sna->render.copy_boxes(sna, GXcopy, &old->drawable, old_priv->gpu_bo, sx, sy, @@ -4302,7 +5271,7 @@ sna_mode_resize(ScrnInfoPtr scrn, int width, int height) { xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); struct sna *sna = to_sna(scrn); - ScreenPtr screen = scrn->pScreen; + ScreenPtr screen = xf86ScrnToScreen(scrn); PixmapPtr new_front; int i; @@ -4337,6 +5306,7 @@ sna_mode_resize(ScrnInfoPtr scrn, int width, int height) for (i = 0; i < sna->mode.num_real_crtc; i++) sna_crtc_disable_shadow(sna, to_sna_crtc(config->crtc[i])); assert(sna->mode.shadow_active == 0); + assert(!sna->mode.shadow_enabled); assert(sna->mode.shadow_damage == NULL); assert(sna->mode.shadow == NULL); @@ -4371,7 +5341,7 @@ sna_mode_resize(ScrnInfoPtr scrn, int width, int height) continue; if (!__sna_crtc_set_mode(crtc)) - sna_crtc_disable(crtc); + sna_crtc_disable(crtc, false); } sna_mode_wakeup(sna); @@ -4381,19 +5351,6 @@ sna_mode_resize(ScrnInfoPtr scrn, int width, int height) } /* cursor handling */ -struct sna_cursor { - struct sna_cursor *next; - uint32_t *image; - Rotation rotation; - int ref; - int size; - int last_width; - int last_height; - unsigned handle; - unsigned serial; - unsigned alloc; -}; - static void rotate_coord(Rotation rotation, int size, int x_dst, int y_dst, @@ -4429,36 +5386,6 @@ rotate_coord(Rotation rotation, int size, *y_src = y_dst; } -static void -rotate_coord_back(Rotation rotation, int size, int *x, int *y) -{ - int t; - - if (rotation & RR_Reflect_X) - *x = size - *x - 1; - if (rotation & RR_Reflect_Y) - *y = size - *y - 1; - - switch (rotation & 0xf) { - case RR_Rotate_0: - break; - case RR_Rotate_90: - t = *x; - *x = *y; - *y = size - t - 1; - break; - case RR_Rotate_180: - *x = size - *x - 1; - *y = size - *y - 1; - break; - case RR_Rotate_270: - t = *x; - *x = size - *y - 1; - *y = t; - break; - } -} - static struct sna_cursor *__sna_create_cursor(struct sna *sna, int size) { struct sna_cursor *c; @@ -4519,6 +5446,17 @@ static uint32_t *get_cursor_argb(CursorPtr c) #endif } +static int __cursor_size(int width, int height) +{ + int i, size; + + i = MAX(width, height); + for (size = 64; size < i; size <<= 1) + ; + + return size; +} + static struct sna_cursor *__sna_get_cursor(struct sna *sna, xf86CrtcPtr crtc) { struct sna_cursor *cursor; @@ -4526,6 +5464,8 @@ static struct sna_cursor *__sna_get_cursor(struct sna *sna, xf86CrtcPtr crtc) const uint32_t *argb; uint32_t *image; int width, height, pitch, size, x, y; + PictTransform cursor_to_fb; + bool transformed; Rotation rotation; assert(sna->cursor.ref); @@ -4537,8 +5477,8 @@ static struct sna_cursor *__sna_get_cursor(struct sna *sna, xf86CrtcPtr crtc) cursor ? cursor->serial : 0, sna->cursor.serial)); if (cursor && cursor->serial == sna->cursor.serial) { - assert(cursor->size == sna->cursor.size); - assert(cursor->rotation == crtc->transform_in_use ? crtc->rotation : RR_Rotate_0); + assert(cursor->size == sna->cursor.size || cursor->transformed); + assert(cursor->rotation == (!to_sna_crtc(crtc)->cursor_transform && crtc->transform_in_use) ? crtc->rotation : RR_Rotate_0); assert(cursor->ref); return cursor; } @@ -4550,22 +5490,44 @@ static struct sna_cursor *__sna_get_cursor(struct sna *sna, xf86CrtcPtr crtc) sna->cursor.serial, get_cursor_argb(sna->cursor.ref) != NULL)); - rotation = crtc->transform_in_use ? crtc->rotation : RR_Rotate_0; + transformed = to_sna_crtc(crtc)->cursor_transform; + rotation = (!transformed && crtc->transform_in_use) ? crtc->rotation : RR_Rotate_0; - if (sna->cursor.use_gtt) { /* Don't allow phys cursor sharing */ + /* Don't allow phys cursor sharing */ + if (sna->cursor.use_gtt && !transformed) { for (cursor = sna->cursor.cursors; cursor; cursor = cursor->next) { - if (cursor->serial == sna->cursor.serial && cursor->rotation == rotation) { + if (cursor->serial == sna->cursor.serial && + cursor->rotation == rotation && + !cursor->transformed) { __DBG(("%s: reusing handle=%d, serial=%d, rotation=%d, size=%d\n", __FUNCTION__, cursor->handle, cursor->serial, cursor->rotation, cursor->size)); assert(cursor->size == sna->cursor.size); return cursor; } } - - cursor = to_sna_crtc(crtc)->cursor; } - size = sna->cursor.size; + if (transformed) { + struct pixman_box16 box; + + box.x1 = box.y1 = 0; + box.x2 = sna->cursor.ref->bits->width; + box.y2 = sna->cursor.ref->bits->height; + + pixman_f_transform_bounds(&crtc->f_crtc_to_framebuffer, &box); + size = __cursor_size(box.x2 - box.x1, box.y2 - box.y1); + } else + size = sna->cursor.size; + + if (crtc->transform_in_use) + RRTransformCompute(0, 0, size, size, + crtc->rotation, + crtc->transformPresent ? &crtc->transform : NULL, + &cursor_to_fb, + &to_sna_crtc(crtc)->cursor_to_fb, + &to_sna_crtc(crtc)->fb_to_cursor); + + cursor = to_sna_crtc(crtc)->cursor; if (cursor && cursor->alloc < 4*size*size) cursor = NULL; @@ -4577,7 +5539,7 @@ static struct sna_cursor *__sna_get_cursor(struct sna *sna, xf86CrtcPtr crtc) } } - width = sna->cursor.ref->bits->width; + width = sna->cursor.ref->bits->width; height = sna->cursor.ref->bits->height; source = sna->cursor.ref->bits->source; mask = sna->cursor.ref->bits->mask; @@ -4585,7 +5547,7 @@ static struct sna_cursor *__sna_get_cursor(struct sna *sna, xf86CrtcPtr crtc) pitch = BitmapBytePad(width); image = cursor->image; - if (image == NULL) { + if (image == NULL || transformed) { image = sna->cursor.scratch; cursor->last_width = cursor->last_height = size; } @@ -4616,6 +5578,19 @@ static struct sna_cursor *__sna_get_cursor(struct sna *sna, xf86CrtcPtr crtc) mask += pitch; source += pitch; } + if (transformed) { + affine_blt(image, cursor->image, 32, + 0, 0, width, height, size * 4, + 0, 0, size, size, size * 4, + &to_sna_crtc(crtc)->cursor_to_fb); + image = cursor->image; + } + } else if (transformed) { + affine_blt(argb, cursor->image, 32, + 0, 0, width, height, width * 4, + 0, 0, size, size, size * 4, + &to_sna_crtc(crtc)->cursor_to_fb); + image = cursor->image; } else memcpy_blt(argb, image, 32, width * 4, size * 4, @@ -4662,6 +5637,7 @@ static struct sna_cursor *__sna_get_cursor(struct sna *sna, xf86CrtcPtr crtc) cursor->size = size; cursor->rotation = rotation; + cursor->transformed = transformed; cursor->serial = sna->cursor.serial; cursor->last_width = width; cursor->last_height = height; @@ -4674,28 +5650,39 @@ sna_realize_cursor(xf86CursorInfoPtr info, CursorPtr cursor) return NULL; } -#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,12,99,901,0) -static inline int sigio_block(void) +static void enable_fb_access(ScrnInfoPtr scrn, int state) { - OsBlockSIGIO(); - return 0; -} -static inline void sigio_unblock(int was_blocked) -{ - OsReleaseSIGIO(); - (void)was_blocked; -} + scrn->EnableDisableFBAccess( +#ifdef XF86_HAS_SCRN_CONV + scrn, #else -#include -static inline int sigio_block(void) + scrn->scrnIndex, +#endif + state); +} + + +static void __restore_swcursor(ScrnInfoPtr scrn) { - return xf86BlockSIGIO(); + DBG(("%s: attempting to restore SW cursor\n", __FUNCTION__)); + enable_fb_access(scrn, FALSE); + enable_fb_access(scrn, TRUE); + + RemoveBlockAndWakeupHandlers((BlockHandlerProcPtr)__restore_swcursor, + (WakeupHandlerProcPtr)NoopDDA, + scrn); } -static inline void sigio_unblock(int was_blocked) + +static void restore_swcursor(struct sna *sna) { - xf86UnblockSIGIO(was_blocked); + /* XXX Force the cursor to be restored (avoiding recursion) */ + FreeCursor(sna->cursor.ref, None); + sna->cursor.ref = NULL; + + RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)__restore_swcursor, + (WakeupHandlerProcPtr)NoopDDA, + sna->scrn); } -#endif static void sna_show_cursors(ScrnInfoPtr scrn) @@ -4721,7 +5708,7 @@ sna_show_cursors(ScrnInfoPtr scrn) if (!crtc->cursor_in_range) { DBG(("%s: skipping cursor outside CRTC (pipe=%d)\n", - __FUNCTION__, sna_crtc->pipe)); + __FUNCTION__, sna_crtc_pipe(crtc))); continue; } @@ -4729,16 +5716,16 @@ sna_show_cursors(ScrnInfoPtr scrn) if (cursor == NULL || (sna_crtc->cursor == cursor && sna_crtc->last_cursor_size == cursor->size)) { DBG(("%s: skipping cursor already show on CRTC (pipe=%d)\n", - __FUNCTION__, sna_crtc->pipe)); + __FUNCTION__, sna_crtc_pipe(crtc))); continue; } DBG(("%s: CRTC pipe=%d, handle->%d\n", __FUNCTION__, - sna_crtc->pipe, cursor->handle)); + sna_crtc_pipe(crtc), cursor->handle)); VG_CLEAR(arg); arg.flags = DRM_MODE_CURSOR_BO; - arg.crtc_id = sna_crtc->id; + arg.crtc_id = __sna_crtc_id(sna_crtc); arg.width = arg.height = cursor->size; arg.handle = cursor->handle; @@ -4750,10 +5737,17 @@ sna_show_cursors(ScrnInfoPtr scrn) cursor->ref++; sna_crtc->cursor = cursor; sna_crtc->last_cursor_size = cursor->size; + } else { + ERR(("%s: failed to show cursor on CRTC:%d [pipe=%d], disabling hwcursor\n", + __FUNCTION__, sna_crtc_id(crtc), sna_crtc_pipe(crtc))); + sna->cursor.disable = true; } } sigio_unblock(sigio); sna->cursor.active = true; + + if (unlikely(sna->cursor.disable)) + restore_swcursor(sna); } static void @@ -4789,24 +5783,45 @@ static void sna_crtc_disable_cursor(struct sna *sna, struct sna_crtc *crtc) { struct drm_mode_cursor arg; + int sigio; if (!crtc->cursor) return; - DBG(("%s: CRTC:%d, handle=%d\n", __FUNCTION__, crtc->id, crtc->cursor->handle)); - assert(crtc->cursor->ref); + sigio = sigio_block(); + if (crtc->cursor) { + DBG(("%s: CRTC:%d, handle=%d\n", __FUNCTION__, __sna_crtc_id(crtc), crtc->cursor->handle)); + assert(crtc->cursor->ref > 0); + crtc->cursor->ref--; + crtc->cursor = NULL; + crtc->last_cursor_size = 0; - VG_CLEAR(arg); - arg.flags = DRM_MODE_CURSOR_BO; - arg.crtc_id = crtc->id; - arg.width = arg.height = 0; - arg.handle = 0; + VG_CLEAR(arg); + arg.flags = DRM_MODE_CURSOR_BO; + arg.crtc_id = __sna_crtc_id(crtc); + arg.width = arg.height = 0; + arg.handle = 0; - (void)drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_CURSOR, &arg); - assert(crtc->cursor->ref > 0); - crtc->cursor->ref--; - crtc->cursor = NULL; - crtc->last_cursor_size = 0; + (void)drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_CURSOR, &arg); + } + sigio_unblock(sigio); +} + +static void +sna_disable_cursors(ScrnInfoPtr scrn) +{ + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn); + struct sna *sna = to_sna(scrn); + int sigio, c; + + DBG(("%s\n", __FUNCTION__)); + + sigio = sigio_block(); + for (c = 0; c < sna->mode.num_real_crtc; c++) { + assert(to_sna_crtc(xf86_config->crtc[c])); + sna_crtc_disable_cursor(sna, to_sna_crtc(xf86_config->crtc[c])); + } + sigio_unblock(sigio); } static void @@ -4876,7 +5891,7 @@ sna_set_cursor_position(ScrnInfoPtr scrn, int x, int y) VG_CLEAR(arg); arg.flags = 0; - arg.crtc_id = sna_crtc->id; + arg.crtc_id = __sna_crtc_id(sna_crtc); arg.handle = 0; if (sna_crtc->bo == NULL) @@ -4885,18 +5900,20 @@ sna_set_cursor_position(ScrnInfoPtr scrn, int x, int y) if (crtc->transform_in_use) { int xhot = sna->cursor.ref->bits->xhot; int yhot = sna->cursor.ref->bits->yhot; - struct pict_f_vector v; + struct pict_f_vector v, hot; - v.v[0] = (x + xhot) + 0.5; - v.v[1] = (y + yhot) + 0.5; - v.v[2] = 1; + v.v[0] = x + xhot + .5; + v.v[1] = y + yhot + .5; + v.v[2] = 1.; pixman_f_transform_point(&crtc->f_framebuffer_to_crtc, &v); - rotate_coord_back(crtc->rotation, sna->cursor.size, &xhot, &yhot); + hot.v[0] = xhot; + hot.v[1] = yhot; + hot.v[2] = 1.; + pixman_f_transform_point(&sna_crtc->fb_to_cursor, &hot); - /* cursor will have 0.5 added to it already so floor is sufficent */ - arg.x = floor(v.v[0]) - xhot; - arg.y = floor(v.v[1]) - yhot; + arg.x = floor(v.v[0] - hot.v[0]); + arg.y = floor(v.v[1] - hot.v[1]); } else { arg.x = x - crtc->x; arg.y = y - crtc->y; @@ -4932,11 +5949,13 @@ disable: } __DBG(("%s: CRTC:%d (%d, %d), handle=%d, flags=%x (old cursor handle=%d), move? %d, update handle? %d\n", - __FUNCTION__, sna_crtc->id, arg.x, arg.y, arg.handle, arg.flags, sna_crtc->cursor ? sna_crtc->cursor->handle : 0, + __FUNCTION__, __sna_crtc_id(sna_crtc), arg.x, arg.y, arg.handle, arg.flags, sna_crtc->cursor ? sna_crtc->cursor->handle : 0, arg.flags & DRM_MODE_CURSOR_MOVE, arg.flags & DRM_MODE_CURSOR_BO)); - if (arg.flags && - drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_CURSOR, &arg) == 0) { + if (arg.flags == 0) + continue; + + if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_CURSOR, &arg) == 0) { if (arg.flags & DRM_MODE_CURSOR_BO) { if (sna_crtc->cursor) { assert(sna_crtc->cursor->ref > 0); @@ -4949,9 +5968,20 @@ disable: } else sna_crtc->last_cursor_size = 0; } + } else { + ERR(("%s: failed to update cursor on CRTC:%d [pipe=%d], disabling hwcursor\n", + __FUNCTION__, sna_crtc_id(crtc), sna_crtc_pipe(crtc))); + /* XXX How to force switch back to SW cursor? + * Right now we just want until the next cursor image + * change, which is fairly frequent. + */ + sna->cursor.disable = true; } } sigio_unblock(sigio); + + if (unlikely(sna->cursor.disable)) + restore_swcursor(sna); } #if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,15,99,902,2) @@ -4978,17 +6008,6 @@ sna_load_cursor_image(ScrnInfoPtr scrn, unsigned char *src) { } -static int __cursor_size(CursorPtr cursor) -{ - int i, size; - - i = MAX(cursor->bits->width, cursor->bits->height); - for (size = 64; size < i; size <<= 1) - ; - - return size; -} - static bool sna_cursor_preallocate(struct sna *sna) { @@ -5006,6 +6025,50 @@ sna_cursor_preallocate(struct sna *sna) return true; } +static bool +transformable_cursor(struct sna *sna, CursorPtr cursor) +{ + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); + int i; + + for (i = 0; i < sna->mode.num_real_crtc; i++) { + xf86CrtcPtr crtc = config->crtc[i]; + struct pixman_box16 box; + int size; + + if (!to_sna_crtc(crtc)->hwcursor) { + DBG(("%s: hwcursor disabled on CRTC:%d [pipe=%d]\n", + __FUNCTION__, sna_crtc_id(crtc), sna_crtc_pipe(crtc))); + return false; + } + + if (!sna->cursor.use_gtt || !sna->cursor.scratch) { + DBG(("%s: unable to use GTT curosor access [%d] or no scratch [%d]\n", + __FUNCTION__, sna->cursor.use_gtt, sna->cursor.scratch)); + return false; + } + + box.x1 = box.y1 = 0; + box.x2 = cursor->bits->width; + box.y2 = cursor->bits->height; + + if (!pixman_f_transform_bounds(&crtc->f_crtc_to_framebuffer, + &box)) { + DBG(("%s: unable to transform bounds\n", __FUNCTION__)); + return false; + } + + size = __cursor_size(box.x2 - box.x1, box.y2 - box.y1); + if (size > sna->cursor.max_size) { + DBG(("%s: transformed cursor size=%d too large, max=%d\n", + __FUNCTION__, size, sna->cursor.max_size)); + return false; + } + } + + return true; +} + static Bool sna_use_hw_cursor(ScreenPtr screen, CursorPtr cursor) { @@ -5014,6 +6077,9 @@ sna_use_hw_cursor(ScreenPtr screen, CursorPtr cursor) DBG(("%s (%dx%d)?\n", __FUNCTION__, cursor->bits->width, cursor->bits->height)); + if (sna->cursor.disable) + return FALSE; + /* cursors are invariant */ if (cursor == sna->cursor.ref) return TRUE; @@ -5023,12 +6089,24 @@ sna_use_hw_cursor(ScreenPtr screen, CursorPtr cursor) sna->cursor.ref = NULL; } - sna->cursor.size = __cursor_size(cursor); - if (sna->cursor.size > sna->cursor.max_size) + sna->cursor.size = + __cursor_size(cursor->bits->width, cursor->bits->height); + if (sna->cursor.size > sna->cursor.max_size) { + DBG(("%s: cursor size=%d too large, max %d: using sw cursor\n", + __FUNCTION__, sna->cursor.size, sna->cursor.max_size)); + return FALSE; + } + + if (sna->mode.rr_active && !transformable_cursor(sna, cursor)) { + DBG(("%s: RandR active [%d] and non-transformable cursor: using sw cursor\n", + __FUNCTION__, sna->mode.rr_active)); return FALSE; + } - if (!sna_cursor_preallocate(sna)) + if (!sna_cursor_preallocate(sna)) { + DBG(("%s: cursor preallocation failed: using sw cursor\n", __FUNCTION__)); return FALSE; + } sna->cursor.ref = cursor; cursor->refcnt++; @@ -5087,11 +6165,9 @@ sna_cursor_pre_init(struct sna *sna) DBG(("%s: cursor updates use_gtt?=%d\n", __FUNCTION__, sna->cursor.use_gtt)); - if (!sna->cursor.use_gtt) { - sna->cursor.scratch = malloc(sna->cursor.max_size * sna->cursor.max_size * 4); - if (!sna->cursor.scratch) - sna->cursor.max_size = 0; - } + sna->cursor.scratch = malloc(sna->cursor.max_size * sna->cursor.max_size * 4); + if (!sna->cursor.scratch && !sna->cursor.use_gtt) + sna->cursor.max_size = 0; sna->cursor.num_stash = -sna->mode.num_real_crtc; @@ -5193,7 +6269,7 @@ sna_crtc_flip(struct sna *sna, struct sna_crtc *crtc, struct kgem_bo *bo, int x, int output_count = 0; int i; - DBG(("%s CRTC:%d [pipe=%d], handle=%d\n", __FUNCTION__, crtc->id, crtc->pipe, bo->handle)); + DBG(("%s CRTC:%d [pipe=%d], handle=%d\n", __FUNCTION__, __sna_crtc_id(crtc), __sna_crtc_pipe(crtc), bo->handle)); assert(sna->mode.num_real_output < ARRAY_SIZE(output_ids)); assert(crtc->bo); @@ -5207,11 +6283,11 @@ sna_crtc_flip(struct sna *sna, struct sna_crtc *crtc, struct kgem_bo *bo, int x, DBG(("%s: attaching output '%s' %d [%d] to crtc:%d (pipe %d) (possible crtc:%x, possible clones:%x)\n", __FUNCTION__, output->name, i, to_connector_id(output), - crtc->id, crtc->pipe, + __sna_crtc_id(crtc), __sna_crtc_pipe(crtc), (uint32_t)output->possible_crtcs, (uint32_t)output->possible_clones)); - assert(output->possible_crtcs & (1 << crtc->pipe) || + assert(output->possible_crtcs & (1 << __sna_crtc_pipe(crtc)) || is_zaphod(sna->scrn)); output_ids[output_count] = to_connector_id(output); @@ -5221,7 +6297,7 @@ sna_crtc_flip(struct sna *sna, struct sna_crtc *crtc, struct kgem_bo *bo, int x, assert(output_count); VG_CLEAR(arg); - arg.crtc_id = crtc->id; + arg.crtc_id = __sna_crtc_id(crtc); arg.fb_id = fb_id(bo); assert(arg.fb_id); arg.x = x; @@ -5232,7 +6308,7 @@ sna_crtc_flip(struct sna *sna, struct sna_crtc *crtc, struct kgem_bo *bo, int x, arg.mode_valid = 1; DBG(("%s: applying crtc [%d, pipe=%d] mode=%dx%d+%d+%d@%d, fb=%d across %d outputs [%d...]\n", - __FUNCTION__, crtc->id, crtc->pipe, + __FUNCTION__, __sna_crtc_id(crtc), __sna_crtc_pipe(crtc), arg.mode.hdisplay, arg.mode.vdisplay, arg.x, arg.y, @@ -5247,6 +6323,37 @@ sna_crtc_flip(struct sna *sna, struct sna_crtc *crtc, struct kgem_bo *bo, int x, return true; } +static void sna_mode_restore(struct sna *sna) +{ + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); + int error = 0; + int i; + + assert(!sna->mode.hidden); + + for (i = 0; i < sna->mode.num_real_crtc; i++) { + xf86CrtcPtr crtc = config->crtc[i]; + + assert(to_sna_crtc(crtc) != NULL); + if (to_sna_crtc(crtc)->bo == NULL) + continue; + + assert(crtc->enabled); + if (!__sna_crtc_set_mode(crtc)) { + sna_crtc_disable(crtc, false); + error++; + } + } + sna_mode_wakeup(sna); + update_flush_interval(sna); + sna_cursors_reload(sna); + sna->mode.dirty = false; + + if (error) + xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR, + "Failed to restore display configuration\n"); +} + int sna_page_flip(struct sna *sna, struct kgem_bo *bo, @@ -5263,9 +6370,9 @@ sna_page_flip(struct sna *sna, assert(bo->refcnt); assert((sna->flags & SNA_IS_HOSTED) == 0); - assert((sna->flags & SNA_TEAR_FREE) == 0); assert(sna->mode.flip_active == 0); assert(sna->mode.front_active); + assert(!sna->mode.hidden); assert(sna->scrn->vtSema); if ((sna->flags & (data ? SNA_HAS_FLIP : SNA_HAS_ASYNC_FLIP)) == 0) @@ -5277,9 +6384,10 @@ sna_page_flip(struct sna *sna, struct sna_crtc *crtc = config->crtc[i]->driver_private; struct drm_mode_crtc_page_flip arg; uint32_t crtc_offset; + int fixup; DBG(("%s: crtc %d id=%d, pipe=%d active? %d\n", - __FUNCTION__, i, crtc->id, crtc->pipe, crtc->bo != NULL)); + __FUNCTION__, i, __sna_crtc_id(crtc), __sna_crtc_pipe(crtc), crtc->bo != NULL)); if (crtc->bo == NULL) continue; assert(!crtc->transform); @@ -5288,13 +6396,17 @@ sna_page_flip(struct sna *sna, assert(crtc->bo->refcnt >= crtc->bo->active_scanout); assert(crtc->flip_bo == NULL); - arg.crtc_id = crtc->id; + if (data == NULL && crtc->bo == bo) + goto next_crtc; + + arg.crtc_id = __sna_crtc_id(crtc); arg.fb_id = get_fb(sna, bo, width, height); if (arg.fb_id == 0) { assert(count == 0); return 0; } + fixup = 0; crtc_offset = crtc->base->y << 16 | crtc->base->x; if (bo->pitch != crtc->bo->pitch || crtc_offset != crtc->offset) { @@ -5303,7 +6415,12 @@ sna_page_flip(struct sna *sna, bo->pitch, crtc->bo->pitch, crtc_offset, crtc->offset)); fixup_flip: + fixup = 1; if (crtc->bo != bo && sna_crtc_flip(sna, crtc, bo, crtc->base->x, crtc->base->y)) { +update_scanout: + DBG(("%s: removing handle=%d [active_scanout=%d] from scanout, installing handle=%d [active_scanout=%d]\n", + __FUNCTION__, crtc->bo->handle, crtc->bo->active_scanout, + bo->handle, bo->active_scanout)); assert(crtc->bo->active_scanout); assert(crtc->bo->refcnt >= crtc->bo->active_scanout); crtc->bo->active_scanout--; @@ -5319,17 +6436,10 @@ fixup_flip: if (data == NULL) goto next_crtc; - - /* queue a flip in order to send the event */ - } else { - if (count && !xf86SetDesiredModes(sna->scrn)) { - xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR, - "failed to restore display configuration\n"); - for (; i < sna->mode.num_real_crtc; i++) - sna_crtc_disable(config->crtc[i]); - } - return 0; - } + + /* queue a flip in order to send the event */ + } else + goto error; } /* Only the reference crtc will finally deliver its page flip @@ -5346,7 +6456,7 @@ fixup_flip: retry_flip: DBG(("%s: crtc %d id=%d, pipe=%d --> fb %d\n", - __FUNCTION__, i, crtc->id, crtc->pipe, arg.fb_id)); + __FUNCTION__, i, __sna_crtc_id(crtc), __sna_crtc_pipe(crtc), arg.fb_id)); if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_PAGE_FLIP, &arg)) { ERR(("%s: pageflip failed with err=%d\n", __FUNCTION__, errno)); @@ -5354,7 +6464,7 @@ retry_flip: struct drm_mode_crtc mode; memset(&mode, 0, sizeof(mode)); - mode.crtc_id = crtc->id; + mode.crtc_id = __sna_crtc_id(crtc); drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCRTC, &mode); DBG(("%s: crtc=%d, valid?=%d, fb attached?=%d, expected=%d\n", @@ -5375,15 +6485,24 @@ retry_flip: goto retry_flip; } + if (!fixup) + goto fixup_flip; + +error: xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR, - "page flipping failed, on CRTC:%d (pipe=%d), disabling %s page flips\n", - crtc->id, crtc->pipe, data ? "synchronous": "asynchronous"); + "page flipping failed, on CRTC:%d (pipe=%d), disabling %s page flips\n", + __sna_crtc_id(crtc), __sna_crtc_pipe(crtc), data ? "synchronous": "asynchronous"); + + if (count || crtc->bo == bo) + sna_mode_restore(sna); + sna->flags &= ~(data ? SNA_HAS_FLIP : SNA_HAS_ASYNC_FLIP); - goto fixup_flip; + return 0; } if (data) { assert(crtc->flip_bo == NULL); + assert(handler); crtc->flip_handler = handler; crtc->flip_data = data; crtc->flip_bo = kgem_bo_reference(bo); @@ -5391,8 +6510,11 @@ retry_flip: crtc->flip_serial = crtc->mode_serial; crtc->flip_pending = true; sna->mode.flip_active++; - } + DBG(("%s: recording flip on CRTC:%d handle=%d, active_scanout=%d, serial=%d\n", + __FUNCTION__, __sna_crtc_id(crtc), crtc->flip_bo->handle, crtc->flip_bo->active_scanout, crtc->flip_serial)); + } else + goto update_scanout; next_crtc: count++; } @@ -5471,7 +6593,7 @@ static void crtc_init_gamma(xf86CrtcPtr crtc) assert(sna_crtc); - lut.crtc_id = sna_crtc->id; + lut.crtc_id = __sna_crtc_id(sna_crtc); lut.gamma_size = 256; lut.red = (uintptr_t)(gamma); lut.green = (uintptr_t)(gamma + 256); @@ -5485,7 +6607,7 @@ static void crtc_init_gamma(xf86CrtcPtr crtc) } DBG(("%s: CRTC:%d, pipe=%d: gamma set?=%d\n", - __FUNCTION__, sna_crtc->id, sna_crtc->pipe, + __FUNCTION__, __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc), gamma_set)); if (!gamma_set) { int i; @@ -5528,6 +6650,7 @@ static bool sna_probe_initial_configuration(struct sna *sna) { ScrnInfoPtr scrn = sna->scrn; xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); + int crtc_active, crtc_enabled; int width, height; int i, j; @@ -5565,6 +6688,7 @@ static bool sna_probe_initial_configuration(struct sna *sna) } /* Copy the existing modes on each CRTCs */ + crtc_active = crtc_enabled = 0; for (i = 0; i < sna->mode.num_real_crtc; i++) { xf86CrtcPtr crtc = config->crtc[i]; struct sna_crtc *sna_crtc = to_sna_crtc(crtc); @@ -5577,12 +6701,12 @@ static bool sna_probe_initial_configuration(struct sna *sna) /* Retrieve the current mode */ VG_CLEAR(mode); - mode.crtc_id = sna_crtc->id; + mode.crtc_id = __sna_crtc_id(sna_crtc); if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCRTC, &mode)) continue; DBG(("%s: CRTC:%d, pipe=%d: has mode?=%d\n", __FUNCTION__, - sna_crtc->id, sna_crtc->pipe, + __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc), mode.mode_valid && mode.mode.clock)); if (!mode.mode_valid || mode.mode.clock == 0) @@ -5593,6 +6717,7 @@ static bool sna_probe_initial_configuration(struct sna *sna) crtc->desiredX = mode.x; crtc->desiredY = mode.y; crtc->desiredTransformPresent = FALSE; + crtc_active++; } /* Reconstruct outputs pointing to active CRTC */ @@ -5604,6 +6729,7 @@ static bool sna_probe_initial_configuration(struct sna *sna) crtc_id = (uintptr_t)output->crtc; output->crtc = NULL; + output->status = XF86OutputStatusUnknown; if (sna->flags & SNA_IS_SLAVED) continue; @@ -5623,7 +6749,7 @@ static bool sna_probe_initial_configuration(struct sna *sna) xf86CrtcPtr crtc = config->crtc[j]; assert(to_sna_crtc(crtc)); - if (to_sna_crtc(crtc)->id != crtc_id) + if (sna_crtc_id(crtc) != crtc_id) continue; if (crtc->desiredMode.status == MODE_OK) { @@ -5641,18 +6767,30 @@ static bool sna_probe_initial_configuration(struct sna *sna) "Output %s using initial mode %s on pipe %d\n", output->name, crtc->desiredMode.name, - to_sna_crtc(crtc)->pipe); + sna_crtc_pipe(crtc)); output->crtc = crtc; + output->status = XF86OutputStatusConnected; crtc->enabled = TRUE; + crtc_enabled++; + + output_set_gamma(output, crtc); + + if (output->conf_monitor) { + output->mm_width = output->conf_monitor->mon_width; + output->mm_height = output->conf_monitor->mon_height; + } + +#if 0 + sna_output_attach_edid(output); + sna_output_attach_tile(output); +#endif if (output->mm_width == 0 || output->mm_height == 0) { output->mm_height = (crtc->desiredMode.VDisplay * 254) / (10*DEFAULT_DPI); output->mm_width = (crtc->desiredMode.HDisplay * 254) / (10*DEFAULT_DPI); } - output_set_gamma(output, crtc); - M = calloc(1, sizeof(DisplayModeRec)); if (M) { *M = crtc->desiredMode; @@ -5673,6 +6811,12 @@ static bool sna_probe_initial_configuration(struct sna *sna) } } + if (crtc_active != crtc_enabled) { + DBG(("%s: only enabled %d out of %d active CRTC, forcing a reconfigure\n", + __FUNCTION__, crtc_enabled, crtc_active)); + return false; + } + width = height = 0; for (i = 0; i < sna->mode.num_real_crtc; i++) { xf86CrtcPtr crtc = config->crtc[i]; @@ -5707,8 +6851,8 @@ static bool sna_probe_initial_configuration(struct sna *sna) if (sna_output->num_modes == 0) continue; - width = sna_output->modes[0].hdisplay; - height= sna_output->modes[0].vdisplay; + width = sna_output->modes[0].hdisplay; + height = sna_output->modes[0].vdisplay; DBG(("%s: panel '%s' is %dx%d\n", __FUNCTION__, output->name, width, height)); @@ -5788,7 +6932,7 @@ probe_capabilities(struct sna *sna) sna->flags &= ~(SNA_HAS_FLIP | SNA_HAS_ASYNC_FLIP); if (has_flip(sna)) sna->flags |= SNA_HAS_FLIP; - if (has_flip__async(sna)) + if (has_flip__async(sna) && (sna->flags & SNA_TEAR_FREE) == 0) sna->flags |= SNA_HAS_ASYNC_FLIP; DBG(("%s: page flips? %s, async? %s\n", __FUNCTION__, sna->flags & SNA_HAS_FLIP ? "enabled" : "disabled", @@ -5814,6 +6958,7 @@ sna_crtc_config_notify(ScreenPtr screen) } update_flush_interval(sna); + sna->cursor.disable = false; /* Reset HW cursor until the next fail */ sna_cursors_reload(sna); probe_capabilities(sna); @@ -5840,6 +6985,7 @@ bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna) } probe_capabilities(sna); + sna->mode.hidden = 1; if (!xf86GetOptValInteger(sna->Options, OPTION_VIRTUAL, &num_fake)) num_fake = 1; @@ -5855,6 +7001,9 @@ bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna) if (res) { xf86CrtcConfigPtr xf86_config; + DBG(("%s: found %d CRTC, %d encoders, %d connectors\n", + __FUNCTION__, res->count_crtcs, res->count_encoders, res->count_connectors)); + assert(res->count_crtcs); assert(res->count_connectors); @@ -5862,6 +7011,7 @@ bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna) xf86_config = XF86_CRTC_CONFIG_PTR(scrn); xf86_config->xf86_crtc_notify = sna_crtc_config_notify; + xf86_config->compat_output = 0; for (i = 0; i < res->count_crtcs; i++) if (!sna_crtc_add(scrn, res->crtcs[i])) @@ -5912,6 +7062,7 @@ bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna) } } sort_config_outputs(sna); + TimerSet(NULL, 0, COLDPLUG_DELAY_MS, sna_mode_coldplug, sna); sna_setup_provider(scrn); return scrn->modes != NULL; @@ -5955,7 +7106,7 @@ sna_mode_set_primary(struct sna *sna) DBG(("%s: setting PrimaryOutput %s\n", __FUNCTION__, output->name)); rr->primaryOutput = output->randr_output; - RROutputChanged(rr->primaryOutput, 0); + RROutputChanged(rr->primaryOutput, FALSE); rr->layoutChanged = TRUE; break; } @@ -5974,12 +7125,9 @@ sna_mode_disable(struct sna *sna) if (!sna->scrn->vtSema) return false; - /* XXX we will cause previously hidden cursors to be reshown, but - * this should be a rare fixup case for severe fragmentation. - */ - sna_hide_cursors(sna->scrn); + sna_disable_cursors(sna->scrn); for (i = 0; i < sna->mode.num_real_crtc; i++) - sna_crtc_disable(config->crtc[i]); + sna_crtc_disable(config->crtc[i], false); assert(sna->mode.front_active == 0); sna_mode_wakeup(sna); @@ -6001,6 +7149,11 @@ sna_mode_enable(struct sna *sna) if (!sna->scrn->vtSema) return; + if (sna->mode.hidden) { + DBG(("%s: hidden outputs\n", __FUNCTION__)); + return; + } + for (i = 0; i < sna->mode.num_real_crtc; i++) { xf86CrtcPtr crtc = config->crtc[i]; @@ -6016,13 +7169,30 @@ sna_mode_enable(struct sna *sna) } update_flush_interval(sna); - sna_show_cursors(sna->scrn); + sna_cursors_reload(sna); sna->mode.dirty = false; } +static void sna_randr_close(struct sna *sna) +{ + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); + int n; + + /* The RR structs are freed early during CloseScreen as they + * are tracked as Resources. However, we may be tempted to + * access them during shutdown so decouple them now. + */ + for (n = 0; n < config->num_output; n++) + config->output[n]->randr_output = NULL; + + for (n = 0; n < config->num_crtc; n++) + config->crtc[n]->randr_crtc = NULL; +} + void sna_mode_close(struct sna *sna) { + sna_randr_close(sna); sna_mode_wakeup(sna); if (sna->flags & SNA_IS_HOSTED) @@ -6077,15 +7247,22 @@ xf86CrtcPtr sna_covering_crtc(struct sna *sna, const BoxRec *box, xf86CrtcPtr desired) { xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); - xf86CrtcPtr best_crtc; - int best_coverage, c; + xf86CrtcPtr best_crtc = NULL; + int best_coverage = -1, c; if (sna->flags & SNA_IS_HOSTED) return NULL; /* If we do not own the VT, we do not own the CRTC either */ - if (!sna->scrn->vtSema) + if (!sna->scrn->vtSema) { + DBG(("%s: none, VT switched\n", __FUNCTION__)); + return NULL; + } + + if (sna->mode.hidden) { + DBG(("%s: none, hidden outputs\n", __FUNCTION__)); return NULL; + } DBG(("%s for box=(%d, %d), (%d, %d)\n", __FUNCTION__, box->x1, box->y1, box->x2, box->y2)); @@ -6107,10 +7284,10 @@ sna_covering_crtc(struct sna *sna, const BoxRec *box, xf86CrtcPtr desired) cover_box.x2, cover_box.y2)); return desired; } + best_crtc = desired; + best_coverage = 0; } - best_crtc = NULL; - best_coverage = 0; for (c = 0; c < sna->mode.num_real_crtc; c++) { xf86CrtcPtr crtc = config->crtc[c]; BoxRec cover_box; @@ -6156,6 +7333,21 @@ sna_covering_crtc(struct sna *sna, const BoxRec *box, xf86CrtcPtr desired) return best_crtc; } +xf86CrtcPtr sna_primary_crtc(struct sna *sna) +{ + rrScrPrivPtr rr = rrGetScrPriv(xf86ScrnToScreen(sna->scrn)); + if (rr && rr->primaryOutput) { + xf86OutputPtr output = rr->primaryOutput->devPrivate; + if (output->crtc && to_sna_crtc(output->crtc)) + return output->crtc; + } + + if (sna->mode.num_real_crtc) + return XF86_CRTC_CONFIG_PTR(sna->scrn)->crtc[0]; + + return NULL; +} + #define MI_LOAD_REGISTER_IMM (0x22<<23) static bool sna_emit_wait_for_scanline_hsw(struct sna *sna, @@ -6433,7 +7625,7 @@ sna_wait_for_scanline(struct sna *sna, y2 /= 2; } - pipe = sna_crtc_to_pipe(crtc); + pipe = sna_crtc_pipe(crtc); DBG(("%s: pipe=%d, y1=%d, y2=%d, full_height?=%d\n", __FUNCTION__, pipe, y1, y2, full_height)); @@ -6457,19 +7649,198 @@ sna_wait_for_scanline(struct sna *sna, return ret; } -void sna_mode_check(struct sna *sna) +static bool sna_mode_shutdown_crtc(xf86CrtcPtr crtc) +{ + struct sna *sna = to_sna(crtc->scrn); + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(crtc->scrn); + bool disabled = false; + int o; + + xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, + "%s: invalid state found on pipe %d, disabling CRTC:%d\n", + __FUNCTION__, + __sna_crtc_pipe(to_sna_crtc(crtc)), + __sna_crtc_id(to_sna_crtc(crtc))); + sna_crtc_disable(crtc, true); +#if XF86_CRTC_VERSION >= 3 + crtc->active = FALSE; +#endif + if (crtc->enabled) { + crtc->enabled = FALSE; + disabled = true; + } + + for (o = 0; o < sna->mode.num_real_output; o++) { + xf86OutputPtr output = config->output[o]; + + if (output->crtc != crtc) + continue; + + output->funcs->dpms(output, DPMSModeOff); + output->crtc = NULL; + } + + return disabled; +} + +static xf86CrtcPtr +lookup_crtc_by_id(struct sna *sna, int id) { xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); + int c; + + for (c = 0; c < sna->mode.num_real_crtc; c++) { + xf86CrtcPtr crtc = config->crtc[c]; + if (__sna_crtc_id(to_sna_crtc(crtc)) == id) + return crtc; + } + + return NULL; +} + +static int plane_type(struct sna *sna, int id) +{ + struct local_mode_obj_get_properties arg; + uint64_t stack_props[24]; + uint32_t *props = (uint32_t *)stack_props; + uint64_t *values = stack_props + 8; + int i, type = -1; + + memset(&arg, 0, sizeof(struct local_mode_obj_get_properties)); + arg.obj_id = id; + arg.obj_type = LOCAL_MODE_OBJECT_PLANE; + + arg.props_ptr = (uintptr_t)props; + arg.prop_values_ptr = (uintptr_t)values; + arg.count_props = 16; + + if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_OBJ_GETPROPERTIES, &arg)) + return -1; + + DBG(("%s: object %d (type %x) has %d props\n", __FUNCTION__, + id, LOCAL_MODE_OBJECT_PLANE, arg.count_props)); + + if (arg.count_props > 16) { + props = malloc(2*sizeof(uint64_t)*arg.count_props); + if (props == NULL) + return -1; + + values = (uint64_t *)props + arg.count_props; + + arg.props_ptr = (uintptr_t)props; + arg.prop_values_ptr = (uintptr_t)values; + + if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_OBJ_GETPROPERTIES, &arg)) + arg.count_props = 0; + } + VG(VALGRIND_MAKE_MEM_DEFINED(arg.props_ptr, sizeof(uint32_t)*arg.count_props)); + VG(VALGRIND_MAKE_MEM_DEFINED(arg.prop_values_ptr, sizeof(uint64_t)*arg.count_props)); + + for (i = 0; i < arg.count_props; i++) { + struct drm_mode_get_property prop; + + memset(&prop, 0, sizeof(prop)); + prop.prop_id = props[i]; + if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETPROPERTY, &prop)) { + ERR(("%s: prop[%d].id=%d GETPROPERTY failed with errno=%d\n", + __FUNCTION__, i, props[i], errno)); + continue; + } + + DBG(("%s: prop[%d] .id=%ld, .name=%s, .flags=%x, .value=%ld\n", __FUNCTION__, i, + (long)props[i], prop.name, (unsigned)prop.flags, (long)values[i])); + + if (strcmp(prop.name, "type") == 0) { + type = values[i]; + break; + } + } + + if (props != (uint32_t *)stack_props) + free(props); + + return type; +} + +static bool +sna_mode_disable_secondary_planes(struct sna *sna) +{ + struct local_mode_get_plane_res r; + uint32_t stack_planes[64]; + uint32_t *planes = stack_planes; + bool disabled = false; int i; + VG_CLEAR(r); + r.plane_id_ptr = (uintptr_t)planes; + r.count_planes = ARRAY_SIZE(stack_planes); + if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_GETPLANERESOURCES, &r)) + return false; + + DBG(("%s: %d planes\n", __FUNCTION__, (int)r.count_planes)); + + if (r.count_planes > ARRAY_SIZE(stack_planes)) { + planes = malloc(sizeof(uint32_t)*r.count_planes); + if (planes == NULL) + return false; + + r.plane_id_ptr = (uintptr_t)planes; + if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_GETPLANERESOURCES, &r)) + r.count_planes = 0; + } + + VG(VALGRIND_MAKE_MEM_DEFINED(planes, sizeof(uint32_t)*r.count_planes)); + + for (i = 0; i < r.count_planes; i++) { + struct local_mode_get_plane p; + struct local_mode_set_plane s; + + VG_CLEAR(p); + p.plane_id = planes[i]; + p.count_format_types = 0; + if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_GETPLANE, &p)) + continue; + + if (p.fb_id == 0 || p.crtc_id == 0) + continue; + + if (plane_type(sna, p.plane_id) == DRM_PLANE_TYPE_PRIMARY) + continue; + + memset(&s, 0, sizeof(s)); + s.plane_id = p.plane_id; + s.crtc_id = p.crtc_id; + if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_MODE_SETPLANE, &s)) { + xf86CrtcPtr crtc = lookup_crtc_by_id(sna, p.crtc_id); + if (crtc) + disabled |= sna_mode_shutdown_crtc(crtc); + } + } + + if (planes != stack_planes) + free(planes); + + return disabled; +} + +void sna_mode_check(struct sna *sna) +{ + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); + bool disabled; + int c, o; + if (sna->flags & SNA_IS_HOSTED) return; - DBG(("%s\n", __FUNCTION__)); + DBG(("%s: hidden?=%d\n", __FUNCTION__, sna->mode.hidden)); + if (sna->mode.hidden) + return; + + disabled = sna_mode_disable_secondary_planes(sna); /* Validate CRTC attachments and force consistency upon the kernel */ - for (i = 0; i < sna->mode.num_real_crtc; i++) { - xf86CrtcPtr crtc = config->crtc[i]; + for (c = 0; c < sna->mode.num_real_crtc; c++) { + xf86CrtcPtr crtc = config->crtc[c]; struct sna_crtc *sna_crtc = to_sna_crtc(crtc); struct drm_mode_crtc mode; uint32_t expected[2]; @@ -6483,7 +7854,7 @@ void sna_mode_check(struct sna *sna) expected[1] = sna_crtc->flip_bo ? fb_id(sna_crtc->flip_bo) : -1; VG_CLEAR(mode); - mode.crtc_id = sna_crtc->id; + mode.crtc_id = __sna_crtc_id(sna_crtc); if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCRTC, &mode)) continue; @@ -6492,16 +7863,12 @@ void sna_mode_check(struct sna *sna) mode.crtc_id, mode.mode_valid, mode.fb_id, expected[0], expected[1])); - if (mode.fb_id != expected[0] && mode.fb_id != expected[1]) { - xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, - "%s: invalid state found on pipe %d, disabling CRTC:%d\n", - __FUNCTION__, sna_crtc->pipe, sna_crtc->id); - sna_crtc_disable(crtc); - } + if (mode.fb_id != expected[0] && mode.fb_id != expected[1]) + disabled |= sna_mode_shutdown_crtc(crtc); } - for (i = 0; i < config->num_output; i++) { - xf86OutputPtr output = config->output[i]; + for (o = 0; o < config->num_output; o++) { + xf86OutputPtr output = config->output[o]; struct sna_output *sna_output; if (output->crtc) @@ -6515,26 +7882,15 @@ void sna_mode_check(struct sna *sna) } update_flush_interval(sna); + + if (disabled) + xf86RandR12TellChanged(xf86ScrnToScreen(sna->scrn)); } static bool sna_crtc_hide_planes(struct sna *sna, struct sna_crtc *crtc) { -#define LOCAL_IOCTL_MODE_SETPLANE DRM_IOWR(0xB7, struct local_mode_set_plane) - struct local_mode_set_plane { - uint32_t plane_id; - uint32_t crtc_id; - uint32_t fb_id; /* fb object contains surface format type */ - uint32_t flags; - - /* Signed dest location allows it to be partially off screen */ - int32_t crtc_x, crtc_y; - uint32_t crtc_w, crtc_h; - - /* Source values are 16.16 fixed point */ - uint32_t src_x, src_y; - uint32_t src_h, src_w; - } s; + struct local_mode_set_plane s; if (crtc->primary.id == 0) return false; @@ -6561,17 +7917,16 @@ void sna_mode_reset(struct sna *sna) DBG(("%s\n", __FUNCTION__)); - sna_hide_cursors(sna->scrn); + sna_disable_cursors(sna->scrn); for (i = 0; i < sna->mode.num_real_crtc; i++) if (!sna_crtc_hide_planes(sna, to_sna_crtc(config->crtc[i]))) - sna_crtc_disable(config->crtc[i]); + sna_crtc_disable(config->crtc[i], true); assert(sna->mode.front_active == 0); for (i = 0; i < sna->mode.num_real_crtc; i++) { struct sna_crtc *sna_crtc = to_sna_crtc(config->crtc[i]); assert(sna_crtc != NULL); - sna_crtc->dpms_mode = -1; /* Force the rotation property to be reset on next use */ rotation_reset(&sna_crtc->primary); @@ -6641,9 +7996,10 @@ sna_crtc_redisplay__fallback(xf86CrtcPtr crtc, RegionPtr region, struct kgem_bo { int16_t sx, sy; struct sna *sna = to_sna(crtc->scrn); - ScreenPtr screen = sna->scrn->pScreen; + ScreenPtr screen = xf86ScrnToScreen(crtc->scrn); DrawablePtr draw = crtc_source(crtc, &sx, &sy); PictFormatPtr format; + PictTransform T; PicturePtr src, dst; PixmapPtr pixmap; int depth, error; @@ -6664,6 +8020,14 @@ sna_crtc_redisplay__fallback(xf86CrtcPtr crtc, RegionPtr region, struct kgem_bo __FUNCTION__, format->format, depth, draw->bitsPerPixel, bo->pitch, crtc->mode.HDisplay, crtc->mode.VDisplay)); + if (sx | sy) + RegionTranslate(region, sx, sy); + error = !sna_drawable_move_region_to_cpu(draw, region, MOVE_READ); + if (sx | sy) + RegionTranslate(region, -sx, -sy); + if (error) + return; + ptr = kgem_bo_map__gtt(&sna->kgem, bo); if (ptr == NULL) return; @@ -6683,9 +8047,37 @@ sna_crtc_redisplay__fallback(xf86CrtcPtr crtc, RegionPtr region, struct kgem_bo if (!src) goto free_pixmap; - error = SetPictureTransform(src, &crtc->crtc_to_framebuffer); - if (error) - goto free_src; + pixman_transform_init_translate(&T, sx << 16, sy << 16); + pixman_transform_multiply(&T, &T, &crtc->crtc_to_framebuffer); + if (!sna_transform_is_integer_translation(&T, &sx, &sy)) { +#define f2d(x) (((double)(x))/65536.) + DBG(("%s: transform=[[%f %f %f], [%f %f %f], [%f %f %f]] (raw [[%x %x %x], [%x %x %x], [%x %x %x]])\n", + __FUNCTION__, + f2d(T.matrix[0][0]), + f2d(T.matrix[0][1]), + f2d(T.matrix[0][2]), + f2d(T.matrix[1][0]), + f2d(T.matrix[1][1]), + f2d(T.matrix[1][2]), + f2d(T.matrix[2][0]), + f2d(T.matrix[2][1]), + f2d(T.matrix[2][2]), + T.matrix[0][0], + T.matrix[0][1], + T.matrix[0][2], + T.matrix[1][0], + T.matrix[1][1], + T.matrix[1][2], + T.matrix[2][0], + T.matrix[2][1], + T.matrix[2][2])); +#undef f2d + + error = SetPictureTransform(src, &T); + if (error) + goto free_src; + sx = sy = 0; + } if (crtc->filter && crtc->transform_in_use) SetPicturePictFilter(src, crtc->filter, @@ -6733,10 +8125,11 @@ sna_crtc_redisplay__composite(xf86CrtcPtr crtc, RegionPtr region, struct kgem_bo { int16_t sx, sy; struct sna *sna = to_sna(crtc->scrn); - ScreenPtr screen = crtc->scrn->pScreen; + ScreenPtr screen = xf86ScrnToScreen(crtc->scrn); DrawablePtr draw = crtc_source(crtc, &sx, &sy); struct sna_composite_op tmp; PictFormatPtr format; + PictTransform T; PicturePtr src, dst; PixmapPtr pixmap; const BoxRec *b; @@ -6777,9 +8170,14 @@ sna_crtc_redisplay__composite(xf86CrtcPtr crtc, RegionPtr region, struct kgem_bo if (!src) goto free_pixmap; - error = SetPictureTransform(src, &crtc->crtc_to_framebuffer); - if (error) - goto free_src; + pixman_transform_init_translate(&T, sx << 16, sy << 16); + pixman_transform_multiply(&T, &T, &crtc->crtc_to_framebuffer); + if (!sna_transform_is_integer_translation(&T, &sx, &sy)) { + error = SetPictureTransform(src, &T); + if (error) + goto free_src; + sx = sy = 0; + } if (crtc->filter && crtc->transform_in_use) SetPicturePictFilter(src, crtc->filter, @@ -6839,7 +8237,7 @@ sna_crtc_redisplay(xf86CrtcPtr crtc, RegionPtr region, struct kgem_bo *bo) struct sna_pixmap *priv = sna_pixmap((PixmapPtr)draw); DBG(("%s: crtc %d [pipe=%d], damage (%d, %d), (%d, %d) x %d\n", - __FUNCTION__, to_sna_crtc(crtc)->id, to_sna_crtc(crtc)->pipe, + __FUNCTION__, sna_crtc_id(crtc), sna_crtc_pipe(crtc), region->extents.x1, region->extents.y1, region->extents.x2, region->extents.y2, region_num_rects(region))); @@ -6908,18 +8306,21 @@ void sna_shadow_set_crtc(struct sna *sna, struct sna_crtc *sna_crtc = to_sna_crtc(crtc); struct sna_pixmap *priv; + assert(sna_crtc); DBG(("%s: setting shadow override for CRTC:%d to handle=%d\n", - __FUNCTION__, sna_crtc->id, bo->handle)); + __FUNCTION__, __sna_crtc_id(sna_crtc), bo->handle)); assert(sna->flags & SNA_TEAR_FREE); - assert(sna_crtc); assert(!sna_crtc->transform); if (sna_crtc->client_bo != bo) { - if (sna_crtc->client_bo) + if (sna_crtc->client_bo) { + assert(sna_crtc->client_bo->refcnt > sna_crtc->client_bo->active_scanout); kgem_bo_destroy(&sna->kgem, sna_crtc->client_bo); + } sna_crtc->client_bo = kgem_bo_reference(bo); + assert(sna_crtc->client_bo->refcnt > sna_crtc->client_bo->active_scanout); sna_crtc_damage(crtc); } @@ -6969,11 +8370,12 @@ void sna_shadow_unset_crtc(struct sna *sna, struct sna_crtc *sna_crtc = to_sna_crtc(crtc); DBG(("%s: clearin shadow override for CRTC:%d\n", - __FUNCTION__, sna_crtc->id)); + __FUNCTION__, __sna_crtc_id(sna_crtc))); if (sna_crtc->client_bo == NULL) return; + assert(sna_crtc->client_bo->refcnt > sna_crtc->client_bo->active_scanout); kgem_bo_destroy(&sna->kgem, sna_crtc->client_bo); sna_crtc->client_bo = NULL; list_del(&sna_crtc->shadow_link); @@ -6982,15 +8384,50 @@ void sna_shadow_unset_crtc(struct sna *sna, sna_crtc_damage(crtc); } +static bool move_crtc_to_gpu(struct sna *sna) +{ + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); + int i; + + for (i = 0; i < sna->mode.num_real_crtc; i++) { + struct sna_crtc *crtc = to_sna_crtc(config->crtc[i]); + + assert(crtc); + + if (crtc->bo == NULL) + continue; + + if (crtc->slave_pixmap) + continue; + + if (crtc->client_bo) + continue; + + DBG(("%s: CRTC %d [pipe=%d] requires frontbuffer\n", + __FUNCTION__, __sna_crtc_id(crtc), __sna_crtc_pipe(crtc))); + return sna_pixmap_move_to_gpu(sna->front, + MOVE_READ | MOVE_ASYNC_HINT | __MOVE_SCANOUT); + } + + return true; +} + void sna_mode_redisplay(struct sna *sna) { xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); RegionPtr region; int i; - if (!sna->mode.shadow_damage) + if (sna->mode.hidden) { + DBG(("%s: hidden outputs, skipping\n", __FUNCTION__)); + return; + } + + if (!sna->mode.shadow_enabled) return; + assert(sna->mode.shadow_damage); + DBG(("%s: posting shadow damage? %d (flips pending? %d, mode reconfiguration pending? %d)\n", __FUNCTION__, !RegionNil(DamageRegion(sna->mode.shadow_damage)), @@ -7012,21 +8449,23 @@ void sna_mode_redisplay(struct sna *sna) region->extents.x2, region->extents.y2)); if (sna->mode.flip_active) { - DamagePtr damage; - - damage = sna->mode.shadow_damage; - sna->mode.shadow_damage = NULL; + DBG(("%s: checking for %d outstanding flip completions\n", + __FUNCTION__, sna->mode.flip_active)); + sna->mode.dirty = true; while (sna->mode.flip_active && sna_mode_wakeup(sna)) ; + sna->mode.dirty = false; - sna->mode.shadow_damage = damage; + DBG(("%s: now %d outstanding flip completions (enabled? %d)\n", + __FUNCTION__, + sna->mode.flip_active, + sna->mode.shadow_enabled)); + if (sna->mode.flip_active || !sna->mode.shadow_enabled) + return; } - if (sna->mode.flip_active) - return; - - if (wedged(sna) || !sna_pixmap_move_to_gpu(sna->front, MOVE_READ | MOVE_ASYNC_HINT | __MOVE_SCANOUT)) { + if (wedged(sna) || !move_crtc_to_gpu(sna)) { DBG(("%s: forcing scanout update using the CPU\n", __FUNCTION__)); if (!sna_pixmap_move_to_cpu(sna->front, MOVE_READ)) return; @@ -7050,7 +8489,7 @@ void sna_mode_redisplay(struct sna *sna) struct kgem_bo *bo = NULL; DBG(("%s: fallback intersects pipe=%d [(%d, %d), (%d, %d)]\n", - __FUNCTION__, sna_crtc->pipe, + __FUNCTION__, __sna_crtc_pipe(sna_crtc), damage.extents.x1, damage.extents.y1, damage.extents.x2, damage.extents.y2)); @@ -7060,7 +8499,7 @@ void sna_mode_redisplay(struct sna *sna) RegionNull(&new_damage); RegionCopy(&new_damage, &damage); - bo = sna_crtc->client_bo; + bo = sna_crtc->cache_bo; if (bo == NULL) { damage.extents = crtc->bounds; damage.data = NULL; @@ -7085,7 +8524,7 @@ void sna_mode_redisplay(struct sna *sna) if (bo != sna_crtc->bo) { struct drm_mode_crtc_page_flip arg; - arg.crtc_id = sna_crtc->id; + arg.crtc_id = __sna_crtc_id(sna_crtc); arg.fb_id = get_fb(sna, bo, crtc->mode.HDisplay, crtc->mode.VDisplay); @@ -7096,6 +8535,9 @@ void sna_mode_redisplay(struct sna *sna) if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_PAGE_FLIP, &arg)) { if (sna_crtc_flip(sna, sna_crtc, bo, 0, 0)) { + DBG(("%s: removing handle=%d [active_scanout=%d] from scanout, installing handle=%d [active_scanout=%d]\n", + __FUNCTION__, sna_crtc->bo->handle, sna_crtc->bo->active_scanout, + bo->handle, bo->active_scanout)); assert(sna_crtc->bo->active_scanout); assert(sna_crtc->bo->refcnt >= sna_crtc->bo->active_scanout); sna_crtc->bo->active_scanout--; @@ -7103,10 +8545,10 @@ void sna_mode_redisplay(struct sna *sna) sna_crtc->bo = bo; sna_crtc->bo->active_scanout++; - sna_crtc->client_bo = NULL; + sna_crtc->cache_bo = NULL; } else { DBG(("%s: flip [fb=%d] on crtc %d [%d, pipe=%d] failed - %d\n", - __FUNCTION__, arg.fb_id, i, sna_crtc->id, sna_crtc->pipe, errno)); + __FUNCTION__, arg.fb_id, i, __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc), errno)); xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR, "Page flipping failed, disabling TearFree\n"); sna->flags &= ~SNA_TEAR_FREE; @@ -7116,7 +8558,7 @@ void sna_mode_redisplay(struct sna *sna) sna_crtc_redisplay__fallback(crtc, &damage, sna_crtc->bo); kgem_bo_destroy(&sna->kgem, bo); - sna_crtc->client_bo = NULL; + sna_crtc->cache_bo = NULL; } } else { sna->mode.flip_active++; @@ -7127,8 +8569,12 @@ void sna_mode_redisplay(struct sna *sna) sna_crtc->flip_bo = bo; sna_crtc->flip_bo->active_scanout++; sna_crtc->flip_serial = sna_crtc->mode_serial; + sna_crtc->flip_pending = true; - sna_crtc->client_bo = kgem_bo_reference(sna_crtc->bo); + sna_crtc->cache_bo = kgem_bo_reference(sna_crtc->bo); + + DBG(("%s: recording flip on CRTC:%d handle=%d, active_scanout=%d, serial=%d\n", + __FUNCTION__, __sna_crtc_id(sna_crtc), sna_crtc->flip_bo->handle, sna_crtc->flip_bo->active_scanout, sna_crtc->flip_serial)); } } } @@ -7201,7 +8647,7 @@ void sna_mode_redisplay(struct sna *sna) damage.extents = crtc->bounds; damage.data = NULL; - bo = sna_crtc->client_bo; + bo = sna_crtc->cache_bo; if (bo == NULL) bo = kgem_create_2d(&sna->kgem, crtc->mode.HDisplay, @@ -7210,12 +8656,12 @@ void sna_mode_redisplay(struct sna *sna) sna_crtc->bo->tiling, CREATE_SCANOUT); if (bo == NULL) - goto disable1; + continue; sna_crtc_redisplay(crtc, &damage, bo); kgem_bo_submit(&sna->kgem, bo); - arg.crtc_id = sna_crtc->id; + arg.crtc_id = __sna_crtc_id(sna_crtc); arg.fb_id = get_fb(sna, bo, crtc->mode.HDisplay, crtc->mode.VDisplay); @@ -7228,6 +8674,9 @@ void sna_mode_redisplay(struct sna *sna) if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_PAGE_FLIP, &arg)) { if (sna_crtc_flip(sna, sna_crtc, bo, 0, 0)) { + DBG(("%s: removing handle=%d [active_scanout=%d] from scanout, installing handle=%d [active_scanout=%d]\n", + __FUNCTION__, sna_crtc->bo->handle, sna_crtc->bo->active_scanout - 1, + bo->handle, bo->active_scanout)); assert(sna_crtc->bo->active_scanout); assert(sna_crtc->bo->refcnt >= sna_crtc->bo->active_scanout); sna_crtc->bo->active_scanout--; @@ -7235,13 +8684,13 @@ void sna_mode_redisplay(struct sna *sna) sna_crtc->bo = kgem_bo_reference(bo); sna_crtc->bo->active_scanout++; - sna_crtc->client_bo = kgem_bo_reference(bo); + sna_crtc->cache_bo = kgem_bo_reference(bo); } else { BoxRec box; DrawableRec tmp; DBG(("%s: flip [fb=%d] on crtc %d [%d, pipe=%d] failed - %d\n", - __FUNCTION__, arg.fb_id, i, sna_crtc->id, sna_crtc->pipe, errno)); + __FUNCTION__, arg.fb_id, i, __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc), errno)); xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR, "Page flipping failed, disabling TearFree\n"); sna->flags &= ~SNA_TEAR_FREE; @@ -7260,12 +8709,12 @@ disable1: &box, 1, COPY_LAST)) { xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, "%s: page flipping failed, disabling CRTC:%d (pipe=%d)\n", - __FUNCTION__, sna_crtc->id, sna_crtc->pipe); - sna_crtc_disable(crtc); + __FUNCTION__, __sna_crtc_id(sna_crtc), __sna_crtc_pipe(sna_crtc)); + sna_crtc_disable(crtc, false); } kgem_bo_destroy(&sna->kgem, bo); - sna_crtc->client_bo = NULL; + sna_crtc->cache_bo = NULL; } continue; } @@ -7279,7 +8728,9 @@ disable1: sna_crtc->flip_serial = sna_crtc->mode_serial; sna_crtc->flip_pending = true; - sna_crtc->client_bo = kgem_bo_reference(sna_crtc->bo); + sna_crtc->cache_bo = kgem_bo_reference(sna_crtc->bo); + DBG(("%s: recording flip on CRTC:%d handle=%d, active_scanout=%d, serial=%d\n", + __FUNCTION__, __sna_crtc_id(sna_crtc), sna_crtc->flip_bo->handle, sna_crtc->flip_bo->active_scanout, sna_crtc->flip_serial)); } else { sna_crtc_redisplay(crtc, &damage, sna_crtc->bo); kgem_scanout_flush(&sna->kgem, sna_crtc->bo); @@ -7315,20 +8766,19 @@ disable1: assert(crtc != NULL); DBG(("%s: crtc %d [%d, pipe=%d] active? %d, transformed? %d\n", - __FUNCTION__, i, crtc->id, crtc->pipe, crtc->bo ? crtc->bo->handle : 0, crtc->transform)); + __FUNCTION__, i, __sna_crtc_id(crtc), __sna_crtc_pipe(crtc), crtc->bo ? crtc->bo->handle : 0, crtc->transform)); if (crtc->bo == NULL || crtc->transform) continue; assert(config->crtc[i]->enabled); - assert(crtc->dpms_mode <= DPMSModeOn); assert(crtc->flip_bo == NULL); - arg.crtc_id = crtc->id; + arg.crtc_id = __sna_crtc_id(crtc); arg.user_data = (uintptr_t)crtc; if (crtc->client_bo) { DBG(("%s: apply shadow override bo for CRTC:%d on pipe=%d, handle=%d\n", - __FUNCTION__, crtc->id, crtc->pipe, crtc->client_bo->handle)); + __FUNCTION__, __sna_crtc_id(crtc), __sna_crtc_pipe(crtc), crtc->client_bo->handle)); arg.fb_id = get_fb(sna, crtc->client_bo, crtc->base->mode.HDisplay, crtc->base->mode.VDisplay); @@ -7365,8 +8815,12 @@ fixup_shadow: y = crtc->base->y; } - if (crtc->bo == flip_bo) + if (crtc->bo == flip_bo) { + assert(crtc->bo->refcnt >= crtc->bo->active_scanout); + DBG(("%s: flip handle=%d is already on the CRTC\n", + __FUNCTION__, flip_bo->handle)); continue; + } if (flip_bo->pitch != crtc->bo->pitch || (y << 16 | x) != crtc->offset) { DBG(("%s: changing pitch (new %d =?= old %d) or offset (new %x =?= old %x)\n", @@ -7375,6 +8829,9 @@ fixup_shadow: y << 16 | x, crtc->offset)); fixup_flip: if (sna_crtc_flip(sna, crtc, flip_bo, x, y)) { + DBG(("%s: removing handle=%d [active_scanout=%d] from scanout, installing handle=%d [active_scanout=%d]\n", + __FUNCTION__, crtc->bo->handle, crtc->bo->active_scanout-1, + flip_bo->handle, flip_bo->active_scanout)); assert(flip_bo != crtc->bo); assert(crtc->bo->active_scanout); assert(crtc->bo->refcnt >= crtc->bo->active_scanout); @@ -7400,15 +8857,15 @@ fixup_flip: xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR, "%s: page flipping failed, disabling CRTC:%d (pipe=%d)\n", - __FUNCTION__, crtc->id, crtc->pipe); - sna_crtc_disable(crtc->base); + __FUNCTION__, __sna_crtc_id(crtc), __sna_crtc_pipe(crtc)); + sna_crtc_disable(crtc->base, false); } continue; } if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_PAGE_FLIP, &arg)) { ERR(("%s: flip [fb=%d] on crtc %d [%d, pipe=%d] failed - %d\n", - __FUNCTION__, arg.fb_id, i, crtc->id, crtc->pipe, errno)); + __FUNCTION__, arg.fb_id, i, __sna_crtc_id(crtc), __sna_crtc_pipe(crtc), errno)); goto fixup_flip; } sna->mode.flip_active++; @@ -7421,6 +8878,9 @@ fixup_flip: crtc->flip_serial = crtc->mode_serial; crtc->flip_pending = true; + DBG(("%s: recording flip on CRTC:%d handle=%d, active_scanout=%d, serial=%d\n", + __FUNCTION__, __sna_crtc_id(crtc), crtc->flip_bo->handle, crtc->flip_bo->active_scanout, crtc->flip_serial)); + { struct drm_i915_gem_busy busy = { flip_bo->handle }; if (drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GEM_BUSY, &busy) == 0) { @@ -7495,13 +8955,19 @@ again: { struct drm_event_vblank *vbl = (struct drm_event_vblank *)e; struct sna_crtc *crtc = (void *)(uintptr_t)vbl->user_data; + uint64_t msc; /* Beware Zaphod! */ sna = to_sna(crtc->base->scrn); - crtc->swap.tv_sec = vbl->tv_sec; - crtc->swap.tv_usec = vbl->tv_usec; - crtc->swap.msc = msc64(crtc, vbl->sequence); + if (msc64(crtc, vbl->sequence, &msc)) { + DBG(("%s: recording last swap on pipe=%d, frame %d [%08llx], time %d.%06d\n", + __FUNCTION__, __sna_crtc_pipe(crtc), vbl->sequence, (long long)msc, vbl->tv_sec, vbl->tv_usec)); + crtc->swap.tv_sec = vbl->tv_sec; + crtc->swap.tv_usec = vbl->tv_usec; + crtc->swap.msc = msc; + } + assert(crtc->flip_pending); crtc->flip_pending = false; assert(crtc->flip_bo); @@ -7509,8 +8975,9 @@ again: assert(crtc->flip_bo->refcnt >= crtc->flip_bo->active_scanout); if (crtc->flip_serial == crtc->mode_serial) { - DBG(("%s: removing handle=%d from scanout, installing handle=%d\n", - __FUNCTION__, crtc->bo->handle, crtc->flip_bo->handle)); + DBG(("%s: removing handle=%d [active_scanout=%d] from scanout, installing handle=%d [active_scanout=%d]\n", + __FUNCTION__, crtc->bo->handle, crtc->bo->active_scanout - 1, + crtc->flip_bo->handle, crtc->flip_bo->active_scanout)); assert(crtc->bo->active_scanout); assert(crtc->bo->refcnt >= crtc->bo->active_scanout); crtc->bo->active_scanout--; @@ -7531,8 +8998,10 @@ again: DBG(("%s: flip complete, pending? %d\n", __FUNCTION__, sna->mode.flip_active)); assert(sna->mode.flip_active); - if (--sna->mode.flip_active == 0) + if (--sna->mode.flip_active == 0) { + assert(crtc->flip_handler); crtc->flip_handler(vbl, crtc->flip_data); + } } break; default: diff --git a/src/sna/sna_display_fake.c b/src/sna/sna_display_fake.c index 4d74c38..a07fe0f 100644 --- a/src/sna/sna_display_fake.c +++ b/src/sna/sna_display_fake.c @@ -192,7 +192,7 @@ static const xf86OutputFuncsRec sna_output_funcs = { static Bool sna_mode_resize(ScrnInfoPtr scrn, int width, int height) { - ScreenPtr screen = scrn->pScreen; + ScreenPtr screen = xf86ScrnToScreen(scrn); PixmapPtr new_front; DBG(("%s (%d, %d) -> (%d, %d)\n", __FUNCTION__, @@ -262,6 +262,7 @@ static bool add_fake_output(struct sna *sna, bool late) output->mm_height = 0; output->interlaceAllowed = FALSE; output->subpixel_order = SubPixelNone; + output->status = XF86OutputStatusDisconnected; output->possible_crtcs = ~((1 << sna->mode.num_real_crtc) - 1); output->possible_clones = ~((1 << sna->mode.num_real_output) - 1); diff --git a/src/sna/sna_dri2.c b/src/sna/sna_dri2.c index e5c4d53..33cf3d9 100644 --- a/src/sna/sna_dri2.c +++ b/src/sna/sna_dri2.c @@ -82,12 +82,23 @@ get_private(void *buffer) return (struct sna_dri2_private *)((DRI2Buffer2Ptr)buffer+1); } +pure static inline DRI2BufferPtr sna_pixmap_get_buffer(PixmapPtr pixmap) +{ + assert(pixmap->refcnt); + return ((void **)__get_private(pixmap, sna_pixmap_key))[2]; +} + +static inline void sna_pixmap_set_buffer(PixmapPtr pixmap, void *ptr) +{ + assert(pixmap->refcnt); + ((void **)__get_private(pixmap, sna_pixmap_key))[2] = ptr; +} + #if DRI2INFOREC_VERSION >= 4 enum event_type { WAITMSC = 0, SWAP, - SWAP_WAIT, - SWAP_THROTTLE, + SWAP_COMPLETE, FLIP, FLIP_THROTTLE, FLIP_COMPLETE, @@ -98,6 +109,7 @@ struct dri_bo { struct list link; struct kgem_bo *bo; uint32_t name; + unsigned flags; }; struct sna_dri2_event { @@ -108,6 +120,8 @@ struct sna_dri2_event { xf86CrtcPtr crtc; int pipe; bool queued; + bool sync; + bool chained; /* for swaps & flips only */ DRI2SwapEventPtr event_complete; @@ -116,35 +130,145 @@ struct sna_dri2_event { DRI2BufferPtr back; struct kgem_bo *bo; + struct copy { + struct kgem_bo *bo; + unsigned flags; + uint32_t name; + uint32_t size; + } pending; + struct sna_dri2_event *chain; - struct list cache; struct list link; - int mode; + int flip_continue; + int keepalive; + int signal; }; +#if DRI2INFOREC_VERSION < 10 +#undef USE_ASYNC_SWAP +#endif + +#if USE_ASYNC_SWAP +#define KEEPALIVE 8 /* wait ~100ms before discarding swap caches */ +#define APPLY_DAMAGE 0 +#else +#define USE_ASYNC_SWAP 0 +#define KEEPALIVE 1 +#define APPLY_DAMAGE 1 +#endif + static void sna_dri2_flip_event(struct sna_dri2_event *flip); +inline static DRI2BufferPtr dri2_window_get_front(WindowPtr win); + +static struct kgem_bo * +__sna_dri2_copy_region(struct sna *sna, DrawablePtr draw, RegionPtr region, + DRI2BufferPtr src, DRI2BufferPtr dst, + unsigned flags); + +inline static void +__sna_dri2_copy_event(struct sna_dri2_event *info, unsigned flags) +{ + DBG(("%s: flags = %x\n", __FUNCTION__, flags)); + info->bo = __sna_dri2_copy_region(info->sna, info->draw, NULL, + info->back, info->front, + flags); + info->front->flags = info->back->flags; +} + +static int front_pitch(DrawablePtr draw) +{ + DRI2BufferPtr buffer; + + buffer = NULL; + if (draw->type != DRAWABLE_PIXMAP) + buffer = dri2_window_get_front((WindowPtr)draw); + if (buffer == NULL) + buffer = sna_pixmap_get_buffer(get_drawable_pixmap(draw)); + + return buffer ? buffer->pitch : 0; +} + +struct dri2_window { + DRI2BufferPtr front; + struct sna_dri2_event *chain; + xf86CrtcPtr crtc; + int64_t msc_delta; + struct list cache; + uint32_t cache_size; + int scanout; +}; + +static struct dri2_window *dri2_window(WindowPtr win) +{ + assert(win->drawable.type != DRAWABLE_PIXMAP); + return ((void **)__get_private(win, sna_window_key))[1]; +} + +static bool use_scanout(struct sna *sna, + DrawablePtr draw, + struct dri2_window *priv) +{ + if (priv->front) + return true; + + if (priv->scanout < 0) + priv->scanout = + (sna->flags & (SNA_LINEAR_FB | SNA_NO_WAIT | SNA_NO_FLIP)) == 0 && + draw->width == sna->front->drawable.width && + draw->height == sna->front->drawable.height && + draw->bitsPerPixel == sna->front->drawable.bitsPerPixel; + + return priv->scanout; +} static void sna_dri2_get_back(struct sna *sna, DrawablePtr draw, - DRI2BufferPtr back, - struct sna_dri2_event *info) + DRI2BufferPtr back) { + struct dri2_window *priv = dri2_window((WindowPtr)draw); + uint32_t size; struct kgem_bo *bo; + struct dri_bo *c; uint32_t name; + int flags; bool reuse; - DBG(("%s: draw size=%dx%d, buffer size=%dx%d\n", + DBG(("%s: draw size=%dx%d, back buffer handle=%d size=%dx%d, is-scanout? %d, active?=%d, pitch=%d, front pitch=%d\n", __FUNCTION__, draw->width, draw->height, - get_private(back)->size & 0xffff, get_private(back)->size >> 16)); - reuse = (draw->height << 16 | draw->width) == get_private(back)->size; + get_private(back)->bo->handle, + get_private(back)->size & 0xffff, get_private(back)->size >> 16, + get_private(back)->bo->scanout, + get_private(back)->bo->active_scanout, + back->pitch, front_pitch(draw))); + assert(priv); + + size = draw->height << 16 | draw->width; + if (size != priv->cache_size) { + while (!list_is_empty(&priv->cache)) { + c = list_first_entry(&priv->cache, struct dri_bo, link); + list_del(&c->link); + + DBG(("%s: releasing cached handle=%d\n", __FUNCTION__, c->bo ? c->bo->handle : 0)); + assert(c->bo); + kgem_bo_destroy(&sna->kgem, c->bo); + + free(c); + } + priv->cache_size = size; + } + + reuse = size == get_private(back)->size; + if (reuse) + reuse = get_private(back)->bo->scanout == use_scanout(sna, draw, priv); + DBG(("%s: reuse backbuffer? %d\n", __FUNCTION__, reuse)); if (reuse) { bo = get_private(back)->bo; assert(bo->refcnt); - DBG(("%s: back buffer handle=%d, scanout?=%d, refcnt=%d\n", - __FUNCTION__, bo->handle, bo->active_scanout, get_private(back)->refcnt)); + DBG(("%s: back buffer handle=%d, active?=%d, refcnt=%d\n", + __FUNCTION__, bo->handle, bo->active_scanout, get_private(back)->refcnt)); if (bo->active_scanout == 0) { DBG(("%s: reuse unattached back\n", __FUNCTION__)); get_private(back)->stale = false; @@ -153,24 +277,33 @@ sna_dri2_get_back(struct sna *sna, } bo = NULL; - if (info) { - struct dri_bo *c; - list_for_each_entry(c, &info->cache, link) { - if (c->bo && c->bo->scanout == 0) { - bo = c->bo; - name = c->name; - DBG(("%s: reuse cache handle=%d\n", __FUNCTION__, bo->handle)); - list_move_tail(&c->link, &info->cache); - c->bo = NULL; - } + list_for_each_entry(c, &priv->cache, link) { + DBG(("%s: cache: handle=%d, active=%d\n", + __FUNCTION__, c->bo ? c->bo->handle : 0, c->bo ? c->bo->active_scanout : -1)); + assert(c->bo); + if (c->bo->active_scanout == 0) { + bo = c->bo; + name = c->name; + flags = c->flags; + DBG(("%s: reuse cache handle=%d, name=%d, flags=%d\n", __FUNCTION__, bo->handle, name, flags)); + c->bo = NULL; + _list_del(&c->link); + break; } } if (bo == NULL) { DBG(("%s: allocating new backbuffer\n", __FUNCTION__)); + flags = CREATE_EXACT; + + if (use_scanout(sna, draw, priv)) { + DBG(("%s: requesting scanout compatible back\n", __FUNCTION__)); + flags |= CREATE_SCANOUT; + } + bo = kgem_create_2d(&sna->kgem, draw->width, draw->height, draw->bitsPerPixel, get_private(back)->bo->tiling, - get_private(back)->bo->scanout ? CREATE_SCANOUT : 0); + flags); if (bo == NULL) return; @@ -179,30 +312,42 @@ sna_dri2_get_back(struct sna *sna, kgem_bo_destroy(&sna->kgem, bo); return; } + + flags = 0; + if (USE_ASYNC_SWAP && back->flags) { + BoxRec box; + + box.x1 = 0; + box.y1 = 0; + box.x2 = draw->width; + box.y2 = draw->height; + + DBG(("%s: filling new buffer with old back\n", __FUNCTION__)); + if (sna->render.copy_boxes(sna, GXcopy, + draw, get_private(back)->bo, 0, 0, + draw, bo, 0, 0, + &box, 1, COPY_LAST | COPY_DRI)) + flags = back->flags; + } } assert(bo->active_scanout == 0); - if (info && reuse) { - bool found = false; - struct dri_bo *c; - - list_for_each_entry_reverse(c, &info->cache, link) { - if (c->bo == NULL) { - found = true; - _list_del(&c->link); - break; - } - } - if (!found) + if (reuse && get_private(back)->bo->refcnt == 1 + get_private(back)->bo->active_scanout) { + if (&c->link == &priv->cache) c = malloc(sizeof(*c)); if (c != NULL) { c->bo = ref(get_private(back)->bo); c->name = back->name; - list_add(&c->link, &info->cache); - DBG(("%s: cacheing handle=%d (name=%d)\n", __FUNCTION__, c->bo->handle, c->name)); + c->flags = back->flags; + list_add(&c->link, &priv->cache); + DBG(("%s: caching handle=%d (name=%d, flags=%d, active_scanout=%d)\n", __FUNCTION__, c->bo->handle, c->name, c->flags, c->bo->active_scanout)); } + } else { + if (&c->link != &priv->cache) + free(c); } + assert(bo->active_scanout == 0); assert(bo != get_private(back)->bo); kgem_bo_destroy(&sna->kgem, get_private(back)->bo); @@ -210,21 +355,12 @@ sna_dri2_get_back(struct sna *sna, get_private(back)->size = draw->height << 16 | draw->width; back->pitch = bo->pitch; back->name = name; + back->flags = flags; - get_private(back)->stale = false; -} - -struct dri2_window { - DRI2BufferPtr front; - struct sna_dri2_event *chain; - xf86CrtcPtr crtc; - int64_t msc_delta; -}; + assert(back->pitch); + assert(back->name); -static struct dri2_window *dri2_window(WindowPtr win) -{ - assert(win->drawable.type != DRAWABLE_PIXMAP); - return ((void **)__get_private(win, sna_window_key))[1]; + get_private(back)->stale = false; } static struct sna_dri2_event * @@ -241,12 +377,14 @@ inline static DRI2BufferPtr dri2_window_get_front(WindowPtr win) } #else inline static void *dri2_window_get_front(WindowPtr win) { return NULL; } +#define APPLY_DAMAGE 1 #endif #if DRI2INFOREC_VERSION < 6 #define xorg_can_triple_buffer() 0 #define swap_limit(d, l) false +#define mark_stale(b) #else @@ -273,6 +411,8 @@ mark_stale(DRI2BufferPtr back) * stale frame. (This is mostly useful for tracking down * driver bugs!) */ + DBG(("%s(handle=%d) => %d\n", __FUNCTION__, + get_private(back)->bo->handle, xorg_can_triple_buffer())); get_private(back)->stale = xorg_can_triple_buffer(); } @@ -290,16 +430,20 @@ sna_dri2_reuse_buffer(DrawablePtr draw, DRI2BufferPtr buffer) __FUNCTION__, get_drawable_pixmap(draw)->drawable.serialNumber, buffer->attachment, get_private(buffer)->bo->handle, buffer->name)); assert(get_private(buffer)->refcnt); - assert(get_private(buffer)->bo->refcnt > get_private(buffer)->bo->active_scanout); + assert(get_private(buffer)->bo->refcnt >= get_private(buffer)->bo->active_scanout); + assert(kgem_bo_flink(&to_sna_from_drawable(draw)->kgem, get_private(buffer)->bo) == buffer->name); if (buffer->attachment == DRI2BufferBackLeft && draw->type != DRAWABLE_PIXMAP) { - DBG(("%s: replacing back buffer\n", __FUNCTION__)); - sna_dri2_get_back(to_sna_from_drawable(draw), draw, buffer, dri2_chain(draw)); + DBG(("%s: replacing back buffer on window %ld\n", __FUNCTION__, draw->id)); + sna_dri2_get_back(to_sna_from_drawable(draw), draw, buffer); - assert(kgem_bo_flink(&to_sna_from_drawable(draw)->kgem, get_private(buffer)->bo) == buffer->name); assert(get_private(buffer)->bo->refcnt); assert(get_private(buffer)->bo->active_scanout == 0); + assert(kgem_bo_flink(&to_sna_from_drawable(draw)->kgem, get_private(buffer)->bo) == buffer->name); + DBG(("%s: reusing back buffer handle=%d, name=%d, pitch=%d, age=%d\n", + __FUNCTION__, get_private(buffer)->bo->handle, + buffer->name, buffer->pitch, buffer->flags)); } } @@ -314,11 +458,6 @@ static bool swap_limit(DrawablePtr draw, int limit) } #endif -#if DRI2INFOREC_VERSION < 10 -#undef USE_ASYNC_SWAP -#define USE_ASYNC_SWAP 0 -#endif - #define COLOR_PREFER_TILING_Y 0 /* Prefer to enable TILING_Y if this buffer will never be a @@ -328,6 +467,9 @@ static uint32_t color_tiling(struct sna *sna, DrawablePtr draw) { uint32_t tiling; + if (!sna->kgem.can_fence) + return I915_TILING_NONE; + if (COLOR_PREFER_TILING_Y && (draw->width != sna->front->drawable.width || draw->height != sna->front->drawable.height)) @@ -355,7 +497,6 @@ static struct kgem_bo *sna_pixmap_set_dri(struct sna *sna, PixmapPtr pixmap) { struct sna_pixmap *priv; - int tiling; DBG(("%s: attaching DRI client to pixmap=%ld\n", __FUNCTION__, pixmap->drawable.serialNumber)); @@ -373,31 +514,29 @@ static struct kgem_bo *sna_pixmap_set_dri(struct sna *sna, return NULL; } - assert(priv->flush == false); + assert(priv->flush == false || priv->pinned & PIN_DRI3); + assert(priv->gpu_bo->flush == false || priv->pinned & PIN_DRI3); assert(priv->cpu_damage == NULL); assert(priv->gpu_bo); assert(priv->gpu_bo->proxy == NULL); - assert(priv->gpu_bo->flush == false); - - tiling = color_tiling(sna, &pixmap->drawable); - if (tiling < 0) - tiling = -tiling; - if (priv->gpu_bo->tiling != tiling) - sna_pixmap_change_tiling(pixmap, tiling); - return priv->gpu_bo; -} + if (!sna->kgem.can_fence) { + if (priv->gpu_bo->tiling && + !sna_pixmap_change_tiling(pixmap, I915_TILING_NONE)) { + DBG(("%s: failed to discard tiling (%d) for DRI2 protocol\n", __FUNCTION__, priv->gpu_bo->tiling)); + return NULL; + } + } else { + int tiling = color_tiling(sna, &pixmap->drawable); + if (tiling < 0) + tiling = -tiling; + if (priv->gpu_bo->tiling < tiling && !priv->gpu_bo->scanout) + sna_pixmap_change_tiling(pixmap, tiling); + } -pure static inline void *sna_pixmap_get_buffer(PixmapPtr pixmap) -{ - assert(pixmap->refcnt); - return ((void **)__get_private(pixmap, sna_pixmap_key))[2]; -} + priv->gpu_bo->active_scanout++; -static inline void sna_pixmap_set_buffer(PixmapPtr pixmap, void *ptr) -{ - assert(pixmap->refcnt); - ((void **)__get_private(pixmap, sna_pixmap_key))[2] = ptr; + return priv->gpu_bo; } void @@ -422,13 +561,18 @@ sna_dri2_pixmap_update_bo(struct sna *sna, PixmapPtr pixmap, struct kgem_bo *bo) if (private->bo == bo) return; + assert(private->bo->active_scanout > 0); + private->bo->active_scanout--; + DBG(("%s: dropping flush hint from handle=%d\n", __FUNCTION__, private->bo->handle)); private->bo->flush = false; kgem_bo_destroy(&sna->kgem, private->bo); + buffer->name = kgem_bo_flink(&sna->kgem, bo); buffer->pitch = bo->pitch; private->bo = ref(bo); + bo->active_scanout++; DBG(("%s: adding flush hint to handle=%d\n", __FUNCTION__, bo->handle)); bo->flush = true; @@ -449,9 +593,9 @@ sna_dri2_create_buffer(DrawablePtr draw, struct sna_dri2_private *private; PixmapPtr pixmap; struct kgem_bo *bo; - unsigned flags = 0; + unsigned bpp = format ?: draw->bitsPerPixel; + unsigned flags = CREATE_EXACT; uint32_t size; - int bpp; DBG(("%s pixmap=%ld, (attachment=%d, format=%d, drawable=%dx%d), window?=%d\n", __FUNCTION__, @@ -468,11 +612,11 @@ sna_dri2_create_buffer(DrawablePtr draw, if (draw->type != DRAWABLE_PIXMAP) buffer = dri2_window_get_front((WindowPtr)draw); if (buffer == NULL) - buffer = sna_pixmap_get_buffer(pixmap); + buffer = (DRI2Buffer2Ptr)sna_pixmap_get_buffer(pixmap); if (buffer) { private = get_private(buffer); - DBG(("%s: reusing front buffer attachment, win=%lu %dx%d, pixmap=%ld [%ld] %dx%d, handle=%d, name=%d\n", + DBG(("%s: reusing front buffer attachment, win=%lu %dx%d, pixmap=%ld [%ld] %dx%d, handle=%d, name=%d, active_scanout=%d\n", __FUNCTION__, draw->type != DRAWABLE_PIXMAP ? (long)draw->id : (long)0, draw->width, draw->height, @@ -480,12 +624,14 @@ sna_dri2_create_buffer(DrawablePtr draw, private->pixmap->drawable.serialNumber, pixmap->drawable.width, pixmap->drawable.height, - private->bo->handle, buffer->name)); + private->bo->handle, buffer->name, + private->bo->active_scanout)); assert(private->pixmap == pixmap); assert(sna_pixmap(pixmap)->flush); assert(sna_pixmap(pixmap)->pinned & PIN_DRI2); assert(kgem_bo_flink(&sna->kgem, private->bo) == buffer->name); + assert(private->bo->pitch == buffer->pitch); private->refcnt++; return buffer; @@ -498,7 +644,6 @@ sna_dri2_create_buffer(DrawablePtr draw, assert(sna_pixmap(pixmap) != NULL); bo = ref(bo); - bpp = pixmap->drawable.bitsPerPixel; if (pixmap == sna->front && !(sna->flags & SNA_LINEAR_FB)) flags |= CREATE_SCANOUT; DBG(("%s: attaching to front buffer %dx%d [%p:%d], scanout? %d\n", @@ -506,6 +651,7 @@ sna_dri2_create_buffer(DrawablePtr draw, pixmap->drawable.width, pixmap->drawable.height, pixmap, pixmap->refcnt, flags & CREATE_SCANOUT)); size = (uint32_t)pixmap->drawable.height << 16 | pixmap->drawable.width; + bpp = pixmap->drawable.bitsPerPixel; break; case DRI2BufferBackLeft: @@ -514,6 +660,7 @@ sna_dri2_create_buffer(DrawablePtr draw, flags |= CREATE_SCANOUT; if (draw->width == sna->front->drawable.width && draw->height == sna->front->drawable.height && + draw->bitsPerPixel == bpp && (sna->flags & (SNA_LINEAR_FB | SNA_NO_WAIT | SNA_NO_FLIP)) == 0) flags |= CREATE_SCANOUT; } @@ -521,7 +668,6 @@ sna_dri2_create_buffer(DrawablePtr draw, case DRI2BufferFrontRight: case DRI2BufferFakeFrontLeft: case DRI2BufferFakeFrontRight: - bpp = draw->bitsPerPixel; DBG(("%s: creating back buffer %dx%d, suitable for scanout? %d\n", __FUNCTION__, draw->width, draw->height, @@ -530,7 +676,7 @@ sna_dri2_create_buffer(DrawablePtr draw, bo = kgem_create_2d(&sna->kgem, draw->width, draw->height, - draw->bitsPerPixel, + bpp, color_tiling(sna, draw), flags); break; @@ -558,7 +704,6 @@ sna_dri2_create_buffer(DrawablePtr draw, * not understand W tiling and the GTT is incapable of * W fencing. */ - bpp = format ? format : draw->bitsPerPixel; bpp *= 2; bo = kgem_create_2d(&sna->kgem, ALIGN(draw->width, 64), @@ -570,7 +715,6 @@ sna_dri2_create_buffer(DrawablePtr draw, case DRI2BufferDepthStencil: case DRI2BufferHiz: case DRI2BufferAccum: - bpp = format ? format : draw->bitsPerPixel, bo = kgem_create_2d(&sna->kgem, draw->width, draw->height, bpp, other_tiling(sna, draw), @@ -614,7 +758,7 @@ sna_dri2_create_buffer(DrawablePtr draw, pixmap->refcnt++; priv = sna_pixmap(pixmap); - assert(priv->flush == false); + assert(priv->flush == false || priv->pinned & PIN_DRI3); assert((priv->pinned & PIN_DRI2) == 0); /* Don't allow this named buffer to be replaced */ @@ -630,14 +774,14 @@ sna_dri2_create_buffer(DrawablePtr draw, if (priv->gpu_bo->exec) sna->kgem.flush = 1; - priv->flush |= 1; + priv->flush |= FLUSH_READ; if (draw->type == DRAWABLE_PIXMAP) { /* DRI2 renders directly into GLXPixmaps, treat as hostile */ kgem_bo_unclean(&sna->kgem, priv->gpu_bo); sna_damage_all(&priv->gpu_damage, pixmap); priv->clear = false; priv->cpu = false; - priv->flush |= 2; + priv->flush |= FLUSH_WRITE; } sna_accel_watch_flush(sna, 1); @@ -651,7 +795,69 @@ err: return NULL; } -static void _sna_dri2_destroy_buffer(struct sna *sna, DRI2Buffer2Ptr buffer) +static void +sna_dri2_cache_bo(struct sna *sna, + DrawablePtr draw, + struct kgem_bo *bo, + uint32_t name, + uint32_t size, + uint32_t flags) +{ + struct dri_bo *c; + + DBG(("%s(handle=%d, name=%d)\n", __FUNCTION__, bo->handle, name)); + + if (draw == NULL) { + DBG(("%s: no draw, releasing handle=%d\n", + __FUNCTION__, bo->handle)); + goto err; + } + + if (draw->type == DRAWABLE_PIXMAP) { + DBG(("%s: not a window, releasing handle=%d\n", + __FUNCTION__, bo->handle)); + goto err; + } + + if (bo->refcnt > 1 + bo->active_scanout) { + DBG(("%s: multiple references [%d], releasing handle\n", + __FUNCTION__, bo->refcnt, bo->handle)); + goto err; + } + + if ((draw->height << 16 | draw->width) != size) { + DBG(("%s: wrong size [%dx%d], releasing handle\n", + __FUNCTION__, + size & 0xffff, size >> 16, + bo->handle)); + goto err; + } + + if (bo->scanout && front_pitch(draw) != bo->pitch) { + DBG(("%s: scanout with pitch change [%d != %d], releasing handle\n", + __FUNCTION__, bo->pitch, front_pitch(draw), bo->handle)); + goto err; + } + + c = malloc(sizeof(*c)); + if (!c) + goto err; + + DBG(("%s: caching handle=%d (name=%d, flags=%d, active_scanout=%d)\n", __FUNCTION__, bo->handle, name, flags, bo->active_scanout)); + + c->bo = bo; + c->name = name; + c->flags = flags; + list_add(&c->link, &dri2_window((WindowPtr)draw)->cache); + return; + +err: + kgem_bo_destroy(&sna->kgem, bo); +} + +static void _sna_dri2_destroy_buffer(struct sna *sna, + DrawablePtr draw, + DRI2Buffer2Ptr buffer) { struct sna_dri2_private *private = get_private(buffer); @@ -669,7 +875,7 @@ static void _sna_dri2_destroy_buffer(struct sna *sna, DRI2Buffer2Ptr buffer) if (private->proxy) { DBG(("%s: destroying proxy\n", __FUNCTION__)); - _sna_dri2_destroy_buffer(sna, private->proxy); + _sna_dri2_destroy_buffer(sna, draw, private->proxy); private->pixmap = NULL; } @@ -683,6 +889,9 @@ static void _sna_dri2_destroy_buffer(struct sna *sna, DRI2Buffer2Ptr buffer) assert(priv->pinned & PIN_DRI2); assert(priv->flush); + assert(priv->gpu_bo->active_scanout > 0); + priv->gpu_bo->active_scanout--; + /* Undo the DRI markings on this pixmap */ DBG(("%s: releasing last DRI pixmap=%ld, scanout?=%d\n", __FUNCTION__, @@ -692,24 +901,29 @@ static void _sna_dri2_destroy_buffer(struct sna *sna, DRI2Buffer2Ptr buffer) list_del(&priv->flush_list); DBG(("%s: dropping flush hint from handle=%d\n", __FUNCTION__, private->bo->handle)); - priv->gpu_bo->flush = false; priv->pinned &= ~PIN_DRI2; - priv->flush = false; + if ((priv->pinned & PIN_DRI3) == 0) { + priv->gpu_bo->flush = false; + priv->flush = false; + } sna_accel_watch_flush(sna, -1); sna_pixmap_set_buffer(pixmap, NULL); pixmap->drawable.pScreen->DestroyPixmap(pixmap); } - assert(private->bo->flush == false); - kgem_bo_destroy(&sna->kgem, private->bo); + sna_dri2_cache_bo(sna, draw, + private->bo, + buffer->name, + private->size, + buffer->flags); free(buffer); } static void sna_dri2_destroy_buffer(DrawablePtr draw, DRI2Buffer2Ptr buffer) { - _sna_dri2_destroy_buffer(to_sna_from_drawable(draw), buffer); + _sna_dri2_destroy_buffer(to_sna_from_drawable(draw), draw, buffer); } static DRI2BufferPtr sna_dri2_reference_buffer(DRI2BufferPtr buffer) @@ -746,10 +960,9 @@ static void set_bo(PixmapPtr pixmap, struct kgem_bo *bo) { struct sna *sna = to_sna_from_pixmap(pixmap); struct sna_pixmap *priv = sna_pixmap(pixmap); - RegionRec region; - DBG(("%s: pixmap=%ld, handle=%d\n", - __FUNCTION__, pixmap->drawable.serialNumber, bo->handle)); + DBG(("%s: pixmap=%ld, handle=%d (old handle=%d)\n", + __FUNCTION__, pixmap->drawable.serialNumber, bo->handle, priv->gpu_bo->handle)); assert(pixmap->drawable.width * pixmap->drawable.bitsPerPixel <= 8*bo->pitch); assert(pixmap->drawable.height * bo->pitch <= kgem_bo_size(bo)); @@ -758,21 +971,34 @@ static void set_bo(PixmapPtr pixmap, struct kgem_bo *bo) assert((priv->pinned & (PIN_PRIME | PIN_DRI3)) == 0); assert(priv->flush); - /* Post damage on the new front buffer so that listeners, such - * as DisplayLink know take a copy and shove it over the USB, - * also for software cursors and the like. - */ - region.extents.x1 = region.extents.y1 = 0; - region.extents.x2 = pixmap->drawable.width; - region.extents.y2 = pixmap->drawable.height; - region.data = NULL; - DamageRegionAppend(&pixmap->drawable, ®ion); + if (APPLY_DAMAGE) { + RegionRec region; + + /* Post damage on the new front buffer so that listeners, such + * as DisplayLink know take a copy and shove it over the USB, + * also for software cursors and the like. + */ + region.extents.x1 = region.extents.y1 = 0; + region.extents.x2 = pixmap->drawable.width; + region.extents.y2 = pixmap->drawable.height; + region.data = NULL; + + /* + * Eeek, beware the sw cursor copying to the old bo + * causing recursion and mayhem. + */ + DBG(("%s: marking whole pixmap as damaged\n", __FUNCTION__)); + sna->ignore_copy_area = true; + DamageRegionAppend(&pixmap->drawable, ®ion); + } damage(pixmap, priv, NULL); assert(bo->refcnt); - if (priv->move_to_gpu) + if (priv->move_to_gpu) { + DBG(("%s: applying final/discard move-to-gpu\n", __FUNCTION__)); priv->move_to_gpu(sna, priv, 0); + } if (priv->gpu_bo != bo) { DBG(("%s: dropping flush hint from handle=%d\n", __FUNCTION__, priv->gpu_bo->handle)); priv->gpu_bo->flush = false; @@ -792,8 +1018,27 @@ static void set_bo(PixmapPtr pixmap, struct kgem_bo *bo) bo->domain = DOMAIN_NONE; assert(bo->flush); - DamageRegionProcessPending(&pixmap->drawable); + if (APPLY_DAMAGE) { + DamageRegionProcessPending(&pixmap->drawable); + sna->ignore_copy_area = false; + } +} + +#if defined(__GNUC__) +#define popcount(x) __builtin_popcount(x) +#else +static int popcount(unsigned int x) +{ + int count = 0; + + while (x) { + count += x&1; + x >>= 1; + } + + return count; } +#endif static void sna_dri2_select_mode(struct sna *sna, struct kgem_bo *dst, struct kgem_bo *src, bool sync) { @@ -823,6 +1068,12 @@ static void sna_dri2_select_mode(struct sna *sna, struct kgem_bo *dst, struct kg return; } + if (sna->render_state.gt < 2 && sna->kgem.has_semaphores) { + DBG(("%s: small GT [%d], not forcing selection\n", + __FUNCTION__, sna->render_state.gt)); + return; + } + VG_CLEAR(busy); busy.handle = src->handle; if (drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GEM_BUSY, &busy)) @@ -859,9 +1110,12 @@ static void sna_dri2_select_mode(struct sna *sna, struct kgem_bo *dst, struct kg * The ultimate question is whether preserving the ring outweighs * the cost of the query. */ - mode = KGEM_RENDER; - if (busy.busy & (0xfffe << 16)) + if (popcount(busy.busy >> 16) > 1) + mode = busy.busy & 0xffff ? KGEM_BLT : KGEM_RENDER; + else if (busy.busy & (0xfffe << 16)) mode = KGEM_BLT; + else + mode = KGEM_RENDER; kgem_bo_mark_busy(&sna->kgem, busy.handle == src->handle ? src : dst, mode); _kgem_set_mode(&sna->kgem, mode); } @@ -871,10 +1125,13 @@ static bool is_front(int attachment) return attachment == DRI2BufferFrontLeft; } +#define DRI2_SYNC 0x1 +#define DRI2_DAMAGE 0x2 +#define DRI2_BO 0x4 static struct kgem_bo * __sna_dri2_copy_region(struct sna *sna, DrawablePtr draw, RegionPtr region, DRI2BufferPtr src, DRI2BufferPtr dst, - bool sync) + unsigned flags) { PixmapPtr pixmap = get_drawable_pixmap(draw); DrawableRec scratch, *src_draw = &pixmap->drawable, *dst_draw = &pixmap->drawable; @@ -886,7 +1143,7 @@ __sna_dri2_copy_region(struct sna *sna, DrawablePtr draw, RegionPtr region, struct kgem_bo *dst_bo; const BoxRec *boxes; int16_t dx, dy, sx, sy; - unsigned flags; + unsigned hint; int n; /* To hide a stale DRI2Buffer, one may choose to substitute @@ -962,8 +1219,9 @@ __sna_dri2_copy_region(struct sna *sna, DrawablePtr draw, RegionPtr region, } } } else - sync = false; + flags &= ~DRI2_SYNC; + scratch.pScreen = draw->pScreen; scratch.x = scratch.y = 0; scratch.width = scratch.height = 0; scratch.depth = draw->depth; @@ -987,11 +1245,12 @@ __sna_dri2_copy_region(struct sna *sna, DrawablePtr draw, RegionPtr region, scratch.height = src_priv->size >> 16; src_draw = &scratch; - DBG(("%s: source size %dx%d, region size %dx%d\n", + DBG(("%s: source size %dx%d, region size %dx%d, src offset %dx%d\n", __FUNCTION__, scratch.width, scratch.height, clip.extents.x2 - clip.extents.x1, - clip.extents.y2 - clip.extents.y1)); + clip.extents.y2 - clip.extents.y1, + -sx, -sy)); source.extents.x1 = -sx; source.extents.y1 = -sy; @@ -1002,6 +1261,10 @@ __sna_dri2_copy_region(struct sna *sna, DrawablePtr draw, RegionPtr region, assert(region == NULL || region == &clip); pixman_region_intersect(&clip, &clip, &source); + if (!pixman_region_not_empty(&clip)) { + DBG(("%s: region doesn't overlap pixmap\n", __FUNCTION__)); + return NULL; + } } dst_bo = dst_priv->bo; @@ -1013,12 +1276,12 @@ __sna_dri2_copy_region(struct sna *sna, DrawablePtr draw, RegionPtr region, /* Preserve the CRTC shadow overrides */ sna_shadow_steal_crtcs(sna, &shadow); - flags = MOVE_WRITE | __MOVE_FORCE; + hint = MOVE_WRITE | __MOVE_FORCE; if (clip.data) - flags |= MOVE_READ; + hint |= MOVE_READ; assert(region == NULL || region == &clip); - priv = sna_pixmap_move_area_to_gpu(pixmap, &clip.extents, flags); + priv = sna_pixmap_move_area_to_gpu(pixmap, &clip.extents, hint); if (priv) { damage(pixmap, priv, region); dst_bo = priv->gpu_bo; @@ -1050,20 +1313,20 @@ __sna_dri2_copy_region(struct sna *sna, DrawablePtr draw, RegionPtr region, assert(region == NULL || region == &clip); pixman_region_intersect(&clip, &clip, &target); - sync = false; + flags &= ~DRI2_SYNC; } if (!wedged(sna)) { xf86CrtcPtr crtc; crtc = NULL; - if (sync && sna_pixmap_is_scanout(sna, pixmap)) + if (flags & DRI2_SYNC && sna_pixmap_is_scanout(sna, pixmap)) crtc = sna_covering_crtc(sna, &clip.extents, NULL); sna_dri2_select_mode(sna, dst_bo, src_bo, crtc != NULL); - sync = (crtc != NULL&& - sna_wait_for_scanline(sna, pixmap, crtc, - &clip.extents)); + if (crtc == NULL || + !sna_wait_for_scanline(sna, pixmap, crtc, &clip.extents)) + flags &= ~DRI2_SYNC; } if (region) { @@ -1075,8 +1338,11 @@ __sna_dri2_copy_region(struct sna *sna, DrawablePtr draw, RegionPtr region, boxes = &clip.extents; n = 1; } - DamageRegionAppend(&pixmap->drawable, region); - + if (APPLY_DAMAGE || flags & DRI2_DAMAGE) { + DBG(("%s: marking region as damaged\n", __FUNCTION__)); + sna->ignore_copy_area = true; + DamageRegionAppend(&pixmap->drawable, region); + } DBG(("%s: copying [(%d, %d), (%d, %d)]x%d src=(%d, %d), dst=(%d, %d)\n", __FUNCTION__, @@ -1084,29 +1350,35 @@ __sna_dri2_copy_region(struct sna *sna, DrawablePtr draw, RegionPtr region, boxes[0].x2, boxes[0].y2, n, sx, sy, dx, dy)); - flags = COPY_LAST; - if (sync) - flags |= COPY_SYNC; + hint = COPY_LAST | COPY_DRI; + if (flags & DRI2_SYNC) + hint |= COPY_SYNC; if (!sna->render.copy_boxes(sna, GXcopy, src_draw, src_bo, sx, sy, dst_draw, dst_bo, dx, dy, - boxes, n, flags)) + boxes, n, hint)) memcpy_copy_boxes(sna, GXcopy, src_draw, src_bo, sx, sy, dst_draw, dst_bo, dx, dy, - boxes, n, flags); - - DBG(("%s: flushing? %d\n", __FUNCTION__, sync)); - if (sync) { /* STAT! */ - struct kgem_request *rq = sna->kgem.next_request; - kgem_submit(&sna->kgem); - if (rq->bo) { - bo = ref(rq->bo); - DBG(("%s: recording sync fence handle=%d\n", __FUNCTION__, bo->handle)); + boxes, n, hint); + + if (flags & (DRI2_SYNC | DRI2_BO)) { /* STAT! */ + struct kgem_request *rq = RQ(dst_bo->rq); + if (rq && rq != (void *)&sna->kgem) { + if (rq->bo == NULL) + kgem_submit(&sna->kgem); + if (rq->bo) { /* Becareful in case the gpu is wedged */ + bo = ref(rq->bo); + DBG(("%s: recording sync fence handle=%d\n", + __FUNCTION__, bo->handle)); + } } } - DamageRegionProcessPending(&pixmap->drawable); + if (APPLY_DAMAGE || flags & DRI2_DAMAGE) { + DamageRegionProcessPending(&pixmap->drawable); + sna->ignore_copy_area = false; + } if (clip.data) pixman_region_fini(&clip); @@ -1142,6 +1414,8 @@ sna_dri2_copy_region(DrawablePtr draw, assert(get_private(src)->refcnt); assert(get_private(dst)->refcnt); + assert(get_private(src)->bo != get_private(dst)->bo); + assert(get_private(src)->bo->refcnt); assert(get_private(dst)->bo->refcnt); @@ -1151,7 +1425,7 @@ sna_dri2_copy_region(DrawablePtr draw, region->extents.x2, region->extents.y2, region_num_rects(region))); - __sna_dri2_copy_region(sna, draw, region, src, dst, false); + __sna_dri2_copy_region(sna, draw, region, src, dst, DRI2_DAMAGE); } inline static uint32_t pipe_select(int pipe) @@ -1169,15 +1443,53 @@ inline static uint32_t pipe_select(int pipe) return 0; } -static inline int sna_wait_vblank(struct sna *sna, union drm_wait_vblank *vbl, int pipe) +static inline bool sna_next_vblank(struct sna_dri2_event *info) +{ + union drm_wait_vblank vbl; + + DBG(("%s(pipe=%d, waiting until next vblank)\n", + __FUNCTION__, info->pipe)); + assert(info->pipe != -1); + + VG_CLEAR(vbl); + vbl.request.type = + DRM_VBLANK_RELATIVE | + DRM_VBLANK_EVENT | + pipe_select(info->pipe); + vbl.request.sequence = 1; + vbl.request.signal = (uintptr_t)info; + + assert(!info->queued); + if (drmIoctl(info->sna->kgem.fd, DRM_IOCTL_WAIT_VBLANK, &vbl)) + return false; + + info->queued = true; + return true; +} + +static inline bool sna_wait_vblank(struct sna_dri2_event *info, + unsigned seq) { - DBG(("%s(pipe=%d, waiting until seq=%u%s)\n", - __FUNCTION__, pipe, vbl->request.sequence, - vbl->request.type & DRM_VBLANK_RELATIVE ? " [relative]" : "")); - assert(pipe != -1); + union drm_wait_vblank vbl; - vbl->request.type |= pipe_select(pipe); - return drmIoctl(sna->kgem.fd, DRM_IOCTL_WAIT_VBLANK, vbl); + DBG(("%s(pipe=%d, waiting until vblank %u)\n", + __FUNCTION__, info->pipe, seq)); + assert(info->pipe != -1); + + VG_CLEAR(vbl); + vbl.request.type = + DRM_VBLANK_ABSOLUTE | + DRM_VBLANK_EVENT | + pipe_select(info->pipe); + vbl.request.sequence = seq; + vbl.request.signal = (uintptr_t)info; + + assert(!info->queued); + if (drmIoctl(info->sna->kgem.fd, DRM_IOCTL_WAIT_VBLANK, &vbl)) + return false; + + info->queued = true; + return true; } #if DRI2INFOREC_VERSION >= 4 @@ -1206,6 +1518,8 @@ draw_current_msc(DrawablePtr draw, xf86CrtcPtr crtc, uint64_t msc) priv->crtc = crtc; priv->msc_delta = 0; priv->chain = NULL; + priv->scanout = -1; + list_init(&priv->cache); dri2_window_attach((WindowPtr)draw, priv); } } else { @@ -1214,8 +1528,8 @@ draw_current_msc(DrawablePtr draw, xf86CrtcPtr crtc, uint64_t msc) const struct ust_msc *this = sna_crtc_last_swap(crtc); DBG(("%s: Window transferring from pipe=%d [msc=%llu] to pipe=%d [msc=%llu], delta now %lld\n", __FUNCTION__, - sna_crtc_to_pipe(priv->crtc), (long long)last->msc, - sna_crtc_to_pipe(crtc), (long long)this->msc, + sna_crtc_pipe(priv->crtc), (long long)last->msc, + sna_crtc_pipe(crtc), (long long)this->msc, (long long)(priv->msc_delta + this->msc - last->msc))); priv->msc_delta += this->msc - last->msc; priv->crtc = crtc; @@ -1248,57 +1562,112 @@ sna_dri2_get_crtc(DrawablePtr draw) NULL); } -static void -sna_dri2_remove_event(WindowPtr win, struct sna_dri2_event *info) +static void frame_swap_complete(struct sna_dri2_event *frame, int type) { - struct dri2_window *priv; - struct sna_dri2_event *chain; - - assert(win->drawable.type == DRAWABLE_WINDOW); - DBG(("%s: remove[%p] from window %ld, active? %d\n", - __FUNCTION__, info, (long)win->drawable.id, info->draw != NULL)); + const struct ust_msc *swap; - priv = dri2_window(win); - assert(priv); - assert(priv->chain != NULL); + assert(frame->signal); + frame->signal = false; - if (priv->chain == info) { - priv->chain = info->chain; + if (frame->client == NULL) { + DBG(("%s: client already gone\n", __FUNCTION__)); return; } - chain = priv->chain; - while (chain->chain != info) - chain = chain->chain; - assert(chain != info); - assert(info->chain != chain); - chain->chain = info->chain; -} - -static void -sna_dri2_event_free(struct sna_dri2_event *info) -{ - DrawablePtr draw = info->draw; + assert(frame->draw); - DBG(("%s(draw?=%d)\n", __FUNCTION__, draw != NULL)); - if (draw && draw->type == DRAWABLE_WINDOW) - sna_dri2_remove_event((WindowPtr)draw, info); + swap = sna_crtc_last_swap(frame->crtc); + DBG(("%s(type=%d): draw=%ld, pipe=%d, frame=%lld [msc=%lld], tv=%d.%06d\n", + __FUNCTION__, type, (long)frame->draw->id, frame->pipe, + (long long)swap->msc, + (long long)draw_current_msc(frame->draw, frame->crtc, swap->msc), + swap->tv_sec, swap->tv_usec)); - _sna_dri2_destroy_buffer(info->sna, info->front); - _sna_dri2_destroy_buffer(info->sna, info->back); + DRI2SwapComplete(frame->client, frame->draw, + draw_current_msc(frame->draw, frame->crtc, swap->msc), + swap->tv_sec, swap->tv_usec, + type, frame->event_complete, frame->event_data); +} - while (!list_is_empty(&info->cache)) { - struct dri_bo *c; +static void fake_swap_complete(struct sna *sna, ClientPtr client, + DrawablePtr draw, xf86CrtcPtr crtc, + int type, DRI2SwapEventPtr func, void *data) +{ + const struct ust_msc *swap; - c = list_first_entry(&info->cache, struct dri_bo, link); - list_del(&c->link); + swap = sna_crtc_last_swap(crtc); + DBG(("%s(type=%d): draw=%ld, pipe=%d, frame=%lld [msc %lld], tv=%d.%06d\n", + __FUNCTION__, type, (long)draw->id, crtc ? sna_crtc_pipe(crtc) : -1, + (long long)swap->msc, + (long long)draw_current_msc(draw, crtc, swap->msc), + swap->tv_sec, swap->tv_usec)); - DBG(("%s: releasing cached handle=%d\n", __FUNCTION__, c->bo ? c->bo->handle : 0)); - if (c->bo) - kgem_bo_destroy(&info->sna->kgem, c->bo); + DRI2SwapComplete(client, draw, + draw_current_msc(draw, crtc, swap->msc), + swap->tv_sec, swap->tv_usec, + type, func, data); +} - free(c); +static void +sna_dri2_remove_event(struct sna_dri2_event *info) +{ + WindowPtr win = (WindowPtr)info->draw; + struct dri2_window *priv; + struct sna_dri2_event *chain; + + assert(win->drawable.type == DRAWABLE_WINDOW); + DBG(("%s: remove[%p] from window %ld, active? %d\n", + __FUNCTION__, info, (long)win->drawable.id, info->draw != NULL)); + assert(!info->signal); + + priv = dri2_window(win); + assert(priv); + assert(priv->chain != NULL); + assert(info->chained); + info->chained = false; + + if (priv->chain != info) { + chain = priv->chain; + while (chain->chain != info) + chain = chain->chain; + assert(chain != info); + assert(info->chain != chain); + chain->chain = info->chain; + return; + } + + priv->chain = info->chain; + if (priv->chain == NULL) { + struct dri_bo *c, *tmp; + + c = list_entry(priv->cache.next->next, struct dri_bo, link); + list_for_each_entry_safe_from(c, tmp, &priv->cache, link) { + list_del(&c->link); + + DBG(("%s: releasing cached handle=%d\n", __FUNCTION__, c->bo ? c->bo->handle : 0)); + assert(c->bo); + kgem_bo_destroy(&info->sna->kgem, c->bo); + free(c); + } } +} + +static void +sna_dri2_event_free(struct sna_dri2_event *info) +{ + DBG(("%s(draw?=%d)\n", __FUNCTION__, info->draw != NULL)); + assert(!info->queued); + assert(!info->signal); + assert(info->pending.bo == NULL); + + if (info->sna->dri2.flip_pending == info) + info->sna->dri2.flip_pending = NULL; + assert(info->sna->dri2.flip_pending != info); + if (info->chained) + sna_dri2_remove_event(info); + + _sna_dri2_destroy_buffer(info->sna, info->draw, info->front); + _sna_dri2_destroy_buffer(info->sna, info->draw, info->back); if (info->bo) { DBG(("%s: releasing batch handle=%d\n", __FUNCTION__, info->bo->handle)); @@ -1331,15 +1700,24 @@ sna_dri2_client_gone(CallbackListPtr *list, void *closure, void *data) event = list_first_entry(&priv->events, struct sna_dri2_event, link); assert(event->client == client); + list_del(&event->link); + event->signal = false; - if (event->queued) { - if (event->draw) - sna_dri2_remove_event((WindowPtr)event->draw, - event); - event->client = NULL; - event->draw = NULL; - list_del(&event->link); - } else + if (event->pending.bo) { + assert(event->pending.bo->active_scanout > 0); + event->pending.bo->active_scanout--; + + kgem_bo_destroy(&sna->kgem, event->pending.bo); + event->pending.bo = NULL; + } + + if (event->chained) + sna_dri2_remove_event(event); + + event->client = NULL; + event->draw = NULL; + + if (!event->queued) sna_dri2_event_free(event); } @@ -1365,7 +1743,10 @@ static bool add_event_to_client(struct sna_dri2_event *info, struct sna *sna, Cl } static struct sna_dri2_event * -sna_dri2_add_event(struct sna *sna, DrawablePtr draw, ClientPtr client) +sna_dri2_add_event(struct sna *sna, + DrawablePtr draw, + ClientPtr client, + xf86CrtcPtr crtc) { struct dri2_window *priv; struct sna_dri2_event *info, *chain; @@ -1382,11 +1763,11 @@ sna_dri2_add_event(struct sna *sna, DrawablePtr draw, ClientPtr client) if (info == NULL) return NULL; - list_init(&info->cache); info->sna = sna; info->draw = draw; - info->crtc = priv->crtc; - info->pipe = sna_crtc_to_pipe(priv->crtc); + info->crtc = crtc; + info->pipe = sna_crtc_pipe(crtc); + info->keepalive = 1; if (!add_event_to_client(info, sna, client)) { free(info); @@ -1394,6 +1775,7 @@ sna_dri2_add_event(struct sna *sna, DrawablePtr draw, ClientPtr client) } assert(priv->chain != info); + info->chained = true; if (priv->chain == NULL) { priv->chain = info; @@ -1423,26 +1805,29 @@ void sna_dri2_decouple_window(WindowPtr win) struct sna *sna = to_sna_from_drawable(&win->drawable); assert(priv->crtc); sna_shadow_unset_crtc(sna, priv->crtc); - _sna_dri2_destroy_buffer(sna, priv->front); + _sna_dri2_destroy_buffer(sna, NULL, priv->front); priv->front = NULL; } + + priv->scanout = -1; } void sna_dri2_destroy_window(WindowPtr win) { struct dri2_window *priv; + struct sna *sna; priv = dri2_window(win); if (priv == NULL) return; DBG(("%s: window=%ld\n", __FUNCTION__, win->drawable.id)); + sna = to_sna_from_drawable(&win->drawable); if (priv->front) { - struct sna *sna = to_sna_from_drawable(&win->drawable); assert(priv->crtc); sna_shadow_unset_crtc(sna, priv->crtc); - _sna_dri2_destroy_buffer(sna, priv->front); + _sna_dri2_destroy_buffer(sna, NULL, priv->front); } if (priv->chain) { @@ -1452,18 +1837,44 @@ void sna_dri2_destroy_window(WindowPtr win) chain = priv->chain; while ((info = chain)) { + assert(info->draw == &win->drawable); + if (info->signal) + frame_swap_complete(info, DRI2_EXCHANGE_COMPLETE); + if (info->pending.bo) { + assert(info->pending.bo->active_scanout > 0); + info->pending.bo->active_scanout--; + + info->signal = true; + frame_swap_complete(info, DRI2_EXCHANGE_COMPLETE); + + kgem_bo_destroy(&sna->kgem, info->pending.bo); + info->pending.bo = NULL; + } + info->draw = NULL; - info->client = NULL; list_del(&info->link); chain = info->chain; info->chain = NULL; + info->chained = false; if (!info->queued) sna_dri2_event_free(info); } } + while (!list_is_empty(&priv->cache)) { + struct dri_bo *c; + + c = list_first_entry(&priv->cache, struct dri_bo, link); + list_del(&c->link); + + DBG(("%s: releasing cached handle=%d\n", __FUNCTION__, c->bo ? c->bo->handle : 0)); + assert(c->bo); + kgem_bo_destroy(&sna->kgem, c->bo); + free(c); + } + free(priv); } @@ -1479,19 +1890,29 @@ sna_dri2_flip(struct sna_dri2_event *info) { struct kgem_bo *bo = get_private(info->back)->bo; struct kgem_bo *tmp_bo; - uint32_t tmp_name; + uint32_t tmp_name, tmp_flags; int tmp_pitch; DBG(("%s(type=%d)\n", __FUNCTION__, info->type)); assert(sna_pixmap_get_buffer(info->sna->front) == info->front); assert(get_drawable_pixmap(info->draw)->drawable.height * bo->pitch <= kgem_bo_size(bo)); + assert(get_private(info->front)->size == get_private(info->back)->size); assert(bo->refcnt); + if (info->sna->mode.flip_active) { + DBG(("%s: %d flips still active, aborting\n", + __FUNCTION__, info->sna->mode.flip_active)); + return false; + } + if (!sna_page_flip(info->sna, bo, sna_dri2_flip_handler, info->type == FLIP_ASYNC ? NULL : info)) return false; + DBG(("%s: queued flip=%p\n", __FUNCTION__, info->type == FLIP_ASYNC ? NULL : info)); + assert(info->signal || info->type != FLIP_THROTTLE); + assert(info->sna->dri2.flip_pending == NULL || info->sna->dri2.flip_pending == info); if (info->type != FLIP_ASYNC) @@ -1505,13 +1926,20 @@ sna_dri2_flip(struct sna_dri2_event *info) tmp_bo = get_private(info->front)->bo; tmp_name = info->front->name; tmp_pitch = info->front->pitch; + tmp_flags = info->front->flags; + + assert(tmp_bo->active_scanout > 0); + tmp_bo->active_scanout--; set_bo(info->sna->front, bo); + info->front->flags = info->back->flags; info->front->name = info->back->name; info->front->pitch = info->back->pitch; get_private(info->front)->bo = bo; + bo->active_scanout++; + info->back->flags = tmp_flags; info->back->name = tmp_name; info->back->pitch = tmp_pitch; get_private(info->back)->bo = tmp_bo; @@ -1521,6 +1949,7 @@ sna_dri2_flip(struct sna_dri2_event *info) assert(get_private(info->back)->bo->refcnt); assert(get_private(info->front)->bo != get_private(info->back)->bo); + info->keepalive = KEEPALIVE; info->queued = true; return true; } @@ -1549,15 +1978,16 @@ can_flip(struct sna * sna, } assert(sna->scrn->vtSema); + assert(!sna->mode.hidden); if ((sna->flags & (SNA_HAS_FLIP | SNA_HAS_ASYNC_FLIP)) == 0) { DBG(("%s: no, pageflips disabled\n", __FUNCTION__)); return false; } - if (front->format != back->format) { + if (front->cpp != back->cpp) { DBG(("%s: no, format mismatch, front = %d, back = %d\n", - __FUNCTION__, front->format, back->format)); + __FUNCTION__, front->cpp, back->cpp)); return false; } @@ -1567,7 +1997,7 @@ can_flip(struct sna * sna, } if (!sna_crtc_is_on(crtc)) { - DBG(("%s: ref-pipe=%d is disabled\n", __FUNCTION__, sna_crtc_to_pipe(crtc))); + DBG(("%s: ref-pipe=%d is disabled\n", __FUNCTION__, sna_crtc_pipe(crtc))); return false; } @@ -1581,7 +2011,7 @@ can_flip(struct sna * sna, if (sna_pixmap_get_buffer(pixmap) != front) { DBG(("%s: no, DRI2 drawable is no longer attached (old name=%d, new name=%d) to pixmap=%ld\n", __FUNCTION__, front->name, - sna_pixmap_get_buffer(pixmap) ? ((DRI2BufferPtr)sna_pixmap_get_buffer(pixmap))->name : 0, + sna_pixmap_get_buffer(pixmap) ? sna_pixmap_get_buffer(pixmap)->name : 0, pixmap->drawable.serialNumber)); return false; } @@ -1661,7 +2091,6 @@ can_flip(struct sna * sna, } DBG(("%s: yes, pixmap=%ld\n", __FUNCTION__, pixmap->drawable.serialNumber)); - assert(dri2_window(win)->front == NULL); return true; } @@ -1680,9 +2109,9 @@ can_xchg(struct sna *sna, if (draw->type == DRAWABLE_PIXMAP) return false; - if (front->format != back->format) { + if (front->cpp != back->cpp) { DBG(("%s: no, format mismatch, front = %d, back = %d\n", - __FUNCTION__, front->format, back->format)); + __FUNCTION__, front->cpp, back->cpp)); return false; } @@ -1714,6 +2143,8 @@ can_xchg(struct sna *sna, return false; } + DBG(("%s: back size=%x, front size=%x\n", + __FUNCTION__, get_private(back)->size, get_private(front)->size)); if (get_private(back)->size != get_private(front)->size) { DBG(("%s: no, back buffer %dx%d does not match front buffer %dx%d\n", __FUNCTION__, @@ -1766,9 +2197,9 @@ overlaps_other_crtc(struct sna *sna, xf86CrtcPtr desired) static bool can_xchg_crtc(struct sna *sna, DrawablePtr draw, + xf86CrtcPtr crtc, DRI2BufferPtr front, - DRI2BufferPtr back, - xf86CrtcPtr crtc) + DRI2BufferPtr back) { WindowPtr win = (WindowPtr)draw; PixmapPtr pixmap; @@ -1785,9 +2216,9 @@ can_xchg_crtc(struct sna *sna, if (draw->type == DRAWABLE_PIXMAP) return false; - if (front->format != back->format) { + if (front->cpp != back->cpp) { DBG(("%s: no, format mismatch, front = %d, back = %d\n", - __FUNCTION__, front->format, back->format)); + __FUNCTION__, front->cpp, back->cpp)); return false; } @@ -1866,7 +2297,6 @@ sna_dri2_xchg(DrawablePtr draw, DRI2BufferPtr front, DRI2BufferPtr back) back_bo = get_private(back)->bo; front_bo = get_private(front)->bo; - assert(front_bo != back_bo); DBG(("%s: win=%ld, exchange front=%d/%d and back=%d/%d, pixmap=%ld %dx%d\n", __FUNCTION__, win->drawable.id, @@ -1876,10 +2306,12 @@ sna_dri2_xchg(DrawablePtr draw, DRI2BufferPtr front, DRI2BufferPtr back) pixmap->drawable.width, pixmap->drawable.height)); - DBG(("%s: back_bo pitch=%d, size=%d, ref=%d, active_scanout?=%d\n", - __FUNCTION__, back_bo->pitch, kgem_bo_size(back_bo), back_bo->refcnt, back_bo->active_scanout)); - DBG(("%s: front_bo pitch=%d, size=%d, ref=%d, active_scanout?=%d\n", - __FUNCTION__, front_bo->pitch, kgem_bo_size(front_bo), front_bo->refcnt, front_bo->active_scanout)); + DBG(("%s: back_bo handle=%d, pitch=%d, size=%d, ref=%d, active_scanout?=%d\n", + __FUNCTION__, back_bo->handle, back_bo->pitch, kgem_bo_size(back_bo), back_bo->refcnt, back_bo->active_scanout)); + DBG(("%s: front_bo handle=%d, pitch=%d, size=%d, ref=%d, active_scanout?=%d\n", + __FUNCTION__, front_bo->handle, front_bo->pitch, kgem_bo_size(front_bo), front_bo->refcnt, front_bo->active_scanout)); + + assert(front_bo != back_bo); assert(front_bo->refcnt); assert(back_bo->refcnt); @@ -1894,6 +2326,10 @@ sna_dri2_xchg(DrawablePtr draw, DRI2BufferPtr front, DRI2BufferPtr back) get_private(back)->bo = front_bo; mark_stale(back); + assert(front_bo->active_scanout > 0); + front_bo->active_scanout--; + back_bo->active_scanout++; + tmp = front->name; front->name = back->name; back->name = tmp; @@ -1902,15 +2338,23 @@ sna_dri2_xchg(DrawablePtr draw, DRI2BufferPtr front, DRI2BufferPtr back) front->pitch = back->pitch; back->pitch = tmp; + tmp = front->flags; + front->flags = back->flags; + back->flags = tmp; + assert(front_bo->refcnt); assert(back_bo->refcnt); + assert(front_bo->pitch == get_private(front)->bo->pitch); + assert(back_bo->pitch == get_private(back)->bo->pitch); + assert(get_private(front)->bo == sna_pixmap(pixmap)->gpu_bo); } static void sna_dri2_xchg_crtc(struct sna *sna, DrawablePtr draw, xf86CrtcPtr crtc, DRI2BufferPtr front, DRI2BufferPtr back) { WindowPtr win = (WindowPtr)draw; + struct dri2_window *priv = dri2_window(win); DRI2Buffer2Ptr tmp; struct kgem_bo *bo; @@ -1922,21 +2366,33 @@ static void sna_dri2_xchg_crtc(struct sna *sna, DrawablePtr draw, xf86CrtcPtr cr get_window_pixmap(win)->drawable.serialNumber, get_window_pixmap(win)->drawable.width, get_window_pixmap(win)->drawable.height)); + assert(can_xchg_crtc(to_sna_from_drawable(draw), draw, crtc, front, back)); - DamageRegionAppend(&win->drawable, &win->clipList); + if (APPLY_DAMAGE) { + DBG(("%s: marking drawable as damaged\n", __FUNCTION__)); + sna->ignore_copy_area = true; + DamageRegionAppend(&win->drawable, &win->clipList); + } sna_shadow_set_crtc(sna, crtc, get_private(back)->bo); - DamageRegionProcessPending(&win->drawable); - - assert(dri2_window(win)->front == NULL); + if (APPLY_DAMAGE) { + DamageRegionProcessPending(&win->drawable); + sna->ignore_copy_area = false; + } tmp = calloc(1, sizeof(*tmp) + sizeof(struct sna_dri2_private)); if (tmp == NULL) { back->attachment = -1; if (get_private(back)->proxy == NULL) { - get_private(back)->pixmap = get_window_pixmap(win); - get_private(back)->proxy = sna_dri2_reference_buffer(sna_pixmap_get_buffer(get_private(back)->pixmap)); + get_private(back)->pixmap = get_private(front)->pixmap; + get_private(back)->proxy = sna_dri2_reference_buffer(get_private(front)->proxy ?: front); } - dri2_window(win)->front = sna_dri2_reference_buffer(back); + + if (priv->front) { + assert(front == priv->front); + assert(get_private(priv->front)->refcnt > 1); + get_private(priv->front)->refcnt--; + } + priv->front = sna_dri2_reference_buffer(back); return; } @@ -1946,73 +2402,53 @@ static void sna_dri2_xchg_crtc(struct sna *sna, DrawablePtr draw, xf86CrtcPtr cr get_private(tmp)->refcnt = 1; get_private(tmp)->bo = get_private(back)->bo; get_private(tmp)->size = get_private(back)->size; - get_private(tmp)->pixmap = get_window_pixmap(win); - get_private(tmp)->proxy = sna_dri2_reference_buffer(sna_pixmap_get_buffer(get_private(tmp)->pixmap)); - dri2_window(win)->front = tmp; - - DBG(("%s: allocating new backbuffer\n", __FUNCTION__)); - back->name = 0; - bo = kgem_create_2d(&sna->kgem, - draw->width, draw->height, draw->bitsPerPixel, - get_private(back)->bo->tiling, - CREATE_SCANOUT); - if (bo != NULL) { - get_private(back)->bo = bo; - back->pitch = bo->pitch; - back->name = kgem_bo_flink(&sna->kgem, bo); - } - if (back->name == 0) { - if (bo != NULL) - kgem_bo_destroy(&sna->kgem, bo); - get_private(back)->bo = NULL; - back->attachment = -1; - } -} - -static void frame_swap_complete(struct sna_dri2_event *frame, int type) -{ - const struct ust_msc *swap; - - if (frame->draw == NULL) - return; - - assert(frame->client); - - swap = sna_crtc_last_swap(frame->crtc); - DBG(("%s(type=%d): draw=%ld, pipe=%d, frame=%lld [msc=%lld], tv=%d.%06d\n", - __FUNCTION__, type, (long)frame->draw, frame->pipe, - (long long)swap->msc, - (long long)draw_current_msc(frame->draw, frame->crtc, swap->msc), - swap->tv_sec, swap->tv_usec)); - - DRI2SwapComplete(frame->client, frame->draw, - draw_current_msc(frame->draw, frame->crtc, swap->msc), - swap->tv_sec, swap->tv_usec, - type, frame->event_complete, frame->event_data); -} - -static void fake_swap_complete(struct sna *sna, ClientPtr client, - DrawablePtr draw, xf86CrtcPtr crtc, - int type, DRI2SwapEventPtr func, void *data) -{ - const struct ust_msc *swap; - - swap = sna_crtc_last_swap(crtc); - DBG(("%s(type=%d): draw=%ld, pipe=%d, frame=%lld [msc %lld], tv=%d.%06d\n", - __FUNCTION__, type, (long)draw->id, crtc ? sna_crtc_to_pipe(crtc) : -1, - (long long)swap->msc, - (long long)draw_current_msc(draw, crtc, swap->msc), - swap->tv_sec, swap->tv_usec)); + get_private(tmp)->pixmap = get_private(front)->pixmap; + get_private(tmp)->proxy = sna_dri2_reference_buffer(get_private(front)->proxy ?: front); + mark_stale(back); - DRI2SwapComplete(client, draw, - draw_current_msc(draw, crtc, swap->msc), - swap->tv_sec, swap->tv_usec, - type, func, data); + if (priv->front) { + DBG(("%s: replacing Window frontbuffer (was handle=%d) now handle=%d\n", + __FUNCTION__, + get_private(priv->front)->bo->handle, + get_private(tmp)->bo->handle)); + assert(front == priv->front); + assert(get_private(priv->front)->refcnt > 1); + get_private(priv->front)->refcnt--; + } + priv->front = tmp; + + if (get_private(front)->proxy) { + DBG(("%s: reusing current proxy frontbuffer\n", __FUNCTION__)); + front->attachment = DRI2BufferBackLeft; + ref(get_private(tmp)->bo); + back->attachment = DRI2BufferFrontLeft; + } else { + DBG(("%s: allocating new backbuffer\n", __FUNCTION__)); + back->name = 0; + back->flags = 0; + bo = kgem_create_2d(&sna->kgem, + draw->width, draw->height, draw->bitsPerPixel, + get_private(back)->bo->tiling, + CREATE_SCANOUT | CREATE_EXACT); + if (bo != NULL) { + get_private(back)->bo = bo; + back->pitch = bo->pitch; + get_private(back)->size = draw->height << 16 | draw->width; + back->name = kgem_bo_flink(&sna->kgem, bo); + } + if (back->name == 0) { + if (bo != NULL) + kgem_bo_destroy(&sna->kgem, bo); + get_private(back)->bo = NULL; + back->attachment = DRI2BufferFrontLeft; + } + } } static void chain_swap(struct sna_dri2_event *chain) { - union drm_wait_vblank vbl; + DBG(("%s: draw=%ld, queued?=%d, type=%d\n", + __FUNCTION__, (long)chain->draw->id, chain->queued, chain->type)); if (chain->draw == NULL) { sna_dri2_event_free(chain); @@ -2023,25 +2459,15 @@ static void chain_swap(struct sna_dri2_event *chain) return; assert(chain == dri2_chain(chain->draw)); - DBG(("%s: chaining draw=%ld, type=%d\n", - __FUNCTION__, (long)chain->draw->id, chain->type)); - chain->queued = true; + assert(chain->signal); switch (chain->type) { - case SWAP_THROTTLE: + case SWAP_COMPLETE: DBG(("%s: emitting chained vsync'ed blit\n", __FUNCTION__)); - if (chain->sna->mode.shadow && - !chain->sna->mode.shadow_damage) { + if (chain->sna->mode.shadow && chain->sna->mode.shadow_wait) { /* recursed from wait_for_shadow(), simply requeue */ DBG(("%s -- recursed from wait_for_shadow(), requeuing\n", __FUNCTION__)); - VG_CLEAR(vbl); - vbl.request.type = - DRM_VBLANK_RELATIVE | - DRM_VBLANK_EVENT; - vbl.request.sequence = 1; - vbl.request.signal = (uintptr_t)chain; - - if (!sna_wait_vblank(chain->sna, &vbl, chain->pipe)) + if (sna_next_vblank(chain)) return; DBG(("%s -- requeue failed, errno=%d\n", __FUNCTION__, errno)); @@ -2049,35 +2475,27 @@ static void chain_swap(struct sna_dri2_event *chain) if (can_xchg(chain->sna, chain->draw, chain->front, chain->back)) { sna_dri2_xchg(chain->draw, chain->front, chain->back); - } else if (can_xchg_crtc(chain->sna, chain->draw, chain->front, chain->back, chain->crtc)) { - sna_dri2_xchg_crtc(chain->sna, chain->draw, chain->crtc, chain->front, chain->back); + } else if (can_xchg_crtc(chain->sna, chain->draw, chain->crtc, + chain->front, chain->back)) { + sna_dri2_xchg_crtc(chain->sna, chain->draw, chain->crtc, + chain->front, chain->back); } else { - assert(chain->queued); - chain->bo = __sna_dri2_copy_region(chain->sna, chain->draw, NULL, - chain->back, chain->front, - true); + __sna_dri2_copy_event(chain, chain->sync | DRI2_BO); } + assert(get_private(chain->back)->bo != get_private(chain->front)->bo); case SWAP: break; default: return; } - VG_CLEAR(vbl); - vbl.request.type = - DRM_VBLANK_RELATIVE | - DRM_VBLANK_EVENT; - vbl.request.sequence = 1; - vbl.request.signal = (uintptr_t)chain; - if (sna_wait_vblank(chain->sna, &vbl, chain->pipe)) { + if ((chain->type == SWAP_COMPLETE && + !swap_limit(chain->draw, 2 + !chain->sync) && + !chain->sync) || + !sna_next_vblank(chain)) { DBG(("%s: vblank wait failed, unblocking client\n", __FUNCTION__)); frame_swap_complete(chain, DRI2_BLIT_COMPLETE); sna_dri2_event_free(chain); - } else { - if (chain->type == SWAP_THROTTLE && !swap_limit(chain->draw, 2)) { - DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__)); - frame_swap_complete(chain, DRI2_BLIT_COMPLETE); - } } } @@ -2099,27 +2517,20 @@ static inline bool rq_is_busy(struct kgem *kgem, struct kgem_bo *bo) return __kgem_busy(kgem, bo->handle); } -static bool sna_dri2_blit_complete(struct sna *sna, - struct sna_dri2_event *info) +static bool sna_dri2_blit_complete(struct sna_dri2_event *info) { - if (rq_is_busy(&sna->kgem, info->bo)) { - union drm_wait_vblank vbl; - + if (rq_is_busy(&info->sna->kgem, info->bo)) { DBG(("%s: vsync'ed blit is still busy, postponing\n", __FUNCTION__)); - - VG_CLEAR(vbl); - vbl.request.type = - DRM_VBLANK_RELATIVE | - DRM_VBLANK_EVENT; - vbl.request.sequence = 1; - vbl.request.signal = (uintptr_t)info; - assert(info->queued); - if (!sna_wait_vblank(sna, &vbl, info->pipe)) + if (sna_next_vblank(info)) return false; } DBG(("%s: blit finished\n", __FUNCTION__)); + if (info->bo) { + kgem_bo_destroy(&info->sna->kgem, info->bo); + info->bo = NULL; + } return true; } @@ -2128,11 +2539,12 @@ void sna_dri2_vblank_handler(struct drm_event_vblank *event) struct sna_dri2_event *info = (void *)(uintptr_t)event->user_data; struct sna *sna = info->sna; DrawablePtr draw; - union drm_wait_vblank vbl; uint64_t msc; - DBG(("%s(type=%d, sequence=%d)\n", __FUNCTION__, info->type, event->sequence)); + DBG(("%s(type=%d, sequence=%d, draw=%ld)\n", __FUNCTION__, info->type, event->sequence, info->draw ? info->draw->serialNumber : 0)); assert(info->queued); + info->queued = false; + msc = sna_crtc_record_event(info->crtc, event); draw = info->draw; @@ -2144,65 +2556,100 @@ void sna_dri2_vblank_handler(struct drm_event_vblank *event) switch (info->type) { case FLIP: /* If we can still flip... */ + assert(info->signal); if (can_flip(sna, draw, info->front, info->back, info->crtc) && sna_dri2_flip(info)) return; /* else fall through to blit */ case SWAP: - assert(info->queued); - if (sna->mode.shadow && !sna->mode.shadow_damage) { + assert(info->signal); + if (sna->mode.shadow && sna->mode.shadow_wait) { /* recursed from wait_for_shadow(), simply requeue */ DBG(("%s -- recursed from wait_for_shadow(), requeuing\n", __FUNCTION__)); - } else if (can_xchg(info->sna, draw, info->front, info->back)) { sna_dri2_xchg(draw, info->front, info->back); - info->type = SWAP_WAIT; - } else if (can_xchg_crtc(sna, draw, info->front, info->back, info->crtc)) { - sna_dri2_xchg_crtc(sna, draw, info->crtc, info->front, info->back); - info->type = SWAP_WAIT; + info->type = SWAP_COMPLETE; + } else if (can_xchg_crtc(sna, draw, info->crtc, + info->front, info->back)) { + sna_dri2_xchg_crtc(sna, draw, info->crtc, + info->front, info->back); + info->type = SWAP_COMPLETE; } else { - assert(info->queued); - info->bo = __sna_dri2_copy_region(sna, draw, NULL, - info->back, info->front, true); - info->type = SWAP_WAIT; + __sna_dri2_copy_event(info, DRI2_BO | DRI2_SYNC); + info->type = SWAP_COMPLETE; } - VG_CLEAR(vbl); - vbl.request.type = - DRM_VBLANK_RELATIVE | - DRM_VBLANK_EVENT; - vbl.request.sequence = 1; - vbl.request.signal = (uintptr_t)info; - - assert(info->queued); - if (!sna_wait_vblank(sna, &vbl, info->pipe)) + if (sna_next_vblank(info)) return; DBG(("%s -- requeue failed, errno=%d\n", __FUNCTION__, errno)); + assert(info->pending.bo == NULL); + assert(info->keepalive == 1); /* fall through to SwapComplete */ - case SWAP_WAIT: - if (!sna_dri2_blit_complete(sna, info)) - return; - - DBG(("%s: swap complete, unblocking client (frame=%d, tv=%d.%06d)\n", __FUNCTION__, - event->sequence, event->tv_sec, event->tv_usec)); - frame_swap_complete(info, DRI2_BLIT_COMPLETE); - break; - - case SWAP_THROTTLE: + case SWAP_COMPLETE: DBG(("%s: %d complete, frame=%d tv=%d.%06d\n", __FUNCTION__, info->type, event->sequence, event->tv_sec, event->tv_usec)); - if (xorg_can_triple_buffer()) { - if (!sna_dri2_blit_complete(sna, info)) + if (info->signal) { + if (!sna_dri2_blit_complete(info)) return; DBG(("%s: triple buffer swap complete, unblocking client (frame=%d, tv=%d.%06d)\n", __FUNCTION__, event->sequence, event->tv_sec, event->tv_usec)); frame_swap_complete(info, DRI2_BLIT_COMPLETE); } + + if (info->pending.bo) { + if (sna->mode.shadow && sna->mode.shadow_wait) { + /* recursed from wait_for_shadow(), simply requeue */ + DBG(("%s -- recursed from wait_for_shadow(), requeuing\n", __FUNCTION__)); + if (sna_next_vblank(info)) + return; + } + + assert(info->pending.bo->active_scanout > 0); + info->pending.bo->active_scanout--; + + sna_dri2_cache_bo(info->sna, info->draw, + get_private(info->back)->bo, + info->back->name, + get_private(info->back)->size, + info->back->flags); + + get_private(info->back)->bo = info->pending.bo; + get_private(info->back)->size = info->pending.size; + info->back->name = info->pending.name; + info->back->pitch = info->pending.bo->pitch; + info->back->flags = info->pending.flags; + info->pending.bo = NULL; + + assert(get_private(info->back)->bo != get_private(info->front)->bo); + + if (can_xchg(info->sna, info->draw, info->front, info->back)) + sna_dri2_xchg(info->draw, info->front, info->back); + else if (can_xchg_crtc(info->sna, info->draw, info->crtc, + info->front, info->back)) + sna_dri2_xchg_crtc(info->sna, info->draw, info->crtc, + info->front, info->back); + else + __sna_dri2_copy_event(info, info->sync | DRI2_BO); + + info->keepalive++; + info->signal = true; + } + + if (--info->keepalive) { + if (sna_next_vblank(info)) + return; + + if (info->signal) { + DBG(("%s: triple buffer swap complete, unblocking client (frame=%d, tv=%d.%06d)\n", __FUNCTION__, + event->sequence, event->tv_sec, event->tv_usec)); + frame_swap_complete(info, DRI2_BLIT_COMPLETE); + } + } break; case WAITMSC: @@ -2218,11 +2665,11 @@ void sna_dri2_vblank_handler(struct drm_event_vblank *event) } if (info->chain) { + DBG(("%s: continuing chain\n", __FUNCTION__)); assert(info->chain != info); assert(info->draw == draw); - sna_dri2_remove_event((WindowPtr)draw, info); + sna_dri2_remove_event(info); chain_swap(info->chain); - info->draw = NULL; } done: @@ -2230,101 +2677,139 @@ done: DBG(("%s complete\n", __FUNCTION__)); } -static bool +static void sna_dri2_immediate_blit(struct sna *sna, struct sna_dri2_event *info, - bool sync, bool event) + bool sync) { DrawablePtr draw = info->draw; - bool ret = false; + struct sna_dri2_event *chain = dri2_chain(draw); if (sna->flags & SNA_NO_WAIT) sync = false; - DBG(("%s: emitting immediate blit, throttling client, synced? %d, chained? %d, send-event? %d\n", - __FUNCTION__, sync, dri2_chain(draw) != info, - event)); + DBG(("%s: emitting immediate blit, throttling client, synced? %d, chained? %d, pipe %d\n", + __FUNCTION__, sync, chain != info, info->pipe)); - info->type = SWAP_THROTTLE; - if (!sync || dri2_chain(draw) == info) { - DBG(("%s: no pending blit, starting chain\n", - __FUNCTION__)); + info->type = SWAP_COMPLETE; + info->sync = sync; + info->keepalive = KEEPALIVE; - info->queued = true; - info->bo = __sna_dri2_copy_region(sna, draw, NULL, - info->back, - info->front, - sync); - if (event) { - if (sync) { - union drm_wait_vblank vbl; - - VG_CLEAR(vbl); - vbl.request.type = - DRM_VBLANK_RELATIVE | - DRM_VBLANK_EVENT; - vbl.request.sequence = 1; - vbl.request.signal = (uintptr_t)info; - ret = !sna_wait_vblank(sna, &vbl, info->pipe); - if (ret) - event = !swap_limit(draw, 2); - } - if (event) { - DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__)); - frame_swap_complete(info, DRI2_BLIT_COMPLETE); - } + if (chain == info) { + DBG(("%s: no pending blit, starting chain\n", __FUNCTION__)); + + if (can_xchg(info->sna, info->draw, info->front, info->back)) { + sna_dri2_xchg(info->draw, info->front, info->back); + } else if (can_xchg_crtc(info->sna, info->draw, info->crtc, + info->front, info->back)) { + sna_dri2_xchg_crtc(info->sna, info->draw, info->crtc, + info->front, info->back); + } else + __sna_dri2_copy_event(info, sync | DRI2_BO); + + assert(info->signal); + + if ((!swap_limit(draw, 2 + !sync) && !sync) || + !sna_next_vblank(info)) { + DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__)); + frame_swap_complete(info, DRI2_BLIT_COMPLETE); + sna_dri2_event_free(info); + } + return; + } + + if (chain->type == SWAP_COMPLETE) { + assert(chain->draw == info->draw); + assert(chain->client == info->client); + assert(chain->event_complete == info->event_complete); + assert(chain->event_data == info->event_data); + assert(chain->queued); + + if ((!sync || !chain->sync) && chain->pending.bo) { + bool signal = chain->signal; + + DBG(("%s: swap elision, unblocking client\n", __FUNCTION__)); + chain->signal = true; + frame_swap_complete(chain, DRI2_EXCHANGE_COMPLETE); + chain->signal = signal; + + assert(chain->pending.bo->active_scanout > 0); + chain->pending.bo->active_scanout--; + + sna_dri2_cache_bo(chain->sna, chain->draw, + chain->pending.bo, + chain->pending.name, + chain->pending.size, + chain->pending.flags); + chain->pending.bo = NULL; + } + + if (chain->pending.bo == NULL && swap_limit(draw, 2 + !sync)) { + DBG(("%s: setting handle=%d as pending blit (current event front=%d, back=%d)\n", __FUNCTION__, + get_private(info->back)->bo->handle, + get_private(chain->front)->bo->handle, + get_private(chain->back)->bo->handle)); + chain->pending.bo = ref(get_private(info->back)->bo); + chain->pending.size = get_private(info->back)->size; + chain->pending.name = info->back->name; + chain->pending.flags = info->back->flags; + chain->sync = sync; + info->signal = false; /* transfer signal to pending */ + + /* Prevent us from handing it back on next GetBuffers */ + chain->pending.bo->active_scanout++; + + sna_dri2_event_free(info); + return; } - } else { - DBG(("%s: pending blit, chained\n", __FUNCTION__)); - ret = true; } - DBG(("%s: continue? %d\n", __FUNCTION__, ret)); - return ret; + DBG(("%s: pending blit, chained\n", __FUNCTION__)); } static bool sna_dri2_flip_continue(struct sna_dri2_event *info) { - DBG(("%s(mode=%d)\n", __FUNCTION__, info->mode)); - - if (info->mode > 0){ - struct kgem_bo *bo = get_private(info->front)->bo; + struct kgem_bo *bo = get_private(info->front)->bo; - info->type = info->mode; + DBG(("%s(mode=%d)\n", __FUNCTION__, info->flip_continue)); + assert(info->flip_continue > 0); + info->type = info->flip_continue; + info->flip_continue = 0; - if (bo != sna_pixmap(info->sna->front)->gpu_bo) - return false; + if (info->sna->mode.front_active == 0) + return false; - if (!sna_page_flip(info->sna, bo, sna_dri2_flip_handler, info)) - return false; + if (bo != sna_pixmap(info->sna->front)->gpu_bo) + return false; - assert(info->sna->dri2.flip_pending == NULL || - info->sna->dri2.flip_pending == info); - info->sna->dri2.flip_pending = info; - assert(info->queued); - } else { - info->type = -info->mode; + if (!sna_page_flip(info->sna, bo, sna_dri2_flip_handler, info)) + return false; - if (!info->draw) - return false; + DBG(("%s: queued flip=%p\n", __FUNCTION__, info)); + assert(info->sna->dri2.flip_pending == NULL || + info->sna->dri2.flip_pending == info); + info->sna->dri2.flip_pending = info; + info->queued = true; + info->signal = info->type == FLIP_THROTTLE; - if (!can_flip(info->sna, info->draw, info->front, info->back, info->crtc)) - return false; + return true; +} - assert(sna_pixmap_get_buffer(get_drawable_pixmap(info->draw)) == info->front); - if (!sna_dri2_flip(info)) - return false; +static bool +sna_dri2_flip_keepalive(struct sna_dri2_event *info) +{ + DBG(("%s(keepalive?=%d)\n", __FUNCTION__, info->keepalive-1)); + assert(info->keepalive > 0); + if (!--info->keepalive) + return false; - if (!xorg_can_triple_buffer()) { - sna_dri2_get_back(info->sna, info->draw, info->back, info); - DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__)); - frame_swap_complete(info, DRI2_FLIP_COMPLETE); - } - } + if (info->draw == NULL) + return false; - info->mode = 0; - return true; + DBG(("%s: marking next flip as complete\n", __FUNCTION__)); + info->flip_continue = FLIP_COMPLETE; + return sna_dri2_flip_continue(info); } static void chain_flip(struct sna *sna) @@ -2332,8 +2817,8 @@ static void chain_flip(struct sna *sna) struct sna_dri2_event *chain = sna->dri2.flip_pending; assert(chain->type == FLIP); - DBG(("%s: chaining type=%d, cancelled?=%d\n", - __FUNCTION__, chain->type, chain->draw == NULL)); + DBG(("%s: chaining type=%d, cancelled?=%d window=%ld\n", + __FUNCTION__, chain->type, chain->draw == NULL, chain->draw ? chain->draw->id : 0)); sna->dri2.flip_pending = NULL; if (chain->draw == NULL) { @@ -2343,31 +2828,18 @@ static void chain_flip(struct sna *sna) assert(chain == dri2_chain(chain->draw)); assert(!chain->queued); - chain->queued = true; if (can_flip(sna, chain->draw, chain->front, chain->back, chain->crtc) && sna_dri2_flip(chain)) { DBG(("%s: performing chained flip\n", __FUNCTION__)); } else { DBG(("%s: emitting chained vsync'ed blit\n", __FUNCTION__)); - chain->bo = __sna_dri2_copy_region(sna, chain->draw, NULL, - chain->back, chain->front, - true); + __sna_dri2_copy_event(chain, DRI2_SYNC); if (xorg_can_triple_buffer()) { - union drm_wait_vblank vbl; - - VG_CLEAR(vbl); - - chain->type = SWAP_WAIT; - vbl.request.type = - DRM_VBLANK_RELATIVE | - DRM_VBLANK_EVENT; - vbl.request.sequence = 1; - vbl.request.signal = (uintptr_t)chain; - - assert(chain->queued); - if (!sna_wait_vblank(sna, &vbl, chain->pipe)) + chain->type = SWAP_COMPLETE; + assert(chain->signal); + if (sna_next_vblank(chain)) return; } @@ -2381,8 +2853,9 @@ static void sna_dri2_flip_event(struct sna_dri2_event *flip) { struct sna *sna = flip->sna; - DBG(("%s(pipe=%d, event=%d)\n", __FUNCTION__, flip->pipe, flip->type)); + DBG(("%s flip=%p (pipe=%d, event=%d)\n", __FUNCTION__, flip, flip->pipe, flip->type)); assert(flip->queued); + flip->queued = false; if (sna->dri2.flip_pending == flip) sna->dri2.flip_pending = NULL; @@ -2399,27 +2872,35 @@ static void sna_dri2_flip_event(struct sna_dri2_event *flip) break; case FLIP_THROTTLE: - DBG(("%s: triple buffer swap complete, unblocking client\n", __FUNCTION__)); - frame_swap_complete(flip, DRI2_FLIP_COMPLETE); + if (flip->signal) { + DBG(("%s: triple buffer swap complete, unblocking client\n", __FUNCTION__)); + frame_swap_complete(flip, DRI2_FLIP_COMPLETE); + } case FLIP_COMPLETE: + assert(!flip->signal); if (sna->dri2.flip_pending) { + DBG(("%s: pending flip\n", __FUNCTION__)); sna_dri2_event_free(flip); chain_flip(sna); - } else if (!flip->mode) { + } else if (!flip->flip_continue) { DBG(("%s: flip chain complete\n", __FUNCTION__)); + if (!sna_dri2_flip_keepalive(flip)) { + if (flip->chain) { + sna_dri2_remove_event(flip); + chain_swap(flip->chain); + } - if (flip->chain) { - sna_dri2_remove_event((WindowPtr)flip->draw, - flip); - chain_swap(flip->chain); - flip->draw = NULL; + sna_dri2_event_free(flip); } - - sna_dri2_event_free(flip); } else if (!sna_dri2_flip_continue(flip)) { DBG(("%s: no longer able to flip\n", __FUNCTION__)); - if (flip->draw == NULL || !sna_dri2_immediate_blit(sna, flip, false, flip->mode < 0)) - sna_dri2_event_free(flip); + if (flip->draw != NULL) + __sna_dri2_copy_event(flip, 0); + if (flip->signal) { + DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__)); + frame_swap_complete(flip, DRI2_BLIT_COMPLETE); + } + sna_dri2_event_free(flip); } break; @@ -2433,17 +2914,27 @@ static void sna_dri2_flip_event(struct sna_dri2_event *flip) } } +static int +sna_query_vblank(struct sna *sna, xf86CrtcPtr crtc, union drm_wait_vblank *vbl) +{ + VG_CLEAR(*vbl); + vbl->request.type = + _DRM_VBLANK_RELATIVE | pipe_select(sna_crtc_pipe(crtc)); + vbl->request.sequence = 0; + + return drmIoctl(sna->kgem.fd, DRM_IOCTL_WAIT_VBLANK, vbl); +} + static uint64_t get_current_msc(struct sna *sna, DrawablePtr draw, xf86CrtcPtr crtc) { union drm_wait_vblank vbl; - uint64_t ret = -1; + uint64_t ret; - VG_CLEAR(vbl); - vbl.request.type = _DRM_VBLANK_RELATIVE; - vbl.request.sequence = 0; - if (sna_wait_vblank(sna, &vbl, sna_crtc_to_pipe(crtc)) == 0) + if (sna_query_vblank(sna, crtc, &vbl) == 0) ret = sna_crtc_record_vblank(crtc, &vbl); + else + ret = sna_crtc_last_swap(crtc)->msc; return draw_current_msc(draw, crtc, ret); } @@ -2494,12 +2985,18 @@ static int use_triple_buffer(struct sna *sna, ClientPtr client, bool async) } static bool immediate_swap(struct sna *sna, - uint64_t target_msc, - uint64_t divisor, DrawablePtr draw, xf86CrtcPtr crtc, + uint64_t *target_msc, + uint64_t divisor, + uint64_t remainder, uint64_t *current_msc) { + /* + * If divisor is zero, or current_msc is smaller than target_msc + * we just need to make sure target_msc passes before initiating + * the swap. + */ if (divisor == 0) { *current_msc = -1; @@ -2508,71 +3005,86 @@ static bool immediate_swap(struct sna *sna, return true; } - if (target_msc) + if (*target_msc) *current_msc = get_current_msc(sna, draw, crtc); DBG(("%s: current_msc=%ld, target_msc=%ld -- %s\n", - __FUNCTION__, (long)*current_msc, (long)target_msc, - (*current_msc >= target_msc - 1) ? "yes" : "no")); - return *current_msc >= target_msc - 1; + __FUNCTION__, (long)*current_msc, (long)*target_msc, + (*current_msc >= *target_msc - 1) ? "yes" : "no")); + return *current_msc >= *target_msc - 1; } DBG(("%s: explicit waits requests, divisor=%ld\n", __FUNCTION__, (long)divisor)); *current_msc = get_current_msc(sna, draw, crtc); - return false; + if (*current_msc >= *target_msc) { + DBG(("%s: missed target, queueing event for next: current=%lld, target=%lld, divisor=%lld, remainder=%lld\n", + __FUNCTION__, + (long long)*current_msc, + (long long)*target_msc, + (long long)divisor, + (long long)remainder)); + + *target_msc = *current_msc + remainder - *current_msc % divisor; + if (*target_msc <= *current_msc) + *target_msc += divisor; + } + + DBG(("%s: target_msc=%lld, current_msc=%lld, immediate?=%d\n", + __FUNCTION__, (long long)*target_msc, (long long)*current_msc, + *current_msc >= *target_msc - 1)); + return *current_msc >= *target_msc - 1; } static bool sna_dri2_schedule_flip(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc, DRI2BufferPtr front, DRI2BufferPtr back, - CARD64 *target_msc, CARD64 divisor, CARD64 remainder, + bool immediate, CARD64 *target_msc, CARD64 current_msc, DRI2SwapEventPtr func, void *data) { struct sna *sna = to_sna_from_drawable(draw); struct sna_dri2_event *info; - uint64_t current_msc; - - if (immediate_swap(sna, *target_msc, divisor, draw, crtc, ¤t_msc)) { - int type; + if (immediate) { + bool signal = false; info = sna->dri2.flip_pending; DBG(("%s: performing immediate swap on pipe %d, pending? %d, mode: %d, continuation? %d\n", - __FUNCTION__, sna_crtc_to_pipe(crtc), - info != NULL, info ? info->mode : 0, + __FUNCTION__, sna_crtc_pipe(crtc), + info != NULL, info ? info->flip_continue : 0, info && info->draw == draw)); if (info && info->draw == draw) { assert(info->type != FLIP); assert(info->front == front); + assert(info->queued); if (info->back != back) { - _sna_dri2_destroy_buffer(sna, info->back); + _sna_dri2_destroy_buffer(sna, draw, info->back); info->back = sna_dri2_reference_buffer(back); } - if (info->mode || current_msc >= *target_msc) { - DBG(("%s: executing xchg of pending flip\n", - __FUNCTION__)); - sna_dri2_xchg(draw, front, back); - info->mode = type = FLIP_COMPLETE; - goto new_back; - } else { + DBG(("%s: executing xchg of pending flip: flip_continue=%d, keepalive=%d, chain?=%d\n", __FUNCTION__, info->flip_continue, info->keepalive, current_msc < *target_msc)); + sna_dri2_xchg(draw, front, back); + info->keepalive = KEEPALIVE; + if (xorg_can_triple_buffer() && + current_msc < *target_msc) { DBG(("%s: chaining flip\n", __FUNCTION__)); - type = FLIP_THROTTLE; - if (xorg_can_triple_buffer()) - info->mode = -type; - else - info->mode = -FLIP_COMPLETE; + info->flip_continue = FLIP_THROTTLE; goto out; + } else { + info->flip_continue = FLIP_COMPLETE; + signal = info->signal; + info->signal = true; + goto new_back; } } - info = sna_dri2_add_event(sna, draw, client); + info = sna_dri2_add_event(sna, draw, client, crtc); if (info == NULL) return false; assert(info->crtc == crtc); info->event_complete = func; info->event_data = data; + info->signal = true; info->front = sna_dri2_reference_buffer(front); info->back = sna_dri2_reference_buffer(back); @@ -2584,26 +3096,31 @@ sna_dri2_schedule_flip(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc, */ DBG(("%s: queueing flip after pending completion\n", __FUNCTION__)); - info->type = type = FLIP; + info->type = FLIP; sna->dri2.flip_pending = info; - assert(info->queued); current_msc++; + } else if (sna->mode.flip_active) { + DBG(("%s: %d outstanding flips from old client, queueing\n", + __FUNCTION__, sna->mode.flip_active)); + goto queue; } else { - info->type = type = use_triple_buffer(sna, client, *target_msc == 0); + info->type = use_triple_buffer(sna, client, *target_msc == 0); if (!sna_dri2_flip(info)) { DBG(("%s: flip failed, falling back\n", __FUNCTION__)); sna_dri2_event_free(info); return false; } + assert(get_private(info->front)->bo->active_scanout); } - swap_limit(draw, 1 + (type == FLIP_THROTTLE)); - if (type >= FLIP_COMPLETE) { + swap_limit(draw, 1 + (info->type == FLIP_THROTTLE)); + if (info->type >= FLIP_COMPLETE) { new_back: if (!xorg_can_triple_buffer()) - sna_dri2_get_back(sna, draw, back, info); + sna_dri2_get_back(sna, draw, back); DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__)); frame_swap_complete(info, DRI2_EXCHANGE_COMPLETE); + info->signal = signal; if (info->type == FLIP_ASYNC) sna_dri2_event_free(info); } @@ -2613,57 +3130,31 @@ out: return true; } - info = sna_dri2_add_event(sna, draw, client); +queue: + if (KEEPALIVE > 1 && sna->dri2.flip_pending) { + info = sna->dri2.flip_pending; + info->keepalive = 1; + } + + info = sna_dri2_add_event(sna, draw, client, crtc); if (info == NULL) return false; assert(info->crtc == crtc); info->event_complete = func; info->event_data = data; + info->signal = true; info->type = FLIP; info->front = sna_dri2_reference_buffer(front); info->back = sna_dri2_reference_buffer(back); - /* - * If divisor is zero, or current_msc is smaller than target_msc - * we just need to make sure target_msc passes before initiating - * the swap. - */ - if (divisor && current_msc >= *target_msc) { - DBG(("%s: missed target, queueing event for next: current=%lld, target=%lld, divisor=%lld, remainder=%lld\n", - __FUNCTION__, - (long long)current_msc, - (long long)*target_msc, - (long long)divisor, - (long long)remainder)); - - *target_msc = current_msc + remainder - current_msc % divisor; - if (*target_msc <= current_msc) - *target_msc += divisor; - } - - if (*target_msc <= current_msc + 1) { - if (!sna_dri2_flip(info)) { - sna_dri2_event_free(info); - return false; - } + if (*target_msc <= current_msc + 1 && sna_dri2_flip(info)) { *target_msc = current_msc + 1; } else { - union drm_wait_vblank vbl; - - VG_CLEAR(vbl); - - vbl.request.type = - DRM_VBLANK_ABSOLUTE | - DRM_VBLANK_EVENT; - /* Account for 1 frame extra pageflip delay */ - vbl.reply.sequence = draw_target_seq(draw, *target_msc - 1); - vbl.request.signal = (uintptr_t)info; - - info->queued = true; - if (sna_wait_vblank(sna, &vbl, info->pipe)) { + if (!sna_wait_vblank(info, + draw_target_seq(draw, *target_msc - 1))) { sna_dri2_event_free(info); return false; } @@ -2674,128 +3165,6 @@ out: return true; } -static bool -sna_dri2_schedule_xchg(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc, - DRI2BufferPtr front, DRI2BufferPtr back, - CARD64 *target_msc, CARD64 divisor, CARD64 remainder, - DRI2SwapEventPtr func, void *data) -{ - struct sna *sna = to_sna_from_drawable(draw); - uint64_t current_msc; - bool sync, event; - - if (!immediate_swap(sna, *target_msc, divisor, draw, crtc, ¤t_msc)) - return false; - - sync = current_msc < *target_msc; - event = dri2_chain(draw) == NULL; - if (!sync || event) { - DBG(("%s: performing immediate xchg on pipe %d\n", - __FUNCTION__, sna_crtc_to_pipe(crtc))); - sna_dri2_xchg(draw, front, back); - } - if (sync) { - struct sna_dri2_event *info; - - info = sna_dri2_add_event(sna, draw, client); - if (!info) - goto complete; - - info->event_complete = func; - info->event_data = data; - - info->front = sna_dri2_reference_buffer(front); - info->back = sna_dri2_reference_buffer(back); - info->type = SWAP_THROTTLE; - - if (event) { - union drm_wait_vblank vbl; - - VG_CLEAR(vbl); - vbl.request.type = - DRM_VBLANK_RELATIVE | - DRM_VBLANK_EVENT; - vbl.request.sequence = 1; - vbl.request.signal = (uintptr_t)info; - - info->queued = true; - if (sna_wait_vblank(sna, &vbl, info->pipe)) { - sna_dri2_event_free(info); - goto complete; - } - - swap_limit(draw, 2); - } - } else { -complete: - fake_swap_complete(sna, client, draw, crtc, DRI2_EXCHANGE_COMPLETE, func, data); - } - - *target_msc = current_msc + 1; - return true; -} - -static bool -sna_dri2_schedule_xchg_crtc(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc, - DRI2BufferPtr front, DRI2BufferPtr back, - CARD64 *target_msc, CARD64 divisor, CARD64 remainder, - DRI2SwapEventPtr func, void *data) -{ - struct sna *sna = to_sna_from_drawable(draw); - uint64_t current_msc; - bool sync, event; - - if (!immediate_swap(sna, *target_msc, divisor, draw, crtc, ¤t_msc)) - return false; - - sync = current_msc < *target_msc; - event = dri2_chain(draw) == NULL; - if (!sync || event) { - DBG(("%s: performing immediate xchg only on pipe %d\n", - __FUNCTION__, sna_crtc_to_pipe(crtc))); - sna_dri2_xchg_crtc(sna, draw, crtc, front, back); - } - if (sync) { - struct sna_dri2_event *info; - - info = sna_dri2_add_event(sna, draw, client); - if (!info) - goto complete; - - info->event_complete = func; - info->event_data = data; - - info->front = sna_dri2_reference_buffer(front); - info->back = sna_dri2_reference_buffer(back); - info->type = SWAP_THROTTLE; - - if (event) { - union drm_wait_vblank vbl; - - VG_CLEAR(vbl); - vbl.request.type = - DRM_VBLANK_RELATIVE | - DRM_VBLANK_EVENT; - vbl.request.sequence = 1; - vbl.request.signal = (uintptr_t)info; - - info->queued = true; - if (sna_wait_vblank(sna, &vbl, info->pipe)) { - sna_dri2_event_free(info); - goto complete; - } - - swap_limit(draw, 2); - } - } else { -complete: - fake_swap_complete(sna, client, draw, crtc, DRI2_EXCHANGE_COMPLETE, func, data); - } - - *target_msc = current_msc + 1; - return true; -} - static bool has_pending_events(struct sna *sna) { struct pollfd pfd; @@ -2830,11 +3199,11 @@ sna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, CARD64 remainder, DRI2SwapEventPtr func, void *data) { struct sna *sna = to_sna_from_drawable(draw); - union drm_wait_vblank vbl; xf86CrtcPtr crtc = NULL; struct sna_dri2_event *info = NULL; int type = DRI2_EXCHANGE_COMPLETE; CARD64 current_msc; + bool immediate; DBG(("%s: draw=%lu %dx%d, pixmap=%ld %dx%d, back=%u (refs=%d/%d, flush=%d) , front=%u (refs=%d/%d, flush=%d)\n", __FUNCTION__, @@ -2860,33 +3229,22 @@ sna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, assert(get_private(front)->refcnt); assert(get_private(back)->refcnt); + assert(get_private(back)->bo != get_private(front)->bo); assert(get_private(front)->bo->refcnt); assert(get_private(back)->bo->refcnt); - if (get_private(front)->pixmap != get_drawable_pixmap(draw)) { - DBG(("%s: decoupled DRI2 front pixmap=%ld, actual pixmap=%ld\n", - __FUNCTION__, - get_private(front)->pixmap->drawable.serialNumber, - get_drawable_pixmap(draw)->drawable.serialNumber)); - goto skip; - } - if (get_private(back)->stale) { DBG(("%s: stale back buffer\n", __FUNCTION__)); goto skip; } - assert(sna_pixmap_from_drawable(draw)->flush); - if (draw->type != DRAWABLE_PIXMAP) { WindowPtr win = (WindowPtr)draw; struct dri2_window *priv = dri2_window(win); - if (priv->front) { - assert(front == priv->front); - assert(get_private(priv->front)->refcnt > 1); - get_private(priv->front)->refcnt--; - priv->front = NULL; - } + + if (priv->front) + front = priv->front; + if (win->clipList.extents.x2 <= win->clipList.extents.x1 || win->clipList.extents.y2 <= win->clipList.extents.y1) { DBG(("%s: window clipped (%d, %d), (%d, %d)\n", @@ -2899,6 +3257,16 @@ sna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, } } + if (get_private(front)->pixmap != get_drawable_pixmap(draw)) { + DBG(("%s: decoupled DRI2 front pixmap=%ld, actual pixmap=%ld\n", + __FUNCTION__, + get_private(front)->pixmap->drawable.serialNumber, + get_drawable_pixmap(draw)->drawable.serialNumber)); + goto skip; + } + + assert(sna_pixmap_from_drawable(draw)->flush); + /* Drawable not displayed... just complete the swap */ if ((sna->flags & SNA_NO_WAIT) == 0) crtc = sna_dri2_get_crtc(draw); @@ -2914,109 +3282,106 @@ sna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, sna_mode_wakeup(sna); } - if (can_xchg(sna, draw, front, back) && - sna_dri2_schedule_xchg(client, draw, crtc, front, back, + immediate = immediate_swap(sna, draw, crtc, target_msc, divisor, remainder, - func, data)) - return TRUE; - - if (can_xchg_crtc(sna, draw, front, back, crtc) && - sna_dri2_schedule_xchg_crtc(client, draw, crtc, front, back, - target_msc, divisor, remainder, - func, data)) - return TRUE; + ¤t_msc); if (can_flip(sna, draw, front, back, crtc) && sna_dri2_schedule_flip(client, draw, crtc, front, back, - target_msc, divisor, remainder, + immediate, target_msc, current_msc, func, data)) return TRUE; - VG_CLEAR(vbl); - - info = sna_dri2_add_event(sna, draw, client); + info = sna_dri2_add_event(sna, draw, client, crtc); if (!info) goto blit; assert(info->crtc == crtc); info->event_complete = func; info->event_data = data; + info->signal = true; info->front = sna_dri2_reference_buffer(front); info->back = sna_dri2_reference_buffer(back); - if (immediate_swap(sna, *target_msc, divisor, draw, crtc, ¤t_msc)) { + if (immediate) { bool sync = current_msc < *target_msc; - if (!sna_dri2_immediate_blit(sna, info, sync, true)) - sna_dri2_event_free(info); + sna_dri2_immediate_blit(sna, info, sync); *target_msc = current_msc + sync; + DBG(("%s: reported target_msc=%llu\n", + __FUNCTION__, *target_msc)); return TRUE; } - vbl.request.type = - DRM_VBLANK_ABSOLUTE | - DRM_VBLANK_EVENT; - vbl.request.signal = (uintptr_t)info; - - /* - * If divisor is zero, or current_msc is smaller than target_msc - * we just need to make sure target_msc passes before initiating - * the swap. - */ info->type = SWAP; - info->queued = true; - if (divisor && current_msc >= *target_msc) { - DBG(("%s: missed target, queueing event for next: current=%lld, target=%lld, divisor=%lld, remainder=%lld\n", - __FUNCTION__, - (long long)current_msc, - (long long)*target_msc, - (long long)divisor, - (long long)remainder)); - - *target_msc = current_msc + remainder - current_msc % divisor; - if (*target_msc <= current_msc) - *target_msc += divisor; - } - vbl.request.sequence = draw_target_seq(draw, *target_msc - 1); if (*target_msc <= current_msc + 1) { DBG(("%s: performing blit before queueing\n", __FUNCTION__)); - assert(info->queued); - info->bo = __sna_dri2_copy_region(sna, draw, NULL, - back, front, - true); - info->type = SWAP_WAIT; - - vbl.request.type = - DRM_VBLANK_RELATIVE | - DRM_VBLANK_EVENT; - vbl.request.sequence = 1; + __sna_dri2_copy_event(info, DRI2_SYNC); + info->type = SWAP_COMPLETE; + if (!sna_next_vblank(info)) + goto fake; + + DBG(("%s: reported target_msc=%llu\n", + __FUNCTION__, *target_msc)); *target_msc = current_msc + 1; - } + swap_limit(draw, 2); + } else { + if (!sna_wait_vblank(info, + draw_target_seq(draw, *target_msc - 1))) + goto blit; - assert(info->queued); - if (sna_wait_vblank(sna, &vbl, info->pipe)) - goto blit; + DBG(("%s: reported target_msc=%llu (in)\n", + __FUNCTION__, *target_msc)); + swap_limit(draw, 1); + } - DBG(("%s: reported target_msc=%llu\n", __FUNCTION__, *target_msc)); - swap_limit(draw, 1 + (info->type == SWAP_WAIT)); return TRUE; blit: DBG(("%s -- blit\n", __FUNCTION__)); - if (info) - sna_dri2_event_free(info); if (can_xchg(sna, draw, front, back)) { sna_dri2_xchg(draw, front, back); } else { - __sna_dri2_copy_region(sna, draw, NULL, back, front, false); + __sna_dri2_copy_region(sna, draw, NULL, back, front, 0); + front->flags = back->flags; type = DRI2_BLIT_COMPLETE; } + if (draw->type == DRAWABLE_PIXMAP) + goto fake; skip: DBG(("%s: unable to show frame, unblocking client\n", __FUNCTION__)); - if (crtc == NULL) - crtc = sna_mode_first_crtc(sna); - fake_swap_complete(sna, client, draw, crtc, type, func, data); - *target_msc = 0; /* offscreen, so zero out target vblank count */ + if (crtc == NULL && (sna->flags & SNA_NO_WAIT) == 0) + crtc = sna_primary_crtc(sna); + if (crtc && sna_crtc_is_on(crtc)) { + if (info == NULL) + info = sna_dri2_add_event(sna, draw, client, crtc); + if (info != dri2_chain(draw)) + goto fake; + + assert(info->crtc == crtc); + + info->type = SWAP_COMPLETE; + info->event_complete = func; + info->event_data = data; + info->signal = true; + + info->front = sna_dri2_reference_buffer(front); + info->back = sna_dri2_reference_buffer(back); + + if (!sna_next_vblank(info)) + goto fake; + + swap_limit(draw, 2); + } else { +fake: + /* XXX Use a Timer to throttle the client? */ + fake_swap_complete(sna, client, draw, crtc, type, func, data); + if (info) { + info->signal = false; + sna_dri2_event_free(info); + } + } + DBG(("%s: reported target_msc=%llu (in)\n", __FUNCTION__, *target_msc)); return TRUE; } @@ -3030,27 +3395,25 @@ sna_dri2_get_msc(DrawablePtr draw, CARD64 *ust, CARD64 *msc) struct sna *sna = to_sna_from_drawable(draw); xf86CrtcPtr crtc = sna_dri2_get_crtc(draw); const struct ust_msc *swap; + union drm_wait_vblank vbl; DBG(("%s(draw=%ld, pipe=%d)\n", __FUNCTION__, draw->id, - crtc ? sna_crtc_to_pipe(crtc) : -1)); + crtc ? sna_crtc_pipe(crtc) : -1)); - if (crtc != NULL) { - union drm_wait_vblank vbl; + /* Drawable not displayed, make up a *monotonic* value */ + if (crtc == NULL) + crtc = sna_primary_crtc(sna); + if (crtc == NULL) + return FALSE; - VG_CLEAR(vbl); - vbl.request.type = _DRM_VBLANK_RELATIVE; - vbl.request.sequence = 0; - if (sna_wait_vblank(sna, &vbl, sna_crtc_to_pipe(crtc)) == 0) - sna_crtc_record_vblank(crtc, &vbl); - } else - /* Drawable not displayed, make up a *monotonic* value */ - crtc = sna_mode_first_crtc(sna); + if (sna_query_vblank(sna, crtc, &vbl) == 0) + sna_crtc_record_vblank(crtc, &vbl); swap = sna_crtc_last_swap(crtc); *msc = draw_current_msc(draw, crtc, swap->msc); *ust = ust64(swap->tv_sec, swap->tv_usec); - DBG(("%s: msc=%llu, ust=%llu\n", __FUNCTION__, - (long long)*msc, (long long)*ust)); + DBG(("%s: msc=%llu [raw=%llu], ust=%llu\n", __FUNCTION__, + (long long)*msc, swap->msc, (long long)*ust)); return TRUE; } @@ -3068,32 +3431,22 @@ sna_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc struct sna_dri2_event *info = NULL; xf86CrtcPtr crtc; CARD64 current_msc; - union drm_wait_vblank vbl; const struct ust_msc *swap; - int pipe; crtc = sna_dri2_get_crtc(draw); DBG(("%s(pipe=%d, target_msc=%llu, divisor=%llu, rem=%llu)\n", - __FUNCTION__, crtc ? sna_crtc_to_pipe(crtc) : -1, + __FUNCTION__, crtc ? sna_crtc_pipe(crtc) : -1, (long long)target_msc, (long long)divisor, (long long)remainder)); /* Drawable not visible, return immediately */ if (crtc == NULL) - goto out_complete; - - pipe = sna_crtc_to_pipe(crtc); - - VG_CLEAR(vbl); - - /* Get current count */ - vbl.request.type = _DRM_VBLANK_RELATIVE; - vbl.request.sequence = 0; - if (sna_wait_vblank(sna, &vbl, pipe)) - goto out_complete; + crtc = sna_primary_crtc(sna); + if (crtc == NULL) + return FALSE; - current_msc = draw_current_msc(draw, crtc, sna_crtc_record_vblank(crtc, &vbl)); + current_msc = get_current_msc(sna, draw, crtc); /* If target_msc already reached or passed, set it to * current_msc to ensure we return a reasonable value back @@ -3104,15 +3457,13 @@ sna_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc if (divisor == 0 && current_msc >= target_msc) goto out_complete; - info = sna_dri2_add_event(sna, draw, client); + info = sna_dri2_add_event(sna, draw, client, crtc); if (!info) goto out_complete; assert(info->crtc == crtc); info->type = WAITMSC; - vbl.request.signal = (uintptr_t)info; - vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; /* * If divisor is zero, or current_msc is smaller than target_msc, * we just need to make sure target_msc passes before waking up the @@ -3129,10 +3480,8 @@ sna_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc if (target_msc <= current_msc) target_msc += divisor; } - vbl.request.sequence = draw_target_seq(draw, target_msc); - info->queued = true; - if (sna_wait_vblank(sna, &vbl, pipe)) + if (!sna_wait_vblank(info, draw_target_seq(draw, target_msc))) goto out_free_info; DRI2BlockClient(client, draw); @@ -3141,8 +3490,6 @@ sna_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc out_free_info: sna_dri2_event_free(info); out_complete: - if (crtc == NULL) - crtc = sna_mode_first_crtc(sna); swap = sna_crtc_last_swap(crtc); DRI2WaitMSCComplete(client, draw, draw_current_msc(draw, crtc, swap->msc), @@ -3231,9 +3578,18 @@ static bool is_level(const char **str) return false; } +static const char *options_get_dri(struct sna *sna) +{ +#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,7,99,901,0) + return xf86GetOptValString(sna->Options, OPTION_DRI); +#else + return NULL; +#endif +} + static const char *dri_driver_name(struct sna *sna) { - const char *s = xf86GetOptValString(sna->Options, OPTION_DRI); + const char *s = options_get_dri(sna); if (is_level(&s)) { if (sna->kgem.gen < 030) @@ -3259,7 +3615,7 @@ bool sna_dri2_open(struct sna *sna, ScreenPtr screen) if (wedged(sna)) { xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING, - "loading DRI2 whilst the GPU is wedged.\n"); + "loading DRI2 whilst acceleration is disabled.\n"); } if (xf86LoaderCheckSymbol("DRI2Version")) @@ -3274,7 +3630,7 @@ bool sna_dri2_open(struct sna *sna, ScreenPtr screen) memset(&info, '\0', sizeof(info)); info.fd = sna->kgem.fd; info.driverName = dri_driver_name(sna); - info.deviceName = intel_get_client_name(sna->dev); + info.deviceName = intel_get_master_name(sna->dev); DBG(("%s: loading dri driver '%s' [gen=%d] for device '%s'\n", __FUNCTION__, info.driverName, sna->kgem.gen, info.deviceName)); @@ -3299,11 +3655,12 @@ bool sna_dri2_open(struct sna *sna, ScreenPtr screen) info.numDrivers = 2; info.driverNames = driverNames; driverNames[0] = info.driverName; - driverNames[1] = info.driverName; + driverNames[1] = "va_gl"; #endif #if DRI2INFOREC_VERSION >= 6 if (xorg_can_triple_buffer()) { + DBG(("%s: enabling Xorg triple buffering\n", __FUNCTION__)); info.version = 6; info.SwapLimitValidate = sna_dri2_swap_limit_validate; info.ReuseBufferNotify = sna_dri2_reuse_buffer; @@ -3311,8 +3668,10 @@ bool sna_dri2_open(struct sna *sna, ScreenPtr screen) #endif #if USE_ASYNC_SWAP + DBG(("%s: enabled async swap and buffer age\n", __FUNCTION__)); info.version = 10; info.scheduleSwap0 = 1; + info.bufferAge = 1; #endif return DRI2ScreenInit(screen, &info); diff --git a/src/sna/sna_dri3.c b/src/sna/sna_dri3.c index f586e24..52ae263 100644 --- a/src/sna/sna_dri3.c +++ b/src/sna/sna_dri3.c @@ -55,7 +55,7 @@ static inline void mark_dri3_pixmap(struct sna *sna, struct sna_pixmap *priv, st if (bo->exec) sna->kgem.flush = 1; if (bo == priv->gpu_bo) - priv->flush |= 3; + priv->flush |= FLUSH_READ | FLUSH_WRITE; else priv->shm = true; @@ -270,6 +270,8 @@ static PixmapPtr sna_dri3_pixmap_from_fd(ScreenPtr screen, priv->ptr = MAKE_STATIC_PTR(pixmap->devPrivate.ptr); } else { assert(priv->gpu_bo == bo); + priv->create = kgem_can_create_2d(&sna->kgem, + width, height, depth); priv->pinned |= PIN_DRI3; } list_add(&priv->cow_list, &sna->dri3.pixmaps); @@ -325,6 +327,15 @@ static int sna_dri3_fd_from_pixmap(ScreenPtr screen, return -1; } + if (bo->tiling && !sna->kgem.can_fence) { + if (!sna_pixmap_change_tiling(pixmap, I915_TILING_NONE)) { + DBG(("%s: unable to discard GPU tiling (%d) for DRI3 protocol\n", + __FUNCTION__, bo->tiling)); + return -1; + } + bo = priv->gpu_bo; + } + fd = kgem_bo_export_to_prime(&sna->kgem, bo); if (fd == -1) { DBG(("%s: exporting handle=%d to fd failed\n", __FUNCTION__, bo->handle)); diff --git a/src/sna/sna_driver.c b/src/sna/sna_driver.c index 8a3599c..14ec2f6 100644 --- a/src/sna/sna_driver.c +++ b/src/sna/sna_driver.c @@ -57,6 +57,13 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#if defined(HAVE_X11_EXTENSIONS_DPMSCONST_H) +#include +#else +#define DPMSModeOn 0 +#define DPMSModeOff 3 +#endif + #include #include #include @@ -69,6 +76,8 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. #if HAVE_DOT_GIT #include "git_version.h" +#else +#define git_version "not compiled from git" #endif #ifdef TEARFREE @@ -185,12 +194,12 @@ sna_set_fallback_mode(ScrnInfoPtr scrn) xf86DisableUnusedFunctions(scrn); #ifdef RANDR_12_INTERFACE - if (get_root_window(scrn->pScreen)) - xf86RandR12TellChanged(scrn->pScreen); + if (get_root_window(xf86ScrnToScreen(scrn))) + xf86RandR12TellChanged(xf86ScrnToScreen(scrn)); #endif } -static Bool sna_set_desired_mode(struct sna *sna) +static void sna_set_desired_mode(struct sna *sna) { ScrnInfoPtr scrn = sna->scrn; @@ -203,7 +212,6 @@ static Bool sna_set_desired_mode(struct sna *sna) } sna_mode_check(sna); - return TRUE; } /** @@ -222,7 +230,7 @@ static Bool sna_create_screen_resources(ScreenPtr screen) screen->width, screen->height, screen->rootDepth)); assert(sna->scrn == xf86ScreenToScrn(screen)); - assert(sna->scrn->pScreen == screen); + assert(to_screen_from_sna(sna) == screen); /* free the data used during miInitScreen */ free(screen->devPrivate); @@ -273,33 +281,89 @@ static Bool sna_create_screen_resources(ScreenPtr screen) if (serverGeneration == 1 && (sna->flags & SNA_IS_HOSTED) == 0) sna_copy_fbcon(sna); - (void)sna_set_desired_mode(sna); + sna_set_desired_mode(sna); } return TRUE; } -static Bool sna_save_screen(ScreenPtr screen, int mode) +static void sna_dpms_set(ScrnInfoPtr scrn, int mode, int flags) { - ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); + struct sna *sna = to_sna(scrn); + bool changed = false; + int i; - DBG(("%s(mode=%d)\n", __FUNCTION__, mode)); + DBG(("%s(mode=%d, flags=%d), vtSema=%d => off?=%d\n", + __FUNCTION__, mode, flags, scrn->vtSema, mode!=DPMSModeOn)); if (!scrn->vtSema) - return FALSE; + return; - xf86SaveScreen(screen, mode); - sna_crtc_config_notify(screen); - return TRUE; + /* Opencoded version of xf86DPMSSet(). + * + * The principle difference is to skip calling crtc->dpms() when + * turning off the display. This (on recent enough kernels at + * least) should be equivalent in power consumption, but require + * less work (hence quicker and less likely to fail) when switching + * back on. + */ + if (mode != DPMSModeOn) { + if (sna->mode.hidden == 0) { + DBG(("%s: hiding %d outputs\n", + __FUNCTION__, config->num_output)); + for (i = 0; i < config->num_output; i++) { + xf86OutputPtr output = config->output[i]; + if (output->crtc != NULL) + output->funcs->dpms(output, mode); + } + sna->mode.hidden = sna->mode.front_active + 1; + sna->mode.front_active = 0; + changed = true; + } + } else { + /* Re-enable CRTC that have been forced off via other means */ + if (sna->mode.hidden != 0) { + DBG(("%s: unhiding %d crtc, %d outputs\n", + __FUNCTION__, config->num_crtc, config->num_output)); + sna->mode.front_active = sna->mode.hidden - 1; + sna->mode.hidden = 0; + for (i = 0; i < config->num_crtc; i++) { + xf86CrtcPtr crtc = config->crtc[i]; + if (crtc->enabled) + crtc->funcs->dpms(crtc, mode); + } + + for (i = 0; i < config->num_output; i++) { + xf86OutputPtr output = config->output[i]; + if (output->crtc != NULL) + output->funcs->dpms(output, mode); + } + changed = true; + } + } + + DBG(("%s: hiding outputs? %d, front active? %d, changed? %d\n", + __FUNCTION__, sna->mode.hidden, sna->mode.front_active, changed)); + + if (changed) + sna_crtc_config_notify(xf86ScrnToScreen(scrn)); } -static void sna_dpms_set(ScrnInfoPtr scrn, int mode, int flags) +static Bool sna_save_screen(ScreenPtr screen, int mode) { - DBG(("%s(mode=%d, flags=%d)\n", __FUNCTION__, mode)); - if (!scrn->vtSema) - return; + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); - xf86DPMSSet(scrn, mode, flags); - sna_crtc_config_notify(xf86ScrnToScreen(scrn)); + DBG(("%s(mode=%d [unblank=%d])\n", + __FUNCTION__, mode, xf86IsUnblank(mode))); + + /* We have to unroll xf86SaveScreen() here as it is called + * by DPMSSet() nullifying our special handling crtc->dpms() + * in sna_dpms_set(). + */ + sna_dpms_set(scrn, + xf86IsUnblank(mode) ? DPMSModeOn : DPMSModeOff, + 0); + return TRUE; } static void sna_selftest(void) @@ -330,107 +394,6 @@ static void sna_setup_capabilities(ScrnInfoPtr scrn, int fd) #endif } -static int -namecmp(const char *s1, const char *s2) -{ - char c1, c2; - - if (!s1 || *s1 == 0) { - if (!s2 || *s2 == 0) - return 0; - else - return 1; - } - - while (*s1 == '_' || *s1 == ' ' || *s1 == '\t') - s1++; - - while (*s2 == '_' || *s2 == ' ' || *s2 == '\t') - s2++; - - c1 = isupper(*s1) ? tolower(*s1) : *s1; - c2 = isupper(*s2) ? tolower(*s2) : *s2; - while (c1 == c2) { - if (c1 == '\0') - return 0; - - s1++; - while (*s1 == '_' || *s1 == ' ' || *s1 == '\t') - s1++; - - s2++; - while (*s2 == '_' || *s2 == ' ' || *s2 == '\t') - s2++; - - c1 = isupper(*s1) ? tolower(*s1) : *s1; - c2 = isupper(*s2) ? tolower(*s2) : *s2; - } - - return c1 - c2; -} - -static Bool sna_option_cast_to_bool(struct sna *sna, int id, Bool val) -{ - const char *str = xf86GetOptValString(sna->Options, id); - - if (str == NULL) - return val; - - if (*str == '\0') - return TRUE; - - if (namecmp(str, "1") == 0) - return TRUE; - if (namecmp(str, "on") == 0) - return TRUE; - if (namecmp(str, "true") == 0) - return TRUE; - if (namecmp(str, "yes") == 0) - return TRUE; - - if (namecmp(str, "0") == 0) - return FALSE; - if (namecmp(str, "off") == 0) - return FALSE; - if (namecmp(str, "false") == 0) - return FALSE; - if (namecmp(str, "no") == 0) - return FALSE; - - return val; -} - -static unsigned sna_option_cast_to_unsigned(struct sna *sna, int id, unsigned val) -{ - const char *str = xf86GetOptValString(sna->Options, id); - unsigned v; - - if (str == NULL || *str == '\0') - return val; - - if (namecmp(str, "on") == 0) - return val; - if (namecmp(str, "true") == 0) - return val; - if (namecmp(str, "yes") == 0) - return val; - - if (namecmp(str, "0") == 0) - return 0; - if (namecmp(str, "off") == 0) - return 0; - if (namecmp(str, "false") == 0) - return 0; - if (namecmp(str, "no") == 0) - return 0; - - v = atoi(str); - if (v) - return v; - - return val; -} - static Bool fb_supports_depth(int fd, int depth) { struct drm_i915_gem_create create; @@ -472,7 +435,7 @@ static void setup_dri(struct sna *sna) sna->dri2.available = false; sna->dri3.available = false; - level = sna_option_cast_to_unsigned(sna, OPTION_DRI, ~0); + level = intel_option_cast_to_unsigned(sna->Options, OPTION_DRI, DEFAULT_DRI_LEVEL); #if HAVE_DRI3 if (level >= 3) sna->dri3.available = !!xf86LoadSubModule(sna->scrn, "dri3"); @@ -612,8 +575,10 @@ static Bool sna_pre_init(ScrnInfoPtr scrn, int probe) } intel_detect_chipset(scrn, sna->dev); - xf86DrvMsg(scrn->scrnIndex, X_PROBED, "CPU: %s\n", - sna_cpu_features_to_string(sna->cpu_features, buf)); + xf86DrvMsg(scrn->scrnIndex, X_PROBED, + "CPU: %s; using a maximum of %d threads\n", + sna_cpu_features_to_string(sna->cpu_features, buf), + sna_use_threads(64*1024, 64*1024, 1)); if (!xf86SetDepthBpp(scrn, 24, 0, 0, Support32bppFb | @@ -651,18 +616,11 @@ static Bool sna_pre_init(ScrnInfoPtr scrn, int probe) kgem_init(&sna->kgem, fd, xf86GetPciInfoForEntity(pEnt->index), sna->info->gen); - if (xf86ReturnOptValBool(sna->Options, OPTION_ACCEL_DISABLE, FALSE) || - !sna_option_cast_to_bool(sna, OPTION_ACCEL_METHOD, TRUE)) { - xf86DrvMsg(sna->scrn->scrnIndex, X_CONFIG, - "Disabling hardware acceleration.\n"); - sna->kgem.wedged = true; - } if (xf86ReturnOptValBool(sna->Options, OPTION_TILING_FB, FALSE)) sna->flags |= SNA_LINEAR_FB; - - if (xf86ReturnOptValBool(sna->Options, OPTION_DELETE_DP12, FALSE)) - sna->flags |= SNA_REMOVE_OUTPUTS; + if (!sna->kgem.can_fence) + sna->flags |= SNA_LINEAR_FB; if (!xf86ReturnOptValBool(sna->Options, OPTION_SWAPBUFFERS_WAIT, TRUE)) sna->flags |= SNA_NO_WAIT; @@ -723,9 +681,10 @@ cleanup: static bool has_shadow(struct sna *sna) { - if (!sna->mode.shadow_damage) + if (!sna->mode.shadow_enabled) return false; + assert(sna->mode.shadow_damage); if (RegionNil(DamageRegion(sna->mode.shadow_damage))) return false; @@ -748,7 +707,7 @@ sna_block_handler(BLOCKHANDLER_ARGS_DECL) sna->BlockHandler(BLOCKHANDLER_ARGS); if (*tv == NULL || ((*tv)->tv_usec | (*tv)->tv_sec) || has_shadow(sna)) - sna_accel_block_handler(sna, tv); + sna_accel_block(sna, tv); } static void @@ -770,8 +729,6 @@ sna_wakeup_handler(WAKEUPHANDLER_ARGS_DECL) sna->WakeupHandler(WAKEUPHANDLER_ARGS); - sna_accel_wakeup_handler(sna); - if (FD_ISSET(sna->kgem.fd, (fd_set*)read_mask)) { sna_mode_wakeup(sna); /* Clear the flag so that subsequent ZaphodHeads don't block */ @@ -780,42 +737,62 @@ sna_wakeup_handler(WAKEUPHANDLER_ARGS_DECL) } #if HAVE_UDEV +#include + static void sna_handle_uevents(int fd, void *closure) { struct sna *sna = closure; - struct udev_device *dev; - const char *str; struct stat s; - dev_t udev_devnum; + struct pollfd pfd; + bool hotplug = false; DBG(("%s\n", __FUNCTION__)); - dev = udev_monitor_receive_device(sna->uevent_monitor); - if (!dev) - return; + pfd.fd = udev_monitor_get_fd(sna->uevent_monitor); + pfd.events = POLLIN; + + if (fstat(sna->kgem.fd, &s)) + memset(&s, 0, sizeof(s)); + + while (poll(&pfd, 1, 0) > 0) { + struct udev_device *dev; + dev_t devnum; + + dev = udev_monitor_receive_device(sna->uevent_monitor); + if (dev == NULL) + break; + + devnum = udev_device_get_devnum(dev); + if (memcmp(&s.st_rdev, &devnum, sizeof(dev_t)) == 0) { + const char *str; + + str = udev_device_get_property_value(dev, "HOTPLUG"); + if (str && atoi(str) == 1) + hotplug = true; + } - udev_devnum = udev_device_get_devnum(dev); - if (fstat(sna->kgem.fd, &s) || memcmp(&s.st_rdev, &udev_devnum, sizeof (dev_t))) { udev_device_unref(dev); - return; } - str = udev_device_get_property_value(dev, "HOTPLUG"); - if (str && atoi(str) == 1) { - ScrnInfoPtr scrn = sna->scrn; + if (hotplug) { + DBG(("%s: hotplug event (vtSema?=%d)\n", + __FUNCTION__, sna->scrn->vtSema)); - DBG(("%s: hotplug event (vtSema?=%d)\n", __FUNCTION__, scrn->vtSema)); - - if (scrn->vtSema) { - sna_mode_discover(sna); - sna_mode_check(sna); - RRGetInfo(xf86ScrnToScreen(scrn), TRUE); - } else + if (sna->scrn->vtSema) + sna_mode_discover(sna, true); + else sna->flags |= SNA_REPROBE; } +} - udev_device_unref(dev); +static bool has_randr(void) +{ +#if HAS_DIXREGISTERPRIVATEKEY + return dixPrivateKeyRegistered(rrPrivKey); +#else + return *rrPrivKey; +#endif } static void @@ -833,7 +810,7 @@ sna_uevent_init(struct sna *sna) /* RandR will be disabled if Xinerama is active, and so generating * RR hotplug events is then verboten. */ - if (!dixPrivateKeyRegistered(rrPrivKey)) + if (!has_randr()) goto out; u = NULL; @@ -861,7 +838,8 @@ sna_uevent_init(struct sna *sna) sna->uevent_monitor = mon; out: - xf86DrvMsg(sna->scrn->scrnIndex, from, "display hotplug detection %s\n", + xf86DrvMsg(sna->scrn->scrnIndex, from, + "Display hotplug detection %s\n", sna->uevent_monitor ? "enabled" : "disabled"); return; @@ -874,17 +852,10 @@ err_dev: static bool sna_uevent_poll(struct sna *sna) { - struct pollfd pfd; - if (sna->uevent_monitor == NULL) return false; - pfd.fd = udev_monitor_get_fd(sna->uevent_monitor); - pfd.events = POLLIN; - - while (poll(&pfd, 1, 0) > 0) - sna_handle_uevents(pfd.fd, sna); - + sna_handle_uevents(udev_monitor_get_fd(sna->uevent_monitor), sna); return true; } @@ -918,8 +889,10 @@ sna_randr_getinfo(ScreenPtr screen, Rotation *rotations) { struct sna *sna = to_sna_from_screen(screen); + DBG(("%s()\n", __FUNCTION__)); + if (!sna_uevent_poll(sna)) - sna_mode_discover(sna); + sna_mode_discover(sna, false); return sna->mode.rrGetInfo(screen, rotations); } @@ -931,8 +904,8 @@ static void sna_leave_vt(VT_FUNC_ARGS_DECL) DBG(("%s\n", __FUNCTION__)); - sna_accel_leave(sna); sna_mode_reset(sna); + sna_accel_leave(sna); if (intel_put_master(sna->dev)) xf86DrvMsg(scrn->scrnIndex, X_WARNING, @@ -1098,7 +1071,7 @@ sna_screen_init(SCREEN_INIT_ARGS_DECL) DBG(("%s\n", __FUNCTION__)); assert(sna->scrn == scrn); - assert(scrn->pScreen == NULL); /* set afterwards */ + assert(to_screen_from_sna(sna) == NULL); /* set afterwards */ assert(sna->freed_pixmap == NULL); @@ -1244,20 +1217,15 @@ static Bool sna_enter_vt(VT_FUNC_ARGS_DECL) if (intel_get_master(sna->dev)) return FALSE; + sna_accel_enter(sna); + if (sna->flags & SNA_REPROBE) { - DBG(("%s: reporting deferred hotplug event\n", - __FUNCTION__)); - sna_mode_discover(sna); - RRGetInfo(xf86ScrnToScreen(scrn), TRUE); - sna->flags &= ~SNA_REPROBE; + DBG(("%s: reporting deferred hotplug event\n", __FUNCTION__)); + sna_mode_discover(sna, true); } - if (!sna_set_desired_mode(sna)) { - intel_put_master(sna->dev); - return FALSE; - } + sna_set_desired_mode(sna); - sna_accel_enter(sna); return TRUE; } @@ -1379,6 +1347,9 @@ static void describe_sna(ScrnInfoPtr scrn) xf86DrvMsg(scrn->scrnIndex, X_INFO, "SNA compiled: %s\n", BUILDER_DESCRIPTION); #endif +#if HAS_DEBUG_FULL + ErrorF("SNA compiled with full debug logging; expect to run slowly\n"); +#endif #if !NDEBUG xf86DrvMsg(scrn->scrnIndex, X_INFO, "SNA compiled with assertions enabled\n"); @@ -1400,6 +1371,7 @@ static void describe_sna(ScrnInfoPtr scrn) "SNA compiled for use with valgrind\n"); VALGRIND_PRINTF("SNA compiled for use with valgrind\n"); #endif + DBG(("xf86-video-intel version: %s\n", git_version)); DBG(("pixman version: %s\n", pixman_version_string())); } diff --git a/src/sna/sna_glyphs.c b/src/sna/sna_glyphs.c index a5dfb06..6ee4033 100644 --- a/src/sna/sna_glyphs.c +++ b/src/sna/sna_glyphs.c @@ -74,7 +74,7 @@ #define NO_GLYPHS_VIA_MASK 0 #define FORCE_SMALL_MASK 0 /* -1 = never, 1 = always */ #define NO_GLYPHS_SLOW 0 -#define NO_DISCARD_MASK 0 +#define DISCARD_MASK 0 /* -1 = never, 1 = always */ #define CACHE_PICTURE_SIZE 1024 #define GLYPH_MIN_SIZE 8 @@ -185,7 +185,7 @@ void sna_glyphs_close(struct sna *sna) */ bool sna_glyphs_create(struct sna *sna) { - ScreenPtr screen = sna->scrn->pScreen; + ScreenPtr screen = to_screen_from_sna(sna); pixman_color_t white = { 0xffff, 0xffff, 0xffff, 0xffff }; unsigned int formats[] = { PIXMAN_a8, @@ -1094,6 +1094,9 @@ sna_glyph_get_image(GlyphPtr g, ScreenPtr s) static inline bool use_small_mask(struct sna *sna, int16_t width, int16_t height, int depth) { + if (depth < 8) + return true; + if (FORCE_SMALL_MASK) return FORCE_SMALL_MASK > 0; @@ -1156,12 +1159,6 @@ glyphs_via_mask(struct sna *sna, src_x += box.x1 - list->xOff; src_y += box.y1 - list->yOff; - if (format->depth < 8) { - format = PictureMatchFormat(screen, 8, PICT_a8); - if (!format) - return false; - } - component_alpha = NeedsComponent(format->format); if (use_small_mask(sna, width, height, format->depth)) { pixman_image_t *mask_image; @@ -1179,7 +1176,7 @@ use_small_mask: return false; mask_image = - pixman_image_create_bits(format->depth << 24 | format->format, + pixman_image_create_bits(pixmap->drawable.bitsPerPixel << 24 | format->format, width, height, pixmap->devPrivate.ptr, pixmap->devKind); @@ -1386,10 +1383,11 @@ next_image: DBG(("%s: atlas format=%08x, mask format=%08x\n", __FUNCTION__, (int)p->atlas->format, - (int)(format->depth << 24 | format->format))); + (int)mask->format)); memset(&tmp, 0, sizeof(tmp)); - if (p->atlas->format == (format->depth << 24 | format->format)) { + if (p->atlas->format == mask->format || + alphaless(p->atlas->format) == mask->format) { ok = sna->render.composite(sna, PictOpAdd, p->atlas, NULL, mask, 0, 0, 0, 0, 0, 0, @@ -1561,6 +1559,9 @@ skip_glyph: } } + assert(format); + DBG(("%s: format=%08d, depth=%d\n", + __FUNCTION__, format->format, format->depth)); out: if (list_extents != stack_extents) free(list_extents); @@ -1573,24 +1574,34 @@ static bool can_discard_mask(uint8_t op, PicturePtr src, PictFormatPtr mask, PictFormatPtr g; uint32_t color; - if (NO_DISCARD_MASK) - return false; + if (DISCARD_MASK) + return DISCARD_MASK > 0; DBG(("%s: nlist=%d, mask=%08x, depth %d, op=%d (bounded? %d)\n", __FUNCTION__, nlist, mask ? (unsigned)mask->format : 0, mask ? mask->depth : 0, op, op_is_bounded(op))); - if (nlist == 1 && list->len == 1) - return true; + if (nlist == 1 && list->len == 1) { + if (mask == list->format) + return true; + + g = list->format; + goto skip; + } - if (!op_is_bounded(op)) + if (!op_is_bounded(op)) { + DBG(("%s: unbounded op, not discarding\n", __FUNCTION__)); return false; + } /* No glyphs overlap and we are not performing a mask conversion. */ g = glyphs_format(nlist, list, glyphs); - if (mask == g) + if (mask == g) { + DBG(("%s: mask matches glyphs format, no conversion, so discard mask\n", + __FUNCTION__)); return true; + } DBG(("%s: preferred mask format %08x, depth %d\n", __FUNCTION__, g ? (unsigned)g->format : 0, g ? g->depth : 0)); @@ -1605,18 +1616,41 @@ static bool can_discard_mask(uint8_t op, PicturePtr src, PictFormatPtr mask, list++; } + + if (!sna_picture_is_solid(src, &color)) + return false; + + return color >> 24 == 0xff; } else { - if (PICT_FORMAT_A(mask->format) >= PICT_FORMAT_A(g->format)) +skip: + if (mask->format == g->format) return true; - if (g->depth != 1) - return false; - } + if (mask->format == alphaless(g->format)) + return true; + + if (PICT_FORMAT_TYPE(g->format) == PICT_TYPE_A && + PICT_FORMAT_TYPE(mask->format) != PICT_TYPE_A) + return true; - if (!sna_picture_is_solid(src, &color)) return false; + } +} - return color >> 24 == 0xff; +static uint32_t pixman_format(PictFormatPtr short_format) +{ + uint32_t bpp; + + bpp = short_format->depth; + if (bpp <= 1) + bpp = 1; + else if (bpp <= 8) + bpp = 8; + else if (bpp <= 16) + bpp = 16; + else + bpp = 32; + return bpp << 24 | short_format->format; } static void @@ -1756,7 +1790,7 @@ next: if (sigtrap_get() == 0) { if (mask_format) { pixman_composite_glyphs(op, src_image, dst_image, - mask_format->format | (mask_format->depth << 24), + pixman_format(mask_format), src_x + src_dx + region.extents.x1 - dst_x, src_y + src_dy + region.extents.y1 - dst_y, region.extents.x1, region.extents.y1, @@ -1815,10 +1849,10 @@ out: x, y, mask_format->depth, (long)mask_format->format, - (long)(mask_format->depth << 24 | mask_format->format), + (long)pixman_format(mask_format), NeedsComponent(mask_format->format))); mask_image = - pixman_image_create_bits(mask_format->depth << 24 | mask_format->format, + pixman_image_create_bits(pixman_format(mask_format), region.extents.x2 - region.extents.x1, region.extents.y2 - region.extents.y1, NULL, 0); @@ -2086,12 +2120,6 @@ glyphs_via_image(struct sna *sna, src_x += box.x1 - list->xOff; src_y += box.y1 - list->yOff; - if (format->depth < 8) { - format = PictureMatchFormat(screen, 8, PICT_a8); - if (!format) - return false; - } - DBG(("%s: small mask [format=%lx, depth=%d, size=%d], rendering glyphs to upload buffer\n", __FUNCTION__, (unsigned long)format->format, format->depth, (uint32_t)width*height*format->depth)); @@ -2104,7 +2132,7 @@ glyphs_via_image(struct sna *sna, return false; mask_image = - pixman_image_create_bits(format->depth << 24 | format->format, + pixman_image_create_bits(pixmap->drawable.bitsPerPixel << 24 | format->format, width, height, pixmap->devPrivate.ptr, pixmap->devKind); diff --git a/src/sna/sna_io.c b/src/sna/sna_io.c index d6aa129..d32bd58 100644 --- a/src/sna/sna_io.c +++ b/src/sna/sna_io.c @@ -105,8 +105,10 @@ read_boxes_inplace__cpu(struct kgem *kgem, if (!download_inplace__cpu(kgem, dst, bo, box, n)) return false; + if (bo->tiling == I915_TILING_Y) + return false; + assert(kgem_bo_can_map__cpu(kgem, bo, false)); - assert(bo->tiling != I915_TILING_Y); src = kgem_bo_map__cpu(kgem, bo); if (src == NULL) @@ -281,6 +283,9 @@ fallback: if (box[n].y2 > extents.y2) extents.y2 = box[n].y2; } + if (!can_blt && sna->render.max_3d_size == 0) + goto fallback; + if (kgem_bo_can_map(kgem, src_bo)) { /* Is it worth detiling? */ if ((extents.y2 - extents.y1 - 1) * src_bo->pitch < 4096) @@ -477,6 +482,7 @@ fallback: goto fallback; _kgem_set_mode(kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, src_bo, NULL); tmp_nbox = nbox; tmp_box = box; @@ -539,6 +545,7 @@ fallback: break; _kgem_set_mode(kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, src_bo, NULL); tmp_box += nbox_this_time; } while (1); } else { @@ -597,6 +604,7 @@ fallback: break; _kgem_set_mode(kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, src_bo, NULL); tmp_box += nbox_this_time; } while (1); } @@ -666,8 +674,10 @@ write_boxes_inplace__tiled(struct kgem *kgem, { uint8_t *dst; + if (bo->tiling == I915_TILING_Y) + return false; + assert(kgem->has_wc_mmap || kgem_bo_can_map__cpu(kgem, bo, true)); - assert(bo->tiling != I915_TILING_Y); if (kgem_bo_can_map__cpu(kgem, bo, true)) { dst = kgem_bo_map__cpu(kgem, bo); @@ -778,6 +788,15 @@ static bool __upload_inplace(struct kgem *kgem, if (FORCE_INPLACE) return FORCE_INPLACE > 0; + if (bo->exec) + return false; + + if (bo->flush) + return true; + + if (kgem_bo_can_map__cpu(kgem, bo, true)) + return true; + /* If we are writing through the GTT, check first if we might be * able to almagamate a series of small writes into a single * operation. @@ -849,6 +868,8 @@ bool sna_write_boxes(struct sna *sna, PixmapPtr dst, if (box[n].y2 > extents.y2) extents.y2 = box[n].y2; } + if (!can_blt && sna->render.max_3d_size == 0) + goto fallback; /* Try to avoid switching rings... */ if (!can_blt || kgem->ring == KGEM_RENDER || @@ -1038,6 +1059,7 @@ tile: goto fallback; _kgem_set_mode(kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, NULL, dst_bo); if (kgem->gen >= 0100) { cmd |= 8; @@ -1129,6 +1151,7 @@ tile: if (nbox) { _kgem_submit(kgem); _kgem_set_mode(kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, NULL, dst_bo); } kgem_bo_destroy(kgem, src_bo); @@ -1224,6 +1247,7 @@ tile: if (nbox) { _kgem_submit(kgem); _kgem_set_mode(kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, NULL, dst_bo); } kgem_bo_destroy(kgem, src_bo); @@ -1541,6 +1565,7 @@ tile: goto fallback; _kgem_set_mode(kgem, KGEM_BLT); } + kgem_bcs_set_tiling(&sna->kgem, NULL, dst_bo); if (sna->kgem.gen >= 0100) { cmd |= 8; @@ -1636,6 +1661,7 @@ tile: if (nbox) { _kgem_submit(kgem); _kgem_set_mode(kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, NULL, dst_bo); } kgem_bo_destroy(kgem, src_bo); @@ -1732,6 +1758,7 @@ tile: if (nbox) { _kgem_submit(kgem); _kgem_set_mode(kgem, KGEM_BLT); + kgem_bcs_set_tiling(&sna->kgem, NULL, dst_bo); } kgem_bo_destroy(kgem, src_bo); diff --git a/src/sna/sna_present.c b/src/sna/sna_present.c index 6dd6fe8..2de5ddf 100644 --- a/src/sna/sna_present.c +++ b/src/sna/sna_present.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -38,10 +39,16 @@ static present_screen_info_rec present_info; struct sna_present_event { - uint64_t event_id; xf86CrtcPtr crtc; + struct sna *sna; + struct list link; + uint64_t *event_id; + uint64_t target_msc; + int n_event_id; }; +static void sna_present_unflip(ScreenPtr screen, uint64_t event_id); + static inline struct sna_present_event * to_present_event(uintptr_t data) { @@ -50,9 +57,24 @@ to_present_event(uintptr_t data) #define MARK_PRESENT(x) ((void *)((uintptr_t)(x) | 2)) -static int pipe_from_crtc(RRCrtcPtr crtc) +static inline xf86CrtcPtr unmask_crtc(xf86CrtcPtr crtc) +{ + return (xf86CrtcPtr)((uintptr_t)crtc & ~1); +} + +static inline xf86CrtcPtr mark_crtc(xf86CrtcPtr crtc) { - return crtc ? sna_crtc_to_pipe(crtc->devPrivate) : -1; + return (xf86CrtcPtr)((uintptr_t)crtc | 1); +} + +static inline bool has_vblank(xf86CrtcPtr crtc) +{ + return (uintptr_t)crtc & 1; +} + +static inline int pipe_from_crtc(RRCrtcPtr crtc) +{ + return crtc ? sna_crtc_pipe(crtc->devPrivate) : -1; } static uint32_t pipe_select(int pipe) @@ -74,6 +96,139 @@ static inline int sna_wait_vblank(struct sna *sna, union drm_wait_vblank *vbl, i return drmIoctl(sna->kgem.fd, DRM_IOCTL_WAIT_VBLANK, vbl); } +static uint64_t gettime_ust64(void) +{ + struct timespec tv; + + if (clock_gettime(CLOCK_MONOTONIC, &tv)) + return GetTimeInMicros(); + + return ust64(tv.tv_sec, tv.tv_nsec / 1000); +} + +static void vblank_complete(struct sna_present_event *info, + uint64_t ust, uint64_t msc) +{ + int n; + + DBG(("%s: %d events complete\n", __FUNCTION__, info->n_event_id)); + for (n = 0; n < info->n_event_id; n++) { + DBG(("%s: pipe=%d tv=%d.%06d msc=%lld (target=%lld), event=%lld complete%s\n", __FUNCTION__, + sna_crtc_pipe(unmask_crtc(info->crtc)), + (int)(ust / 1000000), (int)(ust % 1000000), + (long long)msc, (long long)info->target_msc, + (long long)info->event_id[n], + info->target_msc && msc == (uint32_t)info->target_msc ? "" : ": MISS")); + present_event_notify(info->event_id[n], ust, msc); + } + if (info->n_event_id > 1) + free(info->event_id); + list_del(&info->link); + free(info); +} + +static uint32_t msc_to_delay(xf86CrtcPtr crtc, uint64_t target) +{ + const DisplayModeRec *mode = &crtc->desiredMode; + const struct ust_msc *swap = sna_crtc_last_swap(crtc); + int64_t delay, subframe; + + delay = (target - swap->msc) * mode->VTotal * mode->HTotal / mode->Clock; + subframe = gettime_ust64() - swap_ust(swap); + subframe /= 1000; + if (subframe < delay) + delay -= subframe; + else + delay = 0; + + DBG(("%s: sleep %d frames, %llu ms\n", __FUNCTION__, + (int)(target - swap->msc), (long long)delay)); + assert(delay >= 0); + return delay; +} + +static CARD32 sna_fake_vblank_handler(OsTimerPtr timer, CARD32 now, void *data) +{ + struct sna_present_event *info = data; + union drm_wait_vblank vbl; + uint64_t msc, ust; + + DBG(("%s(event=%lldx%d, now=%d)\n", __FUNCTION__, (long long)info->event_id[0], info->n_event_id, now)); + + VG_CLEAR(vbl); + vbl.request.type = DRM_VBLANK_RELATIVE; + vbl.request.sequence = 0; + if (sna_wait_vblank(info->sna, &vbl, sna_crtc_pipe(info->crtc)) == 0) { + ust = ust64(vbl.reply.tval_sec, vbl.reply.tval_usec); + msc = sna_crtc_record_vblank(info->crtc, &vbl); + DBG(("%s: event=%lld, target msc=%lld, now %lld\n", + __FUNCTION__, (long long)info->event_id[0], (long long)info->target_msc, (long long)msc)); + if (msc < info->target_msc) { + uint32_t delay; + + DBG(("%s: too early, requeuing\n", __FUNCTION__)); + + vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; + vbl.request.sequence = info->target_msc; + vbl.request.signal = (uintptr_t)MARK_PRESENT(info); + if (sna_wait_vblank(info->sna, &vbl, sna_crtc_pipe(info->crtc)) == 0) { + DBG(("%s: scheduled new vblank event for %lld\n", __FUNCTION__, (long long)info->target_msc)); + free(timer); + return 0; + } + + delay = msc_to_delay(info->crtc, info->target_msc); + if (delay) { + DBG(("%s: requeueing timer for %dms delay\n", __FUNCTION__, delay)); + return delay; + } + + /* As a last resort use a blocking wait. + * Less than a millisecond for a rare case. + */ + DBG(("%s: blocking wait!\n", __FUNCTION__)); + vbl.request.type = DRM_VBLANK_ABSOLUTE; + vbl.request.sequence = info->target_msc; + (void)sna_wait_vblank(info->sna, &vbl, sna_crtc_pipe(info->crtc)); + } + } else { + const struct ust_msc *swap = sna_crtc_last_swap(info->crtc); + ust = swap_ust(swap); + msc = swap->msc; + DBG(("%s: event=%lld, CRTC OFF, target msc=%lld, was %lld\n", + __FUNCTION__, (long long)info->event_id[0], (long long)info->target_msc, (long long)msc)); + } + + vblank_complete(info, ust, msc); + free(timer); + return 0; +} + +static bool sna_fake_vblank(struct sna_present_event *info) +{ + uint64_t msc = sna_crtc_last_swap(info->crtc)->msc; + uint32_t delay; + + assert(info->n_event_id == 1); + + if (msc < info->target_msc) + delay = msc_to_delay(info->crtc, info->target_msc); + else + delay = 0; + + DBG(("%s(event=%lld, target_msc=%lld, msc=%lld, delay=%ums)\n", + __FUNCTION__, (long long)info->event_id[0], (long long)info->target_msc, (long long)msc, delay)); + if (delay == 0) { + const struct ust_msc *swap = sna_crtc_last_swap(info->crtc); + present_event_notify(info->event_id[0], swap_ust(swap), swap->msc); + list_del(&info->link); + free(info); + return true; + } + + return TimerSet(NULL, 0, delay, sna_fake_vblank_handler, info); +} + static RRCrtcPtr sna_present_get_crtc(WindowPtr window) { @@ -81,7 +236,10 @@ sna_present_get_crtc(WindowPtr window) BoxRec box; xf86CrtcPtr crtc; - DBG(("%s\n", __FUNCTION__)); + DBG(("%s: window=%ld (pixmap=%ld), box=(%d, %d)x(%d, %d)\n", + __FUNCTION__, window->drawable.id, get_window_pixmap(window)->drawable.serialNumber, + window->drawable.x, window->drawable.y, + window->drawable.width, window->drawable.height)); box.x1 = window->drawable.x; box.y1 = window->drawable.y; @@ -99,24 +257,31 @@ static int sna_present_get_ust_msc(RRCrtcPtr crtc, CARD64 *ust, CARD64 *msc) { struct sna *sna = to_sna_from_screen(crtc->pScreen); - int pipe = pipe_from_crtc(crtc); union drm_wait_vblank vbl; - DBG(("%s(pipe=%d)\n", __FUNCTION__, pipe)); + DBG(("%s(pipe=%d)\n", __FUNCTION__, sna_crtc_pipe(crtc->devPrivate))); + if (sna_crtc_has_vblank(crtc->devPrivate)) { + DBG(("%s: vblank active, reusing last swap msc/ust\n", + __FUNCTION__)); + goto last; + } VG_CLEAR(vbl); vbl.request.type = DRM_VBLANK_RELATIVE; vbl.request.sequence = 0; - if (sna_wait_vblank(sna, &vbl, pipe) == 0) { + if (sna_wait_vblank(sna, &vbl, sna_crtc_pipe(crtc->devPrivate)) == 0) { *ust = ust64(vbl.reply.tval_sec, vbl.reply.tval_usec); *msc = sna_crtc_record_vblank(crtc->devPrivate, &vbl); } else { - const struct ust_msc *swap = sna_crtc_last_swap(crtc->devPrivate); - *ust = ust64(swap->tv_sec, swap->tv_usec); + const struct ust_msc *swap; +last: + swap = sna_crtc_last_swap(crtc->devPrivate); + *ust = swap_ust(swap); *msc = swap->msc; } - DBG(("%s: pipe=%d, tv=%d.%06d msc=%lld\n", __FUNCTION__, pipe, + DBG(("%s: pipe=%d, tv=%d.%06d msc=%lld\n", __FUNCTION__, + sna_crtc_pipe(crtc->devPrivate), (int)(*ust / 1000000), (int)(*ust % 1000000), (long long)*msc)); @@ -127,43 +292,97 @@ void sna_present_vblank_handler(struct drm_event_vblank *event) { struct sna_present_event *info = to_present_event(event->user_data); + xf86CrtcPtr crtc = info->crtc; - DBG(("%s: pipe=%d tv=%d.%06d msc=%d, event %lld complete\n", __FUNCTION__, - sna_crtc_to_pipe(info->crtc), - event->tv_sec, event->tv_usec, event->sequence, - (long long)info->event_id)); - present_event_notify(info->event_id, - ust64(event->tv_sec, event->tv_usec), - sna_crtc_record_event(info->crtc, event)); - free(info); + vblank_complete(info, + ust64(event->tv_sec, event->tv_usec), + sna_crtc_record_event(unmask_crtc(crtc), event)); + if (has_vblank(crtc)) + sna_crtc_clear_vblank(unmask_crtc(crtc)); } static int sna_present_queue_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc) { struct sna *sna = to_sna_from_screen(crtc->pScreen); - struct sna_present_event *event; + struct sna_present_event *info, *tmp; + const struct ust_msc *swap; union drm_wait_vblank vbl; DBG(("%s(pipe=%d, event=%lld, msc=%lld)\n", - __FUNCTION__, pipe_from_crtc(crtc), + __FUNCTION__, sna_crtc_pipe(crtc->devPrivate), (long long)event_id, (long long)msc)); - event = malloc(sizeof(struct sna_present_event)); - if (event == NULL) + swap = sna_crtc_last_swap(crtc->devPrivate); + warn_unless((int64_t)(msc - swap->msc) >= 0); + if ((int64_t)(msc - swap->msc) <= 0) { + DBG(("%s: pipe=%d tv=%d.%06d msc=%lld (target=%lld), event=%lld complete\n", __FUNCTION__, + sna_crtc_pipe(crtc->devPrivate), + swap->tv_sec, swap->tv_usec, + (long long)swap->msc, (long long)msc, + (long long)event_id)); + present_event_notify(event_id, swap_ust(swap), swap->msc); + return Success; + } + + list_for_each_entry(tmp, &sna->present.vblank_queue, link) { + if (tmp->target_msc == msc && + unmask_crtc(tmp->crtc) == crtc->devPrivate) { + uint64_t *events = tmp->event_id; + + if (is_power_of_two(tmp->n_event_id)) { + events = malloc(2*sizeof(uint64_t)*tmp->n_event_id); + if (events == NULL) + return BadAlloc; + + memcpy(events, + tmp->event_id, + tmp->n_event_id*sizeof(uint64_t)); + if (tmp->n_event_id != 1) + free(tmp->event_id); + tmp->event_id = events; + } + + DBG(("%s: appending event=%lld to vblank %lld x %d\n", + __FUNCTION__, (long long)event_id, (long long)msc, tmp->n_event_id+1)); + events[tmp->n_event_id++] = event_id; + return Success; + } + if ((int64_t)(tmp->target_msc - msc) > 0) { + DBG(("%s: previous target_msc=%lld invalid for coalescing\n", + __FUNCTION__, (long long)tmp->target_msc)); + break; + } + } + + info = malloc(sizeof(struct sna_present_event) + sizeof(uint64_t)); + if (info == NULL) return BadAlloc; - event->event_id = event_id; - event->crtc = crtc->devPrivate; + info->crtc = crtc->devPrivate; + info->sna = sna; + info->target_msc = msc; + info->event_id = (uint64_t *)(info + 1); + info->event_id[0] = event_id; + info->n_event_id = 1; + list_add_tail(&info->link, &tmp->link); VG_CLEAR(vbl); vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; vbl.request.sequence = msc; - vbl.request.signal = (uintptr_t)MARK_PRESENT(event); - if (sna_wait_vblank(sna, &vbl, sna_crtc_to_pipe(event->crtc))) { + vbl.request.signal = (uintptr_t)MARK_PRESENT(info); + if (sna_wait_vblank(sna, &vbl, sna_crtc_pipe(info->crtc))) { DBG(("%s: vblank enqueue failed\n", __FUNCTION__)); - free(event); - return BadMatch; + if (!sna_fake_vblank(info)) { + list_del(&info->link); + free(info); + return BadAlloc; + } + } else { + if (msc - swap->msc == 1) { + sna_crtc_set_vblank(info->crtc); + info->crtc = mark_crtc(info->crtc); + } } return Success; @@ -180,14 +399,6 @@ sna_present_abort_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc) static void sna_present_flush(WindowPtr window) { - PixmapPtr pixmap = get_window_pixmap(window); - struct sna_pixmap *priv; - - DBG(("%s(pixmap=%ld)\n", __FUNCTION__, pixmap->drawable.serialNumber)); - - priv = sna_pixmap_move_to_gpu(pixmap, MOVE_READ | MOVE_ASYNC_HINT | __MOVE_FORCE); - if (priv && priv->gpu_bo) - kgem_scanout_flush(&to_sna_from_pixmap(pixmap)->kgem, priv->gpu_bo); } static bool @@ -201,8 +412,13 @@ check_flip__crtc(struct sna *sna, assert(sna->scrn->vtSema); - if (sna->mode.shadow_active) { - DBG(("%s: shadow buffer active\n", __FUNCTION__)); + if (!sna->mode.front_active) { + DBG(("%s: DPMS off, no flips\n", __FUNCTION__)); + return FALSE; + } + + if (sna->mode.rr_active) { + DBG(("%s: RandR transformation active\n", __FUNCTION__)); return false; } @@ -224,6 +440,11 @@ sna_present_check_flip(RRCrtcPtr crtc, pixmap->drawable.serialNumber, sync_flip)); + if (!sna->scrn->vtSema) { + DBG(("%s: VT switched away, no flips\n", __FUNCTION__)); + return FALSE; + } + if (sna->flags & SNA_NO_FLIP) { DBG(("%s: flips not suported\n", __FUNCTION__)); return FALSE; @@ -231,7 +452,7 @@ sna_present_check_flip(RRCrtcPtr crtc, if (sync_flip) { if ((sna->flags & SNA_HAS_FLIP) == 0) { - DBG(("%s: async flips not suported\n", __FUNCTION__)); + DBG(("%s: sync flips not suported\n", __FUNCTION__)); return FALSE; } } else { @@ -257,24 +478,33 @@ sna_present_check_flip(RRCrtcPtr crtc, return FALSE; } - return TRUE; -} - -static uint64_t gettime_ust64(void) -{ - struct timespec tv; - - if (clock_gettime(CLOCK_MONOTONIC, &tv)) - return 0; + if (flip->pinned) { + assert(flip->gpu_bo); + if (sna->flags & SNA_LINEAR_FB) { + if (flip->gpu_bo->tiling != I915_TILING_NONE) { + DBG(("%s: pined bo, tilng=%d needs NONE\n", + __FUNCTION__, flip->gpu_bo->tiling)); + return FALSE; + } + } else { + if (!sna->kgem.can_scanout_y && + flip->gpu_bo->tiling == I915_TILING_Y) { + DBG(("%s: pined bo, tilng=%d and can't scanout Y\n", + __FUNCTION__, flip->gpu_bo->tiling)); + return FALSE; + } + } + } - return ust64(tv.tv_sec, tv.tv_nsec / 1000); + return TRUE; } static Bool -page_flip__async(RRCrtcPtr crtc, - uint64_t event_id, - uint64_t target_msc, - struct kgem_bo *bo) +flip__async(struct sna *sna, + RRCrtcPtr crtc, + uint64_t event_id, + uint64_t target_msc, + struct kgem_bo *bo) { DBG(("%s(pipe=%d, event=%lld, handle=%d)\n", __FUNCTION__, @@ -282,17 +512,17 @@ page_flip__async(RRCrtcPtr crtc, (long long)event_id, bo->handle)); - if (!sna_page_flip(to_sna_from_screen(crtc->pScreen), bo, NULL, NULL)) { + if (!sna_page_flip(sna, bo, NULL, NULL)) { DBG(("%s: async pageflip failed\n", __FUNCTION__)); present_info.capabilities &= ~PresentCapabilityAsync; return FALSE; } - DBG(("%s: pipe=%d tv=%d.%06d msc=%d, event %lld complete\n", __FUNCTION__, + DBG(("%s: pipe=%d tv=%ld.%06d msc=%lld (target=%lld), event=%lld complete\n", __FUNCTION__, pipe_from_crtc(crtc), - gettime_ust64() / 1000000, gettime_ust64() % 1000000, - sna_crtc_last_swap(crtc->devPrivate)->msc, - (long long)event_id)); + (long)(gettime_ust64() / 1000000), (int)(gettime_ust64() % 1000000), + crtc ? (long long)sna_crtc_last_swap(crtc->devPrivate)->msc : 0LL, + (long long)target_msc, (long long)event_id)); present_event_notify(event_id, gettime_ust64(), target_msc); return TRUE; } @@ -303,7 +533,8 @@ present_flip_handler(struct drm_event_vblank *event, void *data) struct sna_present_event *info = data; struct ust_msc swap; - DBG(("%s(sequence=%d)\n", __FUNCTION__, event->sequence)); + DBG(("%s(sequence=%d): event=%lld\n", __FUNCTION__, event->sequence, (long long)info->event_id[0])); + assert(info->n_event_id == 1); if (info->crtc == NULL) { swap.tv_sec = event->tv_sec; @@ -312,22 +543,33 @@ present_flip_handler(struct drm_event_vblank *event, void *data) } else swap = *sna_crtc_last_swap(info->crtc); - DBG(("%s: pipe=%d, tv=%d.%06d msc %lld, event %lld complete\n", __FUNCTION__, - info->crtc ? sna_crtc_to_pipe(info->crtc) : -1, + DBG(("%s: pipe=%d, tv=%d.%06d msc=%lld (target %lld), event=%lld complete%s\n", __FUNCTION__, + info->crtc ? sna_crtc_pipe(info->crtc) : -1, swap.tv_sec, swap.tv_usec, (long long)swap.msc, - (long long)info->event_id)); - present_event_notify(info->event_id, ust64(swap.tv_sec, swap.tv_usec), swap.msc); + (long long)info->target_msc, + (long long)info->event_id[0], + info->target_msc && info->target_msc == swap.msc ? "" : ": MISS")); + present_event_notify(info->event_id[0], swap_ust(&swap), swap.msc); + if (info->crtc) + sna_crtc_clear_vblank(info->crtc); + + if (info->sna->present.unflip) { + DBG(("%s: executing queued unflip (event=%lld)\n", __FUNCTION__, (long long)info->sna->present.unflip)); + sna_present_unflip(xf86ScrnToScreen(info->sna->scrn), + info->sna->present.unflip); + info->sna->present.unflip = 0; + } free(info); } static Bool -page_flip(ScreenPtr screen, - RRCrtcPtr crtc, - uint64_t event_id, - struct kgem_bo *bo) +flip(struct sna *sna, + RRCrtcPtr crtc, + uint64_t event_id, + uint64_t target_msc, + struct kgem_bo *bo) { - struct sna *sna = to_sna_from_screen(screen); - struct sna_present_event *event; + struct sna_present_event *info; DBG(("%s(pipe=%d, event=%lld, handle=%d)\n", __FUNCTION__, @@ -335,18 +577,25 @@ page_flip(ScreenPtr screen, (long long)event_id, bo->handle)); - event = malloc(sizeof(struct sna_present_event)); - if (event == NULL) + info = malloc(sizeof(struct sna_present_event)+sizeof(uint64_t)); + if (info == NULL) return FALSE; - event->event_id = event_id; - event->crtc = crtc ? crtc->devPrivate : NULL; - if (!sna_page_flip(sna, bo, present_flip_handler, event)) { + info->crtc = crtc ? crtc->devPrivate : NULL; + info->sna = sna; + info->event_id = (uint64_t *)(info + 1); + info->event_id[0] = event_id; + info->n_event_id = 1; + info->target_msc = target_msc; + + if (!sna_page_flip(sna, bo, present_flip_handler, info)) { DBG(("%s: pageflip failed\n", __FUNCTION__)); - free(event); + free(info); return FALSE; } + if (info->crtc) + sna_crtc_set_vblank(info->crtc); return TRUE; } @@ -358,12 +607,48 @@ get_flip_bo(PixmapPtr pixmap) DBG(("%s(pixmap=%ld)\n", __FUNCTION__, pixmap->drawable.serialNumber)); - priv = sna_pixmap_move_to_gpu(pixmap, MOVE_READ | __MOVE_FORCE); + priv = sna_pixmap_move_to_gpu(pixmap, MOVE_READ | __MOVE_SCANOUT | __MOVE_FORCE); if (priv == NULL) { DBG(("%s: cannot force pixmap to the GPU\n", __FUNCTION__)); return NULL; } + if (priv->gpu_bo->scanout) + return priv->gpu_bo; + + if (sna->kgem.has_llc && !wedged(sna) && !priv->pinned) { + struct kgem_bo *bo; + uint32_t tiling; + + tiling = I915_TILING_NONE; + if ((sna->flags & SNA_LINEAR_FB) == 0) + tiling = I915_TILING_X; + + bo = kgem_create_2d(&sna->kgem, + pixmap->drawable.width, + pixmap->drawable.height, + pixmap->drawable.bitsPerPixel, + tiling, CREATE_SCANOUT | CREATE_CACHED); + if (bo) { + BoxRec box; + + box.x1 = box.y1 = 0; + box.x2 = pixmap->drawable.width; + box.y2 = pixmap->drawable.height; + + if (sna->render.copy_boxes(sna, GXcopy, + &pixmap->drawable, priv->gpu_bo, 0, 0, + &pixmap->drawable, bo, 0, 0, + &box, 1, 0)) { + sna_pixmap_unmap(pixmap, priv); + kgem_bo_destroy(&sna->kgem, priv->gpu_bo); + + priv->gpu_bo = bo; + } else + kgem_bo_destroy(&sna->kgem, bo); + } + } + if (sna->flags & SNA_LINEAR_FB && priv->gpu_bo->tiling && !sna_pixmap_change_tiling(pixmap, I915_TILING_NONE)) { @@ -372,12 +657,12 @@ get_flip_bo(PixmapPtr pixmap) } if (priv->gpu_bo->tiling == I915_TILING_Y && + !sna->kgem.can_scanout_y && !sna_pixmap_change_tiling(pixmap, I915_TILING_X)) { DBG(("%s: invalid Y-tiling, cannot convert\n", __FUNCTION__)); return NULL; } - priv->pinned |= PIN_SCANOUT; return priv->gpu_bo; } @@ -388,6 +673,7 @@ sna_present_flip(RRCrtcPtr crtc, PixmapPtr pixmap, Bool sync_flip) { + struct sna *sna = to_sna_from_pixmap(pixmap); struct kgem_bo *bo; DBG(("%s(pipe=%d, event=%lld, msc=%lld, pixmap=%ld, sync?=%d)\n", @@ -397,11 +683,34 @@ sna_present_flip(RRCrtcPtr crtc, (long long)target_msc, pixmap->drawable.serialNumber, sync_flip)); - if (!check_flip__crtc(to_sna_from_pixmap(pixmap), crtc)) { + if (!check_flip__crtc(sna, crtc)) { DBG(("%s: flip invalid for CRTC\n", __FUNCTION__)); return FALSE; } + assert(sna->present.unflip == 0); + + if (sna->flags & SNA_TEAR_FREE) { + DBG(("%s: disabling TearFree (was %s) in favour of Present flips\n", + __FUNCTION__, sna->mode.shadow_enabled ? "enabled" : "disabled")); + sna->mode.shadow_enabled = false; + } + assert(!sna->mode.shadow_enabled); + + if (sna->mode.flip_active) { + struct pollfd pfd; + + DBG(("%s: flips still pending, stalling\n", __FUNCTION__)); + pfd.fd = sna->kgem.fd; + pfd.events = POLLIN; + do { + if (poll(&pfd, 1, -1) != 1) + return FALSE; + + sna_mode_wakeup(sna); + } while (sna->mode.flip_active); + } + bo = get_flip_bo(pixmap); if (bo == NULL) { DBG(("%s: flip invalid bo\n", __FUNCTION__)); @@ -409,9 +718,9 @@ sna_present_flip(RRCrtcPtr crtc, } if (sync_flip) - return page_flip(crtc->pScreen, crtc, event_id, bo); + return flip(sna, crtc, event_id, target_msc, bo); else - return page_flip__async(crtc, event_id, target_msc, bo); + return flip__async(sna, crtc, event_id, target_msc, bo); } static void @@ -421,29 +730,53 @@ sna_present_unflip(ScreenPtr screen, uint64_t event_id) struct kgem_bo *bo; DBG(("%s(event=%lld)\n", __FUNCTION__, (long long)event_id)); - if (sna->mode.front_active == 0 || sna->mode.shadow_active) { + if (sna->mode.front_active == 0 || sna->mode.rr_active) { const struct ust_msc *swap; DBG(("%s: no CRTC active, perform no-op flip\n", __FUNCTION__)); notify: - swap = sna_crtc_last_swap(sna_mode_first_crtc(sna)); - DBG(("%s: pipe=%d, tv=%d.%06d msc %lld, event %lld complete\n", __FUNCTION__, + swap = sna_crtc_last_swap(sna_primary_crtc(sna)); + DBG(("%s: pipe=%d, tv=%d.%06d msc=%lld, event=%lld complete\n", __FUNCTION__, -1, swap->tv_sec, swap->tv_usec, (long long)swap->msc, (long long)event_id)); - present_event_notify(event_id, - ust64(swap->tv_sec, swap->tv_usec), - swap->msc); + present_event_notify(event_id, swap_ust(swap), swap->msc); return; } + assert(!sna->mode.shadow_enabled); + if (sna->mode.flip_active) { + DBG(("%s: %d outstanding flips, queueing unflip\n", __FUNCTION__, sna->mode.flip_active)); + assert(sna->present.unflip == 0); + sna->present.unflip = event_id; + return; + } + + if (sna->flags & SNA_TEAR_FREE) { + DBG(("%s: %s TearFree after Present flips\n", + __FUNCTION__, sna->mode.shadow_damage != NULL ? "enabling" : "disabling")); + sna->mode.shadow_enabled = sna->mode.shadow_damage != NULL; + } + bo = get_flip_bo(screen->GetScreenPixmap(screen)); - if (bo == NULL || !page_flip(screen, NULL, event_id, bo)) { + if (bo == NULL) { +reset_mode: DBG(("%s: failed, trying to restore original mode\n", __FUNCTION__)); xf86SetDesiredModes(sna->scrn); goto notify; } + + assert(sna_pixmap(screen->GetScreenPixmap(screen))->pinned & PIN_SCANOUT); + + if (sna->flags & SNA_HAS_ASYNC_FLIP) { + DBG(("%s: trying async flip restore\n", __FUNCTION__)); + if (flip__async(sna, NULL, event_id, 0, bo)) + return; + } + + if (!flip(sna, NULL, event_id, 0, bo)) + goto reset_mode; } static present_screen_info_rec present_info = { @@ -463,10 +796,13 @@ static present_screen_info_rec present_info = { bool sna_present_open(struct sna *sna, ScreenPtr screen) { + DBG(("%s(num_crtc=%d)\n", __FUNCTION__, sna->mode.num_real_crtc)); + if (sna->mode.num_real_crtc == 0) return false; sna_present_update(sna); + list_init(&sna->present.vblank_queue); return present_screen_init(screen, &present_info); } diff --git a/src/sna/sna_render.c b/src/sna/sna_render.c index 3fbb9ec..d8b7756 100644 --- a/src/sna/sna_render.c +++ b/src/sna/sna_render.c @@ -54,7 +54,7 @@ sna_format_for_depth(int depth) { switch (depth) { case 1: return PICT_a1; - case 4: return PICT_a4; + case 4: return PICT_x4a4; case 8: return PICT_a8; case 15: return PICT_x1r5g5b5; case 16: return PICT_r5g6b5; @@ -272,18 +272,6 @@ no_render_context_switch(struct kgem *kgem, } static void -no_render_retire(struct kgem *kgem) -{ - (void)kgem; -} - -static void -no_render_expire(struct kgem *kgem) -{ - (void)kgem; -} - -static void no_render_fini(struct sna *sna) { (void)sna; @@ -316,8 +304,6 @@ const char *no_render_init(struct sna *sna) render->fini = no_render_fini; sna->kgem.context_switch = no_render_context_switch; - sna->kgem.retire = no_render_retire; - sna->kgem.expire = no_render_expire; if (sna->kgem.has_blt) sna->kgem.ring = KGEM_BLT; @@ -567,6 +553,7 @@ static struct kgem_bo *upload(struct sna *sna, assert(priv->gpu_damage == NULL); assert(priv->gpu_bo == NULL); assert(bo->proxy != NULL); + sna_damage_all(&priv->cpu_damage, pixmap); kgem_proxy_bo_attach(bo, &priv->gpu_bo); } } @@ -1275,6 +1262,7 @@ sna_render_picture_extract(struct sna *sna, assert(priv->gpu_damage == NULL); assert(priv->gpu_bo == NULL); assert(bo->proxy != NULL); + sna_damage_all(&priv->cpu_damage, pixmap); kgem_proxy_bo_attach(bo, &priv->gpu_bo); } } @@ -2183,11 +2171,11 @@ copy_overlap(struct sna *sna, uint8_t alu, ret = (sna->render.copy_boxes(sna, GXcopy, draw, bo, src_dx, src_dy, &tmp->drawable, tmp_bo, -extents->x1, -extents->y1, - box, n , 0) && + box, n, 0) && sna->render.copy_boxes(sna, alu, &tmp->drawable, tmp_bo, -extents->x1, -extents->y1, draw, bo, dst_dx, dst_dy, - box, n , 0)); + box, n, 0)); screen->DestroyPixmap(tmp); return ret; @@ -2336,6 +2324,9 @@ memcpy_copy_boxes(struct sna *sna, uint8_t op, if (op != GXcopy) return false; + if (src_draw->depth != dst_draw->depth) + return false; + clipped = (n > 1 || box->x1 + dx > 0 || box->y1 + dy > 0 || @@ -2380,4 +2371,5 @@ void sna_render_mark_wedged(struct sna *sna) { sna->render.copy_boxes = memcpy_copy_boxes; + sna->render.prefer_gpu = 0; } diff --git a/src/sna/sna_render.h b/src/sna/sna_render.h index 6e1fa48..1baf945 100644 --- a/src/sna/sna_render.h +++ b/src/sna/sna_render.h @@ -238,8 +238,9 @@ struct sna_render { int16_t w, int16_t h, unsigned flags, struct sna_composite_op *tmp); -#define COMPOSITE_PARTIAL 0x1 -#define COMPOSITE_FALLBACK 0x80000000 +#define COMPOSITE_PARTIAL 0x1 +#define COMPOSITE_UPLOAD 0x40000000 +#define COMPOSITE_FALLBACK 0x80000000 bool (*check_composite_spans)(struct sna *sna, uint8_t op, PicturePtr dst, PicturePtr src, @@ -286,6 +287,8 @@ struct sna_render { #define COPY_LAST 0x1 #define COPY_SYNC 0x2 #define COPY_NO_OVERLAP 0x4 +#define COPY_SMALL 0x8 +#define COPY_DRI 0x10 bool (*copy)(struct sna *sna, uint8_t alu, PixmapPtr src, struct kgem_bo *src_bo, @@ -538,7 +541,7 @@ enum { struct gen8_render_state { unsigned gt; - + const struct gt_info *info; struct kgem_bo *general_bo; uint32_t vs_state; diff --git a/src/sna/sna_render_inline.h b/src/sna/sna_render_inline.h index 10fbbfe..e162e37 100644 --- a/src/sna/sna_render_inline.h +++ b/src/sna/sna_render_inline.h @@ -304,6 +304,12 @@ color_convert(uint32_t pixel, return pixel; } +inline static uint32_t +solid_color(uint32_t format, uint32_t pixel) +{ + return color_convert(pixel, format, PICT_a8r8g8b8); +} + inline static bool dst_use_gpu(PixmapPtr pixmap) { struct sna_pixmap *priv = sna_pixmap(pixmap); diff --git a/src/sna/sna_tiling.c b/src/sna/sna_tiling.c index 308efc0..8e2627f 100644 --- a/src/sna/sna_tiling.c +++ b/src/sna/sna_tiling.c @@ -369,8 +369,7 @@ sna_tiling_composite_spans_boxes(struct sna *sna, const BoxRec *box, int nbox, float opacity) { while (nbox--) - sna_tiling_composite_spans_box(sna, op->base.priv, box++, opacity); - (void)sna; + sna_tiling_composite_spans_box(sna, op, box++, opacity); } fastcall static void @@ -581,6 +580,7 @@ sna_tiling_composite_spans(uint32_t op, tile->rects = tile->rects_embedded; tile->rect_count = 0; tile->rect_size = ARRAY_SIZE(tile->rects_embedded); + COMPILE_TIME_ASSERT(sizeof(tile->rects_embedded[0]) >= sizeof(struct sna_tile_span)); tmp->box = sna_tiling_composite_spans_box; tmp->boxes = sna_tiling_composite_spans_boxes; diff --git a/src/sna/sna_trapezoids_boxes.c b/src/sna/sna_trapezoids_boxes.c index 9900e3f..bbf8375 100644 --- a/src/sna/sna_trapezoids_boxes.c +++ b/src/sna/sna_trapezoids_boxes.c @@ -198,7 +198,7 @@ composite_aligned_boxes(struct sna *sna, if (op == PictOpClear && sna->clear) src = sna->clear; - DBG(("%s: clipped extents (%d, %d), (%d, %d); now offset by (%d, %d), orgin (%d, %d)\n", + DBG(("%s: clipped extents (%d, %d), (%d, %d); now offset by (%d, %d), origin (%d, %d)\n", __FUNCTION__, clip.extents.x1, clip.extents.y1, clip.extents.x2, clip.extents.y2, @@ -592,6 +592,8 @@ lerp32_opacity(PixmapPtr scratch, uint32_t *ptr; int stride, i; + sigtrap_assert_active(); + ptr = (uint32_t*)((uint8_t *)scratch->devPrivate.ptr + scratch->devKind * y); ptr += x; stride = scratch->devKind / 4; diff --git a/src/sna/sna_trapezoids_imprecise.c b/src/sna/sna_trapezoids_imprecise.c index 37def2f..8bc7c8a 100644 --- a/src/sna/sna_trapezoids_imprecise.c +++ b/src/sna/sna_trapezoids_imprecise.c @@ -962,6 +962,16 @@ tor_add_trapezoid(struct tor *tor, const xTrapezoid *t, int dx, int dy) { + if (!xTrapezoidValid(t)) { + __DBG(("%s: skipping invalid trapezoid: top=%d, bottom=%d, left=(%d, %d), (%d, %d), right=(%d, %d), (%d, %d)\n", + __FUNCTION__, + t->top, t->bottom, + t->left.p1.x, t->left.p1.y, + t->left.p2.x, t->left.p2.y, + t->right.p1.x, t->right.p1.y, + t->right.p2.x, t->right.p2.y)); + return; + } polygon_add_edge(tor->polygon, t, &t->left, 1, dx, dy); polygon_add_edge(tor->polygon, t, &t->right, -1, dx, dy); } @@ -1687,31 +1697,27 @@ struct span_thread { #define SPAN_THREAD_MAX_BOXES (8192/sizeof(struct sna_opacity_box)) struct span_thread_boxes { const struct sna_composite_spans_op *op; + const BoxRec *clip_start, *clip_end; int num_boxes; struct sna_opacity_box boxes[SPAN_THREAD_MAX_BOXES]; }; -static void span_thread_add_boxes(struct sna *sna, void *data, - const BoxRec *box, int count, float alpha) +static void span_thread_add_box(struct sna *sna, void *data, + const BoxRec *box, float alpha) { struct span_thread_boxes *b = data; - __DBG(("%s: adding %d boxes with alpha=%f\n", - __FUNCTION__, count, alpha)); + __DBG(("%s: adding box with alpha=%f\n", __FUNCTION__, alpha)); - assert(count > 0 && count <= SPAN_THREAD_MAX_BOXES); - if (unlikely(b->num_boxes + count > SPAN_THREAD_MAX_BOXES)) { - DBG(("%s: flushing %d boxes, adding %d\n", __FUNCTION__, b->num_boxes, count)); - assert(b->num_boxes <= SPAN_THREAD_MAX_BOXES); + if (unlikely(b->num_boxes == SPAN_THREAD_MAX_BOXES)) { + DBG(("%s: flushing %d boxes\n", __FUNCTION__, b->num_boxes)); b->op->thread_boxes(sna, b->op, b->boxes, b->num_boxes); b->num_boxes = 0; } - do { - b->boxes[b->num_boxes].box = *box++; - b->boxes[b->num_boxes].alpha = alpha; - b->num_boxes++; - } while (--count); + b->boxes[b->num_boxes].box = *box++; + b->boxes[b->num_boxes].alpha = alpha; + b->num_boxes++; assert(b->num_boxes <= SPAN_THREAD_MAX_BOXES); } @@ -1722,8 +1728,22 @@ span_thread_box(struct sna *sna, const BoxRec *box, int coverage) { + struct span_thread_boxes *b = (struct span_thread_boxes *)op; + __DBG(("%s: %d -> %d @ %d\n", __FUNCTION__, box->x1, box->x2, coverage)); - span_thread_add_boxes(sna, op, box, 1, AREA_TO_ALPHA(coverage)); + if (b->num_boxes) { + struct sna_opacity_box *bb = &b->boxes[b->num_boxes-1]; + if (bb->box.x1 == box->x1 && + bb->box.x2 == box->x2 && + bb->box.y2 == box->y1 && + bb->alpha == AREA_TO_ALPHA(coverage)) { + bb->box.y2 = box->y2; + __DBG(("%s: contracted double row: %d -> %d\n", __func__, bb->box.y1, bb->box.y2)); + return; + } + } + + span_thread_add_box(sna, op, box, AREA_TO_ALPHA(coverage)); } static void @@ -1733,20 +1753,28 @@ span_thread_clipped_box(struct sna *sna, const BoxRec *box, int coverage) { - pixman_region16_t region; + struct span_thread_boxes *b = (struct span_thread_boxes *)op; + const BoxRec *c; __DBG(("%s: %d -> %d @ %f\n", __FUNCTION__, box->x1, box->x2, AREA_TO_ALPHA(coverage))); - pixman_region_init_rects(®ion, box, 1); - RegionIntersect(®ion, ®ion, clip); - if (region_num_rects(®ion)) { - span_thread_add_boxes(sna, op, - region_rects(®ion), - region_num_rects(®ion), - AREA_TO_ALPHA(coverage)); + b->clip_start = + find_clip_box_for_y(b->clip_start, b->clip_end, box->y1); + + c = b->clip_start; + while (c != b->clip_end) { + BoxRec clipped; + + if (box->y2 <= c->y1) + break; + + clipped = *box; + if (!box_intersect(&clipped, c++)) + continue; + + span_thread_add_box(sna, op, &clipped, AREA_TO_ALPHA(coverage)); } - pixman_region_fini(®ion); } static span_func_t @@ -1777,6 +1805,16 @@ thread_choose_span(struct sna_composite_spans_op *tmp, return span; } +inline static void +span_thread_boxes_init(struct span_thread_boxes *boxes, + const struct sna_composite_spans_op *op, + const RegionRec *clip) +{ + boxes->op = op; + region_get_boxes(clip, &boxes->clip_start, &boxes->clip_end); + boxes->num_boxes = 0; +} + static void span_thread(void *arg) { @@ -1789,8 +1827,7 @@ span_thread(void *arg) if (!tor_init(&tor, &thread->extents, 2*thread->ntrap)) return; - boxes.op = thread->op; - boxes.num_boxes = 0; + span_thread_boxes_init(&boxes, thread->op, thread->clip); y1 = thread->extents.y1 - thread->draw_y; y2 = thread->extents.y2 - thread->draw_y; @@ -2190,6 +2227,52 @@ static void _tor_blt_src(struct inplace *in, const BoxRec *box, uint8_t v) } while (--h); } +struct clipped_span { + span_func_t span; + const BoxRec *clip_start, *clip_end; +}; + +static void +tor_blt_clipped(struct sna *sna, + struct sna_composite_spans_op *op, + pixman_region16_t *clip, + const BoxRec *box, + int coverage) +{ + struct clipped_span *cs = (struct clipped_span *)clip; + const BoxRec *c; + + cs->clip_start = + find_clip_box_for_y(cs->clip_start, cs->clip_end, box->y1); + + c = cs->clip_start; + while (c != cs->clip_end) { + BoxRec clipped; + + if (box->y2 <= c->y1) + break; + + clipped = *box; + if (!box_intersect(&clipped, c++)) + continue; + + cs->span(sna, op, NULL, &clipped, coverage); + } +} + +inline static span_func_t +clipped_span(struct clipped_span *cs, + span_func_t span, + const RegionRec *clip) +{ + if (clip->data) { + cs->span = span; + region_get_boxes(clip, &cs->clip_start, &cs->clip_end); + span = tor_blt_clipped; + } + return span; +} + static void tor_blt_src(struct sna *sna, struct sna_composite_spans_op *op, @@ -2203,25 +2286,6 @@ tor_blt_src(struct sna *sna, } static void -tor_blt_src_clipped(struct sna *sna, - struct sna_composite_spans_op *op, - pixman_region16_t *clip, - const BoxRec *box, - int coverage) -{ - pixman_region16_t region; - int n; - - pixman_region_init_rects(®ion, box, 1); - RegionIntersect(®ion, ®ion, clip); - n = region_num_rects(®ion); - box = region_rects(®ion); - while (n--) - tor_blt_src(sna, op, NULL, box++, coverage); - pixman_region_fini(®ion); -} - -static void tor_blt_in(struct sna *sna, struct sna_composite_spans_op *op, pixman_region16_t *clip, @@ -2253,25 +2317,6 @@ tor_blt_in(struct sna *sna, } static void -tor_blt_in_clipped(struct sna *sna, - struct sna_composite_spans_op *op, - pixman_region16_t *clip, - const BoxRec *box, - int coverage) -{ - pixman_region16_t region; - int n; - - pixman_region_init_rects(®ion, box, 1); - RegionIntersect(®ion, ®ion, clip); - n = region_num_rects(®ion); - box = region_rects(®ion); - while (n--) - tor_blt_in(sna, op, NULL, box++, coverage); - pixman_region_fini(®ion); -} - -static void tor_blt_add(struct sna *sna, struct sna_composite_spans_op *op, pixman_region16_t *clip, @@ -2310,25 +2355,6 @@ tor_blt_add(struct sna *sna, } static void -tor_blt_add_clipped(struct sna *sna, - struct sna_composite_spans_op *op, - pixman_region16_t *clip, - const BoxRec *box, - int coverage) -{ - pixman_region16_t region; - int n; - - pixman_region_init_rects(®ion, box, 1); - RegionIntersect(®ion, ®ion, clip); - n = region_num_rects(®ion); - box = region_rects(®ion); - while (n--) - tor_blt_add(sna, op, NULL, box++, coverage); - pixman_region_fini(®ion); -} - -static void tor_blt_lerp32(struct sna *sna, struct sna_composite_spans_op *op, pixman_region16_t *clip, @@ -2343,6 +2369,7 @@ tor_blt_lerp32(struct sna *sna, if (coverage == 0) return; + sigtrap_assert_active(); ptr += box->y1 * stride + box->x1; h = box->y2 - box->y1; @@ -2383,25 +2410,6 @@ tor_blt_lerp32(struct sna *sna, } } -static void -tor_blt_lerp32_clipped(struct sna *sna, - struct sna_composite_spans_op *op, - pixman_region16_t *clip, - const BoxRec *box, - int coverage) -{ - pixman_region16_t region; - int n; - - pixman_region_init_rects(®ion, box, 1); - RegionIntersect(®ion, ®ion, clip); - n = region_num_rects(®ion); - box = region_rects(®ion); - while (n--) - tor_blt_lerp32(sna, op, NULL, box++, coverage); - pixman_region_fini(®ion); -} - struct pixman_inplace { pixman_image_t *image, *source, *mask; uint32_t color; @@ -2431,24 +2439,6 @@ pixmask_span_solid(struct sna *sna, pi->dx + box->x1, pi->dy + box->y1, box->x2 - box->x1, box->y2 - box->y1); } -static void -pixmask_span_solid__clipped(struct sna *sna, - struct sna_composite_spans_op *op, - pixman_region16_t *clip, - const BoxRec *box, - int coverage) -{ - pixman_region16_t region; - int n; - - pixman_region_init_rects(®ion, box, 1); - RegionIntersect(®ion, ®ion, clip); - n = region_num_rects(®ion); - box = region_rects(®ion); - while (n--) - pixmask_span_solid(sna, op, NULL, box++, coverage); - pixman_region_fini(®ion); -} static void pixmask_span(struct sna *sna, @@ -2471,24 +2461,6 @@ pixmask_span(struct sna *sna, pi->dx + box->x1, pi->dy + box->y1, box->x2 - box->x1, box->y2 - box->y1); } -static void -pixmask_span__clipped(struct sna *sna, - struct sna_composite_spans_op *op, - pixman_region16_t *clip, - const BoxRec *box, - int coverage) -{ - pixman_region16_t region; - int n; - - pixman_region_init_rects(®ion, box, 1); - RegionIntersect(®ion, ®ion, clip); - n = region_num_rects(®ion); - box = region_rects(®ion); - while (n--) - pixmask_span(sna, op, NULL, box++, coverage); - pixman_region_fini(®ion); -} struct inplace_x8r8g8b8_thread { xTrapezoid *traps; @@ -2507,6 +2479,7 @@ static void inplace_x8r8g8b8_thread(void *arg) struct inplace_x8r8g8b8_thread *thread = arg; struct tor tor; span_func_t span; + struct clipped_span clipped; RegionPtr clip; int y1, y2, n; @@ -2537,12 +2510,11 @@ static void inplace_x8r8g8b8_thread(void *arg) inplace.stride = pixmap->devKind; inplace.color = thread->color; - if (clip->data) - span = tor_blt_lerp32_clipped; - else - span = tor_blt_lerp32; + span = clipped_span(&clipped, tor_blt_lerp32, clip); - tor_render(NULL, &tor, (void*)&inplace, clip, span, false); + tor_render(NULL, &tor, + (void*)&inplace, (void*)&clipped, + span, false); } else if (thread->is_solid) { struct pixman_inplace pi; @@ -2555,12 +2527,11 @@ static void inplace_x8r8g8b8_thread(void *arg) 1, 1, pi.bits, 0); pixman_image_set_repeat(pi.source, PIXMAN_REPEAT_NORMAL); - if (clip->data) - span = pixmask_span_solid__clipped; - else - span = pixmask_span_solid; + span = clipped_span(&clipped, pixmask_span_solid, clip); - tor_render(NULL, &tor, (void*)&pi, clip, span, false); + tor_render(NULL, &tor, + (void*)&pi, (void *)&clipped, + span, false); pixman_image_unref(pi.source); pixman_image_unref(pi.image); @@ -2579,12 +2550,11 @@ static void inplace_x8r8g8b8_thread(void *arg) pi.bits = pixman_image_get_data(pi.mask); pi.op = thread->op; - if (clip->data) - span = pixmask_span__clipped; - else - span = pixmask_span; + span = clipped_span(&clipped, pixmask_span, clip); - tor_render(NULL, &tor, (void*)&pi, clip, span, false); + tor_render(NULL, &tor, + (void*)&pi, (void *)&clipped, + span, false); pixman_image_unref(pi.mask); pixman_image_unref(pi.source); @@ -2698,6 +2668,7 @@ trapezoid_span_inplace__x8r8g8b8(CARD8 op, if (num_threads == 1) { struct tor tor; span_func_t span; + struct clipped_span clipped; if (!tor_init(&tor, ®ion.extents, 2*ntrap)) return true; @@ -2723,17 +2694,15 @@ trapezoid_span_inplace__x8r8g8b8(CARD8 op, inplace.stride = pixmap->devKind; inplace.color = color; - if (dst->pCompositeClip->data) - span = tor_blt_lerp32_clipped; - else - span = tor_blt_lerp32; + span = clipped_span(&clipped, tor_blt_lerp32, dst->pCompositeClip); DBG(("%s: render inplace op=%d, color=%08x\n", __FUNCTION__, op, color)); if (sigtrap_get() == 0) { - tor_render(NULL, &tor, (void*)&inplace, - dst->pCompositeClip, span, false); + tor_render(NULL, &tor, + (void*)&inplace, (void*)&clipped, + span, false); sigtrap_put(); } } else if (is_solid) { @@ -2748,15 +2717,12 @@ trapezoid_span_inplace__x8r8g8b8(CARD8 op, 1, 1, pi.bits, 0); pixman_image_set_repeat(pi.source, PIXMAN_REPEAT_NORMAL); - if (dst->pCompositeClip->data) - span = pixmask_span_solid__clipped; - else - span = pixmask_span_solid; + span = clipped_span(&clipped, pixmask_span_solid, dst->pCompositeClip); if (sigtrap_get() == 0) { - tor_render(NULL, &tor, (void*)&pi, - dst->pCompositeClip, span, - false); + tor_render(NULL, &tor, + (void*)&pi, (void*)&clipped, + span, false); sigtrap_put(); } @@ -2777,15 +2743,12 @@ trapezoid_span_inplace__x8r8g8b8(CARD8 op, pi.bits = pixman_image_get_data(pi.mask); pi.op = op; - if (dst->pCompositeClip->data) - span = pixmask_span__clipped; - else - span = pixmask_span; + span = clipped_span(&clipped, pixmask_span, dst->pCompositeClip); if (sigtrap_get() == 0) { - tor_render(NULL, &tor, (void*)&pi, - dst->pCompositeClip, span, - false); + tor_render(NULL, &tor, + (void*)&pi, (void*)&clipped, + span, false); sigtrap_put(); } @@ -2847,9 +2810,9 @@ trapezoid_span_inplace__x8r8g8b8(CARD8 op, struct inplace_thread { xTrapezoid *traps; - RegionPtr clip; span_func_t span; struct inplace inplace; + struct clipped_span clipped; BoxRec extents; int dx, dy; int draw_x, draw_y; @@ -2874,8 +2837,9 @@ static void inplace_thread(void *arg) tor_add_trapezoid(&tor, &thread->traps[n], thread->dx, thread->dy); } - tor_render(NULL, &tor, (void*)&thread->inplace, - thread->clip, thread->span, thread->unbounded); + tor_render(NULL, &tor, + (void*)&thread->inplace, (void*)&thread->clipped, + thread->span, thread->unbounded); tor_fini(&tor); } @@ -2889,6 +2853,7 @@ imprecise_trapezoid_span_inplace(struct sna *sna, bool fallback) { struct inplace inplace; + struct clipped_span clipped; span_func_t span; PixmapPtr pixmap; struct sna_pixmap *priv; @@ -3005,21 +2970,12 @@ imprecise_trapezoid_span_inplace(struct sna *sna, region.extents.x2, region.extents.y2)); if (op == PictOpSrc) { - if (dst->pCompositeClip->data) - span = tor_blt_src_clipped; - else - span = tor_blt_src; + span = tor_blt_src; } else if (op == PictOpIn) { - if (dst->pCompositeClip->data) - span = tor_blt_in_clipped; - else - span = tor_blt_in; + span = tor_blt_in; } else { assert(op == PictOpAdd); - if (dst->pCompositeClip->data) - span = tor_blt_add_clipped; - else - span = tor_blt_add; + span = tor_blt_add; } DBG(("%s: move-to-cpu\n", __FUNCTION__)); @@ -3037,6 +2993,8 @@ imprecise_trapezoid_span_inplace(struct sna *sna, inplace.stride = pixmap->devKind; inplace.opacity = color >> 24; + span = clipped_span(&clipped, span, dst->pCompositeClip); + num_threads = 1; if ((flags & COMPOSITE_SPANS_RECTILINEAR) == 0) num_threads = sna_use_threads(region.extents.x2 - region.extents.x1, @@ -3057,8 +3015,9 @@ imprecise_trapezoid_span_inplace(struct sna *sna, } if (sigtrap_get() == 0) { - tor_render(NULL, &tor, (void*)&inplace, - dst->pCompositeClip, span, unbounded); + tor_render(NULL, &tor, + (void*)&inplace, (void *)&clipped, + span, unbounded); sigtrap_put(); } @@ -3075,8 +3034,8 @@ imprecise_trapezoid_span_inplace(struct sna *sna, threads[0].traps = traps; threads[0].ntrap = ntrap; threads[0].inplace = inplace; + threads[0].clipped = clipped; threads[0].extents = region.extents; - threads[0].clip = dst->pCompositeClip; threads[0].span = span; threads[0].unbounded = unbounded; threads[0].dx = dx; @@ -3707,8 +3666,7 @@ tristrip_thread(void *arg) if (!tor_init(&tor, &thread->extents, 2*thread->count)) return; - boxes.op = thread->op; - boxes.num_boxes = 0; + span_thread_boxes_init(&boxes, thread->op, thread->clip); cw = 0; ccw = 1; polygon_add_line(tor.polygon, @@ -3874,7 +3832,7 @@ imprecise_tristrip_span_converter(struct sna *sna, break; } while (1); polygon_add_line(tor.polygon, - &points[cw], &points[2+ccw], + &points[cw], &points[ccw], dx, dy); assert(tor.polygon->num_edges <= 2*count); diff --git a/src/sna/sna_trapezoids_mono.c b/src/sna/sna_trapezoids_mono.c index 808703a..07a7867 100644 --- a/src/sna/sna_trapezoids_mono.c +++ b/src/sna/sna_trapezoids_mono.c @@ -72,13 +72,14 @@ struct mono { struct sna *sna; struct sna_composite_op op; pixman_region16_t clip; + const BoxRec *clip_start, *clip_end; fastcall void (*span)(struct mono *, int, int, BoxPtr); struct mono_polygon polygon; }; -#define I(x) pixman_fixed_to_int ((x) + pixman_fixed_1_minus_e/2) +#define I(x) pixman_fixed_to_int((x) + pixman_fixed_1_minus_e/2) static struct quorem floored_muldivrem(int32_t x, int32_t a, int32_t b) @@ -249,22 +250,22 @@ mono_add_line(struct mono *mono, e->dxdy = floored_muldivrem(dx, pixman_fixed_1, dy); - e->x = floored_muldivrem((ytop - dst_y) * pixman_fixed_1 + pixman_fixed_1_minus_e/2 - p1->y, + e->x = floored_muldivrem((ytop - dst_y) * pixman_fixed_1 + pixman_fixed_1/2 - p1->y, dx, dy); e->x.quo += p1->x; e->x.rem -= dy; e->dy = dy; - - __DBG(("%s: initial x=%d [%d.%d/%d] + dxdy=%d.%d/%d\n", - __FUNCTION__, - I(e->x.quo), e->x.quo, e->x.rem, e->dy, - e->dxdy.quo, e->dxdy.rem, e->dy)); } e->x.quo += dst_x*pixman_fixed_1; + __DBG(("%s: initial x=%d [%d.%d/%d] + dxdy=%d.%d/%d\n", + __FUNCTION__, + I(e->x.quo), e->x.quo, e->x.rem, e->dy, + e->dxdy.quo, e->dxdy.rem, e->dy)); { struct mono_edge **ptail = &polygon->y_buckets[ytop - mono->clip.extents.y1]; + assert(ytop - mono->clip.extents.y1 < mono->clip.extents.y2 - mono->clip.extents.y1); if (*ptail) (*ptail)->prev = e; e->next = *ptail; @@ -368,6 +369,10 @@ static struct mono_edge *mono_filter(struct mono_edge *edges) e->x.rem == n->x.rem && e->dxdy.quo == n->dxdy.quo && e->dxdy.rem == n->dxdy.rem) { + assert(e->dy == n->dy); + __DBG(("%s: discarding cancellation pair (%d.%d) + (%d.%d)\n", + __FUNCTION__, e->x.quo, e->x.rem, e->dxdy.quo, e->dxdy.rem)); + if (e->prev) e->prev->next = n->next; else @@ -378,8 +383,11 @@ static struct mono_edge *mono_filter(struct mono_edge *edges) break; e = n->next; - } else + } else { + __DBG(("%s: adding edge (%d.%d) + (%d.%d)/%d, height=%d\n", + __FUNCTION__, n->x.quo, n->x.rem, n->dxdy.quo, n->dxdy.rem, n->dy, n->height_left)); e = n; + } } return edges; @@ -474,6 +482,34 @@ mono_span__fast(struct mono *c, int x1, int x2, BoxPtr box) c->op.box(c->sna, &c->op, box); } +fastcall static void +mono_span__clipped(struct mono *c, int x1, int x2, BoxPtr box) +{ + const BoxRec *b; + + __DBG(("%s [%d, %d]\n", __FUNCTION__, x1, x2)); + + c->clip_start = + find_clip_box_for_y(c->clip_start, c->clip_end, box->y1); + + b = c->clip_start; + while (b != c->clip_end) { + BoxRec clipped; + + if (box->y2 <= b->y1) + break; + + clipped.x1 = x1; + clipped.x2 = x2; + clipped.y1 = box->y1; + clipped.y2 = box->y2; + if (!box_intersect(&clipped, b++)) + continue; + + c->op.box(c->sna, &c->op, &clipped); + } +} + struct mono_span_thread_boxes { const struct sna_composite_op *op; #define MONO_SPAN_MAX_BOXES (8192/sizeof(BoxRec)) @@ -482,40 +518,45 @@ struct mono_span_thread_boxes { }; inline static void -thread_mono_span_add_boxes(struct mono *c, const BoxRec *box, int count) +thread_mono_span_add_box(struct mono *c, const BoxRec *box) { struct mono_span_thread_boxes *b = c->op.priv; - assert(count > 0 && count <= MONO_SPAN_MAX_BOXES); - if (unlikely(b->num_boxes + count > MONO_SPAN_MAX_BOXES)) { + if (unlikely(b->num_boxes == MONO_SPAN_MAX_BOXES)) { b->op->thread_boxes(c->sna, b->op, b->boxes, b->num_boxes); b->num_boxes = 0; } - memcpy(b->boxes + b->num_boxes, box, count*sizeof(BoxRec)); - b->num_boxes += count; + b->boxes[b->num_boxes++] = *box; assert(b->num_boxes <= MONO_SPAN_MAX_BOXES); } fastcall static void thread_mono_span_clipped(struct mono *c, int x1, int x2, BoxPtr box) { - pixman_region16_t region; + const BoxRec *b; __DBG(("%s [%d, %d]\n", __FUNCTION__, x1, x2)); - box->x1 = x1; - box->x2 = x2; + c->clip_start = + find_clip_box_for_y(c->clip_start, c->clip_end, box->y1); - assert(c->clip.data); + b = c->clip_start; + while (b != c->clip_end) { + BoxRec clipped; + + if (box->y2 <= b->y1) + break; + + clipped.x1 = x1; + clipped.x2 = x2; + clipped.y1 = box->y1; + clipped.y2 = box->y2; + if (!box_intersect(&clipped, b++)) + continue; - pixman_region_init_rects(®ion, box, 1); - RegionIntersect(®ion, ®ion, &c->clip); - if (region_num_rects(®ion)) - thread_mono_span_add_boxes(c, - region_rects(®ion), - region_num_rects(®ion)); - pixman_region_fini(®ion); + thread_mono_span_add_box(c, &clipped); + } } fastcall static void @@ -525,7 +566,7 @@ thread_mono_span(struct mono *c, int x1, int x2, BoxPtr box) box->x1 = x1; box->x2 = x2; - thread_mono_span_add_boxes(c, box, 1); + thread_mono_span_add_box(c, box); } inline static void @@ -537,6 +578,8 @@ mono_row(struct mono *c, int16_t y, int16_t h) int winding = 0; BoxRec box; + __DBG(("%s: y=%d, h=%d\n", __FUNCTION__, y, h)); + DBG_MONO_EDGES(edge); VALIDATE_MONO_EDGES(&c->head); @@ -547,6 +590,8 @@ mono_row(struct mono *c, int16_t y, int16_t h) struct mono_edge *next = edge->next; int16_t xend = I(edge->x.quo); + __DBG(("%s: adding edge dir=%d [winding=%d], x=%d [%d]\n", + __FUNCTION__, edge->dir, winding + edge->dir, xend, edge->x.quo)); if (--edge->height_left) { if (edge->dy) { edge->x.quo += edge->dxdy.quo; @@ -555,6 +600,8 @@ mono_row(struct mono *c, int16_t y, int16_t h) ++edge->x.quo; edge->x.rem -= edge->dy; } + __DBG(("%s: stepped edge (%d.%d) + (%d.%d)/%d, height=%d, prev_x=%d\n", + __FUNCTION__, edge->x.quo, edge->x.rem, edge->dxdy.quo, edge->dxdy.rem, edge->dy, edge->height_left, edge->x.quo)); } if (edge->x.quo < prev_x) { @@ -578,17 +625,22 @@ mono_row(struct mono *c, int16_t y, int16_t h) winding += edge->dir; if (winding == 0) { assert(I(next->x.quo) >= xend); - if (I(next->x.quo) > xend + 1) { + if (I(next->x.quo) > xend) { + __DBG(("%s: end span: %d\n", __FUNCTION__, xend)); if (xstart < c->clip.extents.x1) xstart = c->clip.extents.x1; if (xend > c->clip.extents.x2) xend = c->clip.extents.x2; - if (xend > xstart) + if (xend > xstart) { + __DBG(("%s: emit span [%d, %d]\n", __FUNCTION__, xstart, xend)); c->span(c, xstart, xend, &box); + } xstart = INT16_MIN; } - } else if (xstart == INT16_MIN) + } else if (xstart == INT16_MIN) { + __DBG(("%s: starting new span: %d\n", __FUNCTION__, xend)); xstart = xend; + } edge = next; } @@ -650,9 +702,14 @@ mono_render(struct mono *mono) for (i = 0; i < h; i = j) { j = i + 1; + __DBG(("%s: row=%d, new edges? %d\n", __FUNCTION__, + i, polygon->y_buckets[i] != NULL)); + if (polygon->y_buckets[i]) mono_merge_edges(mono, polygon->y_buckets[i]); + __DBG(("%s: row=%d, vertical? %d\n", __FUNCTION__, + i, mono->is_vertical)); if (mono->is_vertical) { struct mono_edge *e = mono->head.next; int min_height = h - i; @@ -667,6 +724,7 @@ mono_render(struct mono *mono) j++; if (j != i + 1) mono_step_edges(mono, j - (i + 1)); + __DBG(("%s: %d vertical rows\n", __FUNCTION__, j-i)); } mono_row(mono, i, j-i); @@ -717,6 +775,7 @@ mono_span_thread(void *arg) if (RegionNil(&mono.clip)) return; } + region_get_boxes(&mono.clip, &mono.clip_start, &mono.clip_end); boxes.op = thread->op; boxes.num_boxes = 0; @@ -891,9 +950,12 @@ mono_trapezoids_span_converter(struct sna *sna, if (mono.clip.data == NULL && mono.op.damage == NULL) mono.span = mono_span__fast; + else if (mono.clip.data != NULL && mono.op.damage == NULL) + mono.span = mono_span__clipped; else mono.span = mono_span; + region_get_boxes(&mono.clip, &mono.clip_start, &mono.clip_end); mono_render(&mono); mono.op.done(mono.sna, &mono.op); mono_fini(&mono); @@ -939,6 +1001,7 @@ mono_trapezoids_span_converter(struct sna *sna, mono.clip.extents.x2 - mono.clip.extents.x1, mono.clip.extents.y2 - mono.clip.extents.y1, COMPOSITE_PARTIAL, memset(&mono.op, 0, sizeof(mono.op)))) { + region_get_boxes(&mono.clip, &mono.clip_start, &mono.clip_end); mono_render(&mono); mono.op.done(mono.sna, &mono.op); } @@ -974,6 +1037,7 @@ mono_inplace_fill_box(struct sna *sna, box->x2 - box->x1, box->y2 - box->y1, fill->color)); + sigtrap_assert_active(); pixman_fill(fill->data, fill->stride, fill->bpp, box->x1, box->y1, box->x2 - box->x1, @@ -995,6 +1059,7 @@ mono_inplace_fill_boxes(struct sna *sna, box->x2 - box->x1, box->y2 - box->y1, fill->color)); + sigtrap_assert_active(); pixman_fill(fill->data, fill->stride, fill->bpp, box->x1, box->y1, box->x2 - box->x1, @@ -1382,10 +1447,13 @@ mono_triangles_span_converter(struct sna *sna, mono_render(&mono); mono.op.done(mono.sna, &mono.op); } + mono_fini(&mono); if (!was_clear && !operator_is_bounded(op)) { xPointFixed p1, p2; + DBG(("%s: performing unbounded clear\n", __FUNCTION__)); + if (!mono_init(&mono, 2+3*count)) return false; @@ -1431,7 +1499,6 @@ mono_triangles_span_converter(struct sna *sna, mono_fini(&mono); } - mono_fini(&mono); REGION_UNINIT(NULL, &mono.clip); return true; } diff --git a/src/sna/sna_trapezoids_precise.c b/src/sna/sna_trapezoids_precise.c index 9187ab4..242b4ac 100644 --- a/src/sna/sna_trapezoids_precise.c +++ b/src/sna/sna_trapezoids_precise.c @@ -1023,6 +1023,16 @@ tor_init(struct tor *converter, const BoxRec *box, int num_edges) static void tor_add_trapezoid(struct tor *tor, const xTrapezoid *t, int dx, int dy) { + if (!xTrapezoidValid(t)) { + __DBG(("%s: skipping invalid trapezoid: top=%d, bottom=%d, left=(%d, %d), (%d, %d), right=(%d, %d), (%d, %d)\n", + __FUNCTION__, + t->top, t->bottom, + t->left.p1.x, t->left.p1.y, + t->left.p2.x, t->left.p2.y, + t->right.p1.x, t->right.p1.y, + t->right.p2.x, t->right.p2.y)); + return; + } polygon_add_edge(tor->polygon, t, &t->left, 1, dx, dy); polygon_add_edge(tor->polygon, t, &t->right, -1, dx, dy); } @@ -1635,31 +1645,27 @@ struct span_thread { #define SPAN_THREAD_MAX_BOXES (8192/sizeof(struct sna_opacity_box)) struct span_thread_boxes { const struct sna_composite_spans_op *op; + const BoxRec *clip_start, *clip_end; int num_boxes; struct sna_opacity_box boxes[SPAN_THREAD_MAX_BOXES]; }; -static void span_thread_add_boxes(struct sna *sna, void *data, - const BoxRec *box, int count, float alpha) +static void span_thread_add_box(struct sna *sna, void *data, + const BoxRec *box, float alpha) { struct span_thread_boxes *b = data; - __DBG(("%s: adding %d boxes with alpha=%f\n", - __FUNCTION__, count, alpha)); + __DBG(("%s: adding box with alpha=%f\n", __FUNCTION__, alpha)); - assert(count > 0 && count <= SPAN_THREAD_MAX_BOXES); - if (unlikely(b->num_boxes + count > SPAN_THREAD_MAX_BOXES)) { - DBG(("%s: flushing %d boxes, adding %d\n", __FUNCTION__, b->num_boxes, count)); - assert(b->num_boxes <= SPAN_THREAD_MAX_BOXES); + if (unlikely(b->num_boxes == SPAN_THREAD_MAX_BOXES)) { + DBG(("%s: flushing %d boxes\n", __FUNCTION__, b->num_boxes)); b->op->thread_boxes(sna, b->op, b->boxes, b->num_boxes); b->num_boxes = 0; } - do { - b->boxes[b->num_boxes].box = *box++; - b->boxes[b->num_boxes].alpha = alpha; - b->num_boxes++; - } while (--count); + b->boxes[b->num_boxes].box = *box++; + b->boxes[b->num_boxes].alpha = alpha; + b->num_boxes++; assert(b->num_boxes <= SPAN_THREAD_MAX_BOXES); } @@ -1670,8 +1676,22 @@ span_thread_box(struct sna *sna, const BoxRec *box, int coverage) { + struct span_thread_boxes *b = (struct span_thread_boxes *)op; + __DBG(("%s: %d -> %d @ %d\n", __FUNCTION__, box->x1, box->x2, coverage)); - span_thread_add_boxes(sna, op, box, 1, AREA_TO_FLOAT(coverage)); + if (b->num_boxes) { + struct sna_opacity_box *bb = &b->boxes[b->num_boxes-1]; + if (bb->box.x1 == box->x1 && + bb->box.x2 == box->x2 && + bb->box.y2 == box->y1 && + bb->alpha == AREA_TO_FLOAT(coverage)) { + bb->box.y2 = box->y2; + __DBG(("%s: contracted double row: %d -> %d\n", __func__, bb->box.y1, bb->box.y2)); + return; + } + } + + span_thread_add_box(sna, op, box, AREA_TO_FLOAT(coverage)); } static void @@ -1681,20 +1701,28 @@ span_thread_clipped_box(struct sna *sna, const BoxRec *box, int coverage) { - pixman_region16_t region; + struct span_thread_boxes *b = (struct span_thread_boxes *)op; + const BoxRec *c; __DBG(("%s: %d -> %d @ %f\n", __FUNCTION__, box->x1, box->x2, AREA_TO_FLOAT(coverage))); - pixman_region_init_rects(®ion, box, 1); - RegionIntersect(®ion, ®ion, clip); - if (region_num_rects(®ion)) { - span_thread_add_boxes(sna, op, - region_rects(®ion), - region_num_rects(®ion), - AREA_TO_FLOAT(coverage)); + b->clip_start = + find_clip_box_for_y(b->clip_start, b->clip_end, box->y1); + + c = b->clip_start; + while (c != b->clip_end) { + BoxRec clipped; + + if (box->y2 <= c->y1) + break; + + clipped = *box; + if (!box_intersect(&clipped, c++)) + continue; + + span_thread_add_box(sna, op, &clipped, AREA_TO_FLOAT(coverage)); } - pixman_region_fini(®ion); } static span_func_t @@ -1712,7 +1740,7 @@ thread_choose_span(struct sna_composite_spans_op *tmp, assert(!is_mono(dst, maskFormat)); assert(tmp->thread_boxes); - DBG(("%s: clipped? %d\n", __FUNCTION__, clip->data != NULL)); + DBG(("%s: clipped? %d x %d\n", __FUNCTION__, clip->data != NULL, region_num_rects(clip))); if (clip->data) span = span_thread_clipped_box; else @@ -1721,6 +1749,17 @@ thread_choose_span(struct sna_composite_spans_op *tmp, return span; } +inline static void +span_thread_boxes_init(struct span_thread_boxes *boxes, + const struct sna_composite_spans_op *op, + const RegionRec *clip) +{ + boxes->op = op; + boxes->clip_start = region_rects(clip); + boxes->clip_end = boxes->clip_start + region_num_rects(clip); + boxes->num_boxes = 0; +} + static void span_thread(void *arg) { @@ -1733,8 +1772,7 @@ span_thread(void *arg) if (!tor_init(&tor, &thread->extents, 2*thread->ntrap)) return; - boxes.op = thread->op; - boxes.num_boxes = 0; + span_thread_boxes_init(&boxes, thread->op, thread->clip); y1 = thread->extents.y1 - thread->draw_y; y2 = thread->extents.y2 - thread->draw_y; @@ -2183,6 +2221,52 @@ static force_inline uint8_t coverage_opacity(int coverage, uint8_t opacity) return opacity == 255 ? coverage : mul_8_8(coverage, opacity); } +struct clipped_span { + span_func_t span; + const BoxRec *clip_start, *clip_end; +}; + +static void +tor_blt_clipped(struct sna *sna, + struct sna_composite_spans_op *op, + pixman_region16_t *clip, + const BoxRec *box, + int coverage) +{ + struct clipped_span *cs = (struct clipped_span *)clip; + const BoxRec *c; + + cs->clip_start = + find_clip_box_for_y(cs->clip_start, cs->clip_end, box->y1); + + c = cs->clip_start; + while (c != cs->clip_end) { + BoxRec clipped; + + if (box->y2 <= c->y1) + break; + + clipped = *box; + if (!box_intersect(&clipped, c++)) + continue; + + cs->span(sna, op, NULL, &clipped, coverage); + } +} + +inline static span_func_t +clipped_span(struct clipped_span *cs, + span_func_t span, + const RegionRec *clip) +{ + if (clip->data) { + cs->span = span; + region_get_boxes(clip, &cs->clip_start, &cs->clip_end); + span = tor_blt_clipped; + } + return span; +} + static void _tor_blt_src(struct inplace *in, const BoxRec *box, uint8_t v) { uint8_t *ptr = in->ptr; @@ -2218,25 +2302,6 @@ tor_blt_src(struct sna *sna, } static void -tor_blt_src_clipped(struct sna *sna, - struct sna_composite_spans_op *op, - pixman_region16_t *clip, - const BoxRec *box, - int coverage) -{ - pixman_region16_t region; - int n; - - pixman_region_init_rects(®ion, box, 1); - RegionIntersect(®ion, ®ion, clip); - n = region_num_rects(®ion); - box = region_rects(®ion); - while (n--) - tor_blt_src(sna, op, NULL, box++, coverage); - pixman_region_fini(®ion); -} - -static void tor_blt_in(struct sna *sna, struct sna_composite_spans_op *op, pixman_region16_t *clip, @@ -2268,25 +2333,6 @@ tor_blt_in(struct sna *sna, } static void -tor_blt_in_clipped(struct sna *sna, - struct sna_composite_spans_op *op, - pixman_region16_t *clip, - const BoxRec *box, - int coverage) -{ - pixman_region16_t region; - int n; - - pixman_region_init_rects(®ion, box, 1); - RegionIntersect(®ion, ®ion, clip); - n = region_num_rects(®ion); - box = region_rects(®ion); - while (n--) - tor_blt_in(sna, op, NULL, box++, coverage); - pixman_region_fini(®ion); -} - -static void tor_blt_add(struct sna *sna, struct sna_composite_spans_op *op, pixman_region16_t *clip, @@ -2325,25 +2371,6 @@ tor_blt_add(struct sna *sna, } static void -tor_blt_add_clipped(struct sna *sna, - struct sna_composite_spans_op *op, - pixman_region16_t *clip, - const BoxRec *box, - int coverage) -{ - pixman_region16_t region; - int n; - - pixman_region_init_rects(®ion, box, 1); - RegionIntersect(®ion, ®ion, clip); - n = region_num_rects(®ion); - box = region_rects(®ion); - while (n--) - tor_blt_add(sna, op, NULL, box++, coverage); - pixman_region_fini(®ion); -} - -static void tor_blt_lerp32(struct sna *sna, struct sna_composite_spans_op *op, pixman_region16_t *clip, @@ -2358,6 +2385,7 @@ tor_blt_lerp32(struct sna *sna, if (coverage == 0) return; + sigtrap_assert_active(); ptr += box->y1 * stride + box->x1; h = box->y2 - box->y1; @@ -2396,25 +2424,6 @@ tor_blt_lerp32(struct sna *sna, } } -static void -tor_blt_lerp32_clipped(struct sna *sna, - struct sna_composite_spans_op *op, - pixman_region16_t *clip, - const BoxRec *box, - int coverage) -{ - pixman_region16_t region; - int n; - - pixman_region_init_rects(®ion, box, 1); - RegionIntersect(®ion, ®ion, clip); - n = region_num_rects(®ion); - box = region_rects(®ion); - while (n--) - tor_blt_lerp32(sna, op, NULL, box++, coverage); - pixman_region_fini(®ion); -} - struct pixman_inplace { pixman_image_t *image, *source, *mask; uint32_t color; @@ -2442,24 +2451,6 @@ pixmask_span_solid(struct sna *sna, pi->dx + box->x1, pi->dy + box->y1, box->x2 - box->x1, box->y2 - box->y1); } -static void -pixmask_span_solid__clipped(struct sna *sna, - struct sna_composite_spans_op *op, - pixman_region16_t *clip, - const BoxRec *box, - int coverage) -{ - pixman_region16_t region; - int n; - - pixman_region_init_rects(®ion, box, 1); - RegionIntersect(®ion, ®ion, clip); - n = region_num_rects(®ion); - box = region_rects(®ion); - while (n--) - pixmask_span_solid(sna, op, NULL, box++, coverage); - pixman_region_fini(®ion); -} static void pixmask_span(struct sna *sna, @@ -2480,24 +2471,6 @@ pixmask_span(struct sna *sna, pi->dx + box->x1, pi->dy + box->y1, box->x2 - box->x1, box->y2 - box->y1); } -static void -pixmask_span__clipped(struct sna *sna, - struct sna_composite_spans_op *op, - pixman_region16_t *clip, - const BoxRec *box, - int coverage) -{ - pixman_region16_t region; - int n; - - pixman_region_init_rects(®ion, box, 1); - RegionIntersect(®ion, ®ion, clip); - n = region_num_rects(®ion); - box = region_rects(®ion); - while (n--) - pixmask_span(sna, op, NULL, box++, coverage); - pixman_region_fini(®ion); -} struct inplace_x8r8g8b8_thread { xTrapezoid *traps; @@ -2516,6 +2489,7 @@ static void inplace_x8r8g8b8_thread(void *arg) struct inplace_x8r8g8b8_thread *thread = arg; struct tor tor; span_func_t span; + struct clipped_span clipped; RegionPtr clip; int y1, y2, n; @@ -2546,12 +2520,11 @@ static void inplace_x8r8g8b8_thread(void *arg) inplace.stride = pixmap->devKind; inplace.color = thread->color; - if (clip->data) - span = tor_blt_lerp32_clipped; - else - span = tor_blt_lerp32; + span = clipped_span(&clipped, tor_blt_lerp32, clip); - tor_render(NULL, &tor, (void*)&inplace, clip, span, false); + tor_render(NULL, &tor, + (void*)&inplace, (void *)&clipped, + span, false); } else if (thread->is_solid) { struct pixman_inplace pi; @@ -2564,10 +2537,7 @@ static void inplace_x8r8g8b8_thread(void *arg) 1, 1, pi.bits, 0); pixman_image_set_repeat(pi.source, PIXMAN_REPEAT_NORMAL); - if (clip->data) - span = pixmask_span_solid__clipped; - else - span = pixmask_span_solid; + span = clipped_span(&clipped, pixmask_span_solid, clip); tor_render(NULL, &tor, (void*)&pi, clip, span, false); @@ -2588,12 +2558,11 @@ static void inplace_x8r8g8b8_thread(void *arg) pi.bits = pixman_image_get_data(pi.mask); pi.op = thread->op; - if (clip->data) - span = pixmask_span__clipped; - else - span = pixmask_span; + span = clipped_span(&clipped, pixmask_span, clip); - tor_render(NULL, &tor, (void*)&pi, clip, span, false); + tor_render(NULL, &tor, + (void*)&pi, (void *)&clipped, + span, false); pixman_image_unref(pi.mask); pixman_image_unref(pi.source); @@ -2712,6 +2681,7 @@ trapezoid_span_inplace__x8r8g8b8(CARD8 op, if (num_threads == 1) { struct tor tor; span_func_t span; + struct clipped_span clipped; if (!tor_init(&tor, ®ion.extents, 2*ntrap)) return true; @@ -2737,17 +2707,14 @@ trapezoid_span_inplace__x8r8g8b8(CARD8 op, inplace.stride = pixmap->devKind; inplace.color = color; - if (dst->pCompositeClip->data) - span = tor_blt_lerp32_clipped; - else - span = tor_blt_lerp32; - + span = clipped_span(&clipped, tor_blt_lerp32, dst->pCompositeClip); DBG(("%s: render inplace op=%d, color=%08x\n", __FUNCTION__, op, color)); if (sigtrap_get() == 0) { - tor_render(NULL, &tor, (void*)&inplace, - dst->pCompositeClip, span, false); + tor_render(NULL, &tor, + (void*)&inplace, (void*)&clipped, + span, false); sigtrap_put(); } } else if (is_solid) { @@ -2762,15 +2729,11 @@ trapezoid_span_inplace__x8r8g8b8(CARD8 op, 1, 1, pi.bits, 0); pixman_image_set_repeat(pi.source, PIXMAN_REPEAT_NORMAL); - if (dst->pCompositeClip->data) - span = pixmask_span_solid__clipped; - else - span = pixmask_span_solid; - + span = clipped_span(&clipped, pixmask_span_solid, dst->pCompositeClip); if (sigtrap_get() == 0) { - tor_render(NULL, &tor, (void*)&pi, - dst->pCompositeClip, span, - false); + tor_render(NULL, &tor, + (void*)&pi, (void*)&clipped, + span, false); sigtrap_put(); } @@ -2791,15 +2754,11 @@ trapezoid_span_inplace__x8r8g8b8(CARD8 op, pi.bits = pixman_image_get_data(pi.mask); pi.op = op; - if (dst->pCompositeClip->data) - span = pixmask_span__clipped; - else - span = pixmask_span; - + span = clipped_span(&clipped, pixmask_span, dst->pCompositeClip); if (sigtrap_get() == 0) { - tor_render(NULL, &tor, (void*)&pi, - dst->pCompositeClip, span, - false); + tor_render(NULL, &tor, + (void*)&pi, (void *)&clipped, + span, false); sigtrap_put(); } @@ -2861,9 +2820,9 @@ trapezoid_span_inplace__x8r8g8b8(CARD8 op, struct inplace_thread { xTrapezoid *traps; - RegionPtr clip; span_func_t span; struct inplace inplace; + struct clipped_span clipped; BoxRec extents; int dx, dy; int draw_x, draw_y; @@ -2888,8 +2847,9 @@ static void inplace_thread(void *arg) tor_add_trapezoid(&tor, &thread->traps[n], thread->dx, thread->dy); } - tor_render(NULL, &tor, (void*)&thread->inplace, - thread->clip, thread->span, thread->unbounded); + tor_render(NULL, &tor, + (void*)&thread->inplace, (void*)&thread->clipped, + thread->span, thread->unbounded); tor_fini(&tor); } @@ -2903,6 +2863,7 @@ precise_trapezoid_span_inplace(struct sna *sna, bool fallback) { struct inplace inplace; + struct clipped_span clipped; span_func_t span; PixmapPtr pixmap; struct sna_pixmap *priv; @@ -3020,21 +2981,12 @@ precise_trapezoid_span_inplace(struct sna *sna, dst->pCompositeClip->data != NULL)); if (op == PictOpSrc) { - if (dst->pCompositeClip->data) - span = tor_blt_src_clipped; - else - span = tor_blt_src; + span = tor_blt_src; } else if (op == PictOpIn) { - if (dst->pCompositeClip->data) - span = tor_blt_in_clipped; - else - span = tor_blt_in; + span = tor_blt_in; } else { assert(op == PictOpAdd); - if (dst->pCompositeClip->data) - span = tor_blt_add_clipped; - else - span = tor_blt_add; + span = tor_blt_add; } DBG(("%s: move-to-cpu(dst)\n", __FUNCTION__)); @@ -3052,6 +3004,8 @@ precise_trapezoid_span_inplace(struct sna *sna, inplace.stride = pixmap->devKind; inplace.opacity = color >> 24; + span = clipped_span(&clipped, span, dst->pCompositeClip); + num_threads = 1; if (!NO_GPU_THREADS && (flags & COMPOSITE_SPANS_RECTILINEAR) == 0) @@ -3074,8 +3028,9 @@ precise_trapezoid_span_inplace(struct sna *sna, } if (sigtrap_get() == 0) { - tor_render(NULL, &tor, (void*)&inplace, - dst->pCompositeClip, span, unbounded); + tor_render(NULL, &tor, + (void*)&inplace, (void *)&clipped, + span, unbounded); sigtrap_put(); } @@ -3093,7 +3048,7 @@ precise_trapezoid_span_inplace(struct sna *sna, threads[0].ntrap = ntrap; threads[0].inplace = inplace; threads[0].extents = region.extents; - threads[0].clip = dst->pCompositeClip; + threads[0].clipped = clipped; threads[0].span = span; threads[0].unbounded = unbounded; threads[0].dx = dx; @@ -3316,8 +3271,7 @@ tristrip_thread(void *arg) if (!tor_init(&tor, &thread->extents, 2*thread->count)) return; - boxes.op = thread->op; - boxes.num_boxes = 0; + span_thread_boxes_init(&boxes, thread->op, thread->clip); cw = 0; ccw = 1; polygon_add_line(tor.polygon, diff --git a/src/sna/sna_video.c b/src/sna/sna_video.c index ed0e7b3..e2b11c3 100644 --- a/src/sna/sna_video.c +++ b/src/sna/sna_video.c @@ -591,6 +591,72 @@ use_gtt: /* copy data, must use GTT so that we keep the overlay uncached */ return true; } +void sna_video_fill_colorkey(struct sna_video *video, + const RegionRec *clip) +{ + struct sna *sna = video->sna; + PixmapPtr front = sna->front; + struct kgem_bo *bo = __sna_pixmap_get_bo(front); + uint8_t *dst, *tmp; + int w, width; + + if (video->AlwaysOnTop || RegionEqual(&video->clip, (RegionPtr)clip)) + return; + + assert(bo); + if (!wedged(sna) && + sna_blt_fill_boxes(sna, GXcopy, bo, + front->drawable.bitsPerPixel, + video->color_key, + region_rects(clip), + region_num_rects(clip))) { + RegionCopy(&video->clip, (RegionPtr)clip); + return; + } + + dst = kgem_bo_map__gtt(&sna->kgem, bo); + if (dst == NULL) + return; + + w = front->drawable.bitsPerPixel/8; + width = (clip->extents.x2 - clip->extents.x1) * w; + tmp = malloc(width); + if (tmp == NULL) + return; + + memcpy(tmp, &video->color_key, w); + while (2 * w < width) { + memcpy(tmp + w, tmp, w); + w *= 2; + } + if (w < width) + memcpy(tmp + w, tmp, width - w); + + if (sigtrap_get() == 0) { + const BoxRec *box = region_rects(clip); + int n = region_num_rects(clip); + + w = front->drawable.bitsPerPixel/8; + do { + int y = box->y1; + uint8_t *row = dst + y*bo->pitch + w*box->x1; + + width = (box->x2 - box->x1) * w; + while (y < box->y2) { + memcpy(row, tmp, width); + row += bo->pitch; + y++; + } + box++; + } while (--n); + sigtrap_put(); + + RegionCopy(&video->clip, (RegionPtr)clip); + } + + free(tmp); +} + XvAdaptorPtr sna_xv_adaptor_alloc(struct sna *sna) { XvAdaptorPtr new_adaptors; diff --git a/src/sna/sna_video.h b/src/sna/sna_video.h index f21605f..dfb8c0c 100644 --- a/src/sna/sna_video.h +++ b/src/sna/sna_video.h @@ -193,6 +193,9 @@ bool sna_video_copy_data(struct sna_video *video, struct sna_video_frame *frame, const uint8_t *buf); +void +sna_video_fill_colorkey(struct sna_video *video, + const RegionRec *clip); void sna_video_buffer_fini(struct sna_video *video); diff --git a/src/sna/sna_video_overlay.c b/src/sna/sna_video_overlay.c index ac81f1a..d782113 100644 --- a/src/sna/sna_video_overlay.c +++ b/src/sna/sna_video_overlay.c @@ -130,7 +130,7 @@ static int sna_video_overlay_stop(ddStopVideo_ARGS) DBG(("%s()\n", __FUNCTION__)); - REGION_EMPTY(scrn->pScreen, &video->clip); + REGION_EMPTY(to_screen_from_sna(sna), &video->clip); request.flags = 0; (void)drmIoctl(sna->kgem.fd, @@ -551,15 +551,7 @@ sna_video_overlay_put_image(ddPutImage_ARGS) ret = Success; if (sna_video_overlay_show (sna, video, &frame, crtc, &dstBox, src_w, src_h, drw_w, drw_h)) { - //xf86XVFillKeyHelperDrawable(draw, video->color_key, &clip); - if (!video->AlwaysOnTop && !RegionEqual(&video->clip, &clip) && - sna_blt_fill_boxes(sna, GXcopy, - __sna_pixmap_get_bo(sna->front), - sna->front->drawable.bitsPerPixel, - video->color_key, - region_rects(&clip), - region_num_rects(&clip))) - RegionCopy(&video->clip, &clip); + sna_video_fill_colorkey(video, &clip); sna_window_set_port((WindowPtr)draw, port); } else { DBG(("%s: failed to show video frame\n", __FUNCTION__)); diff --git a/src/sna/sna_video_sprite.c b/src/sna/sna_video_sprite.c index 92230f9..7c8a73f 100644 --- a/src/sna/sna_video_sprite.c +++ b/src/sna/sna_video_sprite.c @@ -81,14 +81,11 @@ static int sna_video_sprite_stop(ddStopVideo_ARGS) xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(video->sna->scrn); int i; - for (i = 0; i < config->num_crtc; i++) { + for (i = 0; i < video->sna->mode.num_real_crtc; i++) { xf86CrtcPtr crtc = config->crtc[i]; int pipe; - if (sna_crtc_id(crtc) == 0) - break; - - pipe = sna_crtc_to_pipe(crtc); + pipe = sna_crtc_pipe(crtc); if (video->bo[pipe] == NULL) continue; @@ -221,7 +218,7 @@ sna_video_sprite_show(struct sna *sna, BoxPtr dstBox) { struct local_mode_set_plane s; - int pipe = sna_crtc_to_pipe(crtc); + int pipe = sna_crtc_pipe(crtc); /* XXX handle video spanning multiple CRTC */ @@ -402,7 +399,7 @@ static int sna_video_sprite_put_image(ddPutImage_ARGS) goto err; } - for (i = 0; i < config->num_crtc; i++) { + for (i = 0; i < video->sna->mode.num_real_crtc; i++) { xf86CrtcPtr crtc = config->crtc[i]; struct sna_video_frame frame; int pipe; @@ -411,10 +408,7 @@ static int sna_video_sprite_put_image(ddPutImage_ARGS) RegionRec reg; Rotation rotation; - if (sna_crtc_id(crtc) == 0) - break; - - pipe = sna_crtc_to_pipe(crtc); + pipe = sna_crtc_pipe(crtc); sna_video_frame_init(video, format->id, width, height, &frame); @@ -527,14 +521,7 @@ off: goto err; } - if (!video->AlwaysOnTop && !RegionEqual(&video->clip, &clip) && - sna_blt_fill_boxes(sna, GXcopy, - __sna_pixmap_get_bo(sna->front), - sna->front->drawable.bitsPerPixel, - video->color_key, - region_rects(&clip), - region_num_rects(&clip))) - RegionCopy(&video->clip, &clip); + sna_video_fill_colorkey(video, &clip); sna_window_set_port((WindowPtr)draw, port); return Success; @@ -618,7 +605,7 @@ static bool sna_video_has_sprites(struct sna *sna) for (i = 0; i < sna->mode.num_real_crtc; i++) { if (!sna_crtc_to_sprite(config->crtc[i])) { - DBG(("%s: no sprite found on pipe %d\n", __FUNCTION__, sna_crtc_to_pipe(config->crtc[i]))); + DBG(("%s: no sprite found on pipe %d\n", __FUNCTION__, sna_crtc_pipe(config->crtc[i]))); return false; } } diff --git a/src/sna/sna_video_textured.c b/src/sna/sna_video_textured.c index 9501193..3f14a59 100644 --- a/src/sna/sna_video_textured.c +++ b/src/sna/sna_video_textured.c @@ -149,9 +149,13 @@ sna_video_textured_put_image(ddPutImage_ARGS) BoxRec dstBox; RegionRec clip; xf86CrtcPtr crtc; + int16_t dx, dy; bool flush = false; bool ret; + if (wedged(sna)) + return BadAlloc; + clip.extents.x1 = draw->x + drw_x; clip.extents.y1 = draw->y + drw_y; clip.extents.x2 = clip.extents.x1 + drw_w; @@ -181,6 +185,9 @@ sna_video_textured_put_image(ddPutImage_ARGS) &clip)) return Success; + if (get_drawable_deltas(draw, pixmap, &dx, &dy)) + RegionTranslate(&clip, dx, dy); + flags = MOVE_WRITE | __MOVE_FORCE; if (clip.data) flags |= MOVE_READ; @@ -234,7 +241,7 @@ sna_video_textured_put_image(ddPutImage_ARGS) DBG(("%s: failed to render video\n", __FUNCTION__)); ret = BadAlloc; } else - DamageDamageRegion(draw, &clip); + DamageDamageRegion(&pixmap->drawable, &clip); kgem_bo_destroy(&sna->kgem, frame.bo); @@ -316,7 +323,7 @@ void sna_video_textured_setup(struct sna *sna, ScreenPtr screen) if (!sna->render.video) { xf86DrvMsg(sna->scrn->scrnIndex, X_INFO, - "Textured video not supported on this hardware\n"); + "Textured video not supported on this hardware or backend\n"); return; } diff --git a/src/sna/xassert.h b/src/sna/xassert.h index 1bcfd08..28796b8 100644 --- a/src/sna/xassert.h +++ b/src/sna/xassert.h @@ -43,6 +43,23 @@ xorg_backtrace(); \ FatalError("%s:%d assertion '%s' failed\n", __func__, __LINE__, #E); \ } while (0) + +#define warn_unless(E) do if (unlikely(!(E))) { \ + static int __warn_once__; \ + if (!__warn_once__) { \ + xorg_backtrace(); \ + ErrorF("%s:%d assertion '%s' failed\n", __func__, __LINE__, #E); \ + __warn_once__ = 1; \ + } \ +} while (0) + +#define dbg(EXPR) EXPR + +#else + +#define warn_unless(E) +#define dbg(EXPR) + #endif #endif /* __XASSERT_H__ */ diff --git a/src/uxa/i965_video.c b/src/uxa/i965_video.c index 68e6fd3..438ab90 100644 --- a/src/uxa/i965_video.c +++ b/src/uxa/i965_video.c @@ -37,7 +37,6 @@ #include "fourcc.h" #include "intel.h" -#include "intel_xvmc.h" #include "intel_uxa.h" #include "i830_reg.h" #include "i965_reg.h" diff --git a/src/uxa/intel.h b/src/uxa/intel.h index 1b7e533..37b23e9 100644 --- a/src/uxa/intel.h +++ b/src/uxa/intel.h @@ -121,7 +121,6 @@ typedef struct intel_screen_private { void *modes; drm_intel_bo *front_buffer, *back_buffer; - unsigned int back_name; long front_pitch, front_tiling; dri_bufmgr *bufmgr; @@ -285,8 +284,6 @@ typedef struct intel_screen_private { Bool has_kernel_flush; Bool needs_flush; - struct _DRI2FrameEvent *pending_flip[MAX_PIPES]; - /* Broken-out options. */ OptionInfoPtr Options; @@ -368,6 +365,7 @@ typedef void (*intel_drm_abort_proc)(ScrnInfoPtr scrn, extern uint32_t intel_drm_queue_alloc(ScrnInfoPtr scrn, xf86CrtcPtr crtc, void *data, intel_drm_handler_proc handler, intel_drm_abort_proc abort); extern void intel_drm_abort(ScrnInfoPtr scrn, Bool (*match)(void *data, void *match_data), void *match_data); +extern void intel_drm_abort_seq(ScrnInfoPtr scrn, uint32_t seq); extern int intel_get_pipe_from_crtc_id(drm_intel_bufmgr *bufmgr, xf86CrtcPtr crtc); extern int intel_crtc_id(xf86CrtcPtr crtc); @@ -408,7 +406,6 @@ typedef struct _DRI2FrameEvent { ClientPtr client; enum DRI2FrameEventType type; int frame; - int pipe; struct list drawable_resource, client_resource; @@ -418,7 +415,12 @@ typedef struct _DRI2FrameEvent { DRI2BufferPtr front; DRI2BufferPtr back; - struct _DRI2FrameEvent *chain; + /* current scanout for triple buffer */ + int old_width; + int old_height; + int old_pitch; + int old_tiling; + dri_bo *old_buffer; } DRI2FrameEventRec, *DRI2FrameEventPtr; extern Bool intel_do_pageflip(intel_screen_private *intel, @@ -456,10 +458,6 @@ extern xf86CrtcPtr intel_covering_crtc(ScrnInfoPtr scrn, BoxPtr box, Bool I830DRI2ScreenInit(ScreenPtr pScreen); void I830DRI2CloseScreen(ScreenPtr pScreen); -void I830DRI2FrameEventHandler(unsigned int frame, unsigned int tv_sec, - unsigned int tv_usec, DRI2FrameEventPtr flip_info); -void I830DRI2FlipEventHandler(unsigned int frame, unsigned int tv_sec, - unsigned int tv_usec, DRI2FrameEventPtr flip_info); /* intel_dri3.c */ Bool intel_dri3_screen_init(ScreenPtr screen); diff --git a/src/uxa/intel_display.c b/src/uxa/intel_display.c index 7b4d4e0..8bf0184 100644 --- a/src/uxa/intel_display.c +++ b/src/uxa/intel_display.c @@ -89,11 +89,11 @@ struct intel_mode { struct list outputs; struct list crtcs; - void *pageflip_data; - intel_pageflip_handler_proc pageflip_handler; - intel_pageflip_abort_proc pageflip_abort; - - Bool delete_dp_12_displays; + struct { + intel_pageflip_handler_proc handler; + intel_pageflip_abort_proc abort; + void *data; + } pageflip; }; struct intel_pageflip { @@ -114,7 +114,6 @@ struct intel_crtc { struct list link; PixmapPtr scanout_pixmap; uint32_t scanout_fb_id; - int32_t vblank_offset; uint32_t msc_prev; uint64_t msc_high; }; @@ -193,7 +192,7 @@ intel_output_backlight_init(xf86OutputPtr output) str = xf86GetOptValString(intel->Options, OPTION_BACKLIGHT); if (str != NULL) { - if (backlight_exists(str) != BL_NONE) { + if (backlight_exists(str)) { intel_output->backlight_active_level = backlight_open(&intel_output->backlight, strdup(str)); @@ -867,6 +866,48 @@ intel_output_attach_edid(xf86OutputPtr output) xf86OutputSetEDID(output, mon); } +static void +intel_output_attach_tile(xf86OutputPtr output) +{ +#if XF86_OUTPUT_VERSION >= 3 + struct intel_output *intel_output = output->driver_private; + drmModeConnectorPtr koutput = intel_output->mode_output; + struct intel_mode *mode = intel_output->mode; + drmModePropertyBlobPtr blob = NULL; + struct xf86CrtcTileInfo tile_info, *set = NULL; + int i; + + for (i = 0; koutput && i < koutput->count_props; i++) { + drmModePropertyPtr props; + + props = drmModeGetProperty(mode->fd, koutput->props[i]); + if (!props) + continue; + + if (!(props->flags & DRM_MODE_PROP_BLOB)) { + drmModeFreeProperty(props); + continue; + } + + if (!strcmp(props->name, "TILE")) { + blob = drmModeGetPropertyBlob(mode->fd, + koutput->prop_values[i]); + } + drmModeFreeProperty(props); + } + + if (blob) { + if (xf86OutputParseKMSTile(blob->data, + blob->length, + &tile_info)) + set = &tile_info; + drmModeFreePropertyBlob(blob); + } + + xf86OutputSetTile(output, set); +#endif +} + static DisplayModePtr intel_output_panel_edid(xf86OutputPtr output, DisplayModePtr modes) { @@ -922,6 +963,7 @@ intel_output_get_modes(xf86OutputPtr output) int i; intel_output_attach_edid(output); + intel_output_attach_tile(output); if (!koutput) return Modes; @@ -1492,6 +1534,7 @@ intel_output_init(ScrnInfoPtr scrn, struct intel_mode *mode, drmModeResPtr mode_ intel_output = output->driver_private; intel_output->output_id = mode_res->connectors[num]; intel_output->mode_output = koutput; + RROutputChanged(output->randr_output, TRUE); return; } } @@ -1650,9 +1693,6 @@ intel_pageflip_abort(ScrnInfoPtr scrn, xf86CrtcPtr crtc, void *data); static void intel_pageflip_complete(struct intel_mode *mode); -static void -intel_drm_abort_seq (ScrnInfoPtr scrn, uint32_t seq); - Bool intel_do_pageflip(intel_screen_private *intel, dri_bo *new_front, @@ -1671,23 +1711,30 @@ intel_do_pageflip(intel_screen_private *intel, uint32_t new_fb_id; uint32_t flags; uint32_t seq; + int err = 0; int i; /* + * We only have a single length queue in the kernel, so any + * attempts to schedule a second flip before processing the first + * is a bug. Punt it back to the caller. + */ + if (mode->flip_count) + return FALSE; + + /* * Create a new handle for the back buffer */ if (drmModeAddFB(mode->fd, scrn->virtualX, scrn->virtualY, scrn->depth, scrn->bitsPerPixel, pitch, - new_front->handle, &new_fb_id)) + new_front->handle, &new_fb_id)) { + err = errno; goto error_out; + } drm_intel_bo_disable_reuse(new_front); intel_flush(intel); - mode->pageflip_data = pageflip_data; - mode->pageflip_handler = pageflip_handler; - mode->pageflip_abort = pageflip_abort; - /* * Queue flips on all enabled CRTCs * Note that if/when we get per-CRTC buffers, we'll have to update this. @@ -1699,6 +1746,7 @@ intel_do_pageflip(intel_screen_private *intel, */ mode->fe_msc = 0; mode->fe_usec = 0; + memset(&mode->pageflip, 0, sizeof(mode->pageflip)); flags = DRM_MODE_PAGE_FLIP_EVENT; if (async) @@ -1711,8 +1759,7 @@ intel_do_pageflip(intel_screen_private *intel, flip = calloc(1, sizeof(struct intel_pageflip)); if (flip == NULL) { - xf86DrvMsg(scrn->scrnIndex, X_WARNING, - "flip queue: carrier alloc failed.\n"); + err = errno; goto error_undo; } @@ -1724,33 +1771,30 @@ intel_do_pageflip(intel_screen_private *intel, seq = intel_drm_queue_alloc(scrn, config->crtc[i], flip, intel_pageflip_handler, intel_pageflip_abort); if (!seq) { + err = errno; free(flip); goto error_undo; } -again: + mode->flip_count++; + if (drmModePageFlip(mode->fd, crtc_id(crtc), new_fb_id, flags, (void *)(uintptr_t)seq)) { - if (intel_mode_read_drm_events(intel)) { - xf86DrvMsg(scrn->scrnIndex, X_WARNING, - "flip queue retry\n"); - goto again; - } - xf86DrvMsg(scrn->scrnIndex, X_WARNING, - "flip queue failed: %s\n", strerror(errno)); - if (seq) - intel_drm_abort_seq(scrn, seq); - free(flip); + err = errno; + intel_drm_abort_seq(scrn, seq); goto error_undo; } - mode->flip_count++; } mode->old_fb_id = mode->fb_id; mode->fb_id = new_fb_id; + mode->pageflip.data = pageflip_data; + mode->pageflip.handler = pageflip_handler; + mode->pageflip.abort = pageflip_abort; + if (!mode->flip_count) intel_pageflip_complete(mode); @@ -1765,7 +1809,7 @@ error_undo: error_out: xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Page flip failed: %s\n", - strerror(errno)); + strerror(err)); mode->flip_count = 0; return FALSE; @@ -1839,7 +1883,7 @@ intel_drm_abort(ScrnInfoPtr scrn, Bool (*match)(void *data, void *match_data), v /* * Abort by drm queue sequence number */ -static void +void intel_drm_abort_seq(ScrnInfoPtr scrn, uint32_t seq) { struct intel_drm_queue *q; @@ -1911,7 +1955,6 @@ intel_sequence_to_crtc_msc(xf86CrtcPtr crtc, uint32_t sequence) { struct intel_crtc *intel_crtc = crtc->driver_private; - sequence += intel_crtc->vblank_offset; if ((int32_t) (sequence - intel_crtc->msc_prev) < -0x40000000) intel_crtc->msc_high += 0x100000000L; intel_crtc->msc_prev = sequence; @@ -1935,37 +1978,10 @@ intel_get_crtc_msc_ust(ScrnInfoPtr scrn, xf86CrtcPtr crtc, uint64_t *msc, uint64 return 0; } -/* - * Convert a 64-bit adjusted MSC value into a 32-bit kernel sequence number, - * removing the high 32 bits and subtracting out the vblank_offset term. - * - * This also updates the vblank_offset when it notices that the value should - * change. - */ - -#define MAX_VBLANK_OFFSET 1000 - uint32_t intel_crtc_msc_to_sequence(ScrnInfoPtr scrn, xf86CrtcPtr crtc, uint64_t expect) { - struct intel_crtc *intel_crtc = crtc->driver_private; - uint64_t msc, ust; - - if (intel_get_crtc_msc_ust(scrn, crtc, &msc, &ust) == 0) { - int64_t diff = expect - msc; - - /* We're way off here, assume that the kernel has lost its mind - * and smack the vblank back to something sensible - */ - if (diff < -MAX_VBLANK_OFFSET || diff > MAX_VBLANK_OFFSET) { - intel_crtc->vblank_offset += (int32_t) diff; - if (intel_crtc->vblank_offset > -MAX_VBLANK_OFFSET && - intel_crtc->vblank_offset < MAX_VBLANK_OFFSET) - intel_crtc->vblank_offset = 0; - } - } - - return (uint32_t) (expect - intel_crtc->vblank_offset); + return (uint32_t)expect; } /* @@ -1998,14 +2014,13 @@ intel_drm_handler(int fd, uint32_t frame, uint32_t sec, uint32_t usec, void *use static void intel_pageflip_complete(struct intel_mode *mode) { - /* Release framebuffer */ - drmModeRmFB(mode->fd, mode->old_fb_id); - - if (!mode->pageflip_handler) + if (!mode->pageflip.handler) return; - mode->pageflip_handler(mode->fe_msc, mode->fe_usec, - mode->pageflip_data); + /* Release framebuffer */ + drmModeRmFB(mode->fd, mode->old_fb_id); + mode->pageflip.handler(mode->fe_msc, mode->fe_usec, + mode->pageflip.data); } /* @@ -2045,6 +2060,7 @@ intel_pageflip_handler(ScrnInfoPtr scrn, xf86CrtcPtr crtc, if (!mode) return; + intel_pageflip_complete(mode); } @@ -2060,13 +2076,12 @@ intel_pageflip_abort(ScrnInfoPtr scrn, xf86CrtcPtr crtc, void *data) if (!mode) return; - /* Release framebuffer */ - drmModeRmFB(mode->fd, mode->old_fb_id); - - if (!mode->pageflip_abort) + if (!mode->pageflip.abort) return; - mode->pageflip_abort(mode->pageflip_data); + /* Release framebuffer */ + drmModeRmFB(mode->fd, mode->old_fb_id); + mode->pageflip.abort(mode->pageflip.data); } /* @@ -2231,10 +2246,6 @@ Bool intel_mode_pre_init(ScrnInfoPtr scrn, int fd, int cpp) intel->use_pageflipping = TRUE; } - if (xf86ReturnOptValBool(intel->Options, OPTION_DELETE_DP12, FALSE)) { - mode->delete_dp_12_displays = TRUE; - } - intel->modes = mode; drmModeFreeResources(mode_res); return TRUE; @@ -2498,12 +2509,11 @@ intel_mode_hotplug(struct intel_screen_private *intel) int i, j; Bool found; Bool changed = FALSE; - struct intel_mode *mode = intel->modes; + mode_res = drmModeGetResources(intel->drmSubFD); if (!mode_res) goto out; -restart_destroy: for (i = 0; i < config->num_output; i++) { xf86OutputPtr output = config->output[i]; struct intel_output *intel_output; @@ -2522,13 +2532,9 @@ restart_destroy: drmModeFreeConnector(intel_output->mode_output); intel_output->mode_output = NULL; intel_output->output_id = -1; + RROutputChanged(output->randr_output, TRUE); changed = TRUE; - if (mode->delete_dp_12_displays) { - RROutputDestroy(output->randr_output); - xf86OutputDestroy(output); - goto restart_destroy; - } } /* find new output ids we don't have outputs for */ @@ -2552,10 +2558,8 @@ restart_destroy: intel_output_init(scrn, intel->modes, mode_res, i, 1); } - if (changed) { - RRSetChanged(xf86ScrnToScreen(scrn)); + if (changed) RRTellChanged(xf86ScrnToScreen(scrn)); - } drmModeFreeResources(mode_res); out: diff --git a/src/uxa/intel_dri.c b/src/uxa/intel_dri.c index f61c621..524826d 100644 --- a/src/uxa/intel_dri.c +++ b/src/uxa/intel_dri.c @@ -81,6 +81,47 @@ static DevPrivateKeyRec i830_client_key; static int i830_client_key; #endif +static void I830DRI2FlipEventHandler(unsigned int frame, + unsigned int tv_sec, + unsigned int tv_usec, + DRI2FrameEventPtr flip_info); + +static void I830DRI2FrameEventHandler(unsigned int frame, + unsigned int tv_sec, + unsigned int tv_usec, + DRI2FrameEventPtr swap_info); + +static void +i830_dri2_del_frame_event(DRI2FrameEventPtr info); + +static uint32_t pipe_select(int pipe) +{ + if (pipe > 1) + return pipe << DRM_VBLANK_HIGH_CRTC_SHIFT; + else if (pipe > 0) + return DRM_VBLANK_SECONDARY; + else + return 0; +} + +static void +intel_dri2_vblank_handler(ScrnInfoPtr scrn, + xf86CrtcPtr crtc, + uint64_t msc, + uint64_t usec, + void *data) +{ + I830DRI2FrameEventHandler((uint32_t) msc, usec / 1000000, usec % 1000000, data); +} + +static void +intel_dri2_vblank_abort(ScrnInfoPtr scrn, + xf86CrtcPtr crtc, + void *data) +{ + i830_dri2_del_frame_event(data); +} + static uint32_t pixmap_flink(PixmapPtr pixmap) { struct intel_uxa_pixmap *priv = intel_uxa_get_pixmap_private(pixmap); @@ -135,9 +176,6 @@ I830DRI2CreateBuffers(DrawablePtr drawable, unsigned int *attachments, pixmap = NULL; if (attachments[i] == DRI2BufferFrontLeft) { pixmap = get_front_buffer(drawable); - - if (pixmap == NULL) - drawable = &(get_drawable_pixmap(drawable)->drawable); } else if (attachments[i] == DRI2BufferStencil && pDepthPixmap) { pixmap = pDepthPixmap; pixmap->refcnt++; @@ -246,11 +284,8 @@ I830DRI2CreateBuffer(DrawablePtr drawable, unsigned int attachment, } pixmap = NULL; - if (attachment == DRI2BufferFrontLeft) { + if (attachment == DRI2BufferFrontLeft) pixmap = get_front_buffer(drawable); - if (pixmap == NULL) - drawable = &(get_drawable_pixmap(drawable)->drawable); - } if (pixmap == NULL) { unsigned int hint = INTEL_CREATE_PIXMAP_DRI2; @@ -673,6 +708,20 @@ i830_dri2_del_frame_event(DRI2FrameEventPtr info) if (info->back) I830DRI2DestroyBuffer(NULL, info->back); + if (info->old_buffer) { + /* Check that the old buffer still matches the front buffer + * in case a mode change occurred before we woke up. + */ + if (info->intel->back_buffer == NULL && + info->old_width == info->intel->scrn->virtualX && + info->old_height == info->intel->scrn->virtualY && + info->old_pitch == info->intel->front_pitch && + info->old_tiling == info->intel->front_tiling) + info->intel->back_buffer = info->old_buffer; + else + dri_bo_unreference(info->old_buffer); + } + free(info); } @@ -708,16 +757,14 @@ static void I830DRI2ExchangeBuffers(struct intel_screen_private *intel, DRI2BufferPtr front, DRI2BufferPtr back) { I830DRI2BufferPrivatePtr front_priv, back_priv; - int tmp; struct intel_uxa_pixmap *new_front; front_priv = front->driverPrivate; back_priv = back->driverPrivate; /* Swap BO names so DRI works */ - tmp = front->name; front->name = back->name; - back->name = tmp; + back->name = pixmap_flink(front_priv->pixmap); /* Swap pixmap bos */ new_front = intel_exchange_pixmap_buffers(intel, @@ -753,87 +800,30 @@ I830DRI2FlipAbort(void *pageflip_data) i830_dri2_del_frame_event(info); } -/* - * Our internal swap routine takes care of actually exchanging, blitting, or - * flipping buffers as necessary. - */ static Bool -I830DRI2ScheduleFlip(struct intel_screen_private *intel, - DrawablePtr draw, - DRI2FrameEventPtr info) +allocate_back_buffer(struct intel_screen_private *intel) { - I830DRI2BufferPrivatePtr priv = info->back->driverPrivate; - drm_intel_bo *new_back, *old_back; - int tmp_name; - - if (!intel->use_triple_buffer) { - info->type = DRI2_SWAP; - if (!intel_do_pageflip(intel, - get_pixmap_bo(priv), - info->pipe, FALSE, info, - I830DRI2FlipComplete, - I830DRI2FlipAbort)) - return FALSE; - - I830DRI2ExchangeBuffers(intel, info->front, info->back); - return TRUE; - } + drm_intel_bo *bo; + int pitch; + uint32_t tiling; - if (intel->pending_flip[info->pipe]) { - assert(intel->pending_flip[info->pipe]->chain == NULL); - intel->pending_flip[info->pipe]->chain = info; + if (intel->back_buffer) return TRUE; - } - if (intel->back_buffer == NULL) { - new_back = drm_intel_bo_alloc(intel->bufmgr, "front buffer", - intel->front_buffer->size, 0); - if (new_back == NULL) - return FALSE; - - if (intel->front_tiling != I915_TILING_NONE) { - uint32_t tiling = intel->front_tiling; - drm_intel_bo_set_tiling(new_back, &tiling, intel->front_pitch); - if (tiling != intel->front_tiling) { - drm_intel_bo_unreference(new_back); - return FALSE; - } - } - - drm_intel_bo_disable_reuse(new_back); - dri_bo_flink(new_back, &intel->back_name); - } else { - new_back = intel->back_buffer; - intel->back_buffer = NULL; - } + bo = intel_allocate_framebuffer(intel->scrn, + intel->scrn->virtualX, + intel->scrn->virtualY, + intel->cpp, + &pitch, &tiling); + if (bo == NULL) + return FALSE; - old_back = get_pixmap_bo(priv); - if (!intel_do_pageflip(intel, old_back, info->pipe, FALSE, info, I830DRI2FlipComplete, I830DRI2FlipAbort)) { - intel->back_buffer = new_back; + if (pitch != intel->front_pitch || tiling != intel->front_tiling) { + drm_intel_bo_unreference(bo); return FALSE; } - info->type = DRI2_SWAP_CHAIN; - intel->pending_flip[info->pipe] = info; - - priv = info->front->driverPrivate; - - /* Exchange the current front-buffer with the fresh bo */ - - intel->back_buffer = intel->front_buffer; - drm_intel_bo_reference(intel->back_buffer); - intel_set_pixmap_bo(priv->pixmap, new_back); - drm_intel_bo_unreference(new_back); - - tmp_name = info->front->name; - info->front->name = intel->back_name; - intel->back_name = tmp_name; - /* Then flip DRI2 pointers and update the screen pixmap */ - I830DRI2ExchangeBuffers(intel, info->front, info->back); - DRI2SwapComplete(info->client, draw, 0, 0, 0, - DRI2_EXCHANGE_COMPLETE, - info->event_complete, - info->event_data); + intel->back_buffer = bo; return TRUE; } @@ -889,8 +879,88 @@ can_exchange(DrawablePtr drawable, DRI2BufferPtr front, DRI2BufferPtr back) return TRUE; } -void I830DRI2FrameEventHandler(unsigned int frame, unsigned int tv_sec, - unsigned int tv_usec, DRI2FrameEventPtr swap_info) +static Bool +queue_flip(struct intel_screen_private *intel, + DrawablePtr draw, + DRI2FrameEventPtr info) +{ + xf86CrtcPtr crtc = I830DRI2DrawableCrtc(draw); + I830DRI2BufferPrivatePtr priv = info->back->driverPrivate; + drm_intel_bo *old_back = get_pixmap_bo(priv); + + if (crtc == NULL) + return FALSE; + + if (!can_exchange(draw, info->front, info->back)) + return FALSE; + + if (!intel_do_pageflip(intel, old_back, + intel_crtc_to_pipe(crtc), + FALSE, info, + I830DRI2FlipComplete, I830DRI2FlipAbort)) + return FALSE; + +#if DRI2INFOREC_VERSION >= 6 + if (intel->use_triple_buffer && allocate_back_buffer(intel)) { + info->old_width = intel->scrn->virtualX; + info->old_height = intel->scrn->virtualY; + info->old_pitch = intel->front_pitch; + info->old_tiling = intel->front_tiling; + info->old_buffer = intel->front_buffer; + dri_bo_reference(info->old_buffer); + + priv = info->front->driverPrivate; + intel_set_pixmap_bo(priv->pixmap, intel->back_buffer); + + dri_bo_unreference(intel->back_buffer); + intel->back_buffer = NULL; + + DRI2SwapLimit(draw, 2); + } else + DRI2SwapLimit(draw, 1); +#endif + + /* Then flip DRI2 pointers and update the screen pixmap */ + I830DRI2ExchangeBuffers(intel, info->front, info->back); + return TRUE; +} + +static Bool +queue_swap(struct intel_screen_private *intel, + DrawablePtr draw, + DRI2FrameEventPtr info) +{ + xf86CrtcPtr crtc = I830DRI2DrawableCrtc(draw); + drmVBlank vbl; + + if (crtc == NULL) + return FALSE; + + vbl.request.type = + DRM_VBLANK_RELATIVE | + DRM_VBLANK_EVENT | + pipe_select(intel_crtc_to_pipe(crtc)); + vbl.request.sequence = 1; + vbl.request.signal = + intel_drm_queue_alloc(intel->scrn, crtc, info, + intel_dri2_vblank_handler, + intel_dri2_vblank_abort); + if (vbl.request.signal == 0) + return FALSE; + + info->type = DRI2_SWAP; + if (drmWaitVBlank(intel->drmSubFD, &vbl)) { + intel_drm_abort_seq(intel->scrn, vbl.request.signal); + return FALSE; + } + + return TRUE; +} + +static void I830DRI2FrameEventHandler(unsigned int frame, + unsigned int tv_sec, + unsigned int tv_usec, + DRI2FrameEventPtr swap_info) { intel_screen_private *intel = swap_info->intel; DrawablePtr drawable; @@ -906,24 +976,22 @@ void I830DRI2FrameEventHandler(unsigned int frame, unsigned int tv_sec, return; } - switch (swap_info->type) { case DRI2_FLIP: /* If we can still flip... */ - if (can_exchange(drawable, swap_info->front, swap_info->back) && - I830DRI2ScheduleFlip(intel, drawable, swap_info)) - return; - - /* else fall through to exchange/blit */ - case DRI2_SWAP: { - I830DRI2FallbackBlitSwap(drawable, - swap_info->front, swap_info->back); - DRI2SwapComplete(swap_info->client, drawable, frame, tv_sec, tv_usec, - DRI2_BLIT_COMPLETE, - swap_info->client ? swap_info->event_complete : NULL, - swap_info->event_data); - break; - } + if (!queue_flip(intel, drawable, swap_info) && + !queue_swap(intel, drawable, swap_info)) { + case DRI2_SWAP: + I830DRI2FallbackBlitSwap(drawable, + swap_info->front, swap_info->back); + DRI2SwapComplete(swap_info->client, drawable, frame, tv_sec, tv_usec, + DRI2_BLIT_COMPLETE, + swap_info->client ? swap_info->event_complete : NULL, + swap_info->event_data); + break; + } + return; + case DRI2_WAITMSC: if (swap_info->client) DRI2WaitMSCComplete(swap_info->client, drawable, @@ -939,12 +1007,13 @@ void I830DRI2FrameEventHandler(unsigned int frame, unsigned int tv_sec, i830_dri2_del_frame_event(swap_info); } -void I830DRI2FlipEventHandler(unsigned int frame, unsigned int tv_sec, - unsigned int tv_usec, DRI2FrameEventPtr flip_info) +static void I830DRI2FlipEventHandler(unsigned int frame, + unsigned int tv_sec, + unsigned int tv_usec, + DRI2FrameEventPtr flip_info) { struct intel_screen_private *intel = flip_info->intel; DrawablePtr drawable; - DRI2FrameEventPtr chain; drawable = NULL; if (flip_info->drawable_id) @@ -954,6 +1023,7 @@ void I830DRI2FlipEventHandler(unsigned int frame, unsigned int tv_sec, /* We assume our flips arrive in order, so we don't check the frame */ switch (flip_info->type) { + case DRI2_FLIP: case DRI2_SWAP: if (!drawable) break; @@ -984,35 +1054,6 @@ void I830DRI2FlipEventHandler(unsigned int frame, unsigned int tv_sec, flip_info->event_data); break; - case DRI2_SWAP_CHAIN: - assert(intel->pending_flip[flip_info->pipe] == flip_info); - intel->pending_flip[flip_info->pipe] = NULL; - - chain = flip_info->chain; - if (chain) { - DrawablePtr chain_drawable = NULL; - if (chain->drawable_id) - dixLookupDrawable(&chain_drawable, - chain->drawable_id, - serverClient, - M_ANY, DixWriteAccess); - if (chain_drawable == NULL) { - i830_dri2_del_frame_event(chain); - } else if (!can_exchange(chain_drawable, chain->front, chain->back) || - !I830DRI2ScheduleFlip(intel, chain_drawable, chain)) { - I830DRI2FallbackBlitSwap(chain_drawable, - chain->front, - chain->back); - - DRI2SwapComplete(chain->client, chain_drawable, frame, tv_sec, tv_usec, - DRI2_BLIT_COMPLETE, - chain->client ? chain->event_complete : NULL, - chain->event_data); - i830_dri2_del_frame_event(chain); - } - } - break; - default: xf86DrvMsg(intel->scrn->scrnIndex, X_WARNING, "%s: unknown vblank event received\n", __func__); @@ -1023,38 +1064,6 @@ void I830DRI2FlipEventHandler(unsigned int frame, unsigned int tv_sec, i830_dri2_del_frame_event(flip_info); } -static uint32_t pipe_select(int pipe) -{ - if (pipe > 1) - return pipe << DRM_VBLANK_HIGH_CRTC_SHIFT; - else if (pipe > 0) - return DRM_VBLANK_SECONDARY; - else - return 0; -} - -static void -intel_dri2_vblank_handler(ScrnInfoPtr scrn, - xf86CrtcPtr crtc, - uint64_t msc, - uint64_t usec, - void *data) -{ - DRI2FrameEventPtr swap_info = data; - - I830DRI2FrameEventHandler((uint32_t) msc, usec / 1000000, usec % 1000000, swap_info); -} - -static void -intel_dri2_vblank_abort(ScrnInfoPtr scrn, - xf86CrtcPtr crtc, - void *data) -{ - DRI2FrameEventPtr swap_info = data; - - i830_dri2_del_frame_event(swap_info); -} - /* * ScheduleSwap is responsible for requesting a DRM vblank event for the * appropriate frame. @@ -1089,7 +1098,6 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, int pipe = crtc ? intel_crtc_to_pipe(crtc) : -1; int flip = 0; DRI2FrameEventPtr swap_info = NULL; - enum DRI2FrameEventType swap_type = DRI2_SWAP; uint64_t current_msc, current_ust; uint64_t request_msc; uint32_t seq; @@ -1109,7 +1117,7 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, swap_info->event_data = data; swap_info->front = front; swap_info->back = back; - swap_info->pipe = pipe; + swap_info->type = DRI2_SWAP; if (!i830_dri2_add_frame_event(swap_info)) { free(swap_info); @@ -1124,20 +1132,27 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, if (ret) goto blit_fallback; - /* Flips need to be submitted one frame before */ + /* + * If we can, schedule the flip directly from here rather + * than waiting for an event from the kernel for the current + * (or a past) MSC. + */ + if (divisor == 0 && + current_msc >= *target_msc && + queue_flip(intel, draw, swap_info)) + return TRUE; + if (can_exchange(draw, front, back)) { - swap_type = DRI2_FLIP; - flip = 1; + swap_info->type = DRI2_FLIP; + /* Flips need to be submitted one frame before */ + if (*target_msc > 0) + --*target_msc; + flip = 1; } - swap_info->type = swap_type; - - /* Correct target_msc by 'flip' if swap_type == DRI2_FLIP. - * Do it early, so handling of different timing constraints - * for divisor, remainder and msc vs. target_msc works. - */ - if (*target_msc > 0) - *target_msc -= flip; +#if DRI2INFOREC_VERSION >= 6 + DRI2SwapLimit(draw, 1); +#endif /* * If divisor is zero, or current_msc is smaller than target_msc @@ -1145,15 +1160,6 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, * the swap. */ if (divisor == 0 || current_msc < *target_msc) { - /* - * If we can, schedule the flip directly from here rather - * than waiting for an event from the kernel for the current - * (or a past) MSC. - */ - if (flip && divisor == 0 && current_msc >= *target_msc && - I830DRI2ScheduleFlip(intel, draw, swap_info)) - return TRUE; - vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT | pipe_select(pipe); @@ -1168,7 +1174,7 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, * current_msc to ensure we return a reasonable value back * to the caller. This makes swap_interval logic more robust. */ - if (current_msc >= *target_msc) + if (current_msc > *target_msc) *target_msc = current_msc; seq = intel_drm_queue_alloc(scrn, crtc, swap_info, intel_dri2_vblank_handler, intel_dri2_vblank_abort); @@ -1183,6 +1189,8 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, xf86DrvMsg(scrn->scrnIndex, X_WARNING, "divisor 0 get vblank counter failed: %s\n", strerror(errno)); + intel_drm_abort_seq(intel->scrn, seq); + swap_info = NULL; goto blit_fallback; } @@ -1332,7 +1340,6 @@ I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc, if (!i830_dri2_add_frame_event(wait_info)) { free(wait_info); - wait_info = NULL; goto out_complete; } @@ -1374,7 +1381,8 @@ I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc, strerror(errno)); limit--; } - goto out_free; + intel_drm_abort_seq(intel->scrn, seq); + goto out_complete; } wait_info->frame = intel_sequence_to_crtc_msc(crtc, vbl.reply.sequence); @@ -1417,7 +1425,8 @@ I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc, strerror(errno)); limit--; } - goto out_free; + intel_drm_abort_seq(intel->scrn, seq); + goto out_complete; } wait_info->frame = intel_sequence_to_crtc_msc(crtc, vbl.reply.sequence); @@ -1440,13 +1449,92 @@ static int has_i830_dri(void) return access(DRI_DRIVER_PATH "/i830_dri.so", R_OK) == 0; } -static const char *dri_driver_name(intel_screen_private *intel) +static int +namecmp(const char *s1, const char *s2) +{ + char c1, c2; + + if (!s1 || *s1 == 0) { + if (!s2 || *s2 == 0) + return 0; + else + return 1; + } + + while (*s1 == '_' || *s1 == ' ' || *s1 == '\t') + s1++; + + while (*s2 == '_' || *s2 == ' ' || *s2 == '\t') + s2++; + + c1 = isupper(*s1) ? tolower(*s1) : *s1; + c2 = isupper(*s2) ? tolower(*s2) : *s2; + while (c1 == c2) { + if (c1 == '\0') + return 0; + + s1++; + while (*s1 == '_' || *s1 == ' ' || *s1 == '\t') + s1++; + + s2++; + while (*s2 == '_' || *s2 == ' ' || *s2 == '\t') + s2++; + + c1 = isupper(*s1) ? tolower(*s1) : *s1; + c2 = isupper(*s2) ? tolower(*s2) : *s2; + } + + return c1 - c2; +} + +static Bool is_level(const char **str) +{ + const char *s = *str; + char *end; + unsigned val; + + if (s == NULL || *s == '\0') + return TRUE; + + if (namecmp(s, "on") == 0) + return TRUE; + if (namecmp(s, "true") == 0) + return TRUE; + if (namecmp(s, "yes") == 0) + return TRUE; + + if (namecmp(s, "0") == 0) + return TRUE; + if (namecmp(s, "off") == 0) + return TRUE; + if (namecmp(s, "false") == 0) + return TRUE; + if (namecmp(s, "no") == 0) + return TRUE; + + val = strtoul(s, &end, 0); + if (val && *end == '\0') + return TRUE; + if (val && *end == ':') + *str = end + 1; + return FALSE; +} + +static const char *options_get_dri(intel_screen_private *intel) { #if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,7,99,901,0) - const char *s = xf86GetOptValString(intel->Options, OPTION_DRI); - Bool dummy; + return xf86GetOptValString(intel->Options, OPTION_DRI); +#else + return NULL; +#endif +} - if (s == NULL || xf86getBoolValue(&dummy, s)) { +static const char *dri_driver_name(intel_screen_private *intel) +{ + const char *s = options_get_dri(intel); + + if (is_level(&s)) { if (INTEL_INFO(intel)->gen < 030) return has_i830_dri() ? "i830" : "i915"; else if (INTEL_INFO(intel)->gen < 040) @@ -1456,14 +1544,6 @@ static const char *dri_driver_name(intel_screen_private *intel) } return s; -#else - if (INTEL_INFO(intel)->gen < 030) - return has_i830_dri() ? "i830" : "i915"; - else if (INTEL_INFO(intel)->gen < 040) - return "i915"; - else - return "i965"; -#endif } Bool I830DRI2ScreenInit(ScreenPtr screen) @@ -1544,7 +1624,7 @@ Bool I830DRI2ScreenInit(ScreenPtr screen) info.numDrivers = 2; info.driverNames = driverNames; driverNames[0] = info.driverName; - driverNames[1] = info.driverName; + driverNames[1] = "va_gl"; #endif return DRI2ScreenInit(screen, &info); diff --git a/src/uxa/intel_driver.c b/src/uxa/intel_driver.c index 2793da5..8f76b34 100644 --- a/src/uxa/intel_driver.c +++ b/src/uxa/intel_driver.c @@ -237,24 +237,17 @@ static Bool I830GetEarlyOptions(ScrnInfoPtr scrn) return TRUE; } -static Bool intel_option_cast_string_to_bool(intel_screen_private *intel, - int id, Bool val) -{ -#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,7,99,901,0) - xf86getBoolValue(&val, xf86GetOptValString(intel->Options, id)); - return val; -#else - return val; -#endif -} - static void intel_check_dri_option(ScrnInfoPtr scrn) { intel_screen_private *intel = intel_get_screen_private(scrn); + unsigned level; intel->dri2 = intel->dri3 = DRI_NONE; - if (!intel_option_cast_string_to_bool(intel, OPTION_DRI, TRUE)) - intel->dri2 = intel->dri3 = DRI_DISABLED; + level = intel_option_cast_to_unsigned(intel->Options, OPTION_DRI, DEFAULT_DRI_LEVEL); + if (level < 3) + intel->dri3 = DRI_DISABLED; + if (level < 2) + intel->dri2 = DRI_DISABLED; if (scrn->depth != 16 && scrn->depth != 24 && scrn->depth != 30) { xf86DrvMsg(scrn->scrnIndex, X_CONFIG, @@ -371,8 +364,8 @@ static Bool can_accelerate_blt(struct intel_screen_private *intel) if (INTEL_INFO(intel)->gen == -1) return FALSE; - if (xf86ReturnOptValBool(intel->Options, OPTION_ACCEL_DISABLE, FALSE) || - !intel_option_cast_string_to_bool(intel, OPTION_ACCEL_METHOD, TRUE)) { + if (!xf86ReturnOptValBool(intel->Options, OPTION_ACCEL_ENABLE, TRUE) || + !intel_option_cast_to_bool(intel->Options, OPTION_ACCEL_METHOD, TRUE)) { xf86DrvMsg(intel->scrn->scrnIndex, X_CONFIG, "Disabling hardware acceleration.\n"); return FALSE; @@ -735,6 +728,8 @@ intel_flush_callback(CallbackListPtr *list, } #if HAVE_UDEV +#include + static void I830HandleUEvents(int fd, void *closure) { @@ -771,6 +766,15 @@ I830HandleUEvents(int fd, void *closure) udev_device_unref(dev); } +static int has_randr(void) +{ +#if HAS_DIXREGISTERPRIVATEKEY + return dixPrivateKeyRegistered(rrPrivKey); +#else + return *rrPrivKey; +#endif +} + static void I830UeventInit(ScrnInfoPtr scrn) { @@ -780,6 +784,10 @@ I830UeventInit(ScrnInfoPtr scrn) Bool hotplug; MessageType from = X_CONFIG; + /* Without RR, nothing we can do here */ + if (!has_randr()) + return; + if (!xf86GetOptValBool(intel->Options, OPTION_HOTPLUG, &hotplug)) { from = X_DEFAULT; hotplug = TRUE; diff --git a/src/uxa/intel_hwmc.c b/src/uxa/intel_hwmc.c index 829cb8e..7854060 100644 --- a/src/uxa/intel_hwmc.c +++ b/src/uxa/intel_hwmc.c @@ -193,7 +193,7 @@ Bool intel_xvmc_adaptor_init(ScreenPtr pScreen) intel_screen_private *intel = intel_get_screen_private(scrn); struct pci_device *pci; static XF86MCAdaptorRec *pAdapt; - char *name; + const char *name; char buf[64]; if (!intel->XvMCEnabled) diff --git a/src/uxa/intel_memory.c b/src/uxa/intel_memory.c index 0c6cf30..b2d7a36 100644 --- a/src/uxa/intel_memory.c +++ b/src/uxa/intel_memory.c @@ -42,7 +42,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * This is the video memory allocator. Our memory allocation is different from * other graphics chips, where you have a fixed amount of graphics memory * available that you want to put to the best use. Instead, we have almost no - * memory pre-allocated, and we have to choose an appropriate amount of sytem + * memory pre-allocated, and we have to choose an appropriate amount of system * memory to use. * * The allocations we might do: diff --git a/src/uxa/intel_present.c b/src/uxa/intel_present.c index d20043f..a7f904c 100644 --- a/src/uxa/intel_present.c +++ b/src/uxa/intel_present.c @@ -343,29 +343,33 @@ intel_present_unflip(ScreenPtr screen, uint64_t event_id) { ScrnInfoPtr scrn = xf86ScreenToScrn(screen); intel_screen_private *intel = intel_get_screen_private(scrn); - struct intel_present_vblank_event *event; PixmapPtr pixmap = screen->GetScreenPixmap(screen); + struct intel_present_vblank_event *event = NULL; dri_bo *bo; - Bool ret; if (!intel_present_check_flip(NULL, screen->root, pixmap, true)) - return; + goto fail; bo = intel_get_pixmap_bo(pixmap); if (!bo) - return; + goto fail; event = calloc(1, sizeof(struct intel_present_vblank_event)); if (!event) - return; + goto fail; event->event_id = event_id; - ret = intel_do_pageflip(intel, bo, -1, FALSE, event, intel_present_flip_event, intel_present_flip_abort); - if (!ret) { - xf86DrvMsg(scrn->scrnIndex, X_ERROR, - "present unflip failed\n"); - } + if (!intel_do_pageflip(intel, bo, -1, FALSE, event, + intel_present_flip_event, + intel_present_flip_abort)) + goto fail; + + return; +fail: + xf86SetDesiredModes(scrn); + present_event_notify(event_id, 0, 0); + free(event); } static present_screen_info_rec intel_present_screen_info = { diff --git a/test/Makefile.am b/test/Makefile.am index 66ed8eb..7d88810 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -5,6 +5,7 @@ stress_TESTS = \ basic-rectangle \ basic-string \ basic-copyarea \ + basic-copyplane \ basic-copyarea-size \ basic-putimage \ basic-lines \ @@ -12,8 +13,10 @@ stress_TESTS = \ DrawSegments \ cursor-test \ render-fill \ + render-glyphs \ render-trapezoid \ render-trapezoid-image \ + render-triangle \ render-fill-copy \ render-composite-solid \ render-composite-solid-mask \ @@ -28,6 +31,7 @@ stress_TESTS = \ if DRI2 stress_TESTS += \ dri2-race \ + dri2-speed \ dri2-swap \ dri2-test \ $(NULL) @@ -37,6 +41,7 @@ if X11_DRI3 stress_TESTS += \ dri3-test \ present-test \ + present-speed \ $(NULL) endif check_PROGRAMS = $(stress_TESTS) diff --git a/test/basic-copyplane.c b/test/basic-copyplane.c new file mode 100644 index 0000000..f049b82 --- /dev/null +++ b/test/basic-copyplane.c @@ -0,0 +1,99 @@ +#include +#include +#include + +#include /* for XDestroyImage */ +#include /* for pixman blt functions */ + +#include "test.h" + +static uint8_t clock_bits[] = {0x3C, 0x5E, 0xEF, 0xF7, 0x87, 0xFF, 0x7E, 0x3C}; + +/* https://bugs.freedesktop.org/show_bug.cgi?id=91499 */ +static void draw_clock(struct test_display *t, Drawable d, + uint8_t alu, int x, int y, uint32_t fg, uint32_t bg) +{ + Pixmap pixmap; + XGCValues val; + GC gc; + + val.graphics_exposures = 0; + val.function = alu; + val.foreground = fg; + val.background = fg; + + gc = XCreateGC(t->dpy, d, + GCGraphicsExposures | GCForeground | GCBackground | GCFunction, + &val); + pixmap = XCreateBitmapFromData(t->dpy, d, (char *)clock_bits, 8, 8); + + XCopyPlane(t->dpy, pixmap, d, gc, 0, 0, 8, 8, x, y, 1); + + XFreePixmap(t->dpy, pixmap); + XFreeGC(t->dpy, gc); +} + +static void clear(struct test_display *dpy, struct test_target *tt) +{ + XRenderColor render_color = {0}; + XRenderFillRectangle(dpy->dpy, PictOpClear, tt->picture, &render_color, + 0, 0, tt->width, tt->height); +} + +static void clock_tests(struct test *t, int reps, int sets, enum target target) +{ + struct test_target out, ref; + int r, s; + + printf("Testing clock (%s): ", test_target_name(target)); + fflush(stdout); + + test_target_create_render(&t->out, target, &out); + clear(&t->out, &out); + + test_target_create_render(&t->ref, target, &ref); + clear(&t->ref, &ref); + + for (s = 0; s < sets; s++) { + for (r = 0; r < reps; r++) { + int x = rand() % (out.width - 8); + int y = rand() % (out.height - 8); + uint8_t alu = rand() % (GXset + 1); + uint32_t bg = rand(); + uint32_t fg = rand(); + + draw_clock(&t->out, out.draw, alu, x, y, fg, bg); + draw_clock(&t->ref, ref.draw, alu, x, y, fg, bg); + } + + test_compare(t, + out.draw, out.format, + ref.draw, ref.format, + 0, 0, out.width, out.height, + ""); + } + + printf("passed [%d iterations x %d]\n", reps, sets); + + test_target_destroy_render(&t->out, &out); + test_target_destroy_render(&t->ref, &ref); +} + +int main(int argc, char **argv) +{ + struct test test; + int i; + + test_init(&test, argc, argv); + + for (i = 0; i <= DEFAULT_ITERATIONS; i++) { + int reps = REPS(i), sets = SETS(i); + enum target t; + + for (t = TARGET_FIRST; t <= TARGET_LAST; t++) { + clock_tests(&test, reps, sets, t); + } + } + + return 0; +} diff --git a/test/dri2-race.c b/test/dri2-race.c index 8862c84..c589f2b 100644 --- a/test/dri2-race.c +++ b/test/dri2-race.c @@ -5,6 +5,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -17,6 +21,16 @@ #define COUNT 60 +static uint32_t upper_32_bits(uint64_t val) +{ + return val >> 32; +} + +static uint32_t lower_32_bits(uint64_t val) +{ + return val & 0xffffffff; +} + static int dri2_open(Display *dpy) { drm_auth_t auth; @@ -41,15 +55,36 @@ static int dri2_open(Display *dpy) return fd; } -static void run(Display *dpy, int width, int height, - unsigned int *attachments, int nattachments, - const char *name) +static void swap_buffers(Display *dpy, Window win, int divisor, + unsigned int *attachments, int nattachments) +{ + xcb_connection_t *c = XGetXCBConnection(dpy); + unsigned int seq[2]; + + seq[0] = xcb_dri2_swap_buffers_unchecked(c, win, + 0, 0, 0, divisor, 0, 0).sequence; + + + seq[1] = xcb_dri2_get_buffers_unchecked(c, win, + nattachments, nattachments, + attachments).sequence; + + xcb_flush(c); + xcb_discard_reply(c, seq[0]); + xcb_discard_reply(c, seq[1]); +} + +static void race_window(Display *dpy, int width, int height, + unsigned int *attachments, int nattachments, + const char *name) { Window win; XSetWindowAttributes attr; int count, loop; DRI2Buffer *buffers; + printf("%s(%s)\n", __func__, name); + /* Be nasty and install a fullscreen window on top so that we * can guarantee we do not get clipped by children. */ @@ -75,13 +110,234 @@ static void run(Display *dpy, int width, int height, for (count = 0; count < loop; count++) DRI2SwapBuffers(dpy, win, 0, 0, 0); XDestroyWindow(dpy, win); + printf("."); fflush(stdout); + } while (--loop); + printf("*\n"); + + loop = 100; + do { + win = XCreateWindow(dpy, DefaultRootWindow(dpy), + 0, 0, width, height, 0, + DefaultDepth(dpy, DefaultScreen(dpy)), + InputOutput, + DefaultVisual(dpy, DefaultScreen(dpy)), + CWOverrideRedirect, &attr); + XMapWindow(dpy, win); + + DRI2CreateDrawable(dpy, win); + + buffers = DRI2GetBuffers(dpy, win, &width, &height, + attachments, nattachments, &count); + if (count != nattachments) + return; + + free(buffers); + for (count = 0; count < loop; count++) + DRI2SwapBuffers(dpy, win, 0, 1, 0); + XDestroyWindow(dpy, win); + printf("."); fflush(stdout); + } while (--loop); + printf("*\n"); + + loop = 100; + do { + win = XCreateWindow(dpy, DefaultRootWindow(dpy), + 0, 0, width, height, 0, + DefaultDepth(dpy, DefaultScreen(dpy)), + InputOutput, + DefaultVisual(dpy, DefaultScreen(dpy)), + CWOverrideRedirect, &attr); + XMapWindow(dpy, win); + + DRI2CreateDrawable(dpy, win); + + buffers = DRI2GetBuffers(dpy, win, &width, &height, + attachments, nattachments, &count); + if (count != nattachments) + return; + + free(buffers); + for (count = 0; count < loop; count++) + swap_buffers(dpy, win, 0, attachments, nattachments); + XDestroyWindow(dpy, win); + printf("."); fflush(stdout); + } while (--loop); + printf("*\n"); + + loop = 100; + do { + win = XCreateWindow(dpy, DefaultRootWindow(dpy), + 0, 0, width, height, 0, + DefaultDepth(dpy, DefaultScreen(dpy)), + InputOutput, + DefaultVisual(dpy, DefaultScreen(dpy)), + CWOverrideRedirect, &attr); + XMapWindow(dpy, win); + + DRI2CreateDrawable(dpy, win); + + buffers = DRI2GetBuffers(dpy, win, &width, &height, + attachments, nattachments, &count); + if (count != nattachments) + return; + + free(buffers); + for (count = 0; count < loop; count++) + swap_buffers(dpy, win, 1, attachments, nattachments); + XDestroyWindow(dpy, win); + printf("."); fflush(stdout); + } while (--loop); + printf("*\n"); + + loop = 100; + do { + uint64_t ignore, msc; + xcb_connection_t *c = XGetXCBConnection(dpy); + + win = XCreateWindow(dpy, DefaultRootWindow(dpy), + 0, 0, width, height, 0, + DefaultDepth(dpy, DefaultScreen(dpy)), + InputOutput, + DefaultVisual(dpy, DefaultScreen(dpy)), + CWOverrideRedirect, &attr); + XMapWindow(dpy, win); + + DRI2CreateDrawable(dpy, win); + DRI2GetMSC(dpy, win, &ignore, &msc, &ignore); + for (count = 0; count < loop; count++) + xcb_discard_reply(c, + xcb_dri2_wait_msc(c, win, + upper_32_bits(msc + count + 1), + lower_32_bits(msc + count + 1), + 0, 1, 0, 0).sequence); + XFlush(dpy); + XDestroyWindow(dpy, win); + printf("."); fflush(stdout); } while (--loop); + printf("*\n"); XSync(dpy, 1); sleep(2); XSync(dpy, 1); } +static void race_client(int width, int height, + unsigned int *attachments, int nattachments, + const char *name) +{ + XSetWindowAttributes attr; + int count, loop; + + printf("%s(%s)\n", __func__, name); + + /* Be nasty and install a fullscreen window on top so that we + * can guarantee we do not get clipped by children. + */ + attr.override_redirect = 1; + loop = 100; + do { + Display *dpy = XOpenDisplay(NULL); + Window win = XCreateWindow(dpy, DefaultRootWindow(dpy), + 0, 0, width, height, 0, + DefaultDepth(dpy, DefaultScreen(dpy)), + InputOutput, + DefaultVisual(dpy, DefaultScreen(dpy)), + CWOverrideRedirect, &attr); + + XMapWindow(dpy, win); + + DRI2CreateDrawable(dpy, win); + free(DRI2GetBuffers(dpy, win, &width, &height, + attachments, nattachments, &count)); + if (count != nattachments) + return; + + for (count = 0; count < loop; count++) + DRI2SwapBuffers(dpy, win, 0, 0, 0); + XCloseDisplay(dpy); + printf("."); fflush(stdout); + } while (--loop); + printf("*\n"); + + loop = 100; + do { + Display *dpy = XOpenDisplay(NULL); + Window win = XCreateWindow(dpy, DefaultRootWindow(dpy), + 0, 0, width, height, 0, + DefaultDepth(dpy, DefaultScreen(dpy)), + InputOutput, + DefaultVisual(dpy, DefaultScreen(dpy)), + CWOverrideRedirect, &attr); + + XMapWindow(dpy, win); + + DRI2CreateDrawable(dpy, win); + free(DRI2GetBuffers(dpy, win, &width, &height, + attachments, nattachments, &count)); + if (count != nattachments) + return; + + for (count = 0; count < loop; count++) + swap_buffers(dpy, win, 0, attachments, nattachments); + XCloseDisplay(dpy); + printf("."); fflush(stdout); + } while (--loop); + printf("*\n"); + + loop = 100; + do { + Display *dpy = XOpenDisplay(NULL); + Window win = XCreateWindow(dpy, DefaultRootWindow(dpy), + 0, 0, width, height, 0, + DefaultDepth(dpy, DefaultScreen(dpy)), + InputOutput, + DefaultVisual(dpy, DefaultScreen(dpy)), + CWOverrideRedirect, &attr); + + XMapWindow(dpy, win); + + DRI2CreateDrawable(dpy, win); + free(DRI2GetBuffers(dpy, win, &width, &height, + attachments, nattachments, &count)); + if (count != nattachments) + return; + + for (count = 0; count < loop; count++) + swap_buffers(dpy, win, 1, attachments, nattachments); + XCloseDisplay(dpy); + printf("."); fflush(stdout); + } while (--loop); + printf("*\n"); + + loop = 100; + do { + uint64_t ignore, msc; + Display *dpy = XOpenDisplay(NULL); + xcb_connection_t *c = XGetXCBConnection(dpy); + Window win = XCreateWindow(dpy, DefaultRootWindow(dpy), + 0, 0, width, height, 0, + DefaultDepth(dpy, DefaultScreen(dpy)), + InputOutput, + DefaultVisual(dpy, DefaultScreen(dpy)), + CWOverrideRedirect, &attr); + + XMapWindow(dpy, win); + + DRI2CreateDrawable(dpy, win); + DRI2GetMSC(dpy, win, &ignore, &msc, &ignore); + for (count = 0; count < loop; count++) + xcb_discard_reply(c, + xcb_dri2_wait_msc(c, win, + upper_32_bits(msc + count + 1), + lower_32_bits(msc + count + 1), + 0, 1, 0, 0).sequence); + XFlush(dpy); + XCloseDisplay(dpy); + printf("."); fflush(stdout); + } while (--loop); + printf("*\n"); +} + int main(void) { Display *dpy; @@ -101,13 +357,17 @@ int main(void) width = WidthOfScreen(DefaultScreenOfDisplay(dpy)); height = HeightOfScreen(DefaultScreenOfDisplay(dpy)); - run(dpy, width, height, attachments, 1, "fullscreen"); - run(dpy, width, height, attachments, 2, "fullscreen (with front)"); + race_window(dpy, width, height, attachments, 1, "fullscreen"); + race_window(dpy, width, height, attachments, 2, "fullscreen (with front)"); + race_client(width, height, attachments, 1, "fullscreen"); + race_client(width, height, attachments, 2, "fullscreen (with front)"); width /= 2; height /= 2; - run(dpy, width, height, attachments, 1, "windowed"); - run(dpy, width, height, attachments, 2, "windowed (with front)"); + race_window(dpy, width, height, attachments, 1, "windowed"); + race_window(dpy, width, height, attachments, 2, "windowed (with front)"); + race_client(width, height, attachments, 1, "windowed"); + race_client(width, height, attachments, 2, "windowed (with front)"); return 0; } diff --git a/test/dri2-speed.c b/test/dri2-speed.c new file mode 100644 index 0000000..87b9d0b --- /dev/null +++ b/test/dri2-speed.c @@ -0,0 +1,342 @@ +/* + * 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 "dri2.h" + +static int _x_error_occurred; + +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 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, const char *name) +{ + xcb_connection_t *c = XGetXCBConnection(dpy); + struct timespec start, end; + int n, completed = 0; + + _x_error_occurred = 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); + + XSync(dpy, True); + if (_x_error_occurred) + abort(); + + printf("%s: Completed %d swaps in %.1fs, %.3fus each (%.1f FPS)\n", + name, completed, elapsed(&start, &end) / 1000000, + elapsed(&start, &end) / completed, + 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 (!DRI2Connect(dpy, DefaultRootWindow(dpy), &driver, &device)) + return -1; + + printf ("Connecting to %s driver on %s\n", driver, device); + + 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(void) +{ + Display *dpy; + Window root, win; + XRRScreenResources *res; + XRRCrtcInfo **original_crtc; + XSetWindowAttributes attr; + int i, j, fd; + + attr.override_redirect = 1; + + dpy = XOpenDisplay(NULL); + if (dpy == NULL) + return 77; + + 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; + + original_crtc = malloc(sizeof(XRRCrtcInfo *)*res->ncrtc); + for (i = 0; i < res->ncrtc; i++) + original_crtc[i] = XRRGetCrtcInfo(dpy, res, res->crtcs[i]); + + printf("noutput=%d, ncrtc=%d\n", res->noutput, res->ncrtc); + 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); + run(dpy, root, "off"); + XSync(dpy, True); + + 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]); + + for (j = 0; mode && j < 2*output->ncrtc; j++) { + int c = j; + if (c >= output->ncrtc) + c = 2*output->ncrtc - j - 1; + + printf("[%d, %d] -- OUTPUT:%ld, CRTC:%ld: %dx%d\n", + i, c, (long)res->outputs[i], (long)output->crtcs[c], + mode->width, mode->height); + XRRSetCrtcConfig(dpy, res, output->crtcs[c], CurrentTime, + 0, 0, output->modes[0], RR_Rotate_0, &res->outputs[i], 1); + + run(dpy, root, "root"); + XSync(dpy, True); + + win = XCreateWindow(dpy, root, + 0, 0, mode->width, mode->height, 0, + DefaultDepth(dpy, DefaultScreen(dpy)), + InputOutput, + DefaultVisual(dpy, DefaultScreen(dpy)), + CWOverrideRedirect, &attr); + DRI2CreateDrawable(dpy, win); + DRI2SwapInterval(dpy, win, 0); + fullscreen(dpy, win); + XMapWindow(dpy, win); + run(dpy, win, "fullscreen"); + XDestroyWindow(dpy, win); + XSync(dpy, True); + + win = XCreateWindow(dpy, root, + 0, 0, mode->width, mode->height, 0, + DefaultDepth(dpy, DefaultScreen(dpy)), + InputOutput, + DefaultVisual(dpy, DefaultScreen(dpy)), + CWOverrideRedirect, &attr); + DRI2CreateDrawable(dpy, win); + DRI2SwapInterval(dpy, win, 0); + XMapWindow(dpy, win); + run(dpy, win, "windowed"); + XDestroyWindow(dpy, win); + XSync(dpy, True); + + if (has_composite(dpy)) { + Damage damage; + + _x_error_occurred = 0; + win = XCreateWindow(dpy, root, + 0, 0, mode->width, mode->height, 0, + DefaultDepth(dpy, DefaultScreen(dpy)), + InputOutput, + DefaultVisual(dpy, DefaultScreen(dpy)), + CWOverrideRedirect, &attr); + XCompositeRedirectWindow(dpy, win, CompositeRedirectManual); + damage = XDamageCreate(dpy, win, XDamageReportRawRectangles); + DRI2CreateDrawable(dpy, win); + DRI2SwapInterval(dpy, win, 0); + XMapWindow(dpy, win); + XSync(dpy, True); + if (!_x_error_occurred) + run(dpy, win, "composited"); + XDamageDestroy(dpy, damage); + XDestroyWindow(dpy, win); + XSync(dpy, True); + } + + win = XCreateWindow(dpy, root, + 0, 0, mode->width/2, mode->height/2, 0, + DefaultDepth(dpy, DefaultScreen(dpy)), + InputOutput, + DefaultVisual(dpy, DefaultScreen(dpy)), + CWOverrideRedirect, &attr); + DRI2CreateDrawable(dpy, win); + DRI2SwapInterval(dpy, win, 0); + XMapWindow(dpy, win); + run(dpy, win, "half"); + XDestroyWindow(dpy, win); + XSync(dpy, True); + + XRRSetCrtcConfig(dpy, res, output->crtcs[c], CurrentTime, + 0, 0, None, RR_Rotate_0, NULL, 0); + } + + XRRFreeOutputInfo(output); + } + + 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); + return 0; +} diff --git a/test/dri2-test.c b/test/dri2-test.c index dd4179f..88c0e4c 100644 --- a/test/dri2-test.c +++ b/test/dri2-test.c @@ -6,6 +6,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -101,16 +105,41 @@ static uint64_t check_msc(Display *dpy, Window win, uint64_t last_msc) return current_msc; } +static void wait_next_vblank(Display *dpy, Window win) +{ + uint64_t msc, ust, sbc; + DRI2WaitMSC(dpy, win, 0, 1, 0, &ust, &msc, &sbc); +} + +static void swap_buffers(xcb_connection_t *c, Window win, + unsigned int *attachments, int nattachments) +{ + 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, + nattachments, nattachments, + attachments).sequence; + + xcb_flush(c); + xcb_discard_reply(c, seq[0]); + xcb_discard_reply(c, seq[1]); +} + static void run(Display *dpy, int width, int height, unsigned int *attachments, int nattachments, const char *name) { + xcb_connection_t *c = XGetXCBConnection(dpy); Window win; XSetWindowAttributes attr; int count; DRI2Buffer *buffers; struct timespec start, end; - uint64_t msc; + uint64_t start_msc, end_msc; /* Be nasty and install a fullscreen window on top so that we * can guarantee we do not get clipped by children. @@ -125,42 +154,59 @@ static void run(Display *dpy, int width, int height, XMapWindow(dpy, win); DRI2CreateDrawable(dpy, win); - msc = check_msc(dpy, win, 0); + DRI2SwapInterval(dpy, win, 1); + start_msc = check_msc(dpy, win, 0); buffers = DRI2GetBuffers(dpy, win, &width, &height, attachments, nattachments, &count); if (count != nattachments) return; - msc = check_msc(dpy, win, msc); + swap_buffers(c, win, attachments, nattachments); + start_msc = check_msc(dpy, win, start_msc); clock_gettime(CLOCK_MONOTONIC, &start); for (count = 0; count < COUNT; count++) - DRI2SwapBuffers(dpy, win, 0, 0, 0); - msc = check_msc(dpy, win, msc); + swap_buffers(c, win, attachments, nattachments); + end_msc = check_msc(dpy, win, start_msc); clock_gettime(CLOCK_MONOTONIC, &end); - printf("%d %s (%dx%d) swaps in %fs.\n", - count, name, width, height, elapsed(&start, &end)); + printf("%d [%ld] %s (%dx%d) swaps in %fs.\n", + count, (long)(end_msc - start_msc), + name, width, height, elapsed(&start, &end)); - msc = check_msc(dpy, win, msc); + swap_buffers(c, win, attachments, nattachments); + start_msc = check_msc(dpy, win, end_msc); clock_gettime(CLOCK_MONOTONIC, &start); for (count = 0; count < COUNT; count++) dri2_copy_swap(dpy, win, width, height, nattachments == 2); - msc = check_msc(dpy, win, msc); + end_msc = check_msc(dpy, win, start_msc); clock_gettime(CLOCK_MONOTONIC, &end); - printf("%d %s (%dx%d) blits in %fs.\n", - count, name, width, height, elapsed(&start, &end)); + printf("%d [%ld] %s (%dx%d) blits in %fs.\n", + count, (long)(end_msc - start_msc), + name, width, height, elapsed(&start, &end)); DRI2SwapInterval(dpy, win, 0); - msc = check_msc(dpy, win, msc); + swap_buffers(c, win, attachments, nattachments); + start_msc = check_msc(dpy, win, end_msc); + clock_gettime(CLOCK_MONOTONIC, &start); + for (count = 0; count < COUNT; count++) + swap_buffers(c, win, attachments, nattachments); + end_msc = check_msc(dpy, win, start_msc); + clock_gettime(CLOCK_MONOTONIC, &end); + printf("%d [%ld] %s (%dx%d) vblank=0 swaps in %fs.\n", + count, (long)(end_msc - start_msc), + name, width, height, elapsed(&start, &end)); + + start_msc = check_msc(dpy, win, end_msc); clock_gettime(CLOCK_MONOTONIC, &start); for (count = 0; count < COUNT; count++) - DRI2SwapBuffers(dpy, win, 0, 0, 0); - msc = check_msc(dpy, win, msc); + wait_next_vblank(dpy, win); + end_msc = check_msc(dpy, win, start_msc); clock_gettime(CLOCK_MONOTONIC, &end); - printf("%d %s (%dx%d) vblank=0 swaps in %fs.\n", - count, name, width, height, elapsed(&start, &end)); + printf("%d [%ld] %s waits in %fs.\n", + count, (long)(end_msc - start_msc), + name, elapsed(&start, &end)); XDestroyWindow(dpy, win); free(buffers); diff --git a/test/dri3-test.c b/test/dri3-test.c index c66da31..7e98f95 100644 --- a/test/dri3-test.c +++ b/test/dri3-test.c @@ -1020,6 +1020,67 @@ fail: return 1; } +static int gem_set_tiling(int fd, uint32_t handle, int tiling, int stride) +{ + struct drm_i915_gem_set_tiling set_tiling; + + set_tiling.handle = handle; + set_tiling.tiling_mode = tiling; + set_tiling.stride = stride; + + return drmIoctl(fd, DRM_IOCTL_I915_GEM_SET_TILING, &set_tiling) == 0; +} + +static int test_tiling(Display *dpy, int device) +{ + Window root = RootWindow(dpy, DefaultScreen(dpy)); + const int tiling[] = { I915_TILING_NONE, I915_TILING_X, I915_TILING_Y }; + int line = -1; + int t; + + _x_error_occurred = 0; + + for (t = 0; t < sizeof(tiling)/sizeof(tiling[0]); t++) { + uint32_t src; + int src_fd; + Pixmap src_pix; + + src = gem_create(device, 4*4096); + if (!src) { + line = __LINE__; + goto fail; + } + + gem_set_tiling(device, src, tiling[t], 512); + + src_fd = gem_export(device, src); + if (src_fd < 0) { + line = __LINE__; + goto fail; + } + + src_pix = dri3_create_pixmap(dpy, root, + 128, 32, 32, + src_fd, 32, 512, 4*4096); + XSync(dpy, True); + if (_x_error_occurred) { + line = __LINE__; + goto fail; + } + XFreePixmap(dpy, src_pix); + _x_error_occurred = 0; + + close(src_fd); + gem_close(device, src); + } + + return 0; + +fail: + printf("%s failed with tiling %d, line %d\n", __func__, tiling[t], line); + return 1; +} + static int _check_error_handler(Display *display, XErrorEvent *event) @@ -1060,6 +1121,7 @@ int main(void) error += test_bad_size(dpy, device); error += test_bad_pitch(dpy, device); + error += test_tiling(dpy, device); error += test_shm(dpy, device, 400, 300); error += test_shm(dpy, device, 300, 400); diff --git a/test/dri3.c b/test/dri3.c index 45f3285..e564462 100644 --- a/test/dri3.c +++ b/test/dri3.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "dri3.h" @@ -109,12 +110,45 @@ void dri3_fence_free(Display *dpy, struct dri3_fence *fence) xcb_sync_destroy_fence(c, fence->xid); } +static void dri3_query_version(xcb_connection_t *c, int *major, int *minor) +{ + xcb_dri3_query_version_reply_t *reply; + + reply = xcb_dri3_query_version_reply(c, + xcb_dri3_query_version(c, + XCB_DRI3_MAJOR_VERSION, + XCB_DRI3_MINOR_VERSION), + NULL); + if (reply != NULL) { + *major = reply->major_version; + *minor = reply->minor_version; + free(reply); + } +} + +static int dri3_exists(xcb_connection_t *c) +{ + const xcb_query_extension_reply_t *ext; + int major, minor; + + major = minor = -1; + + ext = xcb_get_extension_data(c, &xcb_dri3_id); + if (ext != NULL && ext->present) + dri3_query_version(c, &major, &minor); + + return major >= 0; +} + int dri3_open__full(Display *dpy, Window root, unsigned provider) { xcb_connection_t *c = XGetXCBConnection(dpy); xcb_dri3_open_cookie_t cookie; xcb_dri3_open_reply_t *reply; + if (!dri3_exists(c)) + return -1; + cookie = xcb_dri3_open(c, root, provider); reply = xcb_dri3_open_reply(c, cookie, NULL); diff --git a/test/present-speed.c b/test/present-speed.c new file mode 100644 index 0000000..4d33904 --- /dev/null +++ b/test/present-speed.c @@ -0,0 +1,615 @@ +/* + * 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 "dri3.h" + +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 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; +}; + +#define DRI3 1 +#define NOCOPY 2 +static void run(Display *dpy, Window win, const char *name, unsigned options) +{ + xcb_connection_t *c = XGetXCBConnection(dpy); + struct timespec start, end; +#define N_BACK 8 + char test_name[128]; + 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++) { + buffer[n].pixmap = + XCreatePixmap(dpy, win, width, height, depth); + buffer[n].fence.xid = 0; + buffer[n].fd = -1; + if (options & DRI3) { + xcb_dri3_buffer_from_pixmap_reply_t *reply; + int *fds; + + 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); + } + if (options & NOCOPY) { + update = xcb_generate_id(c); + xcb_xfixes_create_region(c, update, 0, NULL); + present_flags |= XCB_PRESENT_OPTION_COPY; + } + + eid = xcb_generate_id(c); + xcb_present_select_input(c, eid, win, + (options & NOCOPY ? 0 : 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 = (options & NOCOPY) == 0; + 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); + + if (update) + xcb_xfixes_destroy_region(c, update); + for (n = 0; n < N_BACK; n++) { + if (buffer[n].fence.xid) + dri3_fence_free(dpy, &buffer[n].fence); + if (buffer[n].fd != -1) + close(buffer[n].fd); + XFreePixmap(dpy, buffer[n].pixmap); + } + + XSync(dpy, True); + if (_x_error_occurred) + abort(); + + xcb_present_select_input(c, eid, win, 0); + XSync(dpy, True); + xcb_unregister_for_special_event(c, Q); + + test_name[0] = '\0'; + if (options) { + snprintf(test_name, sizeof(test_name), "(%s%s )", + options & NOCOPY ? " no-copy" : "", + options & DRI3 ? " dri3" : ""); + } + printf("%s%s: Completed %d presents in %.1fs, %.3fus each (%.1f FPS)\n", + name, test_name, + completed, elapsed(&start, &end) / 1000000, + elapsed(&start, &end) / completed, + 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 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; +} + +static int has_xfixes(Display *dpy) +{ + xcb_connection_t *c = XGetXCBConnection(dpy); + const xcb_query_extension_reply_t *ext; + void *reply; + + ext = xcb_get_extension_data(c, &xcb_xfixes_id); + if (ext == NULL || !ext->present) + return 0; + + reply = xcb_xfixes_query_version_reply(c, + xcb_xfixes_query_version(c, + XCB_XFIXES_MAJOR_VERSION, + XCB_XFIXES_MINOR_VERSION), + NULL); + free(reply); + + return reply != NULL; +} + +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 void loop(Display *dpy, XRRScreenResources *res, unsigned options) +{ + Window root = DefaultRootWindow(dpy); + Window win; + XSetWindowAttributes attr; + int i, j; + + attr.override_redirect = 1; + + run(dpy, root, "off", options); + XSync(dpy, True); + + 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]); + + for (j = 0; mode && j < 2*output->ncrtc; j++) { + int c = j; + if (c >= output->ncrtc) + c = 2*output->ncrtc - j - 1; + + printf("[%d, %d] -- OUTPUT:%ld, CRTC:%ld: %dx%d\n", + i, c, (long)res->outputs[i], (long)output->crtcs[c], + mode->width, mode->height); + XRRSetCrtcConfig(dpy, res, output->crtcs[c], CurrentTime, + 0, 0, output->modes[0], RR_Rotate_0, &res->outputs[i], 1); + + run(dpy, root, "root", options); + XSync(dpy, True); + + win = XCreateWindow(dpy, root, + 0, 0, mode->width, mode->height, 0, + DefaultDepth(dpy, DefaultScreen(dpy)), + InputOutput, + DefaultVisual(dpy, DefaultScreen(dpy)), + CWOverrideRedirect, &attr); + fullscreen(dpy, win); + XMapWindow(dpy, win); + run(dpy, win, "fullscreen", options); + XDestroyWindow(dpy, win); + XSync(dpy, True); + + win = XCreateWindow(dpy, root, + 0, 0, mode->width, mode->height, 0, + DefaultDepth(dpy, DefaultScreen(dpy)), + InputOutput, + DefaultVisual(dpy, DefaultScreen(dpy)), + CWOverrideRedirect, &attr); + XMapWindow(dpy, win); + run(dpy, win, "windowed", options); + XDestroyWindow(dpy, win); + XSync(dpy, True); + + if (has_composite(dpy)) { + Damage damage; + + _x_error_occurred = 0; + win = XCreateWindow(dpy, root, + 0, 0, mode->width, mode->height, 0, + DefaultDepth(dpy, DefaultScreen(dpy)), + InputOutput, + DefaultVisual(dpy, DefaultScreen(dpy)), + CWOverrideRedirect, &attr); + XCompositeRedirectWindow(dpy, win, CompositeRedirectManual); + damage = XDamageCreate(dpy, win, XDamageReportRawRectangles); + XMapWindow(dpy, win); + XSync(dpy, True); + if (!_x_error_occurred) + run(dpy, win, "composited", options); + XDamageDestroy(dpy, damage); + XDestroyWindow(dpy, win); + XSync(dpy, True); + } + + win = XCreateWindow(dpy, root, + 0, 0, mode->width/2, mode->height/2, 0, + DefaultDepth(dpy, DefaultScreen(dpy)), + InputOutput, + DefaultVisual(dpy, DefaultScreen(dpy)), + CWOverrideRedirect, &attr); + XMapWindow(dpy, win); + run(dpy, win, "half", options); + XDestroyWindow(dpy, win); + XSync(dpy, True); + + XRRSetCrtcConfig(dpy, res, output->crtcs[c], CurrentTime, + 0, 0, None, RR_Rotate_0, NULL, 0); + } + + XRRFreeOutputInfo(output); + } +} + +int main(void) +{ + Display *dpy; + XRRScreenResources *res; + XRRCrtcInfo **original_crtc; + int i; + + dpy = XOpenDisplay(NULL); + if (dpy == NULL) + return 77; + + if (!has_present(dpy)) + return 77; + + if (DPMSQueryExtension(dpy, &i, &i)) + DPMSDisable(dpy); + + signal(SIGALRM, SIG_IGN); + XSetErrorHandler(_check_error_handler); + + res = NULL; + if (XRRQueryVersion(dpy, &i, &i)) + res = _XRRGetScreenResourcesCurrent(dpy, DefaultRootWindow(dpy)); + if (res == NULL) + 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]); + + printf("noutput=%d, ncrtc=%d\n", res->noutput, res->ncrtc); + for (i = 0; i < res->ncrtc; i++) + XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime, + 0, 0, None, RR_Rotate_0, NULL, 0); + + loop(dpy, res, 0); + if (has_xfixes(dpy)) + loop(dpy, res, NOCOPY); + if (has_dri3(dpy)) + loop(dpy, res, DRI3); + + 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); + return 0; +} diff --git a/test/present-test.c b/test/present-test.c index 6b562eb..b33fd2e 100644 --- a/test/present-test.c +++ b/test/present-test.c @@ -31,7 +31,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -44,6 +46,8 @@ #endif #include #include +#include +#include #include #include @@ -134,12 +138,14 @@ static void *setup_msc(Display *dpy, Window win) return q; } -static uint64_t check_msc(Display *dpy, Window win, void *q, uint64_t last_msc) +static uint64_t check_msc(Display *dpy, Window win, void *q, uint64_t last_msc, uint64_t *ust) { xcb_connection_t *c = XGetXCBConnection(dpy); + static uint32_t serial = 1; uint64_t msc = 0; + int complete = 0; - xcb_present_notify_msc(c, win, 0, 0, 0, 0); + xcb_present_notify_msc(c, win, serial ^ 0xcc00ffee, 0, 0, 0); xcb_flush(c); do { @@ -151,82 +157,1044 @@ static uint64_t check_msc(Display *dpy, Window win, void *q, uint64_t last_msc) break; ce = (xcb_present_complete_notify_event_t *)ev; - if (ce->kind != XCB_PRESENT_COMPLETE_KIND_PIXMAP) + if (ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC && + ce->serial == (serial ^ 0xcc00ffee)) { msc = ce->msc; + if (ust) + *ust = ce->ust; + complete = 1; + } + free(ev); + } while (!complete); + + if ((int64_t)(msc - last_msc) < 0) { + printf("Invalid MSC: was %llu, now %llu\n", + (long long)last_msc, (long long)msc); + } + + if (++serial == 0) + serial = 1; + + return msc; +} + +static uint64_t wait_vblank(Display *dpy, Window win, void *q) +{ + xcb_connection_t *c = XGetXCBConnection(dpy); + static uint32_t serial = 1; + uint64_t msc = 0; + int complete = 0; + + xcb_present_notify_msc(c, win, serial ^ 0xdeadbeef, 0, 1, 0); + xcb_flush(c); + + do { + xcb_present_complete_notify_event_t *ce; + xcb_generic_event_t *ev; + + ev = xcb_wait_for_special_event(c, q); + if (ev == NULL) + break; + + ce = (xcb_present_complete_notify_event_t *)ev; + if (ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC && + ce->serial == (serial ^ 0xdeadbeef)) { + msc = ce->msc; + complete = 1; + } + free(ev); + } while (!complete); + + if (++serial == 0) + serial = 1; + + return msc; +} + +static uint64_t msc_interval(Display *dpy, Window win, void *q) +{ + xcb_connection_t *c = XGetXCBConnection(dpy); + uint64_t msc, ust; + int complete = 0; + + msc = check_msc(dpy, win, q, 0, NULL); + + xcb_present_notify_msc(c, win, 0xc0ffee00, msc, 0, 0); + xcb_present_notify_msc(c, win, 0xc0ffee01, msc + 10, 0, 0); + xcb_flush(c); + + ust = msc = 0; + do { + xcb_present_complete_notify_event_t *ce; + xcb_generic_event_t *ev; + + ev = xcb_wait_for_special_event(c, q); + if (ev == NULL) + break; + + ce = (xcb_present_complete_notify_event_t *)ev; + if (ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC && + ce->serial == 0xc0ffee00) { + msc -= ce->msc; + ust -= ce->ust; + complete++; + } + if (ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC && + ce->serial == 0xc0ffee01) { + msc += ce->msc; + ust += ce->ust; + complete++; + } + free(ev); + } while (complete != 2); + + return (ust + msc/2) / msc; +} + +static void teardown_msc(Display *dpy, void *q) +{ + xcb_unregister_for_special_event(XGetXCBConnection(dpy), q); +} + +static int test_whole(Display *dpy, Window win, const char *phase) +{ + xcb_connection_t *c = XGetXCBConnection(dpy); + Pixmap pixmap; + struct dri3_fence fence; + Window root; + unsigned int width, height; + unsigned border, depth; + int x, y, ret = 1; + + XGetGeometry(dpy, win, + &root, &x, &y, &width, &height, &border, &depth); + + if (dri3_create_fence(dpy, win, &fence)) + return 0; + + printf("%s: Testing simple flip: %dx%d\n", phase, width, height); + _x_error_occurred = 0; + + xshmfence_reset(fence.addr); + + pixmap = XCreatePixmap(dpy, win, width, height, depth); + xcb_present_pixmap(c, win, pixmap, 0, + 0, /* valid */ + 0, /* update */ + 0, /* x_off */ + 0, /* y_off */ + None, + None, /* wait fence */ + fence.xid, + XCB_PRESENT_OPTION_NONE, + 0, /* target msc */ + 0, /* divisor */ + 0, /* remainder */ + 0, NULL); + XFreePixmap(dpy, pixmap); + + pixmap = XCreatePixmap(dpy, win, width, height, depth); + xcb_present_pixmap(c, win, pixmap, 0, + 0, /* valid */ + 0, /* update */ + 0, /* x_off */ + 0, /* y_off */ + None, + None, /* wait fence */ + None, /* sync fence */ + XCB_PRESENT_OPTION_NONE, + 0, /* target msc */ + 0, /* divisor */ + 0, /* remainder */ + 0, NULL); + XFreePixmap(dpy, pixmap); + XFlush(dpy); + + ret = !!xshmfence_await(fence.addr); + dri3_fence_free(dpy, &fence); + + XSync(dpy, True); + ret += !!_x_error_occurred; + + return ret; +} + +static uint64_t flush_flips(Display *dpy, Window win, Pixmap pixmap, void *Q, uint64_t *ust) +{ + xcb_connection_t *c = XGetXCBConnection(dpy); + uint64_t msc; + int complete; + + msc = check_msc(dpy, win, Q, 0, NULL); + xcb_present_pixmap(c, win, pixmap, + 0xdeadbeef, /* serial */ + 0, /* valid */ + 0, /* update */ + 0, /* x_off */ + 0, /* y_off */ + None, + None, /* wait fence */ + None, + XCB_PRESENT_OPTION_NONE, + msc + 60, /* target msc */ + 0, /* divisor */ + 0, /* remainder */ + 0, NULL); + xcb_flush(c); + complete = 0; + do { + xcb_present_complete_notify_event_t *ce; + xcb_generic_event_t *ev; + + ev = xcb_wait_for_special_event(c, Q); + if (ev == NULL) + break; + + ce = (xcb_present_complete_notify_event_t *)ev; + complete = (ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP && + ce->serial == 0xdeadbeef); + free(ev); + } while (!complete); + XSync(dpy, True); + + return check_msc(dpy, win, Q, msc, ust); +} + +static int test_double(Display *dpy, Window win, const char *phase, void *Q) +{ +#define COUNT (15*60) + xcb_connection_t *c = XGetXCBConnection(dpy); + Pixmap pixmap; + Window root; + unsigned int width, height; + unsigned border, depth; + int x, y, n, ret; + struct { + uint64_t msc, ust; + } frame[COUNT+1]; + int offset = 0; + + XGetGeometry(dpy, win, + &root, &x, &y, &width, &height, &border, &depth); + + printf("%s: Testing flip double buffering: %dx%d\n", phase, width, height); + _x_error_occurred = 0; + + pixmap = XCreatePixmap(dpy, win, width, height, depth); + flush_flips(dpy, win, pixmap, Q, NULL); + for (n = 0; n <= COUNT; n++) { + int complete; + + xcb_present_pixmap(c, win, pixmap, n, + 0, /* valid */ + 0, /* update */ + 0, /* x_off */ + 0, /* y_off */ + None, + None, /* wait fence */ + None, + XCB_PRESENT_OPTION_NONE, + 0, /* target msc */ + 0, /* divisor */ + 0, /* remainder */ + 0, NULL); + xcb_flush(c); + + complete = 0; + do { + xcb_present_complete_notify_event_t *ce; + xcb_generic_event_t *ev; + + ev = xcb_wait_for_special_event(c, Q); + if (ev == NULL) + break; + + ce = (xcb_present_complete_notify_event_t *)ev; + if (ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP && + ce->serial == n) { + frame[n].msc = ce->msc; + frame[n].ust = ce->ust; + complete = 1; + } + free(ev); + } while (!complete); + } + XFreePixmap(dpy, pixmap); + + XSync(dpy, True); + ret = !!_x_error_occurred; + + if (frame[COUNT].msc - frame[0].msc != COUNT) { + printf("Expected %d frames interval, %d elapsed instead\n", + COUNT, (int)(frame[COUNT].msc - frame[0].msc)); + for (n = 0; n <= COUNT; n++) { + if (frame[n].msc - frame[0].msc != n + offset) { + printf("frame[%d]: msc=%03lld, ust=%lld\n", n, + (long long)(frame[n].msc - frame[0].msc), + (long long)(frame[n].ust - frame[0].ust)); + offset = frame[n].msc - frame[0].msc - n; + ret++; + } + } + } + + return ret; +} + +static int test_future(Display *dpy, Window win, const char *phase, void *Q) +{ + xcb_connection_t *c = XGetXCBConnection(dpy); + Pixmap pixmap; + struct dri3_fence fence; + Window root; + unsigned int width, height; + unsigned border, depth; + int x, y, ret = 0, n; + uint64_t msc, ust; + int complete; + int early = 0, late = 0; + int earliest = 0, latest = 0; + uint64_t interval; + + XGetGeometry(dpy, win, + &root, &x, &y, &width, &height, &border, &depth); + + if (dri3_create_fence(dpy, win, &fence)) + return 0; + + printf("%s: Testing flips into the future: %dx%d\n", phase, width, height); + _x_error_occurred = 0; + + interval = msc_interval(dpy, win, Q); + + pixmap = XCreatePixmap(dpy, win, width, height, depth); + msc = flush_flips(dpy, win, pixmap, Q, &ust); + for (n = 1; n <= 10; n++) + xcb_present_pixmap(c, win, pixmap, + n, /* serial */ + 0, /* valid */ + 0, /* update */ + 0, /* x_off */ + 0, /* y_off */ + None, + None, /* wait fence */ + None, + XCB_PRESENT_OPTION_NONE, + msc + 60 + n*15*60, /* target msc */ + 0, /* divisor */ + 0, /* remainder */ + 0, NULL); + xcb_present_pixmap(c, win, pixmap, + 0xdeadbeef, /* serial */ + 0, /* valid */ + 0, /* update */ + 0, /* x_off */ + 0, /* y_off */ + None, + None, /* wait fence */ + None, + XCB_PRESENT_OPTION_NONE, + msc + 60 + n*15*60, /* target msc */ + 0, /* divisor */ + 0, /* remainder */ + 0, NULL); + xcb_flush(c); + + complete = 0; + do { + xcb_present_complete_notify_event_t *ce; + xcb_generic_event_t *ev; + + ev = xcb_wait_for_special_event(c, Q); + if (ev == NULL) + break; + + ce = (xcb_present_complete_notify_event_t *)ev; + assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP); + + if (ce->serial == 0xdeadbeef) { + int64_t time; + + time = ce->ust - (ust + (60 + 15*60*n) * interval); + if (time < -(int64_t)interval) { + fprintf(stderr, + "\tflips completed too early by %lldms\n", + (long long)(-time / 1000)); + } else if (time > (int64_t)interval) { + fprintf(stderr, + "\tflips completed too late by %lldms\n", + (long long)(time / 1000)); + } + complete = 1; + } else { + int diff = (int64_t)(ce->msc - (15*60*ce->serial + msc + 60)); + if (diff < 0) { + if (-diff > earliest) { + fprintf(stderr, "\tframe %d displayed early by %d frames\n", ce->serial, -diff); + earliest = -diff; + } + early++; + ret++; + } else if (diff > 0) { + if (diff > latest) { + fprintf(stderr, "\tframe %d displayed late by %d frames\n", ce->serial, diff); + latest = diff; + } + late++; + ret++; + } + } + free(ev); + } while (!complete); + + if (early) + printf("\t%d frames shown too early (worst %d)!\n", early, earliest); + if (late) + printf("\t%d frames shown too late (worst %d)!\n", late, latest); + + ret += !!_x_error_occurred; + + return ret; +} + +static int test_exhaustion(Display *dpy, Window win, const char *phase, void *Q) +{ +#define N_VBLANKS 256 /* kernel event queue length: 128 vblanks */ + xcb_connection_t *c = XGetXCBConnection(dpy); + Pixmap pixmap; + struct dri3_fence fence[2]; + Window root; + xcb_xfixes_region_t region; + unsigned int width, height; + unsigned border, depth; + int x, y, ret = 0, n; + uint64_t target, final; + + XGetGeometry(dpy, win, + &root, &x, &y, &width, &height, &border, &depth); + + if (dri3_create_fence(dpy, win, &fence[0]) || + dri3_create_fence(dpy, win, &fence[1])) + return 0; + + printf("%s: Testing flips with long vblank queues: %dx%d\n", phase, width, height); + _x_error_occurred = 0; + + region = xcb_generate_id(c); + xcb_xfixes_create_region(c, region, 0, NULL); + + pixmap = XCreatePixmap(dpy, win, width, height, depth); + xshmfence_reset(fence[0].addr); + xshmfence_reset(fence[1].addr); + target = check_msc(dpy, win, Q, 0, NULL); + for (n = N_VBLANKS; n--; ) + xcb_present_pixmap(c, win, pixmap, 0, + 0, /* valid */ + region, /* update */ + 0, /* x_off */ + 0, /* y_off */ + None, + None, /* wait fence */ + None, + XCB_PRESENT_OPTION_NONE, + target + N_VBLANKS, /* target msc */ + 1, /* divisor */ + 0, /* remainder */ + 0, NULL); + xcb_present_pixmap(c, win, pixmap, 0, + region, /* valid */ + region, /* update */ + 0, /* x_off */ + 0, /* y_off */ + None, + None, /* wait fence */ + fence[0].xid, + XCB_PRESENT_OPTION_NONE, + target, /* target msc */ + 0, /* divisor */ + 0, /* remainder */ + 0, NULL); + for (n = 1; n < N_VBLANKS; n++) + xcb_present_pixmap(c, win, pixmap, 0, + region, /* valid */ + region, /* update */ + 0, /* x_off */ + 0, /* y_off */ + None, + None, /* wait fence */ + None, + XCB_PRESENT_OPTION_NONE, + target + n, /* target msc */ + 0, /* divisor */ + 0, /* remainder */ + 0, NULL); + xcb_present_pixmap(c, win, pixmap, 0, + region, /* valid */ + region, /* update */ + 0, /* x_off */ + 0, /* y_off */ + None, + None, /* wait fence */ + fence[1].xid, + XCB_PRESENT_OPTION_NONE, + target + N_VBLANKS, /* target msc */ + 0, /* divisor */ + 0, /* remainder */ + 0, NULL); + xcb_flush(c); + + ret += !!xshmfence_await(fence[0].addr); + final = check_msc(dpy, win, Q, 0, NULL); + if (final < target) { + printf("\tFirst flip too early, MSC was %llu, expected %llu\n", + (long long)final, (long long)target); + ret++; + } else if (final > target + 1) { + printf("\tFirst flip too late, MSC was %llu, expected %llu\n", + (long long)final, (long long)target); + ret++; + } + + ret += !!xshmfence_await(fence[1].addr); + final = check_msc(dpy, win, Q, 0, NULL); + if (final < target + N_VBLANKS) { + printf("\tLast flip too early, MSC was %llu, expected %llu\n", + (long long)final, (long long)(target + N_VBLANKS)); + ret++; + } else if (final > target + N_VBLANKS + 1) { + printf("\tLast flip too late, MSC was %llu, expected %llu\n", + (long long)final, (long long)(target + N_VBLANKS)); + ret++; + } + + flush_flips(dpy, win, pixmap, Q, NULL); + + XFreePixmap(dpy, pixmap); + xcb_xfixes_destroy_region(c, region); + dri3_fence_free(dpy, &fence[1]); + dri3_fence_free(dpy, &fence[0]); + + XSync(dpy, True); + ret += !!_x_error_occurred; + + return ret; +#undef N_VBLANKS +} + +static int test_accuracy(Display *dpy, Window win, const char *phase, void *Q) +{ +#define N_VBLANKS (60 * 120) /* ~2 minutes */ + xcb_connection_t *c = XGetXCBConnection(dpy); + Pixmap pixmap; + Window root; + unsigned int width, height; + unsigned border, depth; + int x, y, ret = 0, n; + uint64_t target; + int early = 0, late = 0; + int earliest = 0, latest = 0; + int complete; + + XGetGeometry(dpy, win, + &root, &x, &y, &width, &height, &border, &depth); + + printf("%s: Testing flip accuracy: %dx%d\n", phase, width, height); + _x_error_occurred = 0; + + pixmap = XCreatePixmap(dpy, win, width, height, depth); + target = flush_flips(dpy, win, pixmap, Q, NULL); + for (n = 0; n <= N_VBLANKS; n++) + xcb_present_pixmap(c, win, pixmap, + n, /* serial */ + 0, /* valid */ + 0, /* update */ + 0, /* x_off */ + 0, /* y_off */ + None, + None, /* wait fence */ + None, + XCB_PRESENT_OPTION_NONE, + target + 60 + n, /* target msc */ + 0, /* divisor */ + 0, /* remainder */ + 0, NULL); + xcb_present_pixmap(c, win, pixmap, + 0xdeadbeef, /* serial */ + 0, /* valid */ + 0, /* update */ + 0, /* x_off */ + 0, /* y_off */ + None, + None, /* wait fence */ + None, + XCB_PRESENT_OPTION_NONE, + target + 60 + n, /* target msc */ + 0, /* divisor */ + 0, /* remainder */ + 0, NULL); + xcb_flush(c); + + complete = 0; + do { + xcb_present_complete_notify_event_t *ce; + xcb_generic_event_t *ev; + + ev = xcb_wait_for_special_event(c, Q); + if (ev == NULL) + break; + + ce = (xcb_present_complete_notify_event_t *)ev; + assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP); + + if (ce->serial != 0xdeadbeef) { + int diff = (int64_t)(ce->msc - (target + ce->serial + 60)); + if (diff < 0) { + if (-diff > earliest) { + fprintf(stderr, "\tframe %d displayed early by %d frames\n", ce->serial, -diff); + earliest = -diff; + } + early++; + ret++; + } else if (diff > 0) { + if (diff > latest) { + fprintf(stderr, "\tframe %d displayed late by %d frames\n", ce->serial, diff); + latest = diff; + } + late++; + ret++; + } + } else + complete = 1; + free(ev); + } while (!complete); + + if (early) + printf("\t%d frames shown too early (worst %d)!\n", early, earliest); + if (late) + printf("\t%d frames shown too late (worst %d)!\n", late, latest); + + XFreePixmap(dpy, pixmap); + + XSync(dpy, True); + ret += !!_x_error_occurred; + + return ret; +#undef N_VBLANKS +} + +static int test_modulus(Display *dpy, Window win, const char *phase, void *Q) +{ + xcb_connection_t *c = XGetXCBConnection(dpy); + Pixmap pixmap; + Window root; + unsigned int width, height; + unsigned border, depth; + xcb_xfixes_region_t region; + int x, y, ret = 0; + uint64_t target; + int early = 0, late = 0; + int earliest = 0, latest = 0; + int complete; + + XGetGeometry(dpy, win, + &root, &x, &y, &width, &height, &border, &depth); + + printf("%s: Testing flip modulus: %dx%d\n", phase, width, height); + _x_error_occurred = 0; + + region = xcb_generate_id(c); + xcb_xfixes_create_region(c, region, 0, NULL); + + pixmap = XCreatePixmap(dpy, win, width, height, depth); + target = flush_flips(dpy, win, pixmap, Q, NULL); + for (x = 1; x <= 7; x++) { + for (y = 0; y < x; y++) { + xcb_present_pixmap(c, win, pixmap, + y << 16 | x, /* serial */ + region, /* valid */ + region, /* update */ + 0, /* x_off */ + 0, /* y_off */ + None, + None, /* wait fence */ + None, + XCB_PRESENT_OPTION_NONE, + 0, /* target msc */ + x, /* divisor */ + y, /* remainder */ + 0, NULL); + } + } + xcb_present_pixmap(c, win, pixmap, + 0xdeadbeef, /* serial */ + 0, /* valid */ + 0, /* update */ + 0, /* x_off */ + 0, /* y_off */ + None, + None, /* wait fence */ + None, + XCB_PRESENT_OPTION_NONE, + target + 2*x, /* target msc */ + 0, /* divisor */ + 0, /* remainder */ + 0, NULL); + xcb_flush(c); + + complete = 0; + do { + xcb_present_complete_notify_event_t *ce; + xcb_generic_event_t *ev; + + ev = xcb_wait_for_special_event(c, Q); + if (ev == NULL) + break; + + ce = (xcb_present_complete_notify_event_t *)ev; + if (ce->kind != XCB_PRESENT_COMPLETE_KIND_PIXMAP) + break; + + assert(ce->serial); + if (ce->serial != 0xdeadbeef) { + uint64_t msc; + int diff; + + x = ce->serial & 0xffff; + y = ce->serial >> 16; + + msc = target; + msc -= target % x; + msc += y; + if (msc <= target) + msc += x; + + diff = (int64_t)(ce->msc - msc); + if (diff < 0) { + if (-diff > earliest) { + fprintf(stderr, "\tframe (%d, %d) displayed early by %d frames\n", y, x, -diff); + earliest = -diff; + } + early++; + ret++; + } else if (diff > 0) { + if (diff > latest) { + fprintf(stderr, "\tframe (%d, %d) displayed late by %d frames\n", y, x, diff); + latest = diff; + } + late++; + ret++; + } + } else + complete = 1; + free(ev); + } while (!complete); + + if (early) + printf("\t%d frames shown too early (worst %d)!\n", early, earliest); + if (late) + printf("\t%d frames shown too late (worst %d)!\n", late, latest); + + XFreePixmap(dpy, pixmap); + xcb_xfixes_destroy_region(c, region); + + XSync(dpy, True); + ret += !!_x_error_occurred; + + return ret; +} + +static int test_future_msc(Display *dpy, void *Q) +{ + xcb_connection_t *c = XGetXCBConnection(dpy); + Window root = DefaultRootWindow(dpy); + int ret = 0, n; + uint64_t msc, ust; + int complete; + int early = 0, late = 0; + int earliest = 0, latest = 0; + uint64_t interval; + + printf("Testing notifies into the future\n"); + _x_error_occurred = 0; + + interval = msc_interval(dpy, root, Q); + msc = check_msc(dpy, root, Q, 0, &ust); + + for (n = 1; n <= 10; n++) + xcb_present_notify_msc(c, root, n, msc + 60 + n*15*60, 0, 0); + xcb_present_notify_msc(c, root, 0xdeadbeef, msc + 60 + n*15*60, 0, 0); + xcb_flush(c); + + complete = 0; + do { + xcb_present_complete_notify_event_t *ce; + xcb_generic_event_t *ev; + + ev = xcb_wait_for_special_event(c, Q); + if (ev == NULL) + break; + + ce = (xcb_present_complete_notify_event_t *)ev; + assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC); + + if (ce->serial == 0xdeadbeef) { + int64_t time; + + time = ce->ust - (ust + (60 + 15*60*n) * interval); + if (time < -(int64_t)interval) { + fprintf(stderr, + "\tnotifies completed too early by %lldms\n", + (long long)(-time / 1000)); + } else if (time > (int64_t)interval) { + fprintf(stderr, + "\tnotifies completed too late by %lldms\n", + (long long)(time / 1000)); + } + complete = 1; + } else { + int diff = (int64_t)(ce->msc - (15*60*ce->serial + msc + 60)); + if (diff < 0) { + if (-diff > earliest) { + fprintf(stderr, "\tnotify %d early by %d msc\n", ce->serial, -diff); + earliest = -diff; + } + early++; + ret++; + } else if (diff > 0) { + if (diff > latest) { + fprintf(stderr, "\tnotify %d late by %d msc\n", ce->serial, diff); + latest = diff; + } + late++; + ret++; + } + } free(ev); - } while (msc == 0); + } while (!complete); - if (msc < last_msc) { - printf("Invalid MSC: was %llu, now %llu\n", - (long long)last_msc, (long long)msc); - } + if (early) + printf("\t%d notifies too early (worst %d)!\n", early, earliest); + if (late) + printf("\t%d notifies too late (worst %d)!\n", late, latest); - return msc; + XSync(dpy, True); + ret += !!_x_error_occurred; + + return ret; } -static void teardown_msc(Display *dpy, void *q) +static int test_exhaustion_msc(Display *dpy, void *Q) { - xcb_unregister_for_special_event(XGetXCBConnection(dpy), q); +#define N_VBLANKS 256 /* kernel event queue length: 128 vblanks */ + xcb_connection_t *c = XGetXCBConnection(dpy); + Window root = DefaultRootWindow(dpy); + int ret = 0, n, complete; + int earliest = 0, early = 0; + int latest = 0, late = 0; + uint64_t msc; + + printf("Testing notifies with long queues\n"); + _x_error_occurred = 0; + + msc = check_msc(dpy, root, Q, 0, NULL); + for (n = N_VBLANKS; n--; ) + xcb_present_notify_msc(c, root, N_VBLANKS, msc + N_VBLANKS, 0, 0); + for (n = 1; n <= N_VBLANKS ; n++) + xcb_present_notify_msc(c, root, n, msc + n, 0, 0); + xcb_flush(c); + + complete = 2*N_VBLANKS; + do { + xcb_present_complete_notify_event_t *ce; + xcb_generic_event_t *ev; + int diff; + + ev = xcb_wait_for_special_event(c, Q); + if (ev == NULL) + break; + + ce = (xcb_present_complete_notify_event_t *)ev; + assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC); + + diff = (int64_t)(ce->msc - msc - ce->serial); + if (diff < 0) { + if (-diff > earliest) { + fprintf(stderr, "\tnotify %d early by %d msc\n",(int)ce->serial, -diff); + earliest = -diff; + } + early++; + ret++; + } else if (diff > 0) { + if (diff > latest) { + fprintf(stderr, "\tnotify %d late by %d msc\n", (int)ce->serial, diff); + latest = diff; + } + late++; + ret++; + } + free(ev); + } while (--complete); + + if (early) + printf("\t%d notifies too early (worst %d)!\n", early, earliest); + if (late) + printf("\t%d notifies too late (worst %d)!\n", late, latest); + + XSync(dpy, True); + ret += !!_x_error_occurred; + + return ret; +#undef N_VBLANKS } -static int test_whole(Display *dpy) + +static int test_accuracy_msc(Display *dpy, void *Q) { - Pixmap pixmap; - struct dri3_fence fence; - Window root; - unsigned int width, height; - unsigned border, depth; - int x, y, ret = 1; +#define N_VBLANKS (60 * 120) /* ~2 minutes */ + xcb_connection_t *c = XGetXCBConnection(dpy); + Window root = DefaultRootWindow(dpy); + int ret = 0, n; + uint64_t msc; + int early = 0, late = 0; + int earliest = 0, latest = 0; + int complete; - XGetGeometry(dpy, DefaultRootWindow(dpy), - &root, &x, &y, &width, &height, &border, &depth); + printf("Testing notify accuracy\n"); + _x_error_occurred = 0; - if (dri3_create_fence(dpy, root, &fence)) - return 0; + msc = check_msc(dpy, root, Q, 0, NULL); + for (n = 0; n <= N_VBLANKS; n++) + xcb_present_notify_msc(c, root, n, msc + 60 + n, 0, 0); + xcb_present_notify_msc(c, root, 0xdeadbeef, msc + 60 + n, 0, 0); + xcb_flush(c); + + complete = 0; + do { + xcb_present_complete_notify_event_t *ce; + xcb_generic_event_t *ev; + + ev = xcb_wait_for_special_event(c, Q); + if (ev == NULL) + break; + + ce = (xcb_present_complete_notify_event_t *)ev; + assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC); + + if (ce->serial != 0xdeadbeef) { + int diff = (int64_t)(ce->msc - (msc + ce->serial + 60)); + if (diff < 0) { + if (-diff > earliest) { + fprintf(stderr, "\tnotify %d early by %d msc\n", ce->serial, -diff); + earliest = -diff; + } + early++; + ret++; + } else if (diff > 0) { + if (diff > latest) { + fprintf(stderr, "\tnotify %d late by %d msc\n", ce->serial, diff); + latest = diff; + } + late++; + ret++; + } + } else + complete = 1; + free(ev); + } while (!complete); + + if (early) + printf("\t%d notifies too early (worst %d)!\n", early, earliest); + if (late) + printf("\t%d notifies too late (worst %d)!\n", late, latest); - printf("Testing whole screen flip: %dx%d\n", width, height); + XSync(dpy, True); + ret += !!_x_error_occurred; + + return ret; +#undef N_VBLANKS +} + +static int test_modulus_msc(Display *dpy, void *Q) +{ + xcb_connection_t *c = XGetXCBConnection(dpy); + Window root = DefaultRootWindow(dpy); + xcb_present_complete_notify_event_t *ce; + xcb_generic_event_t *ev; + int x, y, ret = 0; + uint64_t target; + int early = 0, late = 0; + int earliest = 0, latest = 0; + int complete; + + printf("Testing notify modulus\n"); _x_error_occurred = 0; - xshmfence_reset(fence.addr); + target = wait_vblank(dpy, root, Q); - pixmap = XCreatePixmap(dpy, root, width, height, depth); - xcb_present_pixmap(XGetXCBConnection(dpy), - root, pixmap, - 0, /* sbc */ - 0, /* valid */ - 0, /* update */ - 0, /* x_off */ - 0, /* y_off */ - None, - None, /* wait fence */ - fence.xid, - XCB_PRESENT_OPTION_NONE, - 0, /* target msc */ - 0, /* divisor */ - 0, /* remainder */ - 0, NULL); - XFreePixmap(dpy, pixmap); + xcb_present_notify_msc(c, root, 0, 0, 0, 0); + for (x = 1; x <= 19; x++) { + for (y = 0; y < x; y++) + xcb_present_notify_msc(c, root, y << 16 | x, 0, x, y); + } + xcb_present_notify_msc(c, root, 0xdeadbeef, target + 2*x, 0, 0); + xcb_flush(c); - pixmap = XCreatePixmap(dpy, root, width, height, depth); - xcb_present_pixmap(XGetXCBConnection(dpy), - root, pixmap, - 0, /* sbc */ - 0, /* valid */ - 0, /* update */ - 0, /* x_off */ - 0, /* y_off */ - None, - None, /* wait fence */ - None, /* sync fence */ - XCB_PRESENT_OPTION_NONE, - 0, /* target msc */ - 0, /* divisor */ - 0, /* remainder */ - 0, NULL); - XFreePixmap(dpy, pixmap); - XFlush(dpy); + ev = xcb_wait_for_special_event(c, Q); + if (ev) { + ce = (xcb_present_complete_notify_event_t *)ev; + assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC); + assert(ce->serial == 0); + assert(target == ce->msc); + target = ce->msc; + } - ret = !!xshmfence_await(fence.addr); - dri3_fence_free(dpy, &fence); + complete = 0; + do { + ev = xcb_wait_for_special_event(c, Q); + if (ev == NULL) + break; + + ce = (xcb_present_complete_notify_event_t *)ev; + assert(ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC); + + assert(ce->serial); + if (ce->serial != 0xdeadbeef) { + uint64_t msc; + int diff; + + x = ce->serial & 0xffff; + y = ce->serial >> 16; + + msc = target; + msc -= target % x; + msc += y; + if (msc <= target) + msc += x; + + diff = (int64_t)(ce->msc - msc); + if (diff < 0) { + if (-diff > earliest) { + fprintf(stderr, "\tnotify (%d, %d) early by %d msc (target %lld, reported %lld)\n", y, x, -diff, (long long)msc, (long long)ce->msc); + earliest = -diff; + } + early++; + ret++; + } else if (diff > 0) { + if (diff > latest) { + fprintf(stderr, "\tnotify (%d, %d) late by %d msc (target %lld, reported %lld)\n", y, x, diff, (long long)msc, (long long)ce->msc); + latest = diff; + } + late++; + ret++; + } + } else + complete = 1; + free(ev); + } while (!complete); + + if (early) + printf("\t%d notifies too early (worst %d)!\n", early, earliest); + if (late) + printf("\t%d notifies too late (worst %d)!\n", late, latest); XSync(dpy, True); ret += !!_x_error_occurred; @@ -279,8 +1247,6 @@ static int for_each_crtc(Display *dpy, for (i = 0; i < res->ncrtc; i++) original_crtc[i] = XRRGetCrtcInfo(dpy, res, res->crtcs[i]); - printf("noutput=%d, ncrtc=%d\n", res->noutput, res->ncrtc); - for (i = 0; i < res->noutput; i++) { XRROutputInfo *output; XRRModeInfo *mode; @@ -322,7 +1288,7 @@ static int for_each_crtc(Display *dpy, free(original_crtc); XRRFreeScreenResources(res); - return j; + return err; } struct test_crtc { @@ -335,6 +1301,7 @@ struct test_crtc { uint64_t msc; }; #define SYNC 0x1 +#define FUTURE 0x2 static int __test_crtc(Display *dpy, RRCrtc crtc, int width, int height, @@ -344,7 +1311,7 @@ static int __test_crtc(Display *dpy, RRCrtc crtc, Pixmap pixmap; int err = 0; - test->msc = check_msc(dpy, test->win, test->queue, test->msc); + test->msc = check_msc(dpy, test->win, test->queue, test->msc, NULL); if (test->flags & SYNC) xshmfence_reset(test->fence.addr); @@ -361,16 +1328,14 @@ static int __test_crtc(Display *dpy, RRCrtc crtc, None, /* wait fence */ test->flags & SYNC ? test->fence.xid : None, XCB_PRESENT_OPTION_NONE, - 0, /* target msc */ + test->msc, /* target msc */ 1, /* divisor */ 0, /* remainder */ 0, NULL); - XFreePixmap(dpy, pixmap); - if (test->flags & SYNC) { - pixmap = XCreatePixmap(dpy, test->win, width, height, test->depth); + Pixmap tmp = XCreatePixmap(dpy, test->win, width, height, test->depth); xcb_present_pixmap(XGetXCBConnection(dpy), - test->win, pixmap, + test->win, tmp, 1, /* sbc */ 0, /* valid */ 0, /* update */ @@ -380,16 +1345,17 @@ static int __test_crtc(Display *dpy, RRCrtc crtc, None, /* wait fence */ None, /* sync fence */ XCB_PRESENT_OPTION_NONE, - 1, /* target msc */ + test->msc + (test->flags & FUTURE ? 5 * 16 : 1), /* target msc */ 1, /* divisor */ 0, /* remainder */ 0, NULL); - XFreePixmap(dpy, pixmap); + XFreePixmap(dpy, tmp); XFlush(dpy); err += !!xshmfence_await(test->fence.addr); } + XFreePixmap(dpy, pixmap); - test->msc = check_msc(dpy, test->win, test->queue, test->msc); + test->msc = check_msc(dpy, test->win, test->queue, test->msc, NULL); return err; } @@ -410,15 +1376,23 @@ static int test_crtc(Display *dpy, void *queue, uint64_t last_msc) printf("Testing each crtc, without waiting for each flip\n"); test.flags = 0; + test.msc = check_msc(dpy, test.win, test.queue, test.msc, NULL); err += for_each_crtc(dpy, __test_crtc, &test); + test.msc = check_msc(dpy, test.win, test.queue, test.msc, NULL); printf("Testing each crtc, waiting for flips to complete\n"); test.flags = SYNC; + test.msc = check_msc(dpy, test.win, test.queue, test.msc, NULL); err += for_each_crtc(dpy, __test_crtc, &test); + test.msc = check_msc(dpy, test.win, test.queue, test.msc, NULL); - test.msc = check_msc(dpy, test.win, test.queue, test.msc); - dri3_fence_free(dpy, &test.fence); + printf("Testing each crtc, with future flips\n"); + test.flags = FUTURE | SYNC; + test.msc = check_msc(dpy, test.win, test.queue, test.msc, NULL); + err += for_each_crtc(dpy, __test_crtc, &test); + test.msc = check_msc(dpy, test.win, test.queue, test.msc, NULL); + dri3_fence_free(dpy, &test.fence); XSync(dpy, True); err += !!_x_error_occurred; @@ -536,6 +1510,31 @@ static int gem_set_caching(int fd, uint32_t handle, int caching) return drmIoctl(fd, LOCAL_IOCTL_I915_GEM_SET_CACHING, &arg) == 0; } +static int gem_set_tiling(int fd, uint32_t handle, int tiling, int stride) +{ + struct drm_i915_gem_set_tiling set_tiling; + int err; + +restart: + set_tiling.handle = handle; + set_tiling.tiling_mode = tiling; + set_tiling.stride = stride; + + if (drmIoctl(fd, DRM_IOCTL_I915_GEM_SET_TILING, &set_tiling) == 0) + return 1; + + err = errno; + if (err == EINTR) + goto restart; + + if (err == EAGAIN) { + sched_yield(); + goto restart; + } + + return 0; +} + static int gem_export(int fd, uint32_t handle) { struct drm_prime_handle args; @@ -557,6 +1556,126 @@ static void gem_close(int fd, uint32_t handle) (void)drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &close); } +static int test_dri3_tiling(Display *dpy) +{ + Window win = DefaultRootWindow(dpy); + const int tiling[] = { I915_TILING_NONE, I915_TILING_X, I915_TILING_Y }; + Window root; + unsigned int width, height; + unsigned border, depth, bpp; + unsigned stride, size; + void *Q; + int x, y; + int device; + int line = -1; + int t; + + device = dri3_open(dpy); + if (device < 0) + return 0; + + if (!is_intel(device)) + return 0; + + printf("Opened Intel DRI3 device\n"); + + XGetGeometry(dpy, win, &root, &x, &y, + &width, &height, &border, &depth); + + switch (depth) { + case 8: bpp = 8; break; + case 15: case 16: bpp = 16; break; + case 24: case 32: bpp = 32; break; + default: return 0; + } + + stride = ALIGN(width * bpp/8, 512); + size = PAGE_ALIGN(stride * ALIGN(height, 32)); + printf("Creating DRI3 %dx%d (source stride=%d, size=%d) for GTT\n", + width, height, stride, size); + + _x_error_occurred = 0; + Q = setup_msc(dpy, root); + + for (t = 0; t < sizeof(tiling)/sizeof(tiling[0]); t++) { + uint64_t msc; + uint32_t src; + int src_fd; + Pixmap src_pix; + + src = gem_create(device, size); + if (!src) { + line = __LINE__; + goto fail; + } + + gem_set_tiling(device, src, tiling[t], stride); + + src_fd = gem_export(device, src); + if (src_fd < 0) { + line = __LINE__; + goto fail; + } + + src_pix = dri3_create_pixmap(dpy, root, + width, height, depth, + src_fd, bpp, stride, size); + + msc = wait_vblank(dpy, root, Q); + + xcb_present_pixmap(XGetXCBConnection(dpy), + win, src_pix, + 0, /* sbc */ + 0, /* valid */ + 0, /* update */ + 0, /* x_off */ + 0, /* y_off */ + None, + None, /* wait fence */ + None, + XCB_PRESENT_OPTION_NONE, + msc + 2, /* target msc */ + 1, /* divisor */ + 0, /* remainder */ + 0, NULL); + + xcb_present_pixmap(XGetXCBConnection(dpy), + win, src_pix, + 0, /* sbc */ + 0, /* valid */ + 0, /* update */ + 0, /* x_off */ + 0, /* y_off */ + None, + None, /* wait fence */ + None, + XCB_PRESENT_OPTION_NONE, + msc + 3, /* target msc */ + 1, /* divisor */ + 0, /* remainder */ + 0, NULL); + + XSync(dpy, True); + if (_x_error_occurred) { + line = __LINE__; + goto fail; + } + XFreePixmap(dpy, src_pix); + _x_error_occurred = 0; + + close(src_fd); + gem_close(device, src); + } + + teardown_msc(dpy, Q); + return 0; + +fail: + printf("%s failed with tiling %d, line %d\n", __func__, tiling[t], line); + teardown_msc(dpy, Q); + return 1; +} + static int test_dri3(Display *dpy) { Window win = DefaultRootWindow(dpy); @@ -670,8 +1789,32 @@ fail: static int has_present(Display *dpy) { xcb_connection_t *c = XGetXCBConnection(dpy); - xcb_present_query_version_reply_t *reply; xcb_generic_error_t *error = NULL; + void *reply; + + reply = xcb_xfixes_query_version_reply(c, + xcb_xfixes_query_version(c, + XCB_XFIXES_MAJOR_VERSION, + XCB_XFIXES_MINOR_VERSION), + &error); + free(reply); + free(error); + if (reply == NULL) { + fprintf(stderr, "XFixes not supported on %s\n", DisplayString(dpy)); + return 0; + } + + reply = xcb_dri3_query_version_reply(c, + xcb_dri3_query_version(c, + XCB_DRI3_MAJOR_VERSION, + XCB_DRI3_MINOR_VERSION), + &error); + free(reply); + free(error); + if (reply == NULL) { + fprintf(stderr, "DRI3 not supported on %s\n", DisplayString(dpy)); + return 0; + } reply = xcb_present_query_version_reply(c, xcb_present_query_version(c, @@ -681,14 +1824,32 @@ static int has_present(Display *dpy) free(reply); free(error); + if (reply == NULL) { + fprintf(stderr, "Present not supported on %s\n", DisplayString(dpy)); + return 0; + } + + return 1; +} - return reply != NULL; +static int has_composite(Display *dpy) +{ + int event, error; + int major, minor; + + if (!XCompositeQueryExtension(dpy, &event, &error)) + return 0; + + XCompositeQueryVersion(dpy, &major, &minor); + + return major > 0 || minor >= 4; } int main(void) { Display *dpy; Window root; + int dummy; int error = 0; uint64_t last_msc; void *queue; @@ -700,27 +1861,132 @@ int main(void) if (!has_present(dpy)) return 77; + if (DPMSQueryExtension(dpy, &dummy, &dummy)) + DPMSDisable(dpy); + root = DefaultRootWindow(dpy); signal(SIGALRM, SIG_IGN); XSetErrorHandler(_check_error_handler); queue = setup_msc(dpy, root); - last_msc = check_msc(dpy, root, queue, 0); + last_msc = check_msc(dpy, root, queue, 0, NULL); + + error += test_future_msc(dpy, queue); + last_msc = check_msc(dpy, root, queue, last_msc, NULL); + + error += test_accuracy_msc(dpy, queue); + last_msc = check_msc(dpy, root, queue, last_msc, NULL); + + error += test_modulus_msc(dpy, queue); + last_msc = check_msc(dpy, root, queue, last_msc, NULL); + + error += test_exhaustion_msc(dpy, queue); + last_msc = check_msc(dpy, root, queue, last_msc, NULL); + + for (dummy = 0; dummy <= 3; dummy++) { + Window win; + uint64_t msc = 0; + XSetWindowAttributes attr; + Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy)); + unsigned int width, height; + unsigned border, depth; + const char *phase; + int x, y; + void *Q; + + attr.override_redirect = 1; + + XGetGeometry(dpy, root, &win, &x, &y, + &width, &height, &border, &depth); + + _x_error_occurred = 0; + switch (dummy) { + case 0: + win = root; + phase = "root"; + break; + case 1: + win = XCreateWindow(dpy, root, + 0, 0, width, height, 0, depth, + InputOutput, visual, + CWOverrideRedirect, &attr); + phase = "fullscreen"; + break; + case 2: + win = XCreateWindow(dpy, root, + 0, 0, width/2, height/2, 0, depth, + InputOutput, visual, + CWOverrideRedirect, &attr); + phase = "window"; + break; + case 3: + if (!has_composite(dpy)) + continue; + + win = XCreateWindow(dpy, root, + 0, 0, width, height, 0, + DefaultDepth(dpy, DefaultScreen(dpy)), + InputOutput, + DefaultVisual(dpy, DefaultScreen(dpy)), + CWOverrideRedirect, &attr); + XCompositeRedirectWindow(dpy, win, CompositeRedirectManual); + phase = "composite"; + break; + + default: + phase = "broken"; + win = root; + abort(); + break; + } + + XMapWindow(dpy, win); + XSync(dpy, True); + if (_x_error_occurred) + continue; + + Q = setup_msc(dpy, win); + msc = check_msc(dpy, win, Q, msc, NULL); - error += test_whole(dpy); - last_msc = check_msc(dpy, root, queue, last_msc); + error += test_whole(dpy, win, phase); + msc = check_msc(dpy, win, Q, msc, NULL); + + error += test_double(dpy, win, phase, Q); + msc = check_msc(dpy, win, Q, msc, NULL); + + error += test_future(dpy, win, phase, Q); + msc = check_msc(dpy, win, Q, msc, NULL); + + error += test_accuracy(dpy, win, phase, Q); + msc = check_msc(dpy, win, Q, msc, NULL); + + error += test_modulus(dpy, win, phase, Q); + msc = check_msc(dpy, win, Q, msc, NULL); + + error += test_exhaustion(dpy, win, phase, Q); + msc = check_msc(dpy, win, Q, msc, NULL); + + teardown_msc(dpy, Q); + if (win != root) + XDestroyWindow(dpy, win); + } error += test_crtc(dpy, queue, last_msc); - last_msc = check_msc(dpy, root, queue, last_msc); + last_msc = check_msc(dpy, root, queue, last_msc, NULL); error += test_shm(dpy); - last_msc = check_msc(dpy, root, queue, last_msc); + last_msc = check_msc(dpy, root, queue, last_msc, NULL); error += test_dri3(dpy); - last_msc = check_msc(dpy, root, queue, last_msc); + last_msc = check_msc(dpy, root, queue, last_msc, NULL); + + error += test_dri3_tiling(dpy); + last_msc = check_msc(dpy, root, queue, last_msc, NULL); teardown_msc(dpy, queue); + if (DPMSQueryExtension(dpy, &dummy, &dummy)) + DPMSEnable(dpy); return !!error; } diff --git a/test/render-glyphs.c b/test/render-glyphs.c new file mode 100644 index 0000000..8822e36 --- /dev/null +++ b/test/render-glyphs.c @@ -0,0 +1,441 @@ +#include +#include +#include +#include +#include +#include + +#include /* for XDestroyImage */ +#include /* for pixman blt functions */ + +#include "test.h" + +static const XRenderColor colors[] = { + /* red, green, blue, alpha */ + { 0 }, + { 0, 0, 0, 0xffff }, + { 0xffff, 0, 0, 0xffff }, + { 0, 0xffff, 0, 0xffff }, + { 0, 0, 0xffff, 0xffff }, + { 0xffff, 0xffff, 0xffff, 0xffff }, +}; + +static struct clip { + void *func; +} clips[] = { + { NULL }, +}; + +static int _x_error_occurred; + +static int +_check_error_handler(Display *display, + XErrorEvent *event) +{ + _x_error_occurred = 1; + return False; /* ignored */ +} + +static void clear(struct test_display *dpy, + struct test_target *tt, + const XRenderColor *c) +{ + XRenderFillRectangle(dpy->dpy, PictOpClear, tt->picture, c, + 0, 0, tt->width, tt->height); +} + +static bool check_op(struct test_display *dpy, int op, struct test_target *tt) +{ + XRenderColor render_color = {0}; + + XSync(dpy->dpy, True); + _x_error_occurred = 0; + + XRenderFillRectangle(dpy->dpy, op, + tt->picture, &render_color, + 0, 0, 0, 0); + + XSync(dpy->dpy, True); + return _x_error_occurred == 0; +} + +struct glyph_iter { + enum { + GLYPHS, OP, DST, SRC, MASK, CLIP, + } stage; + + int glyph_format; + int op; + int dst_color; + int src_color; + int mask_format; + int clip; + + struct { + struct test_display *dpy; + struct test_target tt; + GlyphSet glyphset; + Picture src; + XRenderPictFormat *mask_format; + } ref, out; +}; + +static void glyph_iter_init(struct glyph_iter *gi, + struct test *t, enum target target) +{ + memset(gi, 0, sizeof(*gi)); + + gi->out.dpy = &t->out; + test_target_create_render(&t->out, target, &gi->out.tt); + + gi->ref.dpy = &t->ref; + test_target_create_render(&t->ref, target, &gi->ref.tt); + + gi->stage = GLYPHS; + gi->glyph_format = -1; + gi->op = -1; + gi->dst_color = -1; + gi->src_color = -1; + gi->mask_format = -1; + gi->clip = -1; +} + +static void render_clear(char *image, int image_size, int bpp) +{ + memset(image, 0, image_size); +} + +static void render_black(char *image, int image_size, int bpp) +{ + if (bpp == 4) { + uint32_t *p = (uint32_t *)image; + image_size /= 4; + while (image_size--) + *p++ = 0x000000ff; + } else + memset(image, 0x55, image_size); +} + +static void render_green(char *image, int image_size, int bpp) +{ + if (bpp == 4) { + uint32_t *p = (uint32_t *)image; + image_size /= 4; + while (image_size--) + *p++ = 0xffff0000; + } else + memset(image, 0xaa, image_size); +} + +static void render_white(char *image, int image_size, int bpp) +{ + memset(image, 0xff, image_size); +} + +static GlyphSet create_glyphs(Display *dpy, int format_id) +{ +#define N_GLYPHS 4 + XRenderPictFormat *format; + XGlyphInfo glyph = { 8, 8, 0, 0, 8, 0 }; + char image[4*8*8]; + GlyphSet glyphset; + Glyph gid; + int image_size; + int bpp; + int n; + + format = XRenderFindStandardFormat(dpy, format_id); + if (format == NULL) + return 0; + + switch (format_id) { + case PictStandardARGB32: + case PictStandardRGB24: + image_size = 4 * 8 * 8; + bpp = 4; + break; + case PictStandardA8: + case PictStandardA4: + image_size = 8 * 8; + bpp = 1; + break; + case PictStandardA1: + image_size = 8; + bpp = 0; + break; + default: + return 0; + } + + glyphset = XRenderCreateGlyphSet(dpy, format); + for (n = 0; n < N_GLYPHS; n++) { + gid = n; + + switch (n) { + case 0: render_clear(image, image_size, bpp); break; + case 1: render_black(image, image_size, bpp); break; + case 2: render_green(image, image_size, bpp); break; + case 3: render_white(image, image_size, bpp); break; + } + + XRenderAddGlyphs(dpy, glyphset, + &gid, &glyph, 1, image, image_size); + } + + return glyphset; +} + +static const char *glyph_name(int n) +{ + switch (n) { + case 0: return "clear"; + case 1: return "black"; + case 2: return "green"; + case 3: return "white"; + default: return "unknown"; + } +} + +static bool glyph_iter_next(struct glyph_iter *gi) +{ +restart: + if (gi->stage == GLYPHS) { + if (++gi->glyph_format == PictStandardNUM) + return false; + + if (gi->out.glyphset) + XRenderFreeGlyphSet(gi->out.dpy->dpy, + gi->out.glyphset); + gi->out.glyphset = create_glyphs(gi->out.dpy->dpy, + gi->glyph_format); + + if (gi->ref.glyphset) + XRenderFreeGlyphSet(gi->ref.dpy->dpy, + gi->ref.glyphset); + gi->ref.glyphset = create_glyphs(gi->ref.dpy->dpy, + gi->glyph_format); + + gi->stage++; + } + + if (gi->stage == OP) { + do { + if (++gi->op == 255) + goto reset_op; + } while (!check_op(gi->out.dpy, gi->op, &gi->out.tt) || + !check_op(gi->ref.dpy, gi->op, &gi->ref.tt)); + + gi->stage++; + } + + if (gi->stage == DST) { + if (++gi->dst_color == ARRAY_SIZE(colors)) + goto reset_dst; + + gi->stage++; + } + + if (gi->stage == SRC) { + if (++gi->src_color == ARRAY_SIZE(colors)) + goto reset_src; + + if (gi->ref.src) + XRenderFreePicture(gi->ref.dpy->dpy, gi->ref.src); + gi->ref.src = XRenderCreateSolidFill(gi->ref.dpy->dpy, + &colors[gi->src_color]); + + if (gi->out.src) + XRenderFreePicture(gi->out.dpy->dpy, gi->out.src); + gi->out.src = XRenderCreateSolidFill(gi->out.dpy->dpy, + &colors[gi->src_color]); + + gi->stage++; + } + + if (gi->stage == MASK) { + if (++gi->mask_format > PictStandardNUM) + goto reset_mask; + + if (gi->mask_format == PictStandardRGB24) + gi->mask_format++; + + if (gi->mask_format < PictStandardNUM) { + gi->out.mask_format = XRenderFindStandardFormat(gi->out.dpy->dpy, + gi->mask_format); + gi->ref.mask_format = XRenderFindStandardFormat(gi->ref.dpy->dpy, + gi->mask_format); + } else { + gi->out.mask_format = NULL; + gi->ref.mask_format = NULL; + } + + gi->stage++; + } + + if (gi->stage == CLIP) { + if (++gi->clip == ARRAY_SIZE(clips)) + goto reset_clip; + + gi->stage++; + } + + gi->stage--; + return true; + +reset_op: + gi->op = -1; +reset_dst: + gi->dst_color = -1; +reset_src: + gi->src_color = -1; +reset_mask: + gi->mask_format = -1; +reset_clip: + gi->clip = -1; + gi->stage--; + goto restart; +} + +static void glyph_iter_fini(struct glyph_iter *gi) +{ + if (gi->out.glyphset) + XRenderFreeGlyphSet (gi->out.dpy->dpy, gi->out.glyphset); + if (gi->ref.glyphset) + XRenderFreeGlyphSet (gi->ref.dpy->dpy, gi->ref.glyphset); + + test_target_destroy_render(gi->out.dpy, &gi->out.tt); + test_target_destroy_render(gi->ref.dpy, &gi->ref.tt); +} + +static const char *stdformat_to_str(int id) +{ + switch (id) { + case PictStandardARGB32: return "ARGB32"; + case PictStandardRGB24: return "RGB24"; + case PictStandardA8: return "A8"; + case PictStandardA4: return "A4"; + case PictStandardA1: return "A1"; + default: return "none"; + } +} + +static char *glyph_iter_to_string(struct glyph_iter *gi, + const char *format, + ...) +{ + static char buf[100]; + va_list ap; + int len; + + len = sprintf(buf, "glyphs=%s, op=%d, dst=%08x, src=%08x, mask=%s", + stdformat_to_str(gi->glyph_format), gi->op, + xrender_color(&colors[gi->dst_color]), + xrender_color(&colors[gi->src_color]), + stdformat_to_str(gi->mask_format)); + + if (format) { + buf[len++] = ' '; + va_start(ap, format); + vsprintf(buf+len, format, ap); + va_end(ap); + } + + return buf; +} + +static void single(struct test *t, enum target target) +{ + struct glyph_iter gi; + int n; + + printf("Testing single glyph (%s): ", test_target_name(target)); + fflush(stdout); + + glyph_iter_init(&gi, t, target); + while (glyph_iter_next(&gi)) { + XGlyphElt8 elt; + char id[N_GLYPHS]; + + for (n = 0; n < N_GLYPHS; n++) { + id[n] = n; + + elt.chars = &id[n]; + elt.nchars = 1; + elt.xOff = 0; + elt.yOff = 0; + + clear(gi.out.dpy, &gi.out.tt, &colors[gi.dst_color]); + elt.glyphset = gi.out.glyphset; + XRenderCompositeText8 (gi.out.dpy->dpy, gi.op, + gi.out.src, + gi.out.tt.picture, + gi.out.mask_format, + 0, 0, + 0, 8, + &elt, 1); + + clear(gi.ref.dpy, &gi.ref.tt, &colors[gi.dst_color]); + elt.glyphset = gi.ref.glyphset; + XRenderCompositeText8 (gi.ref.dpy->dpy, gi.op, + gi.ref.src, + gi.ref.tt.picture, + gi.ref.mask_format, + 0, 0, + 0, 8, + &elt, 1); + test_compare(t, + gi.out.tt.draw, gi.out.tt.format, + gi.ref.tt.draw, gi.ref.tt.format, + 0, 0, gi.out.tt.width, gi.out.tt.height, + glyph_iter_to_string(&gi, + "glyph=%s", + glyph_name(n))); + } + + elt.chars = &id[0]; + elt.nchars = n; + clear(gi.out.dpy, &gi.out.tt, &colors[gi.dst_color]); + elt.glyphset = gi.out.glyphset; + XRenderCompositeText8 (gi.out.dpy->dpy, gi.op, + gi.out.src, + gi.out.tt.picture, + gi.out.mask_format, + 0, 0, + 0, 8, + &elt, 1); + + clear(gi.ref.dpy, &gi.ref.tt, &colors[gi.dst_color]); + elt.glyphset = gi.ref.glyphset; + XRenderCompositeText8 (gi.ref.dpy->dpy, gi.op, + gi.ref.src, + gi.ref.tt.picture, + gi.ref.mask_format, + 0, 0, + 0, 8, + &elt, 1); + test_compare(t, + gi.out.tt.draw, gi.out.tt.format, + gi.ref.tt.draw, gi.ref.tt.format, + 0, 0, gi.out.tt.width, gi.out.tt.height, + glyph_iter_to_string(&gi, "all")); + } + glyph_iter_fini(&gi); +} + +int main(int argc, char **argv) +{ + struct test test; + int t; + + test_init(&test, argc, argv); + XSetErrorHandler(_check_error_handler); + + for (t = TARGET_FIRST; t <= TARGET_LAST; t++) { + single(&test, t); + //overlapping(&test, t); + //gap(&test, t); + //mixed(&test, t); + } + + return 0; +} diff --git a/test/render-trapezoid.c b/test/render-trapezoid.c index cd99014..f15a78e 100644 --- a/test/render-trapezoid.c +++ b/test/render-trapezoid.c @@ -403,16 +403,141 @@ static void trap_tests(struct test *t, free(traps); } +enum edge { + EDGE_SHARP = PolyEdgeSharp, + EDGE_SMOOTH, +}; + +static const char *edge_name(enum edge edge) +{ + switch (edge) { + default: + case EDGE_SHARP: return "sharp"; + case EDGE_SMOOTH: return "smooth"; + } +} + +static void set_edge(Display *dpy, Picture p, enum edge edge) +{ + XRenderPictureAttributes a; + + a.poly_edge = edge; + XRenderChangePicture(dpy, p, CPPolyEdge, &a); +} + +static void edge_test(struct test *t, + enum mask mask, + enum edge edge, + enum target target) +{ + struct test_target out, ref; + XRenderColor white = { 0xffff, 0xffff, 0xffff, 0xffff }; + Picture src_ref, src_out; + XTrapezoid trap; + int left_or_right, p; + + test_target_create_render(&t->out, target, &out); + set_edge(t->out.dpy, out.picture, edge); + src_out = XRenderCreateSolidFill(t->out.dpy, &white); + + test_target_create_render(&t->ref, target, &ref); + set_edge(t->ref.dpy, ref.picture, edge); + src_ref = XRenderCreateSolidFill(t->ref.dpy, &white); + + printf("Testing edges (with mask %s and %s edges) (%s): ", + mask_name(mask), + edge_name(edge), + test_target_name(target)); + fflush(stdout); + + for (left_or_right = 0; left_or_right <= 1; left_or_right++) { + for (p = -64; p <= out.width + 64; p++) { + char buf[80]; + + if (left_or_right) { + trap.left.p1.x = 0; + trap.left.p1.y = 0; + trap.left.p2.x = 0; + trap.left.p2.y = out.height << 16; + + trap.right.p1.x = p << 16; + trap.right.p1.y = 0; + trap.right.p2.x = out.width << 16; + trap.right.p2.y = out.height << 16; + } else { + trap.right.p1.x = out.width << 16; + trap.right.p1.y = 0; + trap.right.p2.x = out.width << 16; + trap.right.p2.y = out.height << 16; + + trap.left.p1.x = 0; + trap.left.p1.y = 0; + trap.left.p2.x = p << 16; + trap.left.p2.y = out.height << 16; + } + + trap.top = 0; + trap.bottom = out.height << 16; + + sprintf(buf, + "trap=((%d, %d), (%d, %d)), ((%d, %d), (%d, %d))\n", + trap.left.p1.x >> 16, trap.left.p1.y >> 16, + trap.left.p2.x >> 16, trap.left.p2.y >> 16, + trap.right.p1.x >> 16, trap.right.p1.y >> 16, + trap.right.p2.x >> 16, trap.right.p2.y >> 16); + + clear(&t->out, &out); + XRenderCompositeTrapezoids(t->out.dpy, + PictOpSrc, + src_out, + out.picture, + mask_format(t->out.dpy, mask), + 0, 0, + &trap, 1); + + clear(&t->ref, &ref); + XRenderCompositeTrapezoids(t->ref.dpy, + PictOpSrc, + src_ref, + ref.picture, + mask_format(t->ref.dpy, mask), + 0, 0, + &trap, 1); + + test_compare(t, + out.draw, out.format, + ref.draw, ref.format, + 0, 0, out.width, out.height, + buf); + } + } + + XRenderFreePicture(t->out.dpy, src_out); + test_target_destroy_render(&t->out, &out); + + XRenderFreePicture(t->ref.dpy, src_ref); + test_target_destroy_render(&t->ref, &ref); + + printf("pass\n"); +} + int main(int argc, char **argv) { struct test test; int i, dx, dy; enum target target; enum mask mask; + enum edge edge; enum trapezoid trapezoid; test_init(&test, argc, argv); + for (target = TARGET_FIRST; target <= TARGET_LAST; target++) { + for (mask = MASK_NONE; mask <= MASK_A8; mask++) + for (edge = EDGE_SHARP; edge <= EDGE_SMOOTH; edge++) + edge_test(&test, mask, edge, target); + } + for (i = 0; i <= DEFAULT_ITERATIONS; i++) { int reps = REPS(i), sets = SETS(i); diff --git a/test/render-triangle.c b/test/render-triangle.c new file mode 100644 index 0000000..165834c --- /dev/null +++ b/test/render-triangle.c @@ -0,0 +1,180 @@ +#include +#include +#include + +#include "test.h" + +enum edge { + EDGE_SHARP = PolyEdgeSharp, + EDGE_SMOOTH, +}; + +static void set_edge(Display *dpy, Picture p, enum edge edge) +{ + XRenderPictureAttributes a; + + a.poly_edge = edge; + XRenderChangePicture(dpy, p, CPPolyEdge, &a); +} + +static XRenderPictFormat *mask_format(Display *dpy, enum mask mask) +{ + switch (mask) { + default: + case MASK_NONE: return NULL; + case MASK_A1: return XRenderFindStandardFormat(dpy, PictStandardA1); + case MASK_A8: return XRenderFindStandardFormat(dpy, PictStandardA8); + } +} + +static const char *mask_name(enum mask mask) +{ + switch (mask) { + default: + case MASK_NONE: return "none"; + case MASK_A1: return "a1"; + case MASK_A8: return "a8"; + } +} + +static const char *edge_name(enum edge edge) +{ + switch (edge) { + default: + case EDGE_SHARP: return "sharp"; + case EDGE_SMOOTH: return "smooth"; + } +} + +static void clear(struct test_display *dpy, struct test_target *tt) +{ + XRenderColor render_color = {0}; + XRenderFillRectangle(dpy->dpy, PictOpClear, tt->picture, &render_color, + 0, 0, tt->width, tt->height); +} + +static void step_to_point(int step, int width, int height, XPointFixed *p) +{ + do { + p->x = (step - 64) << 16; + p->y = -64 << 16; + + step -= width - 128; + if (step <= 0) + return; + + p->x = (width + 64) << 16; + p->y = (step - 64) << 16; + step -= height - 128; + + if (step <= 0) + return; + + p->x = (width + 64 - step) << 16; + p->y = (height + 64) << 16; + step -= width - 128; + + if (step <= 0) + return; + + p->x = -64 << 16; + p->y = (height + 64 - step) << 16; + step -= height - 128; + } while (step > 0); +} + +static void edge_test(struct test *t, + enum mask mask, + enum edge edge, + enum target target) +{ + struct test_target out, ref; + XRenderColor white = { 0xffff, 0xffff, 0xffff, 0xffff }; + Picture src_ref, src_out; + XTriangle tri; + unsigned step, max; + + test_target_create_render(&t->out, target, &out); + set_edge(t->out.dpy, out.picture, edge); + src_out = XRenderCreateSolidFill(t->out.dpy, &white); + + test_target_create_render(&t->ref, target, &ref); + set_edge(t->ref.dpy, ref.picture, edge); + src_ref = XRenderCreateSolidFill(t->ref.dpy, &white); + + printf("Testing edges (with mask %s and %s edges) (%s): ", + mask_name(mask), + edge_name(edge), + test_target_name(target)); + fflush(stdout); + + max = 2*(out.width + 128 + out.height+128); + step = 0; + for (step = 0; step <= max; step++) { + char buf[80]; + + step_to_point(step, out.width, out.height, &tri.p1); + step_to_point(step + out.width + 128, + out.width, out.height, + &tri.p2); + step_to_point(step + out.height + 128 + 2*(out.width + 128), + out.width, out.height, + &tri.p3); + + sprintf(buf, + "tri=((%d, %d), (%d, %d), (%d, %d))\n", + tri.p1.x >> 16, tri.p1.y >> 16, + tri.p2.x >> 16, tri.p2.y >> 16, + tri.p3.x >> 16, tri.p3.y >> 16); + + clear(&t->out, &out); + XRenderCompositeTriangles(t->out.dpy, + PictOpSrc, + src_out, + out.picture, + mask_format(t->out.dpy, mask), + 0, 0, + &tri, 1); + + clear(&t->ref, &ref); + XRenderCompositeTriangles(t->ref.dpy, + PictOpSrc, + src_ref, + ref.picture, + mask_format(t->ref.dpy, mask), + 0, 0, + &tri, 1); + + test_compare(t, + out.draw, out.format, + ref.draw, ref.format, + 0, 0, out.width, out.height, + buf); + } + + XRenderFreePicture(t->out.dpy, src_out); + test_target_destroy_render(&t->out, &out); + + XRenderFreePicture(t->ref.dpy, src_ref); + test_target_destroy_render(&t->ref, &ref); + + printf("pass\n"); +} + +int main(int argc, char **argv) +{ + struct test test; + enum target target; + enum mask mask; + enum edge edge; + + test_init(&test, argc, argv); + + for (target = TARGET_FIRST; target <= TARGET_LAST; target++) { + for (mask = MASK_NONE; mask <= MASK_A8; mask++) + for (edge = EDGE_SHARP; edge <= EDGE_SMOOTH; edge++) + edge_test(&test, mask, edge, target); + } + + return 0; +} diff --git a/test/test.h b/test/test.h index a3ef979..9eec1cf 100644 --- a/test/test.h +++ b/test/test.h @@ -107,6 +107,15 @@ static inline uint32_t color(uint8_t red, uint8_t green, uint8_t blue, uint8_t a return alpha << 24 | ra >> 8 << 16 | ga >> 8 << 8 | ba >> 8; } +static inline uint32_t xrender_color(const XRenderColor *c) +{ + uint32_t ra = c->red * c->alpha; + uint32_t ga = c->green * c->alpha; + uint32_t ba = c->blue * c->alpha; + + return c->alpha >> 8 << 24 | ra >> 24 << 16 | ga >> 24 << 8 | ba >> 24; +} + void test_timer_start(struct test_display *t, struct timespec *tv); double test_timer_stop(struct test_display *t, struct timespec *tv); diff --git a/test/test_image.c b/test/test_image.c index d15a8af..1c07699 100644 --- a/test/test_image.c +++ b/test/test_image.c @@ -197,13 +197,10 @@ void test_compare(struct test *t, const char *info) { XImage out_image, ref_image; - Pixmap tmp; - char *out, *ref; + uint32_t *out, *ref; char buf[600]; uint32_t mask; int i, j; - XGCValues gcv; - GC gc; if (w * h * 4 > t->out.max_shm_size) return test_compare_fallback(t, @@ -214,37 +211,24 @@ void test_compare(struct test *t, test_init_image(&out_image, &t->out.shm, out_format, w, h); test_init_image(&ref_image, &t->ref.shm, ref_format, w, h); - gcv.graphics_exposures = 0; - die_unless(out_image.depth == ref_image.depth); die_unless(out_image.bits_per_pixel == ref_image.bits_per_pixel); die_unless(out_image.bits_per_pixel == 32); - mask = depth_mask(out_image.depth); + XShmGetImage(t->out.dpy, out_draw, &out_image, x, y, AllPlanes); + out = (uint32_t *)out_image.data; - tmp = XCreatePixmap(t->out.dpy, out_draw, w, h, out_image.depth); - gc = XCreateGC(t->out.dpy, tmp, GCGraphicsExposures, &gcv); - XCopyArea(t->out.dpy, out_draw, tmp, gc, x, y, w, h, 0, 0); - XShmGetImage(t->out.dpy, tmp, &out_image, 0, 0, AllPlanes); - XFreeGC(t->out.dpy, gc); - XFreePixmap(t->out.dpy, tmp); - out = out_image.data; - - tmp = XCreatePixmap(t->ref.dpy, ref_draw, w, h, ref_image.depth); - gc = XCreateGC(t->ref.dpy, tmp, GCGraphicsExposures, &gcv); - XCopyArea(t->ref.dpy, ref_draw, tmp, gc, x, y, w, h, 0, 0); - XShmGetImage(t->ref.dpy, tmp, &ref_image, 0, 0, AllPlanes); - XFreeGC(t->ref.dpy, gc); - XFreePixmap(t->ref.dpy, tmp); - ref = ref_image.data; + XShmGetImage(t->ref.dpy, ref_draw, &ref_image, x, y, AllPlanes); + ref = (uint32_t *)ref_image.data; /* Start with an exact comparison. However, one quicky desires * a fuzzy comparator to hide hardware inaccuracies... */ + mask = depth_mask(out_image.depth); for (j = 0; j < h; j++) { for (i = 0; i < w; i++) { - uint32_t a = ((uint32_t *)out)[i] & mask; - uint32_t b = ((uint32_t *)ref)[i] & mask; + uint32_t a = out[i] & mask; + uint32_t b = ref[i] & mask; if (a != b && pixel_difference(a, b) > MAX_DELTA) { show_pixels(buf, &out_image, &ref_image, @@ -255,8 +239,8 @@ void test_compare(struct test *t, x,i, y,j, a, b, pixel_difference(a, b), buf, info); } } - out += out_image.bytes_per_line; - ref += ref_image.bytes_per_line; + out = (uint32_t *)((char *)out + out_image.bytes_per_line); + ref = (uint32_t *)((char *)ref + ref_image.bytes_per_line); } } diff --git a/tools/Makefile.am b/tools/Makefile.am index b5de2c9..92df266 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -26,13 +26,30 @@ AM_CFLAGS = \ drivermandir = $(DRIVER_MAN_DIR) policydir = $(datarootdir)/polkit-1/actions +bin_PROGRAMS = +noinst_PROGRAMS = +libexec_PROGRAMS = + if BUILD_TOOLS -bin_PROGRAMS = intel-virtual-output +bin_PROGRAMS += intel-virtual-output driverman_DATA = intel-virtual-output.$(DRIVER_MAN_SUFFIX) endif +if BUILD_TOOL_CURSOR +noinst_PROGRAMS += cursor +cursor_CFLAGS = $(TOOL_CURSOR_CFLAGS) +cursor_LDADD = $(TOOL_CURSOR_LIBS) +endif + +if X11_DRI3 +noinst_PROGRAMS += dri3info +dri3info_SOURCES = dri3info.c +dri3info_CFLAGS = $(X11_DRI3_CFLAGS) $(DRI_CFLAGS) +dri3info_LDADD = $(X11_DRI3_LIBS) $(DRI_LIBS) +endif + if BUILD_BACKLIGHT_HELPER -libexec_PROGRAMS = xf86-video-intel-backlight-helper +libexec_PROGRAMS += xf86-video-intel-backlight-helper nodist_policy_DATA = org.x.xf86-video-intel.backlight-helper.policy backlight_helper = $(libexecdir)/xf86-video-intel-backlight-helper diff --git a/tools/backlight_helper.c b/tools/backlight_helper.c index 8b2667d..a00f0d6 100644 --- a/tools/backlight_helper.c +++ b/tools/backlight_helper.c @@ -9,6 +9,12 @@ #include #include +#if MAJOR_IN_MKDEV +#include +#elif MAJOR_IN_SYSMACROS +#include +#endif + #define DBG 0 #if defined(__GNUC__) && (__GNUC__ > 3) diff --git a/tools/cursor.c b/tools/cursor.c new file mode 100644 index 0000000..6f4e3f8 --- /dev/null +++ b/tools/cursor.c @@ -0,0 +1,111 @@ +/* + * Copyright © 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 + +int main(int argc, char **argv) +{ + Display *dpy; + XFixesCursorImage *cur; + unsigned long *src; /* XXX deep sigh */ + unsigned x, y; + png_struct *png; + png_info *info; + png_byte **rows; + FILE *file; + + dpy = XOpenDisplay(NULL); + if (dpy == NULL) + return 1; + + if (!XFixesQueryExtension(dpy, (int *)&x, (int *)&y)) + return 1; + + cur = XFixesGetCursorImage(dpy); + if (cur == NULL) + return 1; + + printf("Cursor on display '%s': %dx%d, (hotspot %dx%d)\n", + DisplayString(dpy), + cur->width, cur->height, + cur->xhot, cur->yhot); + + file = fopen("cursor.png", "wb"); + if (file == NULL) + return 2; + + png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + info = png_create_info_struct(png); + png_init_io(png, file); + png_set_IHDR(png, info, + cur->width, cur->height, 8, + PNG_COLOR_TYPE_RGB_ALPHA, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + png_write_info(png, info); + + src = cur->pixels; + rows = malloc(cur->height*sizeof(png_byte*)); + if (rows == NULL) + return 3; + + for (y = 0; y < cur->height; y++) { + rows[y] = malloc(cur->width * 4); + for (x = 0; x < cur->width; x++) { + uint32_t p = *src++; + uint8_t r = p >> 0; + uint8_t g = p >> 8; + uint8_t b = p >> 16; + uint8_t a = p >> 24; + + if (a > 0x00 && a < 0xff) { + r = (r * 0xff + a /2) / a; + g = (g * 0xff + a /2) / a; + b = (b * 0xff + a /2) / a; + } + + rows[y][4*x + 0] = b; + rows[y][4*x + 1] = g; + rows[y][4*x + 2] = r; + rows[y][4*x + 3] = a; + } + } + + png_write_image(png, rows); + png_write_end(png, NULL); + fclose(file); + + return 0; +} diff --git a/tools/dri3info.c b/tools/dri3info.c new file mode 100644 index 0000000..0c33fc5 --- /dev/null +++ b/tools/dri3info.c @@ -0,0 +1,329 @@ +/* + * 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. + * + * To compile standalone: gcc -o dri3info dri3info.c `pkg-config --cflags --libs xcb-dri3 x11-xcb xrandr xxf86vm libdrm` + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +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 dri3_exists(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; +} + +static int dri3_open(Display *dpy) +{ + xcb_connection_t *c = XGetXCBConnection(dpy); + xcb_dri3_open_cookie_t cookie; + xcb_dri3_open_reply_t *reply; + + if (!dri3_exists(dpy)) + return -1; + + cookie = xcb_dri3_open(c, RootWindow(dpy, DefaultScreen(dpy)), None); + reply = xcb_dri3_open_reply(c, cookie, NULL); + + if (!reply) + return -1; + + if (reply->nfd != 1) + return -1; + + return xcb_dri3_open_reply_fds(c, reply)[0]; +} + +static void get_device_path(int fd, char *buf, int len) +{ + struct stat remote, local; + int i; + + if (fstat(fd, &remote)) + goto out; + + for (i = 0; i < 16; i++) { + snprintf(buf, len, "/dev/dri/card%d", i); + if (stat(buf, &local)) + continue; + + if (local.st_mode == remote.st_mode && + local.st_rdev == remote.st_rdev) + return; + + snprintf(buf, len, "/dev/dri/renderD%d", i + 128); + if (stat(buf, &local)) + continue; + + if (local.st_mode == remote.st_mode && + local.st_rdev == remote.st_rdev) + return; + } + +out: + strncpy(buf, "unknown path", len); +} + +static void get_driver_name(int fd, char *name, int len) +{ + drm_version_t version; + + memset(name, 0, len); + memset(&version, 0, sizeof(version)); + version.name_len = len; + version.name = name; + + (void)drmIoctl(fd, DRM_IOCTL_VERSION, &version); +} + +static int compute_refresh_rate_from_mode(long n, long d, unsigned flags, + int32_t *numerator, + int32_t *denominator) +{ + int i; + + /* The mode flags are only defined privately to the Xserver (in xf86str.h) + * but they at least bit compatible between VidMode, RandR and DRM. + */ +# define V_INTERLACE 0x010 +# define V_DBLSCAN 0x020 + + if (flags & V_INTERLACE) + n *= 2; + else if (flags & V_DBLSCAN) + d *= 2; + + /* The OML_sync_control spec requires that if the refresh rate is a + * whole number, that the returned numerator be equal to the refresh + * rate and the denominator be 1. + */ + + if (n % d == 0) { + n /= d; + d = 1; + } + else { + static const unsigned f[] = { 13, 11, 7, 5, 3, 2, 0 }; + + /* This is a poor man's way to reduce a fraction. It's far from + * perfect, but it will work well enough for this situation. + */ + + for (i = 0; f[i] != 0; i++) { + while (n % f[i] == 0 && d % f[i] == 0) { + d /= f[i]; + n /= f[i]; + } + } + } + + *numerator = n; + *denominator = d; + return 1; +} + +static int RRGetMscRate(Display *dpy, int32_t *numerator, int32_t *denominator) +{ + int ret = 0; + Window root = RootWindow(dpy, DefaultScreen(dpy)); + XRRScreenResources *res; + int rr_event, rr_error; + RROutput primary; + RRMode mode = 0; + int n; + + if (!XRRQueryExtension(dpy, &rr_event, &rr_error)) + return ret; + + res = XRRGetScreenResourcesCurrent(dpy, root); + if (res == NULL) + return ret; + + /* Use the primary output if specified, otherwise + * use the mode on the first enabled crtc. + */ + primary = XRRGetOutputPrimary(dpy, root); + if (primary) { + XRROutputInfo *output; + + output = XRRGetOutputInfo(dpy, res, primary); + if (output != NULL) { + if (output->crtc) { + XRRCrtcInfo *crtc; + + crtc = XRRGetCrtcInfo(dpy, res, output->crtc); + if (crtc) { + mode = crtc->mode; + XRRFreeCrtcInfo(crtc); + } + } + XRRFreeOutputInfo(output); + } + } + + for (n = 0; mode == 0 && n < res->ncrtc; n++) { + XRRCrtcInfo *crtc; + + crtc = XRRGetCrtcInfo(dpy, res, res->crtcs[n]); + if (crtc) { + mode = crtc->mode; + XRRFreeCrtcInfo(crtc); + } + } + + for (n = 0; n < res->nmode; n++) { + if (res->modes[n].id == mode) { + ret = compute_refresh_rate_from_mode(res->modes[n].dotClock, + res->modes[n].hTotal*res->modes[n].vTotal, + res->modes[n].modeFlags, + numerator, denominator); + break; + } + } + + XRRFreeScreenResources(res); + return ret; +} + +static int VMGetMscRate(Display *dpy, int32_t *numerator, int32_t *denominator) +{ + XF86VidModeModeLine mode_line; + int dot_clock; + int i; + + if (XF86VidModeQueryVersion(dpy, &i, &i) && + XF86VidModeGetModeLine(dpy, DefaultScreen(dpy), &dot_clock, &mode_line)) + return compute_refresh_rate_from_mode(dot_clock * 1000, + mode_line.vtotal * mode_line.htotal, + mode_line.flags, + numerator, denominator); + + return 0; +} + +static int get_refresh_rate(Display *dpy, + int32_t *numerator, + int32_t *denominator) +{ + if (RRGetMscRate(dpy, numerator, denominator)) + return 1; + + if (VMGetMscRate(dpy, numerator, denominator)) + return 1; + + return 0; +} + +static void info(const char *dpyname) +{ + Display *dpy; + int device; + int32_t numerator, denominator; + + dpy = XOpenDisplay(dpyname); + if (dpy == NULL) { + printf("Unable to connect to display '%s'\n", + dpyname ?: getenv("DISPLAY") ?: "unset"); + return; + } + + printf("Display '%s'\n", DisplayString(dpy)); + device = dri3_open(dpy); + if (device < 0) { + printf("\tUnable to connect to DRI3\n"); + } else { + char device_path[1024]; + char driver_name[1024]; + + get_device_path(device, device_path, sizeof(device_path)); + get_driver_name(device, driver_name, sizeof(driver_name)); + + printf("Connected to DRI3, using fd %d which matches %s, driver %s\n", + device, device_path, driver_name); + close(device); + } + + if (get_refresh_rate(dpy, &numerator, &denominator)) + printf("\tPrimary refresh rate: %d/%d (%.1fHz)\n", + numerator, denominator, numerator/(float)denominator); + + XCloseDisplay(dpy); +} + +int main(int argc, char **argv) +{ + int i; + + if (argc > 1) { + for (i = 1; i < argc; i++) + info(argv[i]); + } else + info(NULL); + + return 0; +} diff --git a/tools/virtual.c b/tools/virtual.c index 8e2b4a2..ffda54a 100644 --- a/tools/virtual.c +++ b/tools/virtual.c @@ -111,7 +111,7 @@ struct display { Cursor invisible_cursor; Cursor visible_cursor; - XcursorImage cursor_image; + XcursorImage cursor_image; /* first only */ int cursor_serial; int cursor_x; int cursor_y; @@ -145,6 +145,7 @@ struct output { XRenderPictFormat *use_render; int x, y; + int width, height; XRRModeInfo mode; Rotation rotation; }; @@ -218,6 +219,13 @@ static inline XRRScreenResources *_XRRGetScreenResourcesCurrent(Display *dpy, Wi static int _x_error_occurred; static int +_io_error_handler(Display *display) +{ + fprintf(stderr, "XIO error on display %s\n", DisplayString(display)); + abort(); +} + +static int _check_error_handler(Display *display, XErrorEvent *event) { @@ -243,6 +251,10 @@ can_use_shm(Display *dpy, XExtCodes *codes; int major, minor, has_shm, has_pixmap; + *shm_event = 0; + *shm_opcode = 0; + *shm_pixmap = 0; + if (!XShmQueryExtension(dpy)) return 0; @@ -320,6 +332,7 @@ can_use_shm(Display *dpy, #include #include #include +#include #include #include static Pixmap dri3_create_pixmap(Display *dpy, @@ -357,6 +370,7 @@ 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; @@ -364,7 +378,8 @@ static int dri3_query_version(Display *dpy, int *major, int *minor) xcb_dri3_query_version(c, XCB_DRI3_MAJOR_VERSION, XCB_DRI3_MINOR_VERSION), - NULL); + &error); + free(error); if (reply == NULL) return -1; @@ -377,8 +392,13 @@ static int dri3_query_version(Display *dpy, int *major, int *minor) static int dri3_exists(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; @@ -809,6 +829,10 @@ static int clone_update_modes__fixed(struct clone *clone) RRMode id; int i, j, ret = ENOENT; + DBG(X11, ("%s-%s cloning modes fixed %dx%d\n", + DisplayString(clone->dst.dpy), clone->dst.name, + clone->dst.width, clone->dst.height)); + assert(clone->src.rr_output); res = _XRRGetScreenResourcesCurrent(clone->src.dpy, clone->src.window); @@ -837,8 +861,8 @@ static int clone_update_modes__fixed(struct clone *clone) /* Create matching mode for the real output on the virtual */ memset(&mode, 0, sizeof(mode)); - mode.width = clone->width; - mode.height = clone->height; + mode.width = clone->dst.width; + mode.height = clone->dst.height; mode.nameLength = sprintf(mode_name, "FAKE-%dx%d", mode.width, mode.height); mode.name = mode_name; @@ -1082,20 +1106,20 @@ static int clone_init_xfer(struct clone *clone) width = 0; height = 0; } else if (clone->dri3.xid) { - width = clone->dst.display->width; - height = clone->dst.display->height; + width = clone->dst.width; + height = clone->dst.height; } else { width = mode_width(&clone->src.mode, clone->src.rotation); height = mode_height(&clone->src.mode, clone->src.rotation); } + DBG(DRAW, ("%s-%s create xfer, %dx%d (currently %dx%d)\n", + DisplayString(clone->dst.dpy), clone->dst.name, + width, height, clone->width, clone->height)); + if (width == clone->width && height == clone->height) return 0; - DBG(DRAW, ("%s-%s create xfer, %dx%d\n", - DisplayString(clone->dst.dpy), clone->dst.name, - width, height)); - if (clone->shm.shmaddr) { if (clone->src.use_shm) XShmDetach(clone->src.dpy, &clone->src.shm); @@ -1325,8 +1349,19 @@ static int context_update(struct context *ctx) struct clone *clone; int x1, x2, y1, y2; - if (display->rr_active == 0) + if (display->rr_active == 0) { + for (clone = display->clone; clone; clone = clone->next) { + struct output *output = &clone->src; + if (output->mode.id) { + clone->dst.mode.id = -1; + clone->dst.rr_crtc = -1; + } else { + clone->dst.mode.id = 0; + clone->dst.rr_crtc = 0; + } + } continue; + } x1 = y1 = INT_MAX; x2 = y2 = INT_MIN; @@ -1599,14 +1634,17 @@ static Cursor display_load_invisible_cursor(struct display *display) static Cursor display_get_visible_cursor(struct display *display) { - if (display->cursor_serial != display->cursor_image.size) { - DBG(CURSOR, ("%s updating cursor\n", DisplayString(display->dpy))); + struct display *first = display->ctx->display; + + if (display->cursor_serial != first->cursor_serial) { + DBG(CURSOR, ("%s updating cursor %dx%d, serial %d\n", + DisplayString(display->dpy), first->cursor_image.width, first->cursor_image.height, first->cursor_serial)); if (display->visible_cursor) XFreeCursor(display->dpy, display->visible_cursor); - display->visible_cursor = XcursorImageLoadCursor(display->dpy, &display->cursor_image); - display->cursor_serial = display->cursor_image.size; + display->visible_cursor = XcursorImageLoadCursor(display->dpy, &first->cursor_image); + display->cursor_serial = first->cursor_serial; } return display->visible_cursor; @@ -1629,7 +1667,7 @@ static void display_load_visible_cursor(struct display *display, XFixesCursorIma display->cursor_image.height = cur->height; display->cursor_image.xhot = cur->xhot; display->cursor_image.yhot = cur->yhot; - display->cursor_image.size++; + display->cursor_serial++; n = cur->width*cur->height; src = cur->pixels; @@ -1637,11 +1675,24 @@ static void display_load_visible_cursor(struct display *display, XFixesCursorIma while (n--) *dst++ = *src++; - DBG(CURSOR, ("%s marking cursor changed\n", DisplayString(display->dpy))); - display->cursor_moved++; - if (display->cursor != display->invisible_cursor) { - display->cursor_visible++; - context_enable_timer(display->ctx); + if (verbose & CURSOR) { + int x, y; + + printf("%s cursor image %dx%d, serial %d:\n", + DisplayString(display->dpy), + cur->width, cur->height, + display->cursor_serial); + dst = display->cursor_image.pixels; + for (y = 0; y < cur->height; y++) { + for (x = 0; x < cur->width; x++) { + if (x == cur->xhot && y == cur->yhot) + printf("+"); + else + printf("%c", *dst ? *dst >> 24 >= 127 ? 'x' : '.' : ' '); + dst++; + } + printf("\n"); + } } } @@ -1685,6 +1736,8 @@ static void display_flush_cursor(struct display *display) if (cursor == None) cursor = display->invisible_cursor; if (cursor != display->cursor) { + DBG(CURSOR, ("%s setting cursor shape %lx\n", + DisplayString(display->dpy), (long)cursor)); XDefineCursor(display->dpy, display->root, cursor); display->cursor = cursor; } @@ -1762,6 +1815,8 @@ static void get_src(struct clone *c, const XRectangle *clip) c->image.obdata = (char *)&c->src.shm; if (c->src.use_render) { + DBG(DRAW, ("%s-%s get_src via XRender\n", + DisplayString(c->dst.dpy), c->dst.name)); XRenderComposite(c->src.dpy, PictOpSrc, c->src.win_picture, 0, c->src.pix_picture, clip->x, clip->y, @@ -1782,16 +1837,22 @@ static void get_src(struct clone *c, const XRectangle *clip) &c->image, 0, 0); } } else if (c->src.pixmap) { + DBG(DRAW, ("%s-%s get_src XCopyArea (SHM/DRI3)\n", + DisplayString(c->dst.dpy), c->dst.name)); XCopyArea(c->src.dpy, c->src.window, c->src.pixmap, c->src.gc, clip->x, clip->y, clip->width, clip->height, 0, 0); XSync(c->src.dpy, False); } else if (c->src.use_shm) { + DBG(DRAW, ("%s-%s get_src XShmGetImage\n", + DisplayString(c->dst.dpy), c->dst.name)); ximage_prepare(&c->image, clip->width, clip->height); XShmGetImage(c->src.dpy, c->src.window, &c->image, clip->x, clip->y, AllPlanes); } else { + DBG(DRAW, ("%s-%s get_src XGetSubImage (slow)\n", + DisplayString(c->dst.dpy), c->dst.name)); ximage_prepare(&c->image, c->width, c->height); XGetSubImage(c->src.dpy, c->src.window, clip->x, clip->y, clip->width, clip->height, @@ -1838,7 +1899,7 @@ static void put_dst(struct clone *c, const XRectangle *clip) clip->width, clip->height); c->dst.display->send |= c->dst.use_shm; } else if (c->dst.pixmap) { - DBG(DRAW, ("%s-%s using SHM pixmap\n", + DBG(DRAW, ("%s-%s using SHM or DRI3 pixmap\n", DisplayString(c->dst.dpy), c->dst.name)); c->dst.serial = NextRequest(c->dst.dpy); XCopyArea(c->dst.dpy, c->dst.pixmap, c->dst.window, c->dst.gc, @@ -1870,6 +1931,9 @@ static int clone_paint(struct clone *c) { XRectangle clip; + if (c->width == 0 || c->height == 0) + return 0; + DBG(DRAW, ("%s-%s paint clone, damaged (%d, %d), (%d, %d) [(%d, %d), (%d, %d)]\n", DisplayString(c->dst.dpy), c->dst.name, c->damaged.x1, c->damaged.y1, @@ -1944,6 +2008,10 @@ static int clone_paint(struct clone *c) clip.height = c->damaged.y2 - c->damaged.y1; get_src(c, &clip); + DBG(DRAW, ("%s-%s target offset %dx%d\n", + DisplayString(c->dst.dpy), c->dst.name, + c->dst.x - c->src.x, c->dst.y - c->src.y)); + clip.x += c->dst.x - c->src.x; clip.y += c->dst.y - c->src.y; put_dst(c, &clip); @@ -2252,6 +2320,8 @@ static int clone_init_depth(struct clone *clone) if (ret) return ret; + clone->depth = depth; + DBG(X11, ("%s-%s using depth %d, requires xrender for src? %d, for dst? %d\n", DisplayString(clone->dst.dpy), clone->dst.name, clone->depth, @@ -2592,6 +2662,11 @@ static int last_display_add_clones__randr(struct context *ctx) return ret; } + clone->dst.x = 0; + clone->dst.y = 0; + clone->dst.width = display->width; + clone->dst.height = display->height; + ret = clone_update_modes__randr(clone); if (ret) { fprintf(stderr, "Failed to clone output \"%s\" from display \"%s\"\n", @@ -2668,8 +2743,8 @@ static int last_display_add_clones__xinerama(struct context *ctx) } /* Replace the modes on the local VIRTUAL output with the remote Screen */ - clone->width = xi[n].width; - clone->height = xi[n].height; + clone->dst.width = xi[n].width; + clone->dst.height = xi[n].height; clone->dst.x = xi[n].x_org; clone->dst.y = xi[n].y_org; clone->dst.rr_crtc = -1; @@ -2698,64 +2773,67 @@ static int last_display_add_clones__display(struct context *ctx) Display *dpy = display->dpy; struct clone *clone; Screen *scr; + int count, s; char buf[80]; int ret; RROutput id; + count = ScreenCount(dpy); + DBG(X11, ("%s(%s) - %d screens\n", __func__, DisplayString(dpy), count)); + for (s = 0; s < count; s++) { + clone = add_clone(ctx); + if (clone == NULL) + return -ENOMEM; - DBG(X11, ("%s(%s)\n", __func__, DisplayString(dpy))); - clone = add_clone(ctx); - if (clone == NULL) - return -ENOMEM; + clone->depth = 24; + clone->next = display->clone; + display->clone = clone; - clone->depth = 24; - clone->next = display->clone; - display->clone = clone; + id = claim_virtual(ctx->display, buf, ctx->nclone); + if (id == 0) { + fprintf(stderr, "Failed to find available VirtualHead \"%s\" for on display \"%s\"\n", + buf, DisplayString(dpy)); + } + ret = clone_output_init(clone, &clone->src, ctx->display, buf, id); + if (ret) { + fprintf(stderr, "Failed to add display \"%s\"\n", + DisplayString(ctx->display->dpy)); + return ret; + } - id = claim_virtual(ctx->display, buf, ctx->nclone); - if (id == 0) { - fprintf(stderr, "Failed to find available VirtualHead \"%s\" for on display \"%s\"\n", - buf, DisplayString(dpy)); - } - ret = clone_output_init(clone, &clone->src, ctx->display, buf, id); - if (ret) { - fprintf(stderr, "Failed to add display \"%s\"\n", - DisplayString(ctx->display->dpy)); - return ret; - } + sprintf(buf, "SCREEN%d", s); + ret = clone_output_init(clone, &clone->dst, display, buf, 0); + if (ret) { + fprintf(stderr, "Failed to add display \"%s\"\n", + DisplayString(dpy)); + return ret; + } - sprintf(buf, "WHOLE"); - ret = clone_output_init(clone, &clone->dst, display, buf, 0); - if (ret) { - fprintf(stderr, "Failed to add display \"%s\"\n", - DisplayString(dpy)); - return ret; - } + ret = clone_init_depth(clone); + if (ret) { + fprintf(stderr, "Failed to negotiate image format for display \"%s\"\n", + DisplayString(dpy)); + return ret; + } - ret = clone_init_depth(clone); - if (ret) { - fprintf(stderr, "Failed to negotiate image format for display \"%s\"\n", - DisplayString(dpy)); - return ret; - } + /* Replace the modes on the local VIRTUAL output with the remote Screen */ + scr = ScreenOfDisplay(dpy, s); + clone->dst.width = scr->width; + clone->dst.height = scr->height; + clone->dst.x = 0; + clone->dst.y = 0; + clone->dst.rr_crtc = -1; + ret = clone_update_modes__fixed(clone); + if (ret) { + fprintf(stderr, "Failed to clone display \"%s\"\n", + DisplayString(dpy)); + return ret; + } - /* Replace the modes on the local VIRTUAL output with the remote Screen */ - scr = ScreenOfDisplay(dpy, DefaultScreen(dpy)); - clone->width = scr->width; - clone->height = scr->height; - clone->dst.x = 0; - clone->dst.y = 0; - clone->dst.rr_crtc = -1; - ret = clone_update_modes__fixed(clone); - if (ret) { - fprintf(stderr, "Failed to clone display \"%s\"\n", - DisplayString(dpy)); - return ret; + clone->active = ctx->active; + ctx->active = clone; } - clone->active = ctx->active; - ctx->active = clone; - return 0; } @@ -3168,6 +3246,33 @@ static void context_cleanup(struct context *ctx) XCloseDisplay(dpy); } +static void update_cursor_image(struct context *ctx) +{ + XFixesCursorImage *cur; + int i; + + DBG(CURSOR, ("%s cursor changed\n", + DisplayString(ctx->display->dpy))); + + cur = XFixesGetCursorImage(ctx->display->dpy); + if (cur == NULL) + return; + + display_load_visible_cursor(&ctx->display[0], cur); + for (i = 1; i < ctx->ndisplay; i++) { + struct display *display = &ctx->display[i]; + + DBG(CURSOR, ("%s marking cursor changed\n", DisplayString(display->dpy))); + display->cursor_moved++; + if (display->cursor != display->invisible_cursor) { + display->cursor_visible++; + context_enable_timer(display->ctx); + } + } + + XFree(cur); +} + static int done; static void signal_handler(int sig) @@ -3228,6 +3333,7 @@ int main(int argc, char **argv) return -ret; XSetErrorHandler(_check_error_handler); + XSetIOErrorHandler(_io_error_handler); ret = add_fd(&ctx, display_open(&ctx, src_name)); if (ret) { @@ -3348,6 +3454,7 @@ int main(int argc, char **argv) signal(SIGTERM, signal_handler); ctx.command_continuation = 0; + update_cursor_image(&ctx); while (!done) { XEvent e; int reconfigure = 0; @@ -3380,19 +3487,7 @@ int main(int argc, char **argv) if (ctx.active) context_enable_timer(&ctx); } else if (e.type == ctx.display->xfixes_event + XFixesCursorNotify) { - XFixesCursorImage *cur; - - DBG(CURSOR, ("%s cursor changed\n", - DisplayString(ctx.display->dpy))); - - cur = XFixesGetCursorImage(ctx.display->dpy); - if (cur == NULL) - continue; - - for (i = 1; i < ctx.ndisplay; i++) - display_load_visible_cursor(&ctx.display[i], cur); - - XFree(cur); + update_cursor_image(&ctx); } else if (e.type == ctx.display->rr_event + RRScreenChangeNotify) { DBG(XRR, ("%s screen changed (reconfigure pending? %d)\n", DisplayString(ctx.display->dpy), reconfigure));