diff options
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | src/sna/sna_present.c | 70 | ||||
-rw-r--r-- | test/present-test.c | 139 |
3 files changed, 197 insertions, 14 deletions
diff --git a/configure.ac b/configure.ac index afe162d4..7476e2b5 100644 --- a/configure.ac +++ b/configure.ac @@ -270,7 +270,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 xrender 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") diff --git a/src/sna/sna_present.c b/src/sna/sna_present.c index c2a9c5db..25b4e8bf 100644 --- a/src/sna/sna_present.c +++ b/src/sna/sna_present.c @@ -38,9 +38,10 @@ static present_screen_info_rec present_info; struct sna_present_event { - uint64_t event_id; xf86CrtcPtr crtc; struct sna *sna; + uint64_t event_id; + uint64_t target_msc; }; static void sna_present_unflip(ScreenPtr screen, uint64_t event_id); @@ -77,6 +78,56 @@ 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 uint32_t msc_to_delay(xf86CrtcPtr crtc, uint64_t msc) +{ + DisplayModePtr mode = &crtc->desiredMode; + return msc * mode->VTotal * mode->HTotal / mode->Clock; +} + +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(now=%d)\n", __FUNCTION__, now)); + + VG_CLEAR(vbl); + vbl.request.type = DRM_VBLANK_RELATIVE; + vbl.request.sequence = 0; + if (sna_wait_vblank(info->sna, &vbl, sna_crtc_to_pipe(info->crtc)) == 0) { + ust = ust64(vbl.reply.tval_sec, vbl.reply.tval_usec); + msc = sna_crtc_record_vblank(info->crtc, &vbl); + if (msc < info->target_msc) + return msc_to_delay(info->crtc, msc - info->target_msc); + } else { + const struct ust_msc *swap = sna_crtc_last_swap(info->crtc); + ust = ust64(swap->tv_sec, swap->tv_usec); + msc = swap->msc; + } + + present_event_notify(info->event_id, ust, msc); + free(info); + free(timer); + return 0; +} + +static bool sna_fake_vblank(struct sna_present_event *event) +{ + uint64_t msc = sna_crtc_last_swap(event->crtc)->msc; + uint32_t delay; + + if (msc < event->target_msc) + delay = msc_to_delay(event->crtc, event->target_msc - msc); + else + delay = 0; + + DBG(("%s(target_msc=%lld, msc=%lld, delay=%ums)\n", + __FUNCTION__, event->target_msc, msc, delay)); + + return TimerSet(NULL, 0, delay, sna_fake_vblank_handler, event); +} + static RRCrtcPtr sna_present_get_crtc(WindowPtr window) { @@ -131,10 +182,11 @@ sna_present_vblank_handler(struct drm_event_vblank *event) { struct sna_present_event *info = to_present_event(event->user_data); - DBG(("%s: pipe=%d tv=%d.%06d msc=%d, event %lld complete\n", __FUNCTION__, + DBG(("%s: pipe=%d tv=%d.%06d msc=%d, event %lld complete: target_msc=%lld\n", __FUNCTION__, sna_crtc_to_pipe(info->crtc), event->tv_sec, event->tv_usec, event->sequence, - (long long)info->event_id)); + (long long)info->event_id, + (long long)info->target_msc)); present_event_notify(info->event_id, ust64(event->tv_sec, event->tv_usec), sna_crtc_record_event(info->crtc, event)); @@ -156,9 +208,10 @@ sna_present_queue_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc) if (event == NULL) return BadAlloc; - event->event_id = event_id; event->crtc = crtc->devPrivate; event->sna = sna; + event->target_msc = msc; + event->event_id = event_id; VG_CLEAR(vbl); vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; @@ -166,8 +219,10 @@ sna_present_queue_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc) vbl.request.signal = (uintptr_t)MARK_PRESENT(event); if (sna_wait_vblank(sna, &vbl, sna_crtc_to_pipe(event->crtc))) { DBG(("%s: vblank enqueue failed\n", __FUNCTION__)); - free(event); - return BadMatch; + if (!sna_fake_vblank(event)) { + free(event); + return BadAlloc; + } } return Success; @@ -360,9 +415,10 @@ page_flip(struct sna *sna, if (event == NULL) return FALSE; - event->event_id = event_id; event->crtc = crtc ? crtc->devPrivate : NULL; event->sna = sna; + event->event_id = event_id; + event->target_msc = 0; if (!sna_page_flip(sna, bo, present_flip_handler, event)) { DBG(("%s: pageflip failed\n", __FUNCTION__)); diff --git a/test/present-test.c b/test/present-test.c index dc399c39..e90ef477 100644 --- a/test/present-test.c +++ b/test/present-test.c @@ -44,6 +44,7 @@ #endif #include <xcb/xcb.h> #include <xcb/present.h> +#include <xcb/xfixes.h> #include <xf86drm.h> #include <i915_drm.h> @@ -170,6 +171,7 @@ static void teardown_msc(Display *dpy, void *q) } static int test_whole(Display *dpy) { + xcb_connection_t *c = XGetXCBConnection(dpy); Pixmap pixmap; struct dri3_fence fence; Window root; @@ -189,8 +191,7 @@ static int test_whole(Display *dpy) xshmfence_reset(fence.addr); pixmap = XCreatePixmap(dpy, root, width, height, depth); - xcb_present_pixmap(XGetXCBConnection(dpy), - root, pixmap, + xcb_present_pixmap(c, root, pixmap, 0, /* sbc */ 0, /* valid */ 0, /* update */ @@ -207,8 +208,7 @@ static int test_whole(Display *dpy) XFreePixmap(dpy, pixmap); pixmap = XCreatePixmap(dpy, root, width, height, depth); - xcb_present_pixmap(XGetXCBConnection(dpy), - root, pixmap, + xcb_present_pixmap(c, root, pixmap, 0, /* sbc */ 0, /* valid */ 0, /* update */ @@ -234,6 +234,118 @@ static int test_whole(Display *dpy) return ret; } +static int test_future(Display *dpy, void *Q) +{ +#define N_VBLANKS 256 /* kernel event queue length: 128 vblanks */ + xcb_connection_t *c = XGetXCBConnection(dpy); + Pixmap pixmap; + struct dri3_fence fence; + Window root; + xcb_xfixes_region_t region; + unsigned int width, height; + unsigned border, depth; + int x, y, ret = 1, n; + uint64_t target, final; + + XGetGeometry(dpy, DefaultRootWindow(dpy), + &root, &x, &y, &width, &height, &border, &depth); + + if (dri3_create_fence(dpy, root, &fence)) + return 0; + + printf("Testing whole screen flips into the future: %dx%d\n", width, height); + _x_error_occurred = 0; + + region = xcb_generate_id(c); + xcb_xfixes_create_region(c, region, 0, NULL); + + target = check_msc(dpy, root, Q, 0); + pixmap = XCreatePixmap(dpy, root, width, height, depth); + xshmfence_reset(fence.addr); + for (n = N_VBLANKS; n--; ) + xcb_present_pixmap(c, root, pixmap, + n, /* sbc */ + 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, root, pixmap, + N_VBLANKS, /* sbc */ + region, /* valid */ + region, /* update */ + 0, /* x_off */ + 0, /* y_off */ + None, + None, /* wait fence */ + fence.xid, + XCB_PRESENT_OPTION_NONE, + target, /* target msc */ + 0, /* divisor */ + 0, /* remainder */ + 0, NULL); + xcb_flush(c); + + XSync(dpy, True); + ret = !!xshmfence_await(fence.addr); + + final = check_msc(dpy, root, Q, 0); + if (final < target) { + printf("First flip too early, MSC was %llu, expected %llu\n", + (long long)final, (long long)target); + ret++; + } else if (final > target + 1) { + printf("First flip too late, MSC was %llu, expected %llu\n", + (long long)final, (long long)target); + ret++; + } + + xshmfence_reset(fence.addr); + xcb_present_pixmap(c, root, pixmap, + N_VBLANKS, /* sbc */ + region, /* valid */ + region, /* update */ + 0, /* x_off */ + 0, /* y_off */ + None, + None, /* wait fence */ + fence.xid, + XCB_PRESENT_OPTION_NONE, + target + N_VBLANKS, /* target msc */ + 0, /* divisor */ + 0, /* remainder */ + 0, NULL); + xcb_flush(c); + ret = !!xshmfence_await(fence.addr); + + final = check_msc(dpy, root, Q, 0); + if (final < target + N_VBLANKS) { + printf("Last 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("Last flip too late, MSC was %llu, expected %llu\n", + (long long)final, (long long)(target + N_VBLANKS)); + ret++; + } + + XFreePixmap(dpy, pixmap); + xcb_xfixes_destroy_region(c, region); + dri3_fence_free(dpy, &fence); + + XSync(dpy, True); + ret += !!_x_error_occurred; + + return ret; +} + static inline XRRScreenResources *_XRRGetScreenResourcesCurrent(Display *dpy, Window window) { XRRScreenResources *res; @@ -672,8 +784,18 @@ 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) + return 0; reply = xcb_present_query_version_reply(c, xcb_present_query_version(c, @@ -683,8 +805,10 @@ static int has_present(Display *dpy) free(reply); free(error); + if (reply == NULL) + return 0; - return reply != NULL; + return 1; } int main(void) @@ -713,6 +837,9 @@ int main(void) error += test_whole(dpy); last_msc = check_msc(dpy, root, queue, last_msc); + error += test_future(dpy, queue); + last_msc = check_msc(dpy, root, queue, last_msc); + error += test_crtc(dpy, queue, last_msc); last_msc = check_msc(dpy, root, queue, last_msc); |