From c709f2447dfc6dc36c50ff741d5d9bbdc7c03b58 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 28 May 2012 08:17:21 +0100 Subject: [PATCH] sna/dri: Requeue vblank throttling until the vsync'ed copy completes If the GPU is busy, then we may not actually schedule our copy for several vblanks, resulting in us falsely reporting that the work completed too early and allowing the client to continue scheduling more work and racing ahead of the queued copies. Signed-off-by: Chris Wilson --- src/sna/kgem.c | 33 ++++++++++++++------ src/sna/kgem.h | 1 + src/sna/sna_dri.c | 79 +++++++++++++++++++++++++++++++---------------- 3 files changed, 78 insertions(+), 35 deletions(-) diff --git a/src/sna/kgem.c b/src/sna/kgem.c index 9d2fd991..0ef160c3 100644 --- a/src/sna/kgem.c +++ b/src/sna/kgem.c @@ -1382,17 +1382,18 @@ static bool kgem_retire__requests(struct kgem *kgem) } } - rq->bo->refcnt--; - assert(rq->bo->refcnt == 0); assert(rq->bo->rq == NULL); assert(list_is_empty(&rq->bo->request)); - if (kgem_bo_set_purgeable(kgem, rq->bo)) { - kgem_bo_move_to_inactive(kgem, rq->bo); - retired = true; - } else { - DBG(("%s: closing %d\n", - __FUNCTION__, rq->bo->handle)); - kgem_bo_free(kgem, rq->bo); + + if (--rq->bo->refcnt == 0) { + if (kgem_bo_set_purgeable(kgem, rq->bo)) { + kgem_bo_move_to_inactive(kgem, rq->bo); + retired = true; + } else { + DBG(("%s: closing %d\n", + __FUNCTION__, rq->bo->handle)); + kgem_bo_free(kgem, rq->bo); + } } if (kgem->sync == rq) @@ -4221,3 +4222,17 @@ kgem_replace_bo(struct kgem *kgem, return dst; } + +struct kgem_bo *kgem_get_last_request(struct kgem *kgem) +{ + struct kgem_request *rq; + + if (list_is_empty(&kgem->requests)) + return NULL; + + rq = list_last_entry(&kgem->requests, + struct kgem_request, + list); + + return kgem_bo_reference(rq->bo); +} diff --git a/src/sna/kgem.h b/src/sna/kgem.h index fdabfae9..bb6bcf28 100644 --- a/src/sna/kgem.h +++ b/src/sna/kgem.h @@ -245,6 +245,7 @@ void kgem_bo_set_binding(struct kgem_bo *bo, uint32_t format, uint16_t offset); void kgem_bo_retire(struct kgem *kgem, struct kgem_bo *bo); bool kgem_retire(struct kgem *kgem); +struct kgem_bo *kgem_get_last_request(struct kgem *kgem); void _kgem_submit(struct kgem *kgem); static inline void kgem_submit(struct kgem *kgem) diff --git a/src/sna/sna_dri.c b/src/sna/sna_dri.c index 632c57dc..ee509c05 100644 --- a/src/sna/sna_dri.c +++ b/src/sna/sna_dri.c @@ -90,6 +90,7 @@ struct sna_dri_frame_event { void *event_data; DRI2BufferPtr front; DRI2BufferPtr back; + struct kgem_bo *bo; unsigned int fe_frame; unsigned int fe_tv_sec; @@ -395,13 +396,14 @@ static void set_bo(PixmapPtr pixmap, struct kgem_bo *bo) priv->gpu_bo = ref(bo); } -static void +static struct kgem_bo * sna_dri_copy_to_front(struct sna *sna, DrawablePtr draw, RegionPtr region, struct kgem_bo *dst_bo, struct kgem_bo *src_bo, bool sync) { PixmapPtr pixmap = get_drawable_pixmap(draw); pixman_region16_t clip; + struct kgem_bo *bo = NULL; bool flush = false; xf86CrtcPtr crtc; BoxRec box, *boxes; @@ -421,7 +423,7 @@ sna_dri_copy_to_front(struct sna *sna, DrawablePtr draw, RegionPtr region, if (!pixman_region_not_empty(region)) { DBG(("%s: all clipped\n", __FUNCTION__)); - return; + return NULL; } } @@ -443,7 +445,7 @@ sna_dri_copy_to_front(struct sna *sna, DrawablePtr draw, RegionPtr region, pixman_region_intersect(region, &win->clipList, region); if (!pixman_region_not_empty(region)) { DBG(("%s: all clipped\n", __FUNCTION__)); - return; + return NULL; } if (pixmap == sna->front && sync) { @@ -495,6 +497,7 @@ sna_dri_copy_to_front(struct sna *sna, DrawablePtr draw, RegionPtr region, if (flush) { /* STAT! */ assert(sna_crtc_is_bound(sna, crtc)); kgem_submit(&sna->kgem); + bo = kgem_get_last_request(&sna->kgem); } pixman_region_translate(region, dx, dy); @@ -503,6 +506,8 @@ sna_dri_copy_to_front(struct sna *sna, DrawablePtr draw, RegionPtr region, if (region == &clip) pixman_region_fini(&clip); + + return bo; } static void @@ -650,7 +655,7 @@ sna_dri_copy_region(DrawablePtr draw, if (dst_buffer->attachment == DRI2BufferFrontLeft) { dst = sna_pixmap_get_bo(pixmap); - copy = sna_dri_copy_to_front; + copy = (void *)sna_dri_copy_to_front; } else dst = get_private(dst_buffer)->bo; @@ -884,6 +889,9 @@ sna_dri_frame_event_info_free(struct sna_dri_frame_event *info) sna_dri_frame_event_release_bo(&info->sna->kgem, info->cache.bo); + if (info->bo) + kgem_bo_destroy(&info->sna->kgem, info->bo); + free(info); } @@ -1052,13 +1060,32 @@ static void sna_dri_vblank_handle(int fd, } /* else fall through to blit */ case DRI2_SWAP: - sna_dri_copy_to_front(sna, draw, NULL, - get_private(info->front)->bo, - get_private(info->back)->bo, - true); + info->bo = sna_dri_copy_to_front(sna, draw, NULL, + get_private(info->front)->bo, + get_private(info->back)->bo, + true); + info->type = DRI2_SWAP_THROTTLE; case DRI2_SWAP_THROTTLE: DBG(("%s: %d complete, frame=%d tv=%d.%06d\n", __FUNCTION__, info->type, frame, tv_sec, tv_usec)); + + if (info->bo && kgem_bo_is_busy(info->bo)) { + kgem_retire(&sna->kgem); + if (kgem_bo_is_busy(info->bo)) { + drmVBlank vbl; + + vbl.request.type = + DRM_VBLANK_RELATIVE | + DRM_VBLANK_EVENT; + if (info->pipe > 0) + vbl.request.type |= DRM_VBLANK_SECONDARY; + vbl.request.sequence = 1; + vbl.request.signal = (unsigned long)info; + if (!drmWaitVBlank(sna->kgem.fd, &vbl)) + return; + } + } + DRI2SwapComplete(info->client, draw, frame, tv_sec, tv_usec, @@ -1560,26 +1587,26 @@ sna_dri_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, DBG(("%s: emitting immediate vsync'ed blit, throttling client\n", __FUNCTION__)); - info->type = DRI2_SWAP_THROTTLE; + info->bo = sna_dri_copy_to_front(sna, draw, NULL, + get_private(front)->bo, + get_private(back)->bo, + true); - vbl.request.type = - DRM_VBLANK_RELATIVE | - DRM_VBLANK_EVENT | - DRM_VBLANK_NEXTONMISS; - if (pipe > 0) - vbl.request.type |= DRM_VBLANK_SECONDARY; - vbl.request.sequence = 0; - vbl.request.signal = (unsigned long)info; - if (drmWaitVBlank(sna->kgem.fd, &vbl)) { - sna_dri_frame_event_info_free(info); - DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data); - } + info->type = DRI2_SWAP_THROTTLE; + vbl.request.type = + DRM_VBLANK_RELATIVE | + DRM_VBLANK_NEXTONMISS | + DRM_VBLANK_EVENT; + if (pipe > 0) + vbl.request.type |= DRM_VBLANK_SECONDARY; + vbl.request.sequence = 0; + vbl.request.signal = (unsigned long)info; + if (drmWaitVBlank(sna->kgem.fd, &vbl)) { + sna_dri_frame_event_info_free(info); + DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data); + } - sna_dri_copy_to_front(sna, draw, NULL, - get_private(front)->bo, - get_private(back)->bo, - true); - return TRUE; + return TRUE; } /* Get current count */