sna/present: Queue the keepalive vblank

Insert the keepalive vblank into the sorted list of msc carefully. This
way we can discard redundant keepalives - as we don't want to queue a
second event for the same vblank needlessly.

Reported-by: Adric Blake <promarbler14@gmail.com>
References: https://bugs.freedesktop.org/show_bug.cgi?id=103025#c13
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
This commit is contained in:
Chris Wilson 2017-10-07 16:58:14 +01:00
parent cebb756f3d
commit f50b9d5405
3 changed files with 75 additions and 45 deletions

View File

@ -633,7 +633,7 @@ extern bool sna_crtc_set_sprite_rotation(xf86CrtcPtr crtc, unsigned idx, uint32_
extern uint32_t sna_crtc_to_sprite(xf86CrtcPtr crtc, unsigned idx);
extern bool sna_crtc_is_transformed(xf86CrtcPtr crtc);
#define CRTC_VBLANK 0x3
#define CRTC_VBLANK 0x7
#define CRTC_ON 0x80000000
uint32_t sna_crtc_id(xf86CrtcPtr crtc);
@ -645,6 +645,11 @@ static inline unsigned long *sna_crtc_flags(xf86CrtcPtr crtc)
return flags;
}
static inline struct list *sna_crtc_vblank_queue(xf86CrtcPtr crtc)
{
return (struct list *)(sna_crtc_flags(crtc) + 1);
}
static inline unsigned sna_crtc_pipe(xf86CrtcPtr crtc)
{
return *sna_crtc_flags(crtc) >> 8 & 0xff;
@ -657,12 +662,14 @@ static inline bool sna_crtc_is_on(xf86CrtcPtr crtc)
static inline void sna_crtc_set_vblank(xf86CrtcPtr crtc)
{
assert((*sna_crtc_flags(crtc) & CRTC_VBLANK) < 3);
DBG(("%s: current vblank count: %d\n", __FUNCTION__, *sna_crtc_flags(crtc) & CRTC_VBLANK));
assert((*sna_crtc_flags(crtc) & CRTC_VBLANK) < CRTC_VBLANK);
++*sna_crtc_flags(crtc);
}
static inline void sna_crtc_clear_vblank(xf86CrtcPtr crtc)
{
DBG(("%s: current vblank count: %d\n", __FUNCTION__, *sna_crtc_flags(crtc) & CRTC_VBLANK));
assert(*sna_crtc_flags(crtc) & CRTC_VBLANK);
--*sna_crtc_flags(crtc);
}

View File

@ -193,6 +193,7 @@ struct sna_cursor {
struct sna_crtc {
unsigned long flags;
struct list vblank_queue;
uint32_t id;
xf86CrtcPtr base;
struct drm_mode_modeinfo kmode;
@ -3522,6 +3523,7 @@ sna_crtc_add(ScrnInfoPtr scrn, unsigned id)
if (sna_crtc == NULL)
return false;
list_init(&sna_crtc->vblank_queue);
sna_crtc->id = id;
VG_CLEAR(get_pipe);

View File

@ -197,6 +197,16 @@ static uint32_t msc_to_delay(xf86CrtcPtr crtc, uint64_t target)
return MIN(delay, INT32_MAX);
}
static void add_to_crtc_vblank(struct sna_present_event *info,
int delta)
{
info->queued = true;
if (delta == 1 && info->crtc) {
sna_crtc_set_vblank(info->crtc);
info->crtc = mark_crtc(info->crtc);
}
}
static CARD32 sna_fake_vblank_handler(OsTimerPtr timer, CARD32 now, void *data)
{
struct sna_present_event *info = data;
@ -225,11 +235,7 @@ static CARD32 sna_fake_vblank_handler(OsTimerPtr timer, CARD32 now, void *data)
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);
}
add_to_crtc_vblank(info, delta);
free(timer);
return 0;
}
@ -326,11 +332,7 @@ static bool sna_present_queue(struct sna_present_event *info,
if (!sna_fake_vblank(info))
return false;
} else {
info->queued = true;
if (delta == 1) {
sna_crtc_set_vblank(info->crtc);
info->crtc = mark_crtc(info->crtc);
}
add_to_crtc_vblank(info, delta);
}
return true;
@ -360,6 +362,47 @@ sna_present_get_crtc(WindowPtr window)
return NULL;
}
static void add_keepalive(struct sna *sna, xf86CrtcPtr crtc, uint64_t msc)
{
struct sna_present_event *info, *tmp;
union drm_wait_vblank vbl;
list_for_each_entry(tmp, sna_crtc_vblank_queue(crtc), link) {
if (tmp->target_msc == msc) {
DBG(("%s: vblank already queued for target_msc=%lld\n",
__FUNCTION__, (long long)msc));
return;
}
if ((int64_t)(tmp->target_msc - msc) > 0)
break;
}
DBG(("%s: adding keepalive for target_msc=%lld\n",
__FUNCTION__, (long long)msc));
info = info_alloc(sna);
if (!info)
return;
info->crtc = crtc;
info->sna = sna;
info->target_msc = msc;
info->event_id = (uint64_t *)(info + 1);
info->n_event_id = 0;
VG_CLEAR(vbl);
vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
vbl.request.sequence = msc;
vbl.request.signal = (uintptr_t)MARK_PRESENT(info);
if (sna_wait_vblank(info->sna, &vbl, sna_crtc_pipe(info->crtc)) == 0) {
list_add_tail(&info->link, &tmp->link);
add_to_crtc_vblank(info, 1);
} else
info_free(info);
}
static int
sna_present_get_ust_msc(RRCrtcPtr crtc, CARD64 *ust, CARD64 *msc)
{
@ -377,34 +420,10 @@ sna_present_get_ust_msc(RRCrtcPtr crtc, CARD64 *ust, CARD64 *msc)
vbl.request.type = DRM_VBLANK_RELATIVE;
vbl.request.sequence = 0;
if (sna_wait_vblank(sna, &vbl, sna_crtc_pipe(crtc->devPrivate)) == 0) {
struct sna_present_event *info;
*ust = ust64(vbl.reply.tval_sec, vbl.reply.tval_usec);
*msc = sna_crtc_record_vblank(crtc->devPrivate, &vbl);
info = info_alloc(sna);
if (info) {
info->crtc = crtc->devPrivate;
info->sna = sna;
info->target_msc = *msc + 1;
info->event_id = (uint64_t *)(info + 1);
info->n_event_id = 0;
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) {
list_add(&info->link,
&sna->present.vblank_queue);
info->queued = true;
sna_crtc_set_vblank(info->crtc);
info->crtc = mark_crtc(info->crtc);
} else
info_free(info);
}
add_keepalive(sna, crtc->devPrivate, *msc + 1);
} else {
const struct ust_msc *swap;
last:
@ -477,9 +496,8 @@ sna_present_queue_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
if (warn_unless(msc - swap->msc < 1ull<<31))
return BadValue;
list_for_each_entry(tmp, &sna->present.vblank_queue, link) {
if (tmp->target_msc == msc &&
unmask_crtc(tmp->crtc) == crtc->devPrivate) {
list_for_each_entry(tmp, sna_crtc_vblank_queue(crtc->devPrivate), link) {
if (tmp->target_msc == msc) {
uint64_t *events = tmp->event_id;
if (tmp->n_event_id &&
@ -692,8 +710,10 @@ present_flip_handler(struct drm_event_vblank *event, void *data)
swap.tv_sec = event->tv_sec;
swap.tv_usec = event->tv_usec;
swap.msc = event->sequence;
} else
} else {
info->crtc = unmask_crtc(info->crtc);
swap = *sna_crtc_last_swap(info->crtc);
}
DBG(("%s: pipe=%d, tv=%d.%06d msc=%lld (target %lld), event=%lld complete%s\n", __FUNCTION__,
info->crtc ? sna_crtc_pipe(info->crtc) : -1,
@ -702,8 +722,11 @@ present_flip_handler(struct drm_event_vblank *event, void *data)
(long long)info->event_id[0],
info->target_msc && info->target_msc == swap.msc ? "" : ": MISS"));
present_event_notify(info->event_id[0], swap_ust(&swap), swap.msc);
if (info->crtc)
if (info->crtc) {
sna_crtc_clear_vblank(info->crtc);
if (!sna_crtc_has_vblank(info->crtc))
add_keepalive(info->sna, info->crtc, swap.msc + 1);
}
if (info->sna->present.unflip) {
DBG(("%s: executing queued unflip (event=%lld)\n", __FUNCTION__, (long long)info->sna->present.unflip));
@ -747,9 +770,7 @@ flip(struct sna *sna,
return FALSE;
}
info->queued = true;
if (info->crtc)
sna_crtc_set_vblank(info->crtc);
add_to_crtc_vblank(info, 1);
return TRUE;
}