summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac2
-rw-r--r--src/sna/sna_present.c70
-rw-r--r--test/present-test.c139
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);