summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2016-04-06 09:23:05 +0100
committerChris Wilson <chris@chris-wilson.co.uk>2016-04-06 09:37:35 +0100
commita76560107f7d97b80d47e90b46535a8aba273c32 (patch)
treee64b8eb4d459c339c9bf3547e46270751aa356be /src
parent9ce7d47a867880b7b43eded7ef8e2d09c5dc21ea (diff)
sna/present: Only use the HW vblank for the last frame
Rather than queuing vblanks for future events up to 4000 years and setting the hardware to stay awake for all that period (reporting a tick every refresh), use the timer for the first part. (The timer should allow a precise-(ish) single wakeup of the hardware.) We set the timer a frame ahead, so that we can then queue an actual hw event for the final vblank for precision. References: https://bugs.freedesktop.org/show_bug.cgi?id=94829 Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Diffstat (limited to 'src')
-rw-r--r--src/sna/sna_present.c49
1 files changed, 32 insertions, 17 deletions
diff --git a/src/sna/sna_present.c b/src/sna/sna_present.c
index 5cc34d7a..d27c9125 100644
--- a/src/sna/sna_present.c
+++ b/src/sna/sna_present.c
@@ -146,6 +146,7 @@ static uint32_t msc_to_delay(xf86CrtcPtr crtc, uint64_t target)
int64_t delay, subframe;
delay = target - swap->msc;
+ assert(delay >= 0);
if (delay > 1) /* try to use the hw vblank for the last frame */
delay--;
delay *= mode->VTotal * mode->HTotal / mode->Clock;
@@ -180,18 +181,25 @@ static CARD32 sna_fake_vblank_handler(OsTimerPtr timer, CARD32 now, void *data)
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) {
+ int delta = info->target_msc - 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));
- info->queued = true;
- free(timer);
- return 0;
+ DBG(("%s: too early, requeuing delta=%d\n", __FUNCTION__, delta));
+ assert(info->target_msc - msc < 1ull<<31);
+ if (delta <= 2) {
+ 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));
+ info->queued = true;
+ if (delta == 1) {
+ sna_crtc_set_vblank(info->crtc);
+ info->crtc = mark_crtc(info->crtc);
+ }
+ free(timer);
+ return 0;
+ }
}
delay = msc_to_delay(info->crtc, info->target_msc);
@@ -201,7 +209,7 @@ static CARD32 sna_fake_vblank_handler(OsTimerPtr timer, CARD32 now, void *data)
}
/* As a last resort use a blocking wait.
- * Less than a millisecond for a rare case.
+ * Less than a millisecond for (hopefully) a rare case.
*/
DBG(("%s: blocking wait!\n", __FUNCTION__));
vbl.request.type = DRM_VBLANK_ABSOLUTE;
@@ -209,8 +217,11 @@ static CARD32 sna_fake_vblank_handler(OsTimerPtr timer, CARD32 now, void *data)
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);
- } else
+ } else {
+ DBG(("%s: blocking wait failed, fudging\n",
+ __FUNCTION__));
goto fixup;
+ }
}
} else {
fixup:
@@ -252,25 +263,29 @@ static bool sna_present_queue(struct sna_present_event *info,
uint64_t last_msc)
{
union drm_wait_vblank vbl;
+ int delta = info->target_msc - last_msc;
- DBG(("%s: target msc=%llu, seq=%u (last_msc=%llu)\n",
+ DBG(("%s: target msc=%llu, seq=%u (last_msc=%llu), delta=%d\n",
__FUNCTION__,
(long long)info->target_msc,
(unsigned)info->target_msc,
- (long long)last_msc));
+ (long long)last_msc,
+ delta));
assert(info->target_msc - last_msc < 1ull<<31);
+ assert(delta >= 0);
VG_CLEAR(vbl);
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))) {
- DBG(("%s: vblank enqueue failed, faking\n", __FUNCTION__));
+ if (delta > 2 ||
+ sna_wait_vblank(info->sna, &vbl, sna_crtc_pipe(info->crtc))) {
+ DBG(("%s: vblank enqueue failed, faking delta=%d\n", __FUNCTION__, delta));
if (!sna_fake_vblank(info))
return false;
} else {
info->queued = true;
- if (info->target_msc - last_msc == 1) {
+ if (delta == 1) {
sna_crtc_set_vblank(info->crtc);
info->crtc = mark_crtc(info->crtc);
}