diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2012-12-30 14:50:49 +0000 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2012-12-31 15:55:28 +0000 |
commit | a7988bf77f5a106a48b6e39b6eaf60ef2f8bec11 (patch) | |
tree | 0d348cad9c9341f7980a7b407ce7aa6738fb22ac | |
parent | 736b89504a32239a0c7dfb5961c1b8292dd744bd (diff) |
sna/dri: Fix triple buffering to not penalise missed frames
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
-rw-r--r-- | src/sna/kgem.c | 45 | ||||
-rw-r--r-- | src/sna/kgem.h | 1 | ||||
-rw-r--r-- | src/sna/sna_dri.c | 792 | ||||
-rw-r--r-- | src/sna/sna_video.c | 3 |
4 files changed, 392 insertions, 449 deletions
diff --git a/src/sna/kgem.c b/src/sna/kgem.c index 21d5d1cb..f4c2b1c9 100644 --- a/src/sna/kgem.c +++ b/src/sna/kgem.c @@ -942,6 +942,7 @@ void kgem_init(struct kgem *kgem, int fd, struct pci_device *dev, unsigned gen) list_init(&kgem->large); list_init(&kgem->large_inactive); list_init(&kgem->snoop); + list_init(&kgem->scanout); for (i = 0; i < ARRAY_SIZE(kgem->pinned_batches); i++) list_init(&kgem->pinned_batches[i]); for (i = 0; i < ARRAY_SIZE(kgem->inactive); i++) @@ -1666,7 +1667,6 @@ static void __kgem_bo_destroy(struct kgem *kgem, struct kgem_bo *bo) assert(bo->proxy == NULL); bo->binding.offset = 0; - kgem_bo_clear_scanout(kgem, bo); if (DBG_NO_CACHE) goto destroy; @@ -1708,6 +1708,12 @@ static void __kgem_bo_destroy(struct kgem *kgem, struct kgem_bo *bo) bo->reusable = false; } + if (bo->scanout) { + DBG(("%s: handle=%d -> scanout\n", __FUNCTION__, bo->handle)); + list_add(&bo->list, &kgem->scanout); + return; + } + if (!bo->reusable) { DBG(("%s: handle=%d, not reusable\n", __FUNCTION__, bo->handle)); @@ -2705,6 +2711,13 @@ bool kgem_expire_cache(struct kgem *kgem) } + while (!list_is_empty(&kgem->scanout)) { + bo = list_first_entry(&kgem->scanout, struct kgem_bo, list); + list_del(&bo->list); + kgem_bo_clear_scanout(kgem, bo); + __kgem_bo_destroy(kgem, bo); + } + expire = 0; list_for_each_entry(bo, &kgem->snoop, list) { if (bo->delta) { @@ -3393,6 +3406,36 @@ struct kgem_bo *kgem_create_2d(struct kgem *kgem, size /= PAGE_SIZE; bucket = cache_bucket(size); + if (flags & CREATE_SCANOUT) { + list_for_each_entry(bo, &kgem->scanout, list) { + assert(bo->scanout); + assert(bo->delta); + assert(!bo->purged); + + if (size > num_pages(bo) || num_pages(bo) > 2*size) + continue; + + if (bo->tiling != tiling || + (tiling != I915_TILING_NONE && bo->pitch != pitch)) { + if (!gem_set_tiling(kgem->fd, bo->handle, + tiling, pitch)) + continue; + + bo->tiling = tiling; + bo->pitch = pitch; + } + + list_del(&bo->list); + + bo->unique_id = kgem_get_unique_id(kgem); + DBG((" 1:from scanout: pitch=%d, tiling=%d, handle=%d, id=%d\n", + bo->pitch, bo->tiling, bo->handle, bo->unique_id)); + assert(bo->pitch*kgem_aligned_height(kgem, height, bo->tiling) <= kgem_bo_size(bo)); + bo->refcnt = 1; + return bo; + } + } + if (bucket >= NUM_CACHE_BUCKETS) { DBG(("%s: large bo num pages=%d, bucket=%d\n", __FUNCTION__, size, bucket)); diff --git a/src/sna/kgem.h b/src/sna/kgem.h index bf457930..eed4132c 100644 --- a/src/sna/kgem.h +++ b/src/sna/kgem.h @@ -133,6 +133,7 @@ struct kgem { struct list inactive[NUM_CACHE_BUCKETS]; struct list pinned_batches[2]; struct list snoop; + struct list scanout; struct list batch_buffers, active_buffers; struct list requests[2]; diff --git a/src/sna/sna_dri.c b/src/sna/sna_dri.c index af75a553..9886f371 100644 --- a/src/sna/sna_dri.c +++ b/src/sna/sna_dri.c @@ -52,18 +52,17 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. #endif #if DRI2INFOREC_VERSION < 10 +#undef USE_ASYNC_SWAP #define USE_ASYNC_SWAP 0 #endif #define COLOR_PREFER_TILING_Y 0 -#define FLIP_OFF_DELAY 5 enum frame_event_type { DRI2_SWAP, DRI2_SWAP_WAIT, DRI2_SWAP_THROTTLE, DRI2_XCHG_THROTTLE, - DRI2_ASYNC_FLIP, DRI2_FLIP, DRI2_FLIP_THROTTLE, DRI2_WAITMSC, @@ -92,9 +91,9 @@ struct sna_dri_frame_event { struct dri_bo { struct kgem_bo *bo; uint32_t name; - } old_front, next_front, cache; + } scanout[2], cache; - int off_delay; + int mode; }; struct sna_dri_private { @@ -1019,8 +1018,10 @@ sna_dri_frame_event_info_free(struct sna *sna, _sna_dri_destroy_buffer(sna, info->front); _sna_dri_destroy_buffer(sna, info->back); - if (info->old_front.bo) - kgem_bo_destroy(&sna->kgem, info->old_front.bo); + assert(info->scanout[1].bo == NULL); + + if (info->scanout[0].bo) + kgem_bo_destroy(&sna->kgem, info->scanout[0].bo); if (info->cache.bo) kgem_bo_destroy(&sna->kgem, info->cache.bo); @@ -1035,23 +1036,34 @@ static bool sna_dri_page_flip(struct sna *sna, struct sna_dri_frame_event *info) { struct kgem_bo *bo = get_private(info->back)->bo; + struct dri_bo tmp; DBG(("%s()\n", __FUNCTION__)); assert(sna_pixmap_get_buffer(sna->front) == info->front); assert(get_drawable_pixmap(info->draw)->drawable.height * bo->pitch <= kgem_bo_size(bo)); + assert(info->scanout[0].bo); info->count = sna_page_flip(sna, bo, info, info->pipe); if (!info->count) return false; - info->old_front.name = info->front->name; - info->old_front.bo = get_private(info->front)->bo; + info->scanout[1] = info->scanout[0]; + info->scanout[0].bo = ref(bo); + info->scanout[0].name = info->back->name; + + tmp.bo = get_private(info->front)->bo; + tmp.name = info->front->name; set_bo(sna->front, bo); info->front->name = info->back->name; get_private(info->front)->bo = bo; + + info->back->name = tmp.name; + get_private(info->back)->bo = tmp.bo; + + sna->dri.flip_pending = info; return true; } @@ -1117,6 +1129,7 @@ can_flip(struct sna * sna, return false; } assert(get_private(front)->pixmap == sna->front); + assert(sna_pixmap(sna->front)->gpu_bo == get_private(front)->bo); if (!get_private(back)->scanout) { DBG(("%s: no, DRI2 drawable was too small at time of creation)\n", @@ -1274,10 +1287,12 @@ sna_dri_exchange_buffers(DrawablePtr draw, pixmap->drawable.width, pixmap->drawable.height)); - DBG(("%s: back_bo pitch=%d, size=%d\n", - __FUNCTION__, back_bo->pitch, kgem_bo_size(back_bo))); - DBG(("%s: front_bo pitch=%d, size=%d\n", - __FUNCTION__, front_bo->pitch, kgem_bo_size(front_bo))); + DBG(("%s: back_bo pitch=%d, size=%d, ref=%d\n", + __FUNCTION__, back_bo->pitch, kgem_bo_size(back_bo), back_bo->refcnt)); + DBG(("%s: front_bo pitch=%d, size=%d, ref=%d\n", + __FUNCTION__, front_bo->pitch, kgem_bo_size(front_bo), front_bo->refcnt)); + assert(front_bo->refcnt); + assert(back_bo->refcnt); assert(sna_pixmap_get_buffer(pixmap) == front); assert(pixmap->drawable.height * back_bo->pitch <= kgem_bo_size(back_bo)); @@ -1387,12 +1402,9 @@ void sna_dri_vblank_handler(struct sna *sna, struct drm_event_vblank *event) case DRI2_FLIP: /* If we can still flip... */ if (can_flip(sna, draw, info->front, info->back) && - sna_dri_page_flip(sna, info)) { - info->back->name = info->old_front.name; - get_private(info->back)->bo = info->old_front.bo; - info->old_front.bo = NULL; + sna_dri_page_flip(sna, info)) return; - } + /* else fall through to blit */ case DRI2_SWAP: if (can_blit(sna, draw, info->front, info->back)) @@ -1449,24 +1461,91 @@ done: sna_dri_frame_event_info_free(sna, draw, info); } +static void +sna_dri_flip_get_back(struct sna *sna, struct sna_dri_frame_event *info) +{ + struct kgem_bo *bo; + uint32_t name; + + DBG(("%s: scanout=(%d, %d), back=%d, cache=%d\n", + __FUNCTION__, + info->scanout[0].bo ? info->scanout[0].bo->handle : 0, + info->scanout[1].bo ? info->scanout[1].bo->handle : 0, + get_private(info->back)->bo->handle, + info->cache.bo ? info->cache.bo->handle : 0)); + + bo = get_private(info->back)->bo; + if (!(bo == info->scanout[0].bo || bo == info->scanout[1].bo)) + return; + + bo = info->cache.bo; + name = info->cache.name; + if (bo == NULL || + bo == info->scanout[0].bo || + bo == info->scanout[1].bo) { + if (bo) { + DBG(("%s: discarding old backbuffer\n", __FUNCTION__)); + kgem_bo_destroy(&sna->kgem, bo); + } + DBG(("%s: allocating new backbuffer\n", __FUNCTION__)); + bo = kgem_create_2d(&sna->kgem, + info->draw->width, + info->draw->height, + info->draw->bitsPerPixel, + get_private(info->front)->bo->tiling, + CREATE_SCANOUT | CREATE_EXACT); + name = kgem_bo_flink(&sna->kgem, bo); + } + + info->cache.bo = get_private(info->back)->bo; + info->cache.name = info->back->name; + + get_private(info->back)->bo = bo; + info->back->name = name; + + assert(get_private(info->back)->bo != info->scanout[0].bo); + assert(get_private(info->back)->bo != info->scanout[1].bo); +} + static bool sna_dri_flip_continue(struct sna *sna, struct sna_dri_frame_event *info) { - struct dri_bo tmp; + DBG(("%s(mode=%d)\n", __FUNCTION__, info->mode)); - DBG(("%s()\n", __FUNCTION__)); + if (info->mode > 1){ + if (get_private(info->front)->bo != sna_pixmap(sna->front)->gpu_bo) + return false; - assert(sna_pixmap_get_buffer(get_drawable_pixmap(info->draw)) == info->front); + info->count = sna_page_flip(sna, + get_private(info->front)->bo, + info, info->pipe); + if (!info->count) + return false; - tmp = info->old_front; + info->scanout[1] = info->scanout[0]; + info->scanout[0].bo = ref(get_private(info->front)->bo); + info->scanout[0].name = info->front->name; + sna->dri.flip_pending = info; + } else { + if (!info->draw) + return false; - if (!sna_dri_page_flip(sna, info)) - return false; + assert(sna_pixmap_get_buffer(get_drawable_pixmap(info->draw)) == info->front); + if (!can_flip(sna, info->draw, info->front, info->back)) + return false; - get_private(info->back)->bo = tmp.bo; - info->back->name = tmp.name; + if (!sna_dri_page_flip(sna, info)) + return false; - info->next_front.name = 0; + sna_dri_flip_get_back(sna, info); + DRI2SwapComplete(info->client, info->draw, + 0, 0, 0, + DRI2_FLIP_COMPLETE, + info->client ? info->event_complete : NULL, + info->event_data); + } + + info->mode = 0; return true; } @@ -1488,23 +1567,9 @@ static void chain_flip(struct sna *sna) can_flip(sna, chain->draw, chain->front, chain->back) && sna_dri_page_flip(sna, chain)) { DBG(("%s: performing chained flip\n", __FUNCTION__)); - - chain->back->name = chain->old_front.name; - get_private(chain->back)->bo = chain->old_front.bo; - chain->old_front.bo = NULL; - - if (chain->count == 0) { - DRI2SwapComplete(chain->client, chain->draw, 0, 0, 0, - DRI2_EXCHANGE_COMPLETE, - chain->event_complete, - chain->event_data); - sna_dri_frame_event_info_free(sna, chain->draw, chain); - } else - sna->dri.flip_pending = chain; } else { if (can_blit(sna, chain->draw, chain->front, chain->back)) { DBG(("%s: emitting chained vsync'ed blit\n", __FUNCTION__)); - chain->bo = sna_dri_copy_to_front(sna, chain->draw, NULL, get_private(chain->front)->bo, get_private(chain->back)->bo, @@ -1526,6 +1591,14 @@ static void sna_dri_flip_event(struct sna *sna, flip->fe_tv_usec, flip->type)); + if (flip->cache.bo == NULL) { + flip->cache = flip->scanout[1]; + flip->scanout[1].bo = NULL; + } + if (flip->scanout[1].bo) { + kgem_bo_destroy(&sna->kgem, flip->scanout[1].bo); + flip->scanout[1].bo = NULL; + } if (sna->dri.flip_pending == flip) sna->dri.flip_pending = NULL; @@ -1553,12 +1626,9 @@ static void sna_dri_flip_event(struct sna *sna, if (sna->dri.flip_pending) { sna_dri_frame_event_info_free(sna, flip->draw, flip); chain_flip(sna); - } else if (!flip->next_front.name) { - /* Keep the pageflipping running for a couple of frames - * so we keep the uncached scanouts alive. - */ - DBG(("%s: flip chain complete, off-delay=%d\n", - __FUNCTION__, flip->off_delay)); + } else if (!flip->mode) { + DBG(("%s: flip chain complete\n", __FUNCTION__)); + if (flip->chain) { sna_dri_remove_frame_event((WindowPtr)flip->draw, flip); @@ -1570,31 +1640,8 @@ static void sna_dri_flip_event(struct sna *sna, flip->draw = NULL; } - if (flip->off_delay-- && flip->draw && - can_flip(sna, flip->draw, flip->front, flip->front) && - (flip->count = sna_page_flip(sna, - get_private(flip->front)->bo, - flip, flip->pipe))) { - assert(flip == sna_dri_window_get_chain((WindowPtr)flip->draw)); - sna->dri.flip_pending = flip; - } else { - DBG(("%s: flip chain complete, off\n", __FUNCTION__)); - sna_dri_frame_event_info_free(sna, flip->draw, flip); - } - } else if (flip->draw && - can_flip(sna, flip->draw, flip->front, flip->back) && - sna_dri_flip_continue(sna, flip)) { - DRI2SwapComplete(flip->client, flip->draw, - 0, 0, 0, - DRI2_FLIP_COMPLETE, - flip->client ? flip->event_complete : NULL, - flip->event_data); - if (flip->count) { - flip->off_delay = FLIP_OFF_DELAY; - sna->dri.flip_pending = flip; - } else - sna_dri_frame_event_info_free(sna, flip->draw, flip); - } else { + sna_dri_frame_event_info_free(sna, flip->draw, flip); + } else if (!sna_dri_flip_continue(sna, flip)) { DBG(("%s: no longer able to flip\n", __FUNCTION__)); if (flip->draw) { @@ -1615,77 +1662,6 @@ static void sna_dri_flip_event(struct sna *sna, } break; -#if USE_ASYNC_SWAP - case DRI2_ASYNC_FLIP: - DBG(("%s: async swap flip completed on pipe %d, pending? %d, new? %d\n", - __FUNCTION__, flip->pipe, - sna->dri.flip_pending != NULL, - flip->front->name != flip->next_front.name)); - - if (sna->dri.flip_pending) { - chain_flip(sna); - goto finish_async_flip; - } else if (flip->front->name != flip->next_front.name) { - DBG(("%s: async flip continuing\n", __FUNCTION__)); - - flip->cache = flip->old_front; - flip->old_front = flip->next_front; - - flip->count = sna_page_flip(sna, - get_private(flip->front)->bo, - flip, flip->pipe); - if (flip->count == 0) { - if (flip->chain) - goto chain_async_flip; - else - goto finish_async_flip; - } - - flip->next_front.bo = get_private(flip->front)->bo; - flip->next_front.name = flip->front->name; - flip->off_delay = FLIP_OFF_DELAY; - - sna->dri.flip_pending = flip; - } else { - if (flip->chain) { -chain_async_flip: - sna_dri_remove_frame_event((WindowPtr)flip->draw, - flip); - chain_swap(sna, flip->draw, - flip->fe_frame, - flip->fe_tv_sec, - flip->fe_tv_usec, - flip->chain); - flip->draw = NULL; - } - - if (flip->draw && - can_flip(sna, flip->draw, flip->front, flip->back) && - flip->off_delay--) { - assert(flip == sna_dri_window_get_chain((WindowPtr)flip->draw)); - DBG(("%s: queuing no-flip [delay=%d]\n", - __FUNCTION__, flip->off_delay)); - /* Just queue a no-op flip to trigger another event */ - flip->count = sna_page_flip(sna, - get_private(flip->front)->bo, - flip, flip->pipe); - if (flip->count == 0) - goto finish_async_flip; - - assert(flip->next_front.bo == get_private(flip->front)->bo); - assert(flip->next_front.name == flip->front->name); - - sna->dri.flip_pending = flip; - } else { -finish_async_flip: - DBG(("%s: async flip completed (drawable gone? %d)\n", - __FUNCTION__, flip->draw == NULL)); - sna_dri_frame_event_info_free(sna, flip->draw, flip); - } - } - break; -#endif - default: xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING, "%s: unknown vblank event received\n", __func__); @@ -1715,6 +1691,105 @@ sna_dri_page_flip_handler(struct sna *sna, sna_dri_flip_event(sna, info); } +static void +sna_dri_immediate_xchg(struct sna *sna, + DrawablePtr draw, + struct sna_dri_frame_event *info, + bool sync) +{ + drmVBlank vbl; + + if (sna->flags & SNA_NO_WAIT) + sync = false; + + DBG(("%s: emitting immediate exchange, throttling client, synced? %d\n", + __FUNCTION__, sync)); + VG_CLEAR(vbl); + + if (sync) { + info->type = DRI2_XCHG_THROTTLE; + if (sna_dri_window_get_chain((WindowPtr)draw) == info) { + DBG(("%s: no pending xchg, starting chain\n", + __FUNCTION__)); + + sna_dri_exchange_buffers(draw, info->front, info->back); + DRI2SwapComplete(info->client, draw, 0, 0, 0, + DRI2_EXCHANGE_COMPLETE, + info->event_complete, + info->event_data); + vbl.request.type = + DRM_VBLANK_RELATIVE | + DRM_VBLANK_NEXTONMISS | + DRM_VBLANK_EVENT | + pipe_select(info->pipe); + vbl.request.sequence = 0; + vbl.request.signal = (unsigned long)info; + if (sna_wait_vblank(sna, &vbl)) + sna_dri_frame_event_info_free(sna, draw, info); + } + } else { + sna_dri_exchange_buffers(draw, info->front, info->back); + DRI2SwapComplete(info->client, draw, 0, 0, 0, + DRI2_EXCHANGE_COMPLETE, + info->event_complete, + info->event_data); + sna_dri_frame_event_info_free(sna, draw, info); + } +} + +static void +sna_dri_immediate_blit(struct sna *sna, + DrawablePtr draw, + struct sna_dri_frame_event *info, + bool sync) +{ + if (sna->flags & SNA_NO_WAIT) + sync = false; + + DBG(("%s: emitting immediate blit, throttling client, synced? %d\n", + __FUNCTION__, sync)); + + if (sync) { + info->type = DRI2_SWAP_THROTTLE; + if (sna_dri_window_get_chain((WindowPtr)draw) == info) { + drmVBlank vbl; + + DBG(("%s: no pending blit, starting chain\n", + __FUNCTION__)); + + info->bo = sna_dri_copy_to_front(sna, draw, NULL, + get_private(info->front)->bo, + get_private(info->back)->bo, + true); + DRI2SwapComplete(info->client, draw, 0, 0, 0, + DRI2_BLIT_COMPLETE, + info->event_complete, + info->event_data); + + VG_CLEAR(vbl); + vbl.request.type = + DRM_VBLANK_RELATIVE | + DRM_VBLANK_NEXTONMISS | + DRM_VBLANK_EVENT | + pipe_select(info->pipe); + vbl.request.sequence = 0; + vbl.request.signal = (unsigned long)info; + if (sna_wait_vblank(sna, &vbl)) + sna_dri_frame_event_info_free(sna, draw, info); + } + } else { + info->bo = sna_dri_copy_to_front(sna, draw, NULL, + get_private(info->front)->bo, + get_private(info->back)->bo, + false); + DRI2SwapComplete(info->client, draw, 0, 0, 0, + DRI2_BLIT_COMPLETE, + info->event_complete, + info->event_data); + sna_dri_frame_event_info_free(sna, draw, info); + } +} + static CARD64 get_current_msc_for_target(struct sna *sna, CARD64 target_msc, int pipe) { @@ -1734,50 +1809,43 @@ get_current_msc_for_target(struct sna *sna, CARD64 target_msc, int pipe) } static bool -sna_dri_schedule_flip(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, - DRI2BufferPtr back, CARD64 *target_msc, CARD64 divisor, - CARD64 remainder, DRI2SwapEventPtr func, void *data) +sna_dri_schedule_flip(ClientPtr client, DrawablePtr draw, + DRI2BufferPtr front, DRI2BufferPtr back, int pipe, + CARD64 *target_msc, CARD64 divisor, CARD64 remainder, + DRI2SwapEventPtr func, void *data) { struct sna *sna = to_sna_from_drawable(draw); struct sna_dri_frame_event *info; drmVBlank vbl; - int pipe; CARD64 current_msc; - DBG(("%s(target_msc=%llu, divisor=%llu, remainder=%llu)\n", - __FUNCTION__, - (long long)*target_msc, - (long long)divisor, - (long long)remainder)); - - VG_CLEAR(vbl); - - pipe = sna_dri_get_pipe(draw); - if (pipe == -1) { - /* XXX WARN_ON(sna->dri.flip_pending) ? */ - if (sna->dri.flip_pending == NULL) { - sna_dri_exchange_buffers(draw, front, back); - DRI2SwapComplete(client, draw, 0, 0, 0, - DRI2_EXCHANGE_COMPLETE, func, data); - return true; - } else - return false; - } - current_msc = get_current_msc_for_target(sna, *target_msc, pipe); DBG(("%s: target_msc=%u, current_msc=%u, divisor=%u\n", __FUNCTION__, (uint32_t)*target_msc, (uint32_t)current_msc, (uint32_t)divisor)); - if (divisor == 0 && current_msc >= *target_msc) { - DBG(("%s: performing immediate swap on pipe %d, pending? %d\n", - __FUNCTION__, pipe, sna->dri.flip_pending != NULL)); - + if (divisor == 0 && current_msc >= *target_msc - 1) { info = sna->dri.flip_pending; - if (info && info->draw == draw && info->type == DRI2_FLIP_THROTTLE) { - DBG(("%s: chaining flip\n", __FUNCTION__)); - info->next_front.name = 1; - return true; + + DBG(("%s: performing immediate swap on pipe %d, pending? %d, mode: %d\n", + __FUNCTION__, pipe, info != NULL, info ? info->mode : 0)); + + if (info && info->draw == draw) { + assert(info->type == DRI2_FLIP_THROTTLE); + assert(info->front == front); + assert(info->back == back); + if (current_msc >= *target_msc) { + DBG(("%s: executing xchg of pending flip\n", + __FUNCTION__)); + sna_dri_exchange_buffers(draw, front, back); + info->mode = 2; + goto new_back; + } else { + DBG(("%s: chaining flip\n", __FUNCTION__)); + info->mode = 1; + current_msc++; + goto out; + } } info = calloc(1, sizeof(struct sna_dri_frame_event)); @@ -1794,6 +1862,9 @@ sna_dri_schedule_flip(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, info->back = back; info->pipe = pipe; + info->scanout[0].bo = ref(get_private(front)->bo); + info->scanout[0].name = info->front->name; + sna_dri_add_frame_event(draw, info); sna_dri_reference_buffer(front); sna_dri_reference_buffer(back); @@ -1807,202 +1878,109 @@ sna_dri_schedule_flip(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, __FUNCTION__)); info->type = DRI2_FLIP; sna->dri.flip_pending = info; + *target_msc = current_msc + 1; return true; } if (!sna_dri_page_flip(sna, info)) { sna_dri_frame_event_info_free(sna, draw, info); return false; - } else if (info->type != DRI2_FLIP) { - get_private(info->back)->bo = - kgem_create_2d(&sna->kgem, - draw->width, - draw->height, - draw->bitsPerPixel, - get_private(info->front)->bo->tiling, - CREATE_SCANOUT | CREATE_EXACT); - info->back->name = kgem_bo_flink(&sna->kgem, - get_private(info->back)->bo); - info->off_delay = FLIP_OFF_DELAY; - sna->dri.flip_pending = info; + } - DRI2SwapComplete(info->client, draw, 0, 0, 0, + if (info->type != DRI2_FLIP) { + current_msc++; +new_back: + sna_dri_flip_get_back(sna, info); + DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_EXCHANGE_COMPLETE, - info->event_complete, - info->event_data); - } else { - info->back->name = info->old_front.name; - get_private(info->back)->bo = info->old_front.bo; - info->old_front.bo = NULL; + func, data); } - } else { - info = calloc(1, sizeof(struct sna_dri_frame_event)); - if (info == NULL) - return false; - - info->draw = draw; - info->client = client; - info->event_complete = func; - info->event_data = data; - info->front = front; - info->back = back; - info->pipe = pipe; - info->type = DRI2_FLIP; - - sna_dri_add_frame_event(draw, info); - sna_dri_reference_buffer(front); - sna_dri_reference_buffer(back); - - *target_msc &= 0xffffffff; - remainder &= 0xffffffff; - - vbl.request.type = - DRM_VBLANK_ABSOLUTE | - DRM_VBLANK_EVENT | - pipe_select(pipe); +out: + *target_msc = current_msc; + return true; + } - /* - * If divisor is zero, or current_msc is smaller than target_msc - * we just need to make sure target_msc passes before initiating - * the swap. - */ - if (current_msc < *target_msc) { - DBG(("%s: waiting for swap: current=%d, target=%d, divisor=%d\n", - __FUNCTION__, - (int)current_msc, - (int)*target_msc, - (int)divisor)); - vbl.request.sequence = *target_msc; - } else { - DBG(("%s: missed target, queueing event for next: current=%d, target=%d, divisor=%d\n", - __FUNCTION__, - (int)current_msc, - (int)*target_msc, - (int)divisor)); - - if (divisor == 0) - divisor = 1; - - vbl.request.sequence = current_msc - current_msc % divisor + remainder; - - /* - * If the calculated deadline vbl.request.sequence is - * smaller than or equal to current_msc, it means - * we've passed the last point when effective onset - * frame seq could satisfy *seq % divisor == remainder, - * so we need to wait for the next time this will - * happen. - * - * This comparison takes the 1 frame swap delay - * in pageflipping mode into account. - */ - if (vbl.request.sequence <= current_msc) - vbl.request.sequence += divisor; + info = calloc(1, sizeof(struct sna_dri_frame_event)); + if (info == NULL) + return false; - /* Adjust returned value for 1 frame pageflip offset */ - *target_msc = vbl.reply.sequence + 1; - } + info->draw = draw; + info->client = client; + info->event_complete = func; + info->event_data = data; + info->front = front; + info->back = back; + info->pipe = pipe; + info->type = DRI2_FLIP; - /* Account for 1 frame extra pageflip delay */ - vbl.request.sequence -= 1; - vbl.request.signal = (unsigned long)info; - if (sna_wait_vblank(sna, &vbl)) { - sna_dri_frame_event_info_free(sna, draw, info); - return false; - } - } + info->scanout[0].bo = ref(get_private(front)->bo); + info->scanout[0].name = info->front->name; - return true; -} + sna_dri_add_frame_event(draw, info); + sna_dri_reference_buffer(front); + sna_dri_reference_buffer(back); -static void -sna_dri_immediate_xchg(struct sna *sna, - DrawablePtr draw, - struct sna_dri_frame_event *info) -{ - drmVBlank vbl; + *target_msc &= 0xffffffff; + remainder &= 0xffffffff; - DBG(("%s: emitting immediate exchange, throttling client\n", - __FUNCTION__)); VG_CLEAR(vbl); - if ((sna->flags & SNA_NO_WAIT) == 0) { - info->type = DRI2_XCHG_THROTTLE; - if (sna_dri_window_get_chain((WindowPtr)draw) == info) { - DBG(("%s: no pending xchg, starting chain\n", - __FUNCTION__)); + vbl.request.type = + DRM_VBLANK_ABSOLUTE | + DRM_VBLANK_EVENT | + pipe_select(pipe); - sna_dri_exchange_buffers(draw, info->front, info->back); - DRI2SwapComplete(info->client, draw, 0, 0, 0, - DRI2_EXCHANGE_COMPLETE, - info->event_complete, - info->event_data); - vbl.request.type = - DRM_VBLANK_RELATIVE | - DRM_VBLANK_NEXTONMISS | - DRM_VBLANK_EVENT | - pipe_select(info->pipe); - vbl.request.sequence = 0; - vbl.request.signal = (unsigned long)info; - if (sna_wait_vblank(sna, &vbl)) - sna_dri_frame_event_info_free(sna, draw, info); - } + /* + * If divisor is zero, or current_msc is smaller than target_msc + * we just need to make sure target_msc passes before initiating + * the swap. + */ + if (current_msc <= *target_msc - 1) { + DBG(("%s: waiting for swap: current=%d, target=%d, divisor=%d\n", + __FUNCTION__, + (int)current_msc, + (int)*target_msc, + (int)divisor)); + vbl.request.sequence = *target_msc; } else { - sna_dri_exchange_buffers(draw, info->front, info->back); - DRI2SwapComplete(info->client, draw, 0, 0, 0, - DRI2_EXCHANGE_COMPLETE, - info->event_complete, - info->event_data); - sna_dri_frame_event_info_free(sna, draw, info); - } -} + DBG(("%s: missed target, queueing event for next: current=%d, target=%d, divisor=%d\n", + __FUNCTION__, + (int)current_msc, + (int)*target_msc, + (int)divisor)); -static void -sna_dri_immediate_blit(struct sna *sna, - DrawablePtr draw, - struct sna_dri_frame_event *info) -{ - DBG(("%s: emitting immediate blit, throttling client\n", __FUNCTION__)); + if (divisor == 0) + divisor = 1; - if ((sna->flags & SNA_NO_WAIT) == 0) { - info->type = DRI2_SWAP_THROTTLE; - if (sna_dri_window_get_chain((WindowPtr)draw) == info) { - drmVBlank vbl; + vbl.request.sequence = current_msc - current_msc % divisor + remainder; - DBG(("%s: no pending blit, starting chain\n", - __FUNCTION__)); + /* + * If the calculated deadline vbl.request.sequence is + * smaller than or equal to current_msc, it means + * we've passed the last point when effective onset + * frame seq could satisfy *seq % divisor == remainder, + * so we need to wait for the next time this will + * happen. + * + * This comparison takes the 1 frame swap delay + * in pageflipping mode into account. + */ + if (vbl.request.sequence <= current_msc) + vbl.request.sequence += divisor; - info->bo = sna_dri_copy_to_front(sna, draw, NULL, - get_private(info->front)->bo, - get_private(info->back)->bo, - true); - DRI2SwapComplete(info->client, draw, 0, 0, 0, - DRI2_BLIT_COMPLETE, - info->event_complete, - info->event_data); + /* Adjust returned value for 1 frame pageflip offset */ + *target_msc = vbl.reply.sequence; + } - VG_CLEAR(vbl); - vbl.request.type = - DRM_VBLANK_RELATIVE | - DRM_VBLANK_NEXTONMISS | - DRM_VBLANK_EVENT | - pipe_select(info->pipe); - vbl.request.sequence = 0; - vbl.request.signal = (unsigned long)info; - if (sna_wait_vblank(sna, &vbl)) - sna_dri_frame_event_info_free(sna, draw, info); - } - } else { - info->bo = sna_dri_copy_to_front(sna, draw, NULL, - get_private(info->front)->bo, - get_private(info->back)->bo, - false); - DRI2SwapComplete(info->client, draw, 0, 0, 0, - DRI2_BLIT_COMPLETE, - info->event_complete, - info->event_data); + /* Account for 1 frame extra pageflip delay */ + vbl.request.sequence -= 1; + vbl.request.signal = (unsigned long)info; + if (sna_wait_vblank(sna, &vbl)) { sna_dri_frame_event_info_free(sna, draw, info); + return false; } + + return true; } /* @@ -2051,30 +2029,19 @@ sna_dri_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, divisor &= 0xffffffff; remainder &= 0xffffffff; - if (can_flip(sna, draw, front, back)) { - DBG(("%s: try flip\n", __FUNCTION__)); - if (sna_dri_schedule_flip(client, draw, front, back, - target_msc, divisor, remainder, - func, data)) - return TRUE; - } - /* Drawable not displayed... just complete the swap */ pipe = sna_dri_get_pipe(draw); if (pipe == -1) { - if (can_exchange(sna, draw, front, back)) { - DBG(("%s: unattached, exchange pixmaps\n", __FUNCTION__)); - sna_dri_exchange_buffers(draw, front, back); - - DRI2SwapComplete(client, draw, 0, 0, 0, - DRI2_EXCHANGE_COMPLETE, func, data); - return TRUE; - } - DBG(("%s: off-screen, immediate update\n", __FUNCTION__)); goto blit_fallback; } + if (can_flip(sna, draw, front, back) && + sna_dri_schedule_flip(client, draw, front, back, pipe, + target_msc, divisor, remainder, + func, data)) + return TRUE; + VG_CLEAR(vbl); info = calloc(1, sizeof(struct sna_dri_frame_event)); @@ -2099,16 +2066,19 @@ sna_dri_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, DBG(("%s: target_msc=%u, current_msc=%u, divisor=%u\n", __FUNCTION__, (uint32_t)*target_msc, (uint32_t)current_msc, (uint32_t)divisor)); - if (divisor == 0 && current_msc >= *target_msc) { + if (divisor == 0 && current_msc >= *target_msc - 1) { if (can_exchange(sna, draw, front, back)) { - sna_dri_immediate_xchg(sna, draw, info); + sna_dri_immediate_xchg(sna, draw, info, + current_msc < *target_msc); } else if (can_blit(sna, draw, front, back)) { - sna_dri_immediate_blit(sna, draw, info); + sna_dri_immediate_blit(sna, draw, info, + current_msc < *target_msc); } else { DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data); sna_dri_frame_event_info_free(sna, draw, info); } + *target_msc = current_msc + 1; return TRUE; } @@ -2168,13 +2138,13 @@ sna_dri_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, */ if (vbl.request.sequence < current_msc) vbl.request.sequence += divisor; - vbl.request.sequence -= 1; + *target_msc = vbl.reply.sequence; + vbl.request.sequence -= 1; vbl.request.signal = (unsigned long)info; if (sna_wait_vblank(sna, &vbl)) goto blit_fallback; - *target_msc = vbl.reply.sequence; return TRUE; blit_fallback: @@ -2204,19 +2174,20 @@ sna_dri_async_swap(ClientPtr client, DrawablePtr draw, DRI2SwapEventPtr func, void *data) { struct sna *sna = to_sna_from_drawable(draw); - struct sna_dri_frame_event *info; - struct kgem_bo *bo; - int name; + CARD64 target_msc = 0; + int pipe; DBG(("%s()\n", __FUNCTION__)); - if (!can_flip(sna, draw, front, back)) { -blit: - name = DRI2_BLIT_COMPLETE; + if (!can_flip(sna, draw, front, back) || + (pipe = sna_dri_get_pipe(draw)) < 0 || + !sna_dri_schedule_flip(client, draw, front, back, pipe, + &target_msc, 0, 0, func, data)) { + pipe = DRI2_BLIT_COMPLETE; if (can_exchange(sna, draw, front, back)) { DBG(("%s: unable to flip, so xchg\n", __FUNCTION__)); sna_dri_exchange_buffers(draw, front, back); - name = DRI2_EXCHANGE_COMPLETE; + pipe = DRI2_EXCHANGE_COMPLETE; } else if (can_blit(sna, draw, front, back)) { DBG(("%s: unable to flip, so blit\n", __FUNCTION__)); sna_dri_copy_to_front(sna, draw, NULL, @@ -2225,80 +2196,9 @@ blit: false); } - DRI2SwapComplete(client, draw, 0, 0, 0, name, func, data); - return name == DRI2_EXCHANGE_COMPLETE; + DRI2SwapComplete(client, draw, 0, 0, 0, pipe, func, data); + return pipe == DRI2_EXCHANGE_COMPLETE; } - - bo = NULL; - name = 0; - - info = sna->dri.flip_pending; - if (info == NULL) { - int pipe = sna_dri_get_pipe(draw); - if (pipe == -1) - goto blit; - - DBG(("%s: no pending flip, so updating scanout\n", - __FUNCTION__)); - - info = calloc(1, sizeof(struct sna_dri_frame_event)); - if (!info) - goto blit; - - info->client = client; - info->draw = draw; - info->type = DRI2_ASYNC_FLIP; - info->pipe = pipe; - info->front = front; - info->back = back; - - sna_dri_add_frame_event(draw, info); - sna_dri_reference_buffer(front); - sna_dri_reference_buffer(back); - - if (!sna_dri_page_flip(sna, info)) - goto blit; - - info->next_front.name = info->front->name; - info->next_front.bo = get_private(info->front)->bo; - info->off_delay = FLIP_OFF_DELAY; - } else if (info->type != DRI2_ASYNC_FLIP) { - /* A normal vsync'ed client is finishing, wait for it - * to unpin the old framebuffer before taking over. - */ - goto blit; - } else { - DBG(("%s: pending flip, chaining next\n", __FUNCTION__)); - if (info->next_front.name == info->front->name) { - name = info->cache.name; - bo = info->cache.bo; - } else { - name = info->front->name; - bo = get_private(info->front)->bo; - } - info->front->name = info->back->name; - get_private(info->front)->bo = get_private(info->back)->bo; - } - - if (bo == NULL) { - DBG(("%s: creating new back buffer\n", __FUNCTION__)); - bo = kgem_create_2d(&sna->kgem, - draw->width, - draw->height, - draw->bitsPerPixel, - get_private(info->front)->bo->tiling, - CREATE_SCANOUT | CREATE_EXACT); - name = kgem_bo_flink(&sna->kgem, bo); - } - assert(bo->refcnt); - get_private(info->back)->bo = bo; - info->back->name = name; - - set_bo(sna->front, get_private(info->front)->bo); - sna->dri.flip_pending = info; - - DRI2SwapComplete(client, draw, 0, 0, 0, - DRI2_EXCHANGE_COMPLETE, func, data); return TRUE; } #endif diff --git a/src/sna/sna_video.c b/src/sna/sna_video.c index 8d111d54..05d76dda 100644 --- a/src/sna/sna_video.c +++ b/src/sna/sna_video.c @@ -104,8 +104,7 @@ sna_video_buffer(struct sna *sna, if (video->tiled) { video->buf = kgem_create_2d(&sna->kgem, frame->width, frame->height, 32, - I915_TILING_X, - CREATE_EXACT | CREATE_SCANOUT); + I915_TILING_X, CREATE_EXACT); } else { video->buf = kgem_create_linear(&sna->kgem, frame->size, CREATE_GTT_MAP); |