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 | |
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>
-rw-r--r-- | src/sna/kgem.c | 83 | ||||
-rw-r--r-- | src/sna/sna.h | 8 | ||||
-rw-r--r-- | src/sna/sna_accel.c | 2 | ||||
-rw-r--r-- | src/sna/sna_display.c | 295 | ||||
-rw-r--r-- | src/sna/sna_dri2.c | 280 |
5 files changed, 573 insertions, 95 deletions
diff --git a/src/sna/kgem.c b/src/sna/kgem.c index e90959fa..45a2572d 100644 --- a/src/sna/kgem.c +++ b/src/sna/kgem.c @@ -4400,7 +4400,7 @@ struct kgem_bo *kgem_create_2d(struct kgem *kgem, bucket = cache_bucket(size); if (flags & CREATE_SCANOUT) { - struct kgem_bo *last = NULL, *first = NULL; + struct kgem_bo *last = NULL; list_for_each_entry_reverse(bo, &kgem->scanout, list) { assert(bo->scanout); @@ -4414,11 +4414,8 @@ struct kgem_bo *kgem_create_2d(struct kgem *kgem, /* No tiling/pitch without recreating fb */ continue; - if (bo->delta && !check_scanout_size(kgem, bo, width, height)) { - if (first == NULL) - first = bo; + if (bo->delta && !check_scanout_size(kgem, bo, width, height)) continue; - } if (flags & CREATE_INACTIVE && bo->rq) { last = bo; @@ -4448,44 +4445,60 @@ struct kgem_bo *kgem_create_2d(struct kgem *kgem, return last; } - if (first) { - ScrnInfoPtr scrn = - container_of(kgem, struct sna, kgem)->scrn; + if (container_of(kgem, struct sna, kgem)->scrn->vtSema) { + ScrnInfoPtr scrn = container_of(kgem, struct sna, kgem)->scrn; - if (scrn->vtSema) { - DBG(("%s: recreate fb %dx%d@%d/%d\n", - __FUNCTION__, width, height, scrn->depth, scrn->bitsPerPixel)); + list_for_each_entry_reverse(bo, &kgem->scanout, list) { + struct drm_mode_fb_cmd arg; - if (first->tiling != tiling || - (tiling != I915_TILING_NONE && first->pitch != pitch)) { - if (gem_set_tiling(kgem->fd, first->handle, - tiling, pitch)) { - first->tiling = tiling; - first->pitch = pitch; - } - } + assert(bo->scanout); - if (first->tiling == tiling && first->pitch == pitch) { - struct drm_mode_fb_cmd arg; + if (size > num_pages(bo) || num_pages(bo) > 2*size) + continue; - VG_CLEAR(arg); - arg.width = width; - arg.height = height; - arg.pitch = first->pitch; - arg.bpp = scrn->bitsPerPixel; - arg.depth = scrn->depth; - arg.handle = first->handle; + if (flags & CREATE_INACTIVE && bo->rq) + continue; - kgem_bo_rmfb(kgem, first); - if (do_ioctl(kgem->fd, DRM_IOCTL_MODE_ADDFB, &arg)) { - kgem_bo_free(kgem, first); + list_del(&bo->list); + + if (bo->tiling != tiling || bo->pitch != pitch) { + if (bo->delta) { + kgem_bo_rmfb(kgem, bo); + bo->delta = 0; + } + + if (gem_set_tiling(kgem->fd, bo->handle, + tiling, pitch)) { + bo->tiling = tiling; + bo->pitch = pitch; } else { - DBG(("%s: attached fb=%d to handle=%d\n", - __FUNCTION__, arg.fb_id, arg.handle)); - first->delta = arg.fb_id; - return first; + kgem_bo_free(kgem, bo); + break; } } + + VG_CLEAR(arg); + arg.width = width; + arg.height = height; + arg.pitch = bo->pitch; + arg.bpp = scrn->bitsPerPixel; + arg.depth = scrn->depth; + arg.handle = bo->handle; + + if (do_ioctl(kgem->fd, DRM_IOCTL_MODE_ADDFB, &arg)) { + kgem_bo_free(kgem, bo); + break; + } + + bo->delta = arg.fb_id; + bo->unique_id = kgem_get_unique_id(kgem); + + DBG((" 2: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)); + assert_tiling(kgem, bo); + bo->refcnt = 1; + return bo; } } diff --git a/src/sna/sna.h b/src/sna/sna.h index 31f80a0d..2238e803 100644 --- a/src/sna/sna.h +++ b/src/sna/sna.h @@ -291,6 +291,8 @@ struct sna { int max_crtc_width, max_crtc_height; RegionRec shadow_region; + struct list shadow_crtc; + bool shadow_dirty; unsigned num_real_crtc; unsigned num_real_output; @@ -424,6 +426,8 @@ extern void sna_mode_check(struct sna *sna); extern void sna_mode_reset(struct sna *sna); extern void sna_mode_wakeup(struct sna *sna); extern void sna_mode_redisplay(struct sna *sna); +extern void sna_shadow_set_crtc(struct sna *sna, xf86CrtcPtr crtc, struct kgem_bo *bo); +extern void sna_shadow_unset_crtc(struct sna *sna, xf86CrtcPtr crtc); extern void sna_pixmap_discard_shadow_damage(struct sna_pixmap *priv, RegionPtr region); extern void sna_mode_close(struct sna *sna); @@ -570,7 +574,8 @@ extern bool sna_crtc_set_sprite_rotation(xf86CrtcPtr crtc, uint32_t rotation); extern int sna_crtc_to_pipe(xf86CrtcPtr crtc); extern uint32_t sna_crtc_to_sprite(xf86CrtcPtr crtc); extern uint32_t sna_crtc_id(xf86CrtcPtr crtc); -extern int sna_crtc_is_on(xf86CrtcPtr crtc); +extern bool sna_crtc_is_on(xf86CrtcPtr crtc); +extern bool sna_crtc_is_transformed(xf86CrtcPtr crtc); CARD32 sna_format_for_depth(int depth); CARD32 sna_render_format_for_depth(int depth); @@ -688,6 +693,7 @@ sna_pixmap_undo_cow(struct sna *sna, struct sna_pixmap *priv, unsigned flags); #define MOVE_WHOLE_HINT 0x20 #define __MOVE_FORCE 0x40 #define __MOVE_DRI 0x80 +#define __MOVE_SCANOUT 0x100 struct sna_pixmap * sna_pixmap_move_area_to_gpu(PixmapPtr pixmap, const BoxRec *box, unsigned int flags); diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c index e9c1ef23..78f422db 100644 --- a/src/sna/sna_accel.c +++ b/src/sna/sna_accel.c @@ -16868,7 +16868,7 @@ static void sna_scanout_flush(struct sna *sna) if (priv && sna_pixmap_force_to_gpu(priv->pixmap, - MOVE_READ | MOVE_ASYNC_HINT)) + MOVE_READ | MOVE_ASYNC_HINT | __MOVE_SCANOUT)) kgem_scanout_flush(&sna->kgem, priv->gpu_bo); sna_mode_redisplay(sna); 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); diff --git a/src/sna/sna_dri2.c b/src/sna/sna_dri2.c index 8553169b..5e1e7eaa 100644 --- a/src/sna/sna_dri2.c +++ b/src/sna/sna_dri2.c @@ -72,7 +72,7 @@ static inline void unref(struct kgem_bo *bo) struct sna_dri2_private { PixmapPtr pixmap; struct kgem_bo *bo; - bool scanout; + DRI2Buffer2Ptr proxy; bool stale; uint32_t size; int refcnt; @@ -129,6 +129,7 @@ static void sna_dri2_flip_event(struct sna *sna, static void sna_dri2_get_back(struct sna *sna, + DrawablePtr draw, DRI2BufferPtr back, struct sna_dri2_event *info) { @@ -160,12 +161,11 @@ sna_dri2_get_back(struct sna *sna, } } if (bo == NULL) { - DrawablePtr draw = &sna->front->drawable; DBG(("%s: allocating new backbuffer\n", __FUNCTION__)); bo = kgem_create_2d(&sna->kgem, draw->width, draw->height, draw->bitsPerPixel, get_private(back)->bo->tiling, - CREATE_SCANOUT); + get_private(back)->bo->scanout ? CREATE_SCANOUT : 0); if (bo == NULL) return; @@ -203,6 +203,7 @@ sna_dri2_get_back(struct sna *sna, } struct dri2_window { + DRI2BufferPtr front; struct sna_dri2_event *chain; xf86CrtcPtr crtc; int64_t msc_delta; @@ -248,7 +249,7 @@ sna_dri2_reuse_buffer(DrawablePtr draw, DRI2BufferPtr buffer) if (buffer->attachment == DRI2BufferBackLeft && draw->type != DRAWABLE_PIXMAP) { DBG(("%s: replacing back buffer\n", __FUNCTION__)); - sna_dri2_get_back(to_sna_from_drawable(draw), buffer, dri2_chain(draw)); + sna_dri2_get_back(to_sna_from_drawable(draw), draw, buffer, dri2_chain(draw)); assert(kgem_bo_flink(&to_sna_from_drawable(draw)->kgem, get_private(buffer)->bo) == buffer->name); assert(get_private(buffer)->bo->active_scanout == 0); @@ -409,22 +410,30 @@ sna_dri2_create_buffer(DrawablePtr draw, switch (attachment) { case DRI2BufferFrontLeft: pixmap = get_drawable_pixmap(draw); - buffer = sna_pixmap_get_buffer(pixmap); + buffer = NULL; + if (draw->type != DRAWABLE_PIXMAP) + buffer = dri2_window((WindowPtr)draw)->front; + if (buffer == NULL) + buffer = sna_pixmap_get_buffer(pixmap); if (buffer) { private = get_private(buffer); - DBG(("%s: reusing front buffer attachment, pixmap=%ld, handle=%d, name=%d\n", - __FUNCTION__, pixmap->drawable.serialNumber, + DBG(("%s: reusing front buffer attachment, win=%lu %dx%d, pixmap=%ld %dx%d, handle=%d, name=%d\n", + __FUNCTION__, + draw->type != DRAWABLE_PIXMAP ? (long)draw->id : (long)0, + draw->width, draw->height, + pixmap->drawable.serialNumber, + pixmap->drawable.width, + pixmap->drawable.height, private->bo->handle, buffer->name)); assert(private->pixmap == pixmap); - assert(private->bo->flush); assert(sna_pixmap(pixmap)->flush); - assert(sna_pixmap(pixmap)->gpu_bo == private->bo); assert(sna_pixmap(pixmap)->pinned & PIN_DRI2); + assert(private->proxy != NULL || sna_pixmap(pixmap)->gpu_bo == private->bo); assert(kgem_bo_flink(&sna->kgem, private->bo) == buffer->name); - assert(8*private->bo->pitch >= pixmap->drawable.width * pixmap->drawable.bitsPerPixel); - assert(private->bo->pitch * pixmap->drawable.height <= kgem_bo_size(private->bo)); + + buffer->attachment = DRI2BufferFrontLeft; private->refcnt++; return buffer; @@ -448,10 +457,14 @@ sna_dri2_create_buffer(DrawablePtr draw, break; case DRI2BufferBackLeft: - if (draw->width == sna->front->drawable.width && - draw->height == sna->front->drawable.height && - (sna->flags & (SNA_NO_WAIT | SNA_NO_FLIP)) == 0) - flags |= CREATE_SCANOUT; + if (draw->type != DRAWABLE_PIXMAP) { + if (dri2_window((WindowPtr)draw)->front) + flags |= CREATE_SCANOUT; + if (draw->width == sna->front->drawable.width && + draw->height == sna->front->drawable.height && + (sna->flags & (SNA_NO_WAIT | SNA_NO_FLIP)) == 0) + flags |= CREATE_SCANOUT; + } case DRI2BufferBackRight: case DRI2BufferFrontRight: case DRI2BufferFakeFrontLeft: @@ -533,7 +546,6 @@ sna_dri2_create_buffer(DrawablePtr draw, private->refcnt = 1; private->bo = bo; private->pixmap = pixmap; - private->scanout = !!(flags & CREATE_SCANOUT); private->size = size; if (buffer->name == 0) @@ -593,6 +605,12 @@ static void _sna_dri2_destroy_buffer(struct sna *sna, DRI2Buffer2Ptr buffer) return; assert(private->bo); + + if (private->proxy) { + _sna_dri2_destroy_buffer(sna, private->proxy); + private->pixmap = NULL; + } + if (private->pixmap) { PixmapPtr pixmap = private->pixmap; struct sna_pixmap *priv = sna_pixmap(pixmap); @@ -1078,6 +1096,7 @@ draw_current_msc(DrawablePtr draw, xf86CrtcPtr crtc, uint64_t msc) if (priv == NULL) { priv = malloc(sizeof(*priv)); if (priv != NULL) { + priv->front = NULL; priv->crtc = crtc; priv->msc_delta = 0; priv->chain = NULL; @@ -1226,6 +1245,7 @@ sna_dri2_event_free(struct sna *sna, void sna_dri2_destroy_window(WindowPtr win) { + struct sna *sna; struct dri2_window *priv; priv = dri2_window(win); @@ -1233,9 +1253,15 @@ void sna_dri2_destroy_window(WindowPtr win) return; DBG(("%s: window=%ld\n", __FUNCTION__, win->drawable.serialNumber)); + sna = to_sna_from_drawable(&win->drawable); + + if (priv->front) { + assert(priv->crtc); + sna_shadow_unset_crtc(sna, priv->crtc); + _sna_dri2_destroy_buffer(sna, priv->front); + } if (priv->chain) { - struct sna *sna = to_sna_from_drawable(&win->drawable); struct sna_dri2_event *info, *chain; DBG(("%s: freeing chain\n", __FUNCTION__)); @@ -1377,7 +1403,7 @@ can_flip(struct sna * sna, assert(get_private(front)->pixmap == sna->front); assert(sna_pixmap(sna->front)->gpu_bo == get_private(front)->bo); - if (!get_private(back)->scanout) { + if (!get_private(back)->bo->scanout) { DBG(("%s: no, DRI2 drawable was too small at time of creation)\n", __FUNCTION__)); return false; @@ -1513,6 +1539,107 @@ can_xchg(struct sna * sna, return true; } +static bool +overlaps_other_crtc(struct sna *sna, xf86CrtcPtr desired) +{ + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn); + int c; + + for (c = 0; c < sna->mode.num_real_crtc; c++) { + xf86CrtcPtr crtc = config->crtc[c]; + + if (crtc == desired) + continue; + + if (!crtc->enabled) + continue; + + if (desired->bounds.x1 < crtc->bounds.x2 && + desired->bounds.x2 > crtc->bounds.x1 && + desired->bounds.y1 < crtc->bounds.y2 && + desired->bounds.y2 > crtc->bounds.y1) + return true; + } + + return false; +} + +static bool +can_xchg_one(struct sna *sna, + DrawablePtr draw, + DRI2BufferPtr front, + DRI2BufferPtr back, + xf86CrtcPtr crtc) +{ + WindowPtr win = (WindowPtr)draw; + PixmapPtr pixmap; + + if ((sna->flags & SNA_TEAR_FREE) == 0) { + DBG(("%s: no, requires TearFree\n", + __FUNCTION__)); + return false; + } + + if (draw->type == DRAWABLE_PIXMAP) + return false; + + if (front->format != back->format) { + DBG(("%s: no, format mismatch, front = %d, back = %d\n", + __FUNCTION__, front->format, back->format)); + return false; + } + + if (front->attachment != DRI2BufferFrontLeft) { + DBG(("%s: no, front attachment [%d] is not FrontLeft [%d]\n", + __FUNCTION__, + front->attachment, + DRI2BufferFrontLeft)); + return false; + } + + if (sna_crtc_is_transformed(crtc)) { + DBG(("%s: no, CRTC is rotated\n", __FUNCTION__)); + return false; + } + + pixmap = get_window_pixmap(win); + if (pixmap != sna->front) { + DBG(("%s: no, not attached to front buffer\n", __FUNCTION__)); + return false; + } + + DBG(("%s: window size: %dx%d, clip=(%d, %d), (%d, %d) x %d\n", + __FUNCTION__, + win->drawable.width, win->drawable.height, + win->clipList.extents.x1, win->clipList.extents.y1, + win->clipList.extents.x2, win->clipList.extents.y2, + RegionNumRects(&win->clipList))); + if (is_clipped(&win->clipList, &win->drawable)) { + DBG(("%s: no, %dx%d window is clipped: clip region=(%d, %d), (%d, %d)\n", + __FUNCTION__, + draw->width, draw->height, + win->clipList.extents.x1, + win->clipList.extents.y1, + win->clipList.extents.x2, + win->clipList.extents.y2)); + return false; + } + + if (overlaps_other_crtc(sna, crtc)) { + DBG(("%s: no, overlaps other CRTC\n", __FUNCTION__)); + return false; + } + + if (get_private(back)->size != (draw->height << 16 | draw->width)) { + DBG(("%s: no, DRI2 buffers does not fit window\n", + __FUNCTION__)); + return false; + } + + DBG(("%s: yes\n", __FUNCTION__)); + return true; +} + static void sna_dri2_exchange_buffers(DrawablePtr draw, DRI2BufferPtr front, @@ -1900,7 +2027,7 @@ sna_dri2_flip_continue(struct sna *sna, struct sna_dri2_event *info) return false; if (!XORG_CAN_TRIPLE_BUFFER) { - sna_dri2_get_back(sna, info->back, info); + sna_dri2_get_back(sna, info->draw, info->back, info); DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__)); frame_swap_complete(sna, info, DRI2_FLIP_COMPLETE); } @@ -2180,7 +2307,7 @@ sna_dri2_schedule_flip(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc, if (type >= FLIP_COMPLETE) { new_back: if (!XORG_CAN_TRIPLE_BUFFER) - sna_dri2_get_back(sna, back, info); + sna_dri2_get_back(sna, draw, back, info); DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__)); frame_swap_complete(sna, info, DRI2_EXCHANGE_COMPLETE); if (info->type == FLIP_ASYNC) @@ -2314,6 +2441,94 @@ complete: return true; } +static bool +sna_dri2_schedule_xchg_one(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc, + DRI2BufferPtr front, DRI2BufferPtr back, + CARD64 *target_msc, CARD64 divisor, CARD64 remainder, + DRI2SwapEventPtr func, void *data) +{ + struct sna *sna = to_sna_from_drawable(draw); + uint64_t current_msc; + bool sync, event; + + if (!immediate_swap(sna, *target_msc, divisor, draw, crtc, ¤t_msc)) + return false; + + sync = current_msc < *target_msc; + event = dri2_chain(draw) == NULL; + if (!sync || event) { + WindowPtr win = (WindowPtr)draw; + PixmapPtr pixmap = get_window_pixmap(win); + + DBG(("%s: performing immediate xchg only on pipe %d\n", + __FUNCTION__, sna_crtc_to_pipe(crtc))); + DBG(("%s: exchange front=%d/%d and back=%d/%d, win id=%lu, pixmap=%ld %dx%d\n", + __FUNCTION__, + get_private(front)->bo->handle, front->name, + get_private(back)->bo->handle, back->name, + win->drawable.id, + pixmap->drawable.serialNumber, + pixmap->drawable.width, + pixmap->drawable.height)); + + sna_shadow_set_crtc(sna, crtc, get_private(back)->bo); + DamageRegionProcessPending(&win->drawable); + + if (get_private(front)->size == (draw->height << 16 | draw->width)) { + front->attachment = DRI2BufferBackLeft; + get_private(front)->stale = true; + } else + front->attachment = -1; + back->attachment = DRI2BufferFrontLeft; + if (get_private(back)->proxy == NULL) { + get_private(back)->proxy = sna_dri2_reference_buffer(sna_pixmap_get_buffer(pixmap)); + get_private(back)->pixmap = pixmap; + } + + assert(dri2_window(win)->front == NULL); + dri2_window(win)->front = sna_dri2_reference_buffer(back); + } + if (sync) { + struct sna_dri2_event *info; + + info = sna_dri2_add_event(draw, client); + if (!info) + goto complete; + + info->event_complete = func; + info->event_data = data; + + info->front = sna_dri2_reference_buffer(front); + info->back = sna_dri2_reference_buffer(back); + info->type = SWAP_THROTTLE; + + if (event) { + union drm_wait_vblank vbl; + + VG_CLEAR(vbl); + vbl.request.type = + DRM_VBLANK_RELATIVE | + DRM_VBLANK_EVENT; + vbl.request.sequence = 1; + vbl.request.signal = (uintptr_t)info; + + info->queued = true; + if (sna_wait_vblank(sna, &vbl, info->pipe)) { + sna_dri2_event_free(sna, draw, info); + goto complete; + } + + swap_limit(draw, 2); + } + } else { +complete: + fake_swap_complete(sna, client, draw, crtc, DRI2_EXCHANGE_COMPLETE, func, data); + } + + *target_msc = current_msc + 1; + return true; +} + static bool has_pending_events(struct sna *sna) { struct pollfd pfd; @@ -2353,9 +2568,12 @@ sna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, struct sna_dri2_event *info = NULL; CARD64 current_msc; - DBG(("%s: draw=%ld, pixmap=%ld, back=%u (refs=%d/%d, flush=%d) , fron=%u (refs=%d/%d, flush=%d)\n", + DBG(("%s: draw=%lu %dx%d, pixmap=%ld %dx%d, back=%u (refs=%d/%d, flush=%d) , front=%u (refs=%d/%d, flush=%d)\n", __FUNCTION__, - (long)draw->id, get_drawable_pixmap(draw)->drawable.serialNumber, + (long)draw->id, draw->width, draw->height, + get_drawable_pixmap(draw)->drawable.serialNumber, + get_drawable_pixmap(draw)->drawable.width, + get_drawable_pixmap(draw)->drawable.height, get_private(back)->bo->handle, get_private(back)->refcnt, get_private(back)->bo->refcnt, @@ -2375,10 +2593,18 @@ sna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, assert(get_private(back)->refcnt); assert(get_private(front)->bo->refcnt); - assert(get_private(front)->bo->flush); - assert(get_private(back)->bo->refcnt); + if (draw->type != DRAWABLE_PIXMAP) { + struct dri2_window *priv = dri2_window((WindowPtr)draw); + if (priv->front) { + assert(front == priv->front); + assert(get_private(priv->front)->refcnt > 1); + get_private(priv->front)->refcnt--; + priv->front = NULL; + } + } + if (get_private(front)->pixmap != get_drawable_pixmap(draw)) goto skip; @@ -2408,6 +2634,12 @@ sna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, func, data)) return TRUE; + if (can_xchg_one(sna, draw, front, back, crtc) && + sna_dri2_schedule_xchg_one(client, draw, crtc, front, back, + target_msc, divisor, remainder, + func, data)) + return TRUE; + if (can_flip(sna, draw, front, back, crtc) && sna_dri2_schedule_flip(client, draw, crtc, front, back, target_msc, divisor, remainder, |