summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/sna/sna.h5
-rw-r--r--src/sna/sna_present.c87
-rw-r--r--test/present-test.c226
3 files changed, 263 insertions, 55 deletions
diff --git a/src/sna/sna.h b/src/sna/sna.h
index 90751671..1ff36f3d 100644
--- a/src/sna/sna.h
+++ b/src/sna/sna.h
@@ -542,6 +542,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);
diff --git a/src/sna/sna_present.c b/src/sna/sna_present.c
index 25b4e8bf..7bff86e3 100644
--- a/src/sna/sna_present.c
+++ b/src/sna/sna_present.c
@@ -78,10 +78,34 @@ 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)
+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 uint32_t msc_to_delay(xf86CrtcPtr crtc, uint64_t target)
{
- DisplayModePtr mode = &crtc->desiredMode;
- return msc * mode->VTotal * mode->HTotal / mode->Clock;
+ 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)
@@ -90,7 +114,7 @@ static CARD32 sna_fake_vblank_handler(OsTimerPtr timer, CARD32 now, void *data)
union drm_wait_vblank vbl;
uint64_t msc, ust;
- DBG(("%s(now=%d)\n", __FUNCTION__, now));
+ DBG(("%s(event=%lld, now=%d)\n", __FUNCTION__, (long long)info->event_id, now));
VG_CLEAR(vbl);
vbl.request.type = DRM_VBLANK_RELATIVE;
@@ -98,12 +122,19 @@ static CARD32 sna_fake_vblank_handler(OsTimerPtr timer, CARD32 now, void *data)
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);
+ DBG(("%s: event=%lld, target msc=%lld, now %lld\n",
+ __FUNCTION__, (long long)info->event_id, (long long)info->target_msc, (long long)msc));
+ if (msc < info->target_msc) {
+ uint32_t delay = msc_to_delay(info->crtc, info->target_msc);
+ if (delay)
+ return delay;
+ }
} else {
const struct ust_msc *swap = sna_crtc_last_swap(info->crtc);
- ust = ust64(swap->tv_sec, swap->tv_usec);
+ 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, (long long)info->target_msc, (long long)msc));
}
present_event_notify(info->event_id, ust, msc);
@@ -112,20 +143,26 @@ static CARD32 sna_fake_vblank_handler(OsTimerPtr timer, CARD32 now, void *data)
return 0;
}
-static bool sna_fake_vblank(struct sna_present_event *event)
+static bool sna_fake_vblank(struct sna_present_event *info)
{
- uint64_t msc = sna_crtc_last_swap(event->crtc)->msc;
+ uint64_t msc = sna_crtc_last_swap(info->crtc)->msc;
uint32_t delay;
- if (msc < event->target_msc)
- delay = msc_to_delay(event->crtc, event->target_msc - msc);
+ if (msc < info->target_msc)
+ delay = msc_to_delay(info->crtc, info->target_msc);
else
delay = 0;
- DBG(("%s(target_msc=%lld, msc=%lld, delay=%ums)\n",
- __FUNCTION__, event->target_msc, msc, delay));
+ DBG(("%s(event=%lld, target_msc=%lld, msc=%lld, delay=%ums)\n",
+ __FUNCTION__, (long long)info->event_id, (long long)info->target_msc, msc, delay));
+ if (delay == 0) {
+ const struct ust_msc *swap = sna_crtc_last_swap(info->crtc);
+ present_event_notify(info->event_id, swap_ust(swap), swap->msc);
+ free(info);
+ return true;
+ }
- return TimerSet(NULL, 0, delay, sna_fake_vblank_handler, event);
+ return TimerSet(NULL, 0, delay, sna_fake_vblank_handler, info);
}
static RRCrtcPtr
@@ -166,7 +203,7 @@ sna_present_get_ust_msc(RRCrtcPtr crtc, CARD64 *ust, CARD64 *msc)
*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);
+ *ust = swap_ust(swap);
*msc = swap->msc;
}
@@ -213,6 +250,8 @@ sna_present_queue_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
event->target_msc = msc;
event->event_id = event_id;
+ assert((int64_t)(msc - sna_crtc_last_swap(event->crtc)->msc) >= 0);
+
VG_CLEAR(vbl);
vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
vbl.request.sequence = msc;
@@ -300,7 +339,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 {
@@ -329,16 +368,6 @@ sna_present_check_flip(RRCrtcPtr crtc,
return TRUE;
}
-static uint64_t gettime_ust64(void)
-{
- struct timespec tv;
-
- if (clock_gettime(CLOCK_MONOTONIC, &tv))
- return 0;
-
- return ust64(tv.tv_sec, tv.tv_nsec / 1000);
-}
-
static Bool
page_flip__async(struct sna *sna,
RRCrtcPtr crtc,
@@ -386,7 +415,7 @@ present_flip_handler(struct drm_event_vblank *event, void *data)
info->crtc ? sna_crtc_to_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);
+ present_event_notify(info->event_id, swap_ust(&swap), swap.msc);
if (info->sna->present.unflip) {
DBG(("%s: executing queued unflip (event=%lld)\n", __FUNCTION__, info->sna->present.unflip));
@@ -519,9 +548,7 @@ notify:
-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;
}
diff --git a/test/present-test.c b/test/present-test.c
index 6f6d6a88..9e5e96b0 100644
--- a/test/present-test.c
+++ b/test/present-test.c
@@ -45,6 +45,7 @@
#include <xcb/xcb.h>
#include <xcb/present.h>
#include <xcb/xfixes.h>
+#include <xcb/dri3.h>
#include <xf86drm.h>
#include <i915_drm.h>
@@ -138,9 +139,11 @@ static void *setup_msc(Display *dpy, Window win)
static uint64_t check_msc(Display *dpy, Window win, void *q, uint64_t last_msc)
{
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, 0, 0, 0);
xcb_flush(c);
do {
@@ -152,16 +155,22 @@ 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) {
msc = ce->msc;
+ complete = 1;
+ }
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 (++serial == 0)
+ serial = 1;
+
return msc;
}
@@ -191,8 +200,7 @@ static int test_whole(Display *dpy)
xshmfence_reset(fence.addr);
pixmap = XCreatePixmap(dpy, root, width, height, depth);
- xcb_present_pixmap(c, root, pixmap,
- 0, /* sbc */
+ xcb_present_pixmap(c, root, pixmap, 0,
0, /* valid */
0, /* update */
0, /* x_off */
@@ -208,8 +216,7 @@ static int test_whole(Display *dpy)
XFreePixmap(dpy, pixmap);
pixmap = XCreatePixmap(dpy, root, width, height, depth);
- xcb_present_pixmap(c, root, pixmap,
- 0, /* sbc */
+ xcb_present_pixmap(c, root, pixmap, 0,
0, /* valid */
0, /* update */
0, /* x_off */
@@ -244,8 +251,9 @@ static int test_future(Display *dpy, void *Q)
xcb_xfixes_region_t region;
unsigned int width, height;
unsigned border, depth;
- int x, y, ret = 1, n;
+ int x, y, ret = 0, n;
uint64_t target, final;
+ int complete;
XGetGeometry(dpy, DefaultRootWindow(dpy),
&root, &x, &y, &width, &height, &border, &depth);
@@ -263,8 +271,7 @@ static int test_future(Display *dpy, void *Q)
pixmap = XCreatePixmap(dpy, root, width, height, depth);
xshmfence_reset(fence.addr);
for (n = N_VBLANKS; n--; )
- xcb_present_pixmap(c, root, pixmap,
- n, /* sbc */
+ xcb_present_pixmap(c, root, pixmap, 0,
0, /* valid */
region, /* update */
0, /* x_off */
@@ -277,8 +284,7 @@ static int test_future(Display *dpy, void *Q)
1, /* divisor */
0, /* remainder */
0, NULL);
- xcb_present_pixmap(c, root, pixmap,
- N_VBLANKS, /* sbc */
+ xcb_present_pixmap(c, root, pixmap, 0,
region, /* valid */
region, /* update */
0, /* x_off */
@@ -294,7 +300,7 @@ static int test_future(Display *dpy, void *Q)
xcb_flush(c);
XSync(dpy, True);
- ret = !!xshmfence_await(fence.addr);
+ ret += !!xshmfence_await(fence.addr);
final = check_msc(dpy, root, Q, 0);
if (final < target) {
@@ -308,8 +314,7 @@ static int test_future(Display *dpy, void *Q)
}
xshmfence_reset(fence.addr);
- xcb_present_pixmap(c, root, pixmap,
- N_VBLANKS, /* sbc */
+ xcb_present_pixmap(c, root, pixmap, 0,
region, /* valid */
region, /* update */
0, /* x_off */
@@ -323,7 +328,7 @@ static int test_future(Display *dpy, void *Q)
0, /* remainder */
0, NULL);
xcb_flush(c);
- ret = !!xshmfence_await(fence.addr);
+ ret += !!xshmfence_await(fence.addr);
final = check_msc(dpy, root, Q, 0);
if (final < target + N_VBLANKS) {
@@ -336,6 +341,37 @@ static int test_future(Display *dpy, void *Q)
ret++;
}
+ xcb_present_pixmap(c, root, pixmap, 0xdeadbeef,
+ 0, /* valid */
+ 0, /* update */
+ 0, /* x_off */
+ 0, /* y_off */
+ None,
+ None, /* wait fence */
+ 0,
+ XCB_PRESENT_OPTION_NONE,
+ final + N_VBLANKS + 1, /* 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;
+
+ complete = ce->serial == 0xdeadbeef;
+ free(ev);
+ } while (!complete);
+
XFreePixmap(dpy, pixmap);
xcb_xfixes_destroy_region(c, region);
dri3_fence_free(dpy, &fence);
@@ -344,6 +380,121 @@ static int test_future(Display *dpy, void *Q)
ret += !!_x_error_occurred;
return ret;
+#undef N_VBLANKS
+}
+
+static int test_accuracy(Display *dpy, 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 complete;
+
+ XGetGeometry(dpy, DefaultRootWindow(dpy),
+ &root, &x, &y, &width, &height, &border, &depth);
+
+ printf("Testing whole screen flip acccuracy: %dx%d\n", width, height);
+ _x_error_occurred = 0;
+
+ target = check_msc(dpy, root, Q, 0);
+ pixmap = XCreatePixmap(dpy, root, width, height, depth);
+ xcb_present_pixmap(c, root, pixmap,
+ 0xdeadbeef, /* serial */
+ 0, /* valid */
+ 0, /* update */
+ 0, /* x_off */
+ 0, /* y_off */
+ None,
+ None, /* wait fence */
+ None,
+ XCB_PRESENT_OPTION_NONE,
+ target + 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;
+ if (ce->kind != XCB_PRESENT_COMPLETE_KIND_PIXMAP)
+ break;
+
+ complete = ce->serial == 0xdeadbeef;
+ free(ev);
+ } while (!complete);
+ XSync(dpy, True);
+
+ target = check_msc(dpy, root, Q, 0);
+ for (n = 0; n <= N_VBLANKS; n++)
+ xcb_present_pixmap(c, root, pixmap,
+ target + 60 + 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_flush(c);
+
+ complete = 0;
+ do {
+ xcb_present_complete_notify_event_t *ce;
+ xcb_generic_event_t *ev;
+ int64_t msc;
+
+ 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);
+
+ msc = ce->msc - ce->serial;
+ if (msc < 0) {
+ fprintf(stderr, "frame %d displayed early by %lld frames\n", (int)(ce->serial - target - 60), (long long)msc);
+ ret += -msc;
+ early++;
+ } else if (msc > 1) { /* allow the frame to slip by a vblank */
+ ret += msc;
+ late++;
+ }
+ complete = ce->serial == target + 60 + N_VBLANKS;
+ free(ev);
+ } while (!complete);
+
+ if (early)
+ printf("%d frames shown too early! ", early);
+ if (late)
+ printf("%d fames shown too late!", late);
+ if (early|late)
+ printf("\n");
+
+ ret += !!_x_error_occurred;
+
+ return ret;
+#undef N_VBLANKS
}
static inline XRRScreenResources *_XRRGetScreenResourcesCurrent(Display *dpy, Window window)
@@ -447,6 +598,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,
@@ -473,16 +625,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 */
@@ -492,14 +642,15 @@ 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);
return err;
@@ -532,6 +683,12 @@ static int test_crtc(Display *dpy, void *queue, uint64_t last_msc)
err += for_each_crtc(dpy, __test_crtc, &test);
test.msc = check_msc(dpy, test.win, test.queue, test.msc);
+ printf("Testing each crtc, with future flips\n");
+ test.flags = FUTURE | SYNC;
+ test.msc = check_msc(dpy, test.win, test.queue, test.msc);
+ err += for_each_crtc(dpy, __test_crtc, &test);
+ test.msc = check_msc(dpy, test.win, test.queue, test.msc);
+
dri3_fence_free(dpy, &test.fence);
XSync(dpy, True);
err += !!_x_error_occurred;
@@ -794,8 +951,22 @@ static int has_present(Display *dpy)
&error);
free(reply);
free(error);
- if (reply == NULL)
+ 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,
@@ -805,8 +976,10 @@ static int has_present(Display *dpy)
free(reply);
free(error);
- if (reply == NULL)
+ if (reply == NULL) {
+ fprintf(stderr, "Present not supported on %s\n", DisplayString(dpy));
return 0;
+ }
return 1;
}
@@ -840,6 +1013,9 @@ int main(void)
error += test_future(dpy, queue);
last_msc = check_msc(dpy, root, queue, last_msc);
+ error += test_accuracy(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);