diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2016-04-06 09:23:05 +0100 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2016-04-06 09:37:35 +0100 |
commit | a76560107f7d97b80d47e90b46535a8aba273c32 (patch) | |
tree | e64b8eb4d459c339c9bf3547e46270751aa356be /src/sna | |
parent | 9ce7d47a867880b7b43eded7ef8e2d09c5dc21ea (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/sna')
-rw-r--r-- | src/sna/sna_present.c | 49 |
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); } |