xf86-video-intel: 3 commits - src/sna/kgem.c src/sna/sna_accel.c src/sna/sna_display.c src/sna/sna_dri2.c src/sna/sna.h

Chris Wilson ickle at kemper.freedesktop.org
Mon Jun 9 06:59:41 PDT 2014


 src/sna/kgem.c        |   83 +++++++-----
 src/sna/sna.h         |    8 +
 src/sna/sna_accel.c   |    2 
 src/sna/sna_display.c |  339 +++++++++++++++++++++++++++++++++++++++++++-------
 src/sna/sna_dri2.c    |  288 ++++++++++++++++++++++++++++++++++++++----
 5 files changed, 615 insertions(+), 105 deletions(-)

New commits:
commit 8901a99afb7be4f6f377a8e84e62e5d768c8443d
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jun 9 14:56:02 2014 +0100

    sna/dri2: Avoid trying to flip between bo with different pitches
    
    It could happen that we create front/back buffers with different
    pitches. The kernel refuses to flip between such buffers, and so we will
    hit some fallback paths that try to fix up the failed flips. Circumvent
    such by avoiding the flip.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_dri2.c b/src/sna/sna_dri2.c
index 5e1e7ea..1baaf2b 100644
--- a/src/sna/sna_dri2.c
+++ b/src/sna/sna_dri2.c
@@ -1459,6 +1459,14 @@ can_flip(struct sna * sna,
 		return false;
 	}
 
+	if (get_private(front)->bo->pitch != get_private(back)->bo->pitch) {
+		DBG(("%s -- no, pitch mismatch: front %d, back=%d\n",
+		     __FUNCTION__,
+		     get_private(front)->bo->pitch,
+		     get_private(back)->bo->pitch));
+		return false;
+	}
+
 	if (sna_pixmap(pixmap)->pinned & ~(PIN_DRI2 | PIN_SCANOUT)) {
 		DBG(("%s -- no, pinned: front %x\n",
 		     __FUNCTION__, sna_pixmap(pixmap)->pinned));
commit 16574754a99c4472c1c90c426d9c1665a898f1d6
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jun 9 14:54:26 2014 +0100

    sna: Fixup normal flips between different pitches
    
    Apply the manual CRTC fixup in case pageflipping fails. This can happen
    if the pitches between the front and back differ for example.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_display.c b/src/sna/sna_display.c
index 8df3a90..876884f 100644
--- a/src/sna/sna_display.c
+++ b/src/sna/sna_display.c
@@ -4347,6 +4347,71 @@ sna_cursors_fini(struct sna *sna)
 	}
 }
 
+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;
+}
+
 static int do_page_flip(struct sna *sna, struct kgem_bo *bo,
 			sna_flip_handler_t handler, void *data)
 {
@@ -4374,13 +4439,16 @@ static int do_page_flip(struct sna *sna, struct kgem_bo *bo,
 	for (i = 0; i < sna->mode.num_real_crtc; i++) {
 		struct sna_crtc *crtc = config->crtc[i]->driver_private;
 		struct drm_mode_crtc_page_flip arg;
+		uint32_t crtc_offset;
 
 		DBG(("%s: crtc %d id=%d, pipe=%d active? %d\n",
 		     __FUNCTION__, i, crtc->id, crtc->pipe, crtc->bo != NULL));
 		if (crtc->bo == NULL)
 			continue;
+		assert(!crtc->transform);
 		assert(crtc->bo->active_scanout);
 		assert(crtc->bo->refcnt >= crtc->bo->active_scanout);
+		assert(crtc->flip_bo == NULL);
 
 		arg.crtc_id = crtc->id;
 		arg.fb_id = get_fb(sna, bo, width, height);
@@ -4389,6 +4457,36 @@ static int do_page_flip(struct sna *sna, struct kgem_bo *bo,
 			return 0;
 		}
 
+		crtc_offset = crtc->base->y << 16 | crtc->base->x;
+
+		if (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(bo);
+				crtc->bo->active_scanout++;
+
+				count++;
+				continue;
+			} else {
+				if (count && !xf86SetDesiredModes(sna->scrn)) {
+					xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR,
+						   "failed to restore display configuration\n");
+					for (; i < sna->mode.num_real_crtc; i++)
+						sna_crtc_disable(config->crtc[i]);
+				}
+				return 0;
+			}
+		}
+
 		/* Only the reference crtc will finally deliver its page flip
 		 * completion event. All other crtc's events will be discarded.
 		 */
@@ -4404,20 +4502,11 @@ static int do_page_flip(struct sna *sna, struct kgem_bo *bo,
 		DBG(("%s: crtc %d id=%d, pipe=%d  --> fb %d\n",
 		     __FUNCTION__, i, crtc->id, crtc->pipe, arg.fb_id));
 		if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_PAGE_FLIP, &arg)) {
-			DBG(("%s: flip [fb=%d] on crtc %d id=%d pipe=%d failed - %d\n",
-			     __FUNCTION__, arg.fb_id, i, crtc->id, crtc->pipe, errno));
 			xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR,
 				   "page flipping failed, on CRTC:%d (pipe=%d), disabling %s page flips\n",
 				   crtc->id, crtc->pipe, data ? "synchronous": "asynchronous");
-			if (count && !xf86SetDesiredModes(sna->scrn)) {
-				xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR,
-					   "failed to restore display configuration\n");
-				for (; i < sna->mode.num_real_crtc; i++)
-					sna_crtc_disable(config->crtc[i]);
-			}
-
 			sna->flags &= ~(data ? SNA_HAS_FLIP : SNA_HAS_ASYNC_FLIP);
-			return 0;
+			goto fixup_flip;
 		}
 
 		if (data) {
@@ -5802,71 +5891,6 @@ void sna_shadow_unset_crtc(struct sna *sna,
 	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);
commit 3932e97057fca16615adaefbc1eb25a0d51a1d8b
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jun 9 08:58:15 2014 +0100

    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 at chris-wilson.co.uk>

diff --git a/src/sna/kgem.c b/src/sna/kgem.c
index e90959f..45a2572 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 31f80a0..2238e80 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 e9c1ef2..78f422d 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 5187515..8df3a90 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, &region);
+		}
+	}
+
 	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, &region);
+
+	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 8553169..5e1e7ea 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, &current_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,


More information about the xorg-commit mailing list