diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2014-06-09 08:58:15 +0100 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2014-06-09 14:57:57 +0100 |
commit | 3932e97057fca16615adaefbc1eb25a0d51a1d8b (patch) | |
tree | 34cfc923aadfba5599a4e6ff32b0ffe896de85c2 /src/sna/sna_display.c | |
parent | af3130cbba8375b9060a3a8cef5b03189d01c419 (diff) |
sna/dri2: Allow TearFree flipping to individual CRTC
Baby step. We first take advantage of TearFree to allow us to redirect a
single CRTC to the DRI2 frontbuffer and so allow a fullscreen game
covering a single monitor to avoid expensive blits when running in a
multi-monitor setup.
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Diffstat (limited to 'src/sna/sna_display.c')
-rw-r--r-- | src/sna/sna_display.c | 295 |
1 files changed, 261 insertions, 34 deletions
diff --git a/src/sna/sna_display.c b/src/sna/sna_display.c index 51875151..8df3a90c 100644 --- a/src/sna/sna_display.c +++ b/src/sna/sna_display.c @@ -99,12 +99,14 @@ union compat_mode_get_connector{ extern XF86ConfigPtr xf86configptr; struct sna_crtc { + xf86CrtcPtr base; struct drm_mode_modeinfo kmode; int dpms_mode; PixmapPtr scanout_pixmap; struct kgem_bo *bo, *shadow_bo; struct sna_cursor *cursor; uint32_t sprite; + uint32_t offset; bool shadow; bool fallback_shadow; bool transform; @@ -125,6 +127,8 @@ struct sna_crtc { sna_flip_handler_t flip_handler; struct kgem_bo *flip_bo; void *flip_data; + + struct list shadow_link; }; struct sna_property { @@ -234,12 +238,18 @@ uint32_t sna_crtc_to_sprite(xf86CrtcPtr crtc) return to_sna_crtc(crtc)->sprite; } -int sna_crtc_is_on(xf86CrtcPtr crtc) +bool sna_crtc_is_on(xf86CrtcPtr crtc) { assert(to_sna_crtc(crtc)); return to_sna_crtc(crtc)->bo != NULL; } +bool sna_crtc_is_transformed(xf86CrtcPtr crtc) +{ + assert(to_sna_crtc(crtc)); + return to_sna_crtc(crtc)->transform; +} + static inline uint64_t msc64(struct sna_crtc *sna_crtc, uint32_t seq) { if (seq < sna_crtc->last_seq) { @@ -988,9 +998,11 @@ sna_crtc_apply(xf86CrtcPtr crtc) if (sna_crtc->transform) { arg.x = 0; arg.y = 0; + sna_crtc->offset = 0; } else { arg.x = crtc->x; arg.y = crtc->y; + sna_crtc->offset = arg.y << 16 | arg.x; } arg.set_connectors_ptr = (uintptr_t)output_ids; arg.count_connectors = output_count; @@ -1032,6 +1044,34 @@ static bool wait_for_shadow(struct sna *sna, struct sna_pixmap *priv, unsigned f if (flags == 0 || pixmap != sna->front || !sna->mode.shadow_damage) goto done; + if ((flags & __MOVE_SCANOUT) == 0) { + while (!list_is_empty(&sna->mode.shadow_crtc)) { + struct sna_crtc *crtc = + list_first_entry(&sna->mode.shadow_crtc, struct sna_crtc, shadow_link); + RegionRec region; + + DBG(("%s: copying replaced CRTC: (%d, %d), (%d, %d)\n", + __FUNCTION__, + crtc->base->bounds.x1, + crtc->base->bounds.y1, + crtc->base->bounds.x2, + crtc->base->bounds.y2)); + ret = sna->render.copy_boxes(sna, GXcopy, + pixmap, crtc->shadow_bo, -crtc->base->bounds.x1, -crtc->base->bounds.y1, + pixmap, priv->gpu_bo, 0, 0, + &crtc->base->bounds, 1, + 0); + + kgem_bo_destroy(&sna->kgem, crtc->shadow_bo); + crtc->shadow_bo = NULL; + list_del(&crtc->shadow_link); + + region.extents = crtc->base->bounds; + region.data = NULL; + RegionSubtract(&sna->mode.shadow_region, &sna->mode.shadow_region, ®ion); + } + } + if ((flags & MOVE_WRITE) == 0) return true; @@ -1123,7 +1163,8 @@ static bool wait_for_shadow(struct sna *sna, struct sna_pixmap *priv, unsigned f sna_dri2_pixmap_update_bo(sna, pixmap, bo); done: - RegionUninit(&sna->mode.shadow_region); + RegionEmpty(&sna->mode.shadow_region); + sna->mode.shadow_dirty = false; priv->move_to_gpu_data = NULL; priv->move_to_gpu = NULL; @@ -1199,6 +1240,7 @@ static void sna_mode_disable_shadow(struct sna *sna) } assert(sna->mode.shadow_active == 0); + sna->mode.shadow_dirty = false; } static bool sna_crtc_enable_shadow(struct sna *sna, struct sna_crtc *crtc) @@ -1235,6 +1277,13 @@ static void sna_crtc_disable_shadow(struct sna *sna, struct sna_crtc *crtc) sna_mode_disable_shadow(sna); if (crtc->shadow_bo) { + if (!crtc->transform) { + sna->render.copy_boxes(sna, GXcopy, + sna->front, crtc->shadow_bo, -crtc->base->bounds.x1, -crtc->base->bounds.y1, + sna->front, __sna_pixmap_get_bo(sna->front), 0, 0, + &crtc->base->bounds, 1, 0); + list_del(&crtc->shadow_link); + } kgem_bo_destroy(&sna->kgem, crtc->shadow_bo); crtc->shadow_bo = NULL; } @@ -1556,9 +1605,6 @@ static void set_shadow(struct sna *sna, RegionPtr region) assert((priv->pinned & PIN_PRIME) == 0); assert(sna->mode.shadow != priv->gpu_bo); - assert(priv->move_to_gpu == NULL); - - RegionNull(&sna->mode.shadow_region); RegionCopy(&sna->mode.shadow_region, region); priv->move_to_gpu = wait_for_shadow; @@ -1635,7 +1681,7 @@ static struct kgem_bo *sna_crtc_attach(xf86CrtcPtr crtc) return NULL; if (sna->flags & SNA_TEAR_FREE) { - DBG(("%s: creating TearFree shadow\n", __FUNCTION__)); + DBG(("%s: enabling TearFree shadow\n", __FUNCTION__)); if (!sna_crtc_enable_shadow(sna, sna_crtc)) return NULL; @@ -1643,6 +1689,8 @@ static struct kgem_bo *sna_crtc_attach(xf86CrtcPtr crtc) RegionRec region; struct kgem_bo *shadow; + DBG(("%s: creating TearFree shadow bo\n", __FUNCTION__)); + region.extents.x1 = 0; region.extents.y1 = 0; region.extents.x2 = sna->scrn->virtualX; @@ -1778,6 +1826,12 @@ sna_crtc_damage(xf86CrtcPtr crtc) assert(sna->mode.shadow_damage && sna->mode.shadow_active); damage = DamageRegion(sna->mode.shadow_damage); RegionUnion(damage, damage, ®ion); + + DBG(("%s: damage now %ldx[(%d, %d), (%d, %d)]\n", + __FUNCTION__, + (long)RegionNumRects(damage), + damage->extents.x1, damage->extents.y1, + damage->extents.x2, damage->extents.y2)); } static char *outputs_for_crtc(xf86CrtcPtr crtc, char *outputs, int max) @@ -1831,6 +1885,7 @@ sna_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, struct sna_crtc *sna_crtc = to_sna_crtc(crtc); struct kgem_bo *saved_bo, *bo; struct drm_mode_modeinfo saved_kmode; + uint32_t saved_offset; bool saved_transform; char outputs[256]; @@ -1859,6 +1914,7 @@ sna_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, saved_kmode = sna_crtc->kmode; saved_bo = sna_crtc->bo; saved_transform = sna_crtc->transform; + saved_offset = sna_crtc->offset; sna_crtc->fallback_shadow = false; retry: /* Attach per-crtc pixmap or direct */ @@ -1881,6 +1937,7 @@ retry: /* Attach per-crtc pixmap or direct */ xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, "failed to set mode: %s\n", strerror(errno)); + sna_crtc->offset = saved_offset; sna_crtc->transform = saved_transform; sna_crtc->bo = saved_bo; sna_crtc->kmode = saved_kmode; @@ -2112,6 +2169,8 @@ sna_crtc_add(ScrnInfoPtr scrn, int id) sna_crtc->pipe = get_pipe.pipe; sna_crtc->sprite = sna_crtc_find_sprite(sna, sna_crtc->pipe); + list_init(&sna_crtc->shadow_link); + if (xf86IsEntityShared(scrn->entityList[0]) && scrn->confScreen->device->screen != sna_crtc->pipe) { free(sna_crtc); @@ -2128,6 +2187,7 @@ sna_crtc_add(ScrnInfoPtr scrn, int id) sna_crtc_init__cursor(sna, sna_crtc); crtc->driver_private = sna_crtc; + sna_crtc->base = crtc; DBG(("%s: attached crtc[%d] pipe=%d\n", __FUNCTION__, id, sna_crtc->pipe)); @@ -4873,6 +4933,9 @@ bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna) sna->mode.max_crtc_width = res->max_width; sna->mode.max_crtc_height = res->max_height; + RegionEmpty(&sna->mode.shadow_region); + list_init(&sna->mode.shadow_crtc); + drmModeFreeResources(res); sna_cursor_pre_init(sna); @@ -5689,6 +5752,121 @@ sna_crtc_redisplay(xf86CrtcPtr crtc, RegionPtr region) #define shadow_flip_handler (sna_flip_handler_t)sna_mode_redisplay +void sna_shadow_set_crtc(struct sna *sna, + xf86CrtcPtr crtc, + struct kgem_bo *bo) +{ + struct sna_crtc *sna_crtc = to_sna_crtc(crtc); + struct sna_pixmap *priv; + + DBG(("%s: setting shadow override for CRTC:%d to handle=%d\n", + __FUNCTION__, sna_crtc->id, bo->handle)); + + assert(sna->flags & SNA_TEAR_FREE); + assert(sna_crtc); + assert(!sna_crtc->transform); + + if (sna_crtc->shadow_bo != bo) { + if (sna_crtc->shadow_bo) + kgem_bo_destroy(&sna->kgem, sna_crtc->shadow_bo); + + sna_crtc->shadow_bo = kgem_bo_reference(bo); + sna_crtc_damage(crtc); + } + + list_move(&sna_crtc->shadow_link, &sna->mode.shadow_crtc); + sna->mode.shadow_dirty = true; + + priv = sna_pixmap(sna->front); + assert(priv->gpu_bo); + priv->move_to_gpu = wait_for_shadow; + priv->move_to_gpu_data = sna; +} + +void sna_shadow_unset_crtc(struct sna *sna, + xf86CrtcPtr crtc) +{ + struct sna_crtc *sna_crtc = to_sna_crtc(crtc); + + DBG(("%s: clearin shadow override for CRTC:%d\n", + __FUNCTION__, sna_crtc->id)); + + if (sna_crtc->shadow_bo == NULL) + return; + + kgem_bo_destroy(&sna->kgem, sna_crtc->shadow_bo); + sna_crtc->shadow_bo = NULL; + list_del(&sna_crtc->shadow_link); + sna->mode.shadow_dirty = true; + + sna_crtc_damage(crtc); +} + +static bool +sna_crtc_flip(struct sna *sna, struct sna_crtc *crtc) +{ + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); + struct kgem_bo *bo = crtc->shadow_bo ? crtc->shadow_bo : crtc->bo; + struct drm_mode_crtc arg; + uint32_t output_ids[32]; + int output_count = 0; + int i; + + DBG(("%s CRTC:%d [pipe=%d], handle=%d\n", __FUNCTION__, crtc->id, crtc->pipe, bo->handle)); + + assert(config->num_output < ARRAY_SIZE(output_ids)); + + for (i = 0; i < config->num_output; i++) { + xf86OutputPtr output = config->output[i]; + + if (output->crtc != crtc->base) + continue; + + DBG(("%s: attaching output '%s' %d [%d] to crtc:%d (pipe %d) (possible crtc:%x, possible clones:%x)\n", + __FUNCTION__, output->name, i, to_connector_id(output), + crtc->id, crtc->pipe, + (uint32_t)output->possible_crtcs, + (uint32_t)output->possible_clones)); + + assert(output->possible_crtcs & (1 << crtc->pipe) || + xf86IsEntityShared(sna->scrn->entityList[0])); + + output_ids[output_count] = to_connector_id(output); + if (++output_count == ARRAY_SIZE(output_ids)) + return false; + } + + VG_CLEAR(arg); + arg.crtc_id = crtc->id; + arg.fb_id = fb_id(bo); + assert(arg.fb_id); + if (bo != crtc->bo) { + arg.x = 0; + arg.y = 0; + crtc->offset = 0; + } else { + arg.x = crtc->base->x; + arg.y = crtc->base->y; + crtc->offset = arg.y << 16 | arg.x; + } + arg.set_connectors_ptr = (uintptr_t)output_ids; + arg.count_connectors = output_count; + arg.mode = crtc->kmode; + arg.mode_valid = 1; + + DBG(("%s: applying crtc [%d, pipe=%d] mode=%dx%d+%d+%d@%d, fb=%d%s update to %d outputs [%d...]\n", + __FUNCTION__, crtc->id, crtc->pipe, + arg.mode.hdisplay, + arg.mode.vdisplay, + arg.x, arg.y, + arg.mode.clock, + arg.fb_id, + bo != crtc->bo ? " [shadow]" : "", + output_count, output_count ? output_ids[0] : 0)); + + return drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_SETCRTC, &arg) == 0; +} + void sna_mode_redisplay(struct sna *sna) { xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); @@ -5729,7 +5907,8 @@ void sna_mode_redisplay(struct sna *sna) if (sna->mode.flip_active) return; - if (wedged(sna) || !sna_pixmap_move_to_gpu(sna->front, MOVE_READ | MOVE_ASYNC_HINT)) { + if (wedged(sna) || !sna_pixmap_move_to_gpu(sna->front, MOVE_READ | MOVE_ASYNC_HINT | __MOVE_SCANOUT)) { + DBG(("%s: forcing scanout update using the CPU\n", __FUNCTION__)); if (!sna_pixmap_move_to_cpu(sna->front, MOVE_READ)) return; @@ -5764,12 +5943,14 @@ void sna_mode_redisplay(struct sna *sna) assert(priv != NULL); if (priv->move_to_gpu) { - if (priv->move_to_gpu == wait_for_shadow) { + if (priv->move_to_gpu == wait_for_shadow && + !sna->mode.shadow_dirty) { /* No damage written to new scanout * (backbuffer), ignore redisplay request * and continue with the current intact * scanout (frontbuffer). */ + DBG(("%s: shadow idle, skipping update\n", __FUNCTION__)); RegionEmpty(region); return; } @@ -5875,31 +6056,33 @@ disable1: struct kgem_bo *new = __sna_pixmap_get_bo(sna->front); struct kgem_bo *old = sna->mode.shadow; struct drm_mode_crtc_page_flip arg; + uint32_t fb_id; DBG(("%s: flipping tear-free outputs, current scanout handle=%d [active?=%d], new handle=%d [active=%d]\n", __FUNCTION__, old->handle, old->active_scanout, new->handle, new->active_scanout)); assert(new != old); assert(new->refcnt); - assert(new->active_scanout == 0); - - arg.fb_id = get_fb(sna, new, - sna->scrn->virtualX, - sna->scrn->virtualY); - if (arg.fb_id == 0) { - BoxRec box; + fb_id = get_fb(sna, new, + sna->scrn->virtualX, + sna->scrn->virtualY); + if (fb_id == 0) { fixup_shadow: - box.x1 = 0; - box.y1 = 0; - box.x2 = sna->scrn->virtualX; - box.y2 = sna->scrn->virtualY; - if (sna->render.copy_boxes(sna, GXcopy, - sna->front, new, 0, 0, - sna->front, old, 0, 0, - &box, 1, COPY_LAST)) { - kgem_submit(&sna->kgem); - RegionEmpty(region); + if (sna_pixmap_move_to_gpu(sna->front, MOVE_READ | MOVE_ASYNC_HINT)) { + BoxRec box; + + box.x1 = 0; + box.y1 = 0; + box.x2 = sna->scrn->virtualX; + box.y2 = sna->scrn->virtualY; + if (sna->render.copy_boxes(sna, GXcopy, + sna->front, __sna_pixmap_get_bo(sna->front), 0, 0, + sna->front, old, 0, 0, + &box, 1, COPY_LAST)) { + kgem_submit(&sna->kgem); + RegionEmpty(region); + } } return; @@ -5912,6 +6095,8 @@ fixup_shadow: for (i = 0; i < sna->mode.num_real_crtc; i++) { struct sna_crtc *crtc = config->crtc[i]->driver_private; + struct kgem_bo *flip_bo; + uint32_t crtc_offset = 0; assert(crtc != NULL); DBG(("%s: crtc %d [%d, pipe=%d] active? %d, transformed? %d\n", @@ -5925,23 +6110,64 @@ fixup_shadow: arg.crtc_id = crtc->id; arg.user_data = (uintptr_t)crtc; + if (crtc->shadow_bo) { + DBG(("%s: apply shadow override bo for CRTC:%d on pipe=%d, handle=%d\n", + __FUNCTION__, crtc->id, crtc->pipe, crtc->shadow_bo->handle)); + arg.fb_id = get_fb(sna, crtc->shadow_bo, + crtc->base->mode.HDisplay, + crtc->base->mode.VDisplay); + flip_bo = crtc->shadow_bo; + crtc_offset = 0; + } else { + arg.fb_id = fb_id; + flip_bo = new; + crtc_offset = crtc->base->y << 16 | crtc->base->x; + } + + if (crtc->bo == flip_bo) + continue; + + if (flip_bo->pitch != crtc->bo->pitch || crtc_offset != crtc->offset) { + DBG(("%s: changing pitch (%d == %d) or offset (%x == %x)\n", + __FUNCTION__, + flip_bo->pitch, crtc->bo->pitch, + crtc_offset, crtc->offset)); +fixup_flip: + if (sna_crtc_flip(sna, crtc)) { + assert(crtc->bo->active_scanout); + assert(crtc->bo->refcnt >= crtc->bo->active_scanout); + crtc->bo->active_scanout--; + kgem_bo_destroy(&sna->kgem, crtc->bo); + + crtc->bo = kgem_bo_reference(flip_bo); + crtc->bo->active_scanout++; + + if (crtc->shadow_bo) + sna_shadow_set_crtc(sna, crtc->base, flip_bo); + } else { + if (sna->mode.flip_active == 0) { + DBG(("%s: abandoning flip attempt\n", __FUNCTION__)); + goto fixup_shadow; + } + + xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR, + "%s: page flipping failed, disabling CRTC:%d (pipe=%d)\n", + __FUNCTION__, crtc->id, crtc->pipe); + sna_crtc_disable(crtc->base); + } + continue; + } + if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_PAGE_FLIP, &arg)) { ERR(("%s: flip [fb=%d] on crtc %d [%d, pipe=%d] failed - %d\n", __FUNCTION__, arg.fb_id, i, crtc->id, crtc->pipe, errno)); - if (sna->mode.flip_active == 0) - goto fixup_shadow; - - xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR, - "%s: page flipping failed, disabling CRTC:%d (pipe=%d)\n", - __FUNCTION__, crtc->id, crtc->pipe); - sna_crtc_disable(config->crtc[i]); - continue; + goto fixup_flip; } sna->mode.flip_active++; assert(crtc->flip_bo == NULL); crtc->flip_handler = shadow_flip_handler; - crtc->flip_bo = kgem_bo_reference(new); + crtc->flip_bo = kgem_bo_reference(flip_bo); crtc->flip_bo->active_scanout++; } @@ -6012,6 +6238,7 @@ void sna_mode_wakeup(struct sna *sna) crtc->flip_bo = NULL; } + DBG(("%s: flip complete, pending? %d\n", __FUNCTION__, sna->mode.flip_active)); assert(sna->mode.flip_active); if (--sna->mode.flip_active == 0) crtc->flip_handler(sna, vbl, crtc->flip_data); |