xf86-video-intel: 3 commits - src/sna/kgem.c src/sna/kgem.h src/sna/sna_accel.c src/sna/sna_composite.c src/sna/sna_damage.h src/sna/sna_display.c src/sna/sna_dri2.c src/sna/sna_dri3.c src/sna/sna_driver.c src/sna/sna.h src/sna/sna_present.c src/sna/sna_render.c src/sna/sna_trapezoids.c src/sna/sna_trapezoids_imprecise.c

Chris Wilson ickle at kemper.freedesktop.org
Thu Jun 5 05:58:44 PDT 2014


 src/sna/kgem.c                     |    8 
 src/sna/kgem.h                     |    1 
 src/sna/sna.h                      |   36 --
 src/sna/sna_accel.c                |  193 ++++-------
 src/sna/sna_composite.c            |   17 
 src/sna/sna_damage.h               |   16 
 src/sna/sna_display.c              |  485 ++++++++++++++++-----------
 src/sna/sna_dri2.c                 |  648 +++++++++++++++++++------------------
 src/sna/sna_dri3.c                 |    8 
 src/sna/sna_driver.c               |   21 -
 src/sna/sna_present.c              |   41 --
 src/sna/sna_render.c               |    2 
 src/sna/sna_trapezoids.c           |    4 
 src/sna/sna_trapezoids_imprecise.c |    4 
 14 files changed, 774 insertions(+), 710 deletions(-)

New commits:
commit 9566fc0ccc71bc4fcd6bf83b567a41cc5366f5ee
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Jun 5 12:26:23 2014 +0100

    sna: Curry parameters to sna_damage_all()
    
    It is far easily to pass the PixmapPtr into the function and have it
    pluck out the width and height than do so in all callers.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index 70211eb..a307d9e 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -768,14 +768,10 @@ struct sna_pixmap *sna_pixmap_attach_to_bo(PixmapPtr pixmap, struct kgem_bo *bo)
 
 	if (bo->snoop) {
 		priv->cpu_bo = bo;
-		sna_damage_all(&priv->cpu_damage,
-				pixmap->drawable.width,
-				pixmap->drawable.height);
+		sna_damage_all(&priv->cpu_damage, pixmap);
 	} else {
 		priv->gpu_bo = bo;
-		sna_damage_all(&priv->gpu_damage,
-				pixmap->drawable.width,
-				pixmap->drawable.height);
+		sna_damage_all(&priv->gpu_damage, pixmap);
 	}
 
 	return priv;
@@ -996,7 +992,7 @@ fallback:
 	priv->shm = true;
 	priv->stride = pitch;
 	priv->ptr = MAKE_STATIC_PTR(addr);
-	sna_damage_all(&priv->cpu_damage, width, height);
+	sna_damage_all(&priv->cpu_damage, pixmap);
 
 	pixmap->devKind = pitch;
 	pixmap->devPrivate.ptr = addr;
@@ -1061,7 +1057,7 @@ sna_pixmap_create_scratch(ScreenPtr screen,
 		return NullPixmap;
 	}
 
-	sna_damage_all(&priv->gpu_damage, width, height);
+	sna_damage_all(&priv->gpu_damage, pixmap);
 
 	assert(to_sna_from_pixmap(pixmap) == sna);
 	assert(pixmap->drawable.pScreen == screen);
@@ -1198,9 +1194,7 @@ sna_set_shared_pixmap_backing(PixmapPtr pixmap, void *fd_handle)
 	if (bo == NULL)
 		return FALSE;
 
-	sna_damage_all(&priv->gpu_damage,
-		       pixmap->drawable.width,
-		       pixmap->drawable.height);
+	sna_damage_all(&priv->gpu_damage, pixmap);
 
 	bo->pitch = pixmap->devKind;
 	priv->stride = pixmap->devKind;
@@ -1274,7 +1268,7 @@ sna_create_pixmap_shared(struct sna *sna, ScreenPtr screen,
 		priv->mapped = MAPPED_GTT;
 		assert_pixmap_map(pixmap, priv);
 
-		sna_damage_all(&priv->gpu_damage, width, height);
+		sna_damage_all(&priv->gpu_damage, pixmap);
 	}
 
 	return pixmap;
@@ -2131,9 +2125,7 @@ _sna_pixmap_move_to_cpu(PixmapPtr pixmap, unsigned int flags)
 			assert(has_coherent_ptr(sna, priv, flags));
 
 			assert(priv->gpu_bo->proxy == NULL);
-			sna_damage_all(&priv->gpu_damage,
-				       pixmap->drawable.width,
-				       pixmap->drawable.height);
+			sna_damage_all(&priv->gpu_damage, pixmap);
 			sna_damage_destroy(&priv->cpu_damage);
 			priv->clear = false;
 			list_del(&priv->flush_list);
@@ -2194,9 +2186,7 @@ skip_inplace_map:
 
 			if (flags & MOVE_WRITE) {
 				assert(priv->gpu_bo->proxy == NULL);
-				sna_damage_all(&priv->gpu_damage,
-					       pixmap->drawable.width,
-					       pixmap->drawable.height);
+				sna_damage_all(&priv->gpu_damage, pixmap);
 				sna_damage_destroy(&priv->cpu_damage);
 				sna_pixmap_free_cpu(sna, priv, priv->cpu);
 				list_del(&priv->flush_list);
@@ -2236,9 +2226,7 @@ skip_inplace_map:
 
 			if (flags & MOVE_WRITE) {
 				assert(priv->gpu_bo->proxy == NULL);
-				sna_damage_all(&priv->gpu_damage,
-					       pixmap->drawable.width,
-					       pixmap->drawable.height);
+				sna_damage_all(&priv->gpu_damage, pixmap);
 				sna_damage_destroy(&priv->cpu_damage);
 				sna_pixmap_free_cpu(sna, priv, priv->cpu);
 				list_del(&priv->flush_list);
@@ -2301,9 +2289,7 @@ skip_inplace_map:
 					    priv->clear_color);
 			}
 
-			sna_damage_all(&priv->cpu_damage,
-				       pixmap->drawable.width,
-				       pixmap->drawable.height);
+			sna_damage_all(&priv->cpu_damage, pixmap);
 			sna_pixmap_free_gpu(sna, priv);
 			assert(priv->gpu_damage == NULL);
 			assert(priv->clear == false);
@@ -2334,9 +2320,7 @@ skip_inplace_map:
 	if (flags & MOVE_WRITE || priv->create & KGEM_CAN_CREATE_LARGE) {
 mark_damage:
 		DBG(("%s: marking as damaged\n", __FUNCTION__));
-		sna_damage_all(&priv->cpu_damage,
-			       pixmap->drawable.width,
-			       pixmap->drawable.height);
+		sna_damage_all(&priv->cpu_damage, pixmap);
 		sna_pixmap_free_gpu(sna, priv);
 		assert(priv->gpu_damage == NULL);
 		assert(priv->clear == false);
@@ -2572,9 +2556,7 @@ sna_drawable_move_region_to_cpu(DrawablePtr drawable,
 							sna_add_flush_pixmap(sna, priv, priv->gpu_bo);
 
 						assert(priv->cpu_damage == NULL);
-						sna_damage_all(&priv->gpu_damage,
-							       pixmap->drawable.width,
-							       pixmap->drawable.height);
+						sna_damage_all(&priv->gpu_damage, pixmap);
 						sna_damage_subtract(&priv->gpu_damage, region);
 						discard_gpu = false;
 					}
@@ -2773,9 +2755,7 @@ move_to_cpu:
 		sna_damage_subtract(&priv->cpu_damage, region);
 		if (sna_pixmap_move_to_gpu(pixmap, MOVE_READ | MOVE_ASYNC_HINT)) {
 			assert(priv->gpu_bo);
-			sna_damage_all(&priv->gpu_damage,
-				       pixmap->drawable.width,
-				       pixmap->drawable.height);
+			sna_damage_all(&priv->gpu_damage, pixmap);
 			sna_pixmap_free_cpu(sna, priv, false);
 		}
 	}
@@ -2976,9 +2956,7 @@ done:
 		assert(!DAMAGE_IS_ALL(priv->cpu_damage));
 		assert_pixmap_contains_box(pixmap, RegionExtents(region));
 		sna_damage_add(&priv->cpu_damage, region);
-		sna_damage_reduce_all(&priv->cpu_damage,
-				      pixmap->drawable.width,
-				      pixmap->drawable.height);
+		sna_damage_reduce_all(&priv->cpu_damage, pixmap);
 		if (DAMAGE_IS_ALL(priv->cpu_damage)) {
 			DBG(("%s: replaced entire pixmap\n", __FUNCTION__));
 			sna_pixmap_free_gpu(sna, priv);
@@ -3141,9 +3119,7 @@ __sna_pixmap_for_gpu(struct sna *sna, PixmapPtr pixmap, unsigned flags)
 		if (priv == NULL)
 			return NULL;
 
-		sna_damage_all(&priv->cpu_damage,
-			       pixmap->drawable.width,
-			       pixmap->drawable.height);
+		sna_damage_all(&priv->cpu_damage, pixmap);
 
 		assert(priv->gpu_bo == NULL);
 		assert(priv->gpu_damage == NULL);
@@ -3406,9 +3382,7 @@ done:
 			     __FUNCTION__));
 			assert(priv->gpu_bo);
 			assert(priv->gpu_bo->proxy == NULL);
-			sna_damage_all(&priv->gpu_damage,
-				       pixmap->drawable.width,
-				       pixmap->drawable.height);
+			sna_damage_all(&priv->gpu_damage, pixmap);
 		}
 		if (DAMAGE_IS_ALL(priv->gpu_damage))
 			sna_pixmap_free_cpu(sna, priv, priv->cpu);
@@ -3892,8 +3866,8 @@ sna_pixmap_create_upload(ScreenPtr screen,
 	 * but will work so long as we always check before doing the
 	 * transfer.
 	 */
-	sna_damage_all(&priv->gpu_damage, width, height);
-	sna_damage_all(&priv->cpu_damage, width, height);
+	sna_damage_all(&priv->gpu_damage, pixmap);
+	sna_damage_all(&priv->cpu_damage, pixmap);
 
 	pixmap->devKind = priv->gpu_bo->pitch;
 	pixmap->devPrivate.ptr = ptr;
@@ -4016,9 +3990,7 @@ sna_pixmap_move_to_gpu(PixmapPtr pixmap, unsigned flags)
 				priv->cpu_bo = NULL;
 				priv->ptr = NULL;
 				pixmap->devPrivate.ptr = NULL;
-				sna_damage_all(&priv->gpu_damage,
-					       pixmap->drawable.width,
-					       pixmap->drawable.height);
+				sna_damage_all(&priv->gpu_damage, pixmap);
 				sna_damage_destroy(&priv->cpu_damage);
 				goto done;
 			}
@@ -4051,9 +4023,7 @@ sna_pixmap_move_to_gpu(PixmapPtr pixmap, unsigned flags)
 			 */
 			assert(priv->gpu_bo);
 			assert(priv->gpu_bo->proxy == NULL);
-			sna_damage_all(&priv->gpu_damage,
-				       pixmap->drawable.width,
-				       pixmap->drawable.height);
+			sna_damage_all(&priv->gpu_damage, pixmap);
 			DBG(("%s: marking as all-damaged for GPU\n",
 			     __FUNCTION__));
 			goto active;
@@ -4085,9 +4055,7 @@ sna_pixmap_move_to_gpu(PixmapPtr pixmap, unsigned flags)
 		priv->cpu_bo = NULL;
 		priv->ptr = NULL;
 		pixmap->devPrivate.ptr = NULL;
-		sna_damage_all(&priv->gpu_damage,
-			       pixmap->drawable.width,
-			       pixmap->drawable.height);
+		sna_damage_all(&priv->gpu_damage, pixmap);
 		sna_damage_destroy(&priv->cpu_damage);
 		goto done;
 	}
@@ -4147,18 +4115,14 @@ sna_pixmap_move_to_gpu(PixmapPtr pixmap, unsigned flags)
 		assert(!priv->shm);
 		assert(priv->gpu_bo);
 		assert(priv->gpu_bo->proxy == NULL);
-		sna_damage_all(&priv->gpu_damage,
-			       pixmap->drawable.width,
-			       pixmap->drawable.height);
+		sna_damage_all(&priv->gpu_damage, pixmap);
 		sna_pixmap_free_cpu(sna, priv,
 				    (priv->create & KGEM_CAN_CREATE_LARGE) ? false : priv->cpu);
 	}
 done:
 	list_del(&priv->flush_list);
 
-	sna_damage_reduce_all(&priv->gpu_damage,
-			      pixmap->drawable.width,
-			      pixmap->drawable.height);
+	sna_damage_reduce_all(&priv->gpu_damage, pixmap);
 	if (DAMAGE_IS_ALL(priv->gpu_damage))
 		sna_pixmap_free_cpu(sna, priv, priv->cpu);
 
@@ -4520,14 +4484,10 @@ try_upload_blt(PixmapPtr pixmap, RegionRec *region,
 	if (!DAMAGE_IS_ALL(priv->gpu_damage)) {
 		assert(!priv->clear);
 		if (region_subsumes_drawable(region, &pixmap->drawable)) {
-			sna_damage_all(&priv->gpu_damage,
-				       pixmap->drawable.width,
-				       pixmap->drawable.height);
+			sna_damage_all(&priv->gpu_damage, pixmap);
 		} else {
 			sna_damage_add(&priv->gpu_damage, region);
-			sna_damage_reduce_all(&priv->gpu_damage,
-					      pixmap->drawable.width,
-					      pixmap->drawable.height);
+			sna_damage_reduce_all(&priv->gpu_damage, pixmap);
 		}
 		if (DAMAGE_IS_ALL(priv->gpu_damage))
 			sna_damage_destroy(&priv->cpu_damage);
@@ -4603,9 +4563,7 @@ try_upload_tiled_x(PixmapPtr pixmap, RegionRec *region,
 			sna_pixmap_free_gpu(sna, priv);
 			ignore_cpu = priv->cpu_damage == NULL;
 			if (priv->ptr)
-				sna_damage_all(&priv->cpu_damage,
-						pixmap->drawable.width,
-						pixmap->drawable.height);
+				sna_damage_all(&priv->cpu_damage, pixmap);
 		}
 	}
 
@@ -4721,14 +4679,10 @@ try_upload_tiled_x(PixmapPtr pixmap, RegionRec *region,
 	if (!DAMAGE_IS_ALL(priv->gpu_damage)) {
 		assert(!priv->clear);
 		if (replaces) {
-			sna_damage_all(&priv->gpu_damage,
-					pixmap->drawable.width,
-					pixmap->drawable.height);
+			sna_damage_all(&priv->gpu_damage, pixmap);
 		} else {
 			sna_damage_add(&priv->gpu_damage, region);
-			sna_damage_reduce_all(&priv->gpu_damage,
-					      pixmap->drawable.width,
-					      pixmap->drawable.height);
+			sna_damage_reduce_all(&priv->gpu_damage, pixmap);
 		}
 		if (DAMAGE_IS_ALL(priv->gpu_damage))
 			sna_damage_destroy(&priv->cpu_damage);
@@ -5499,9 +5453,7 @@ sna_self_copy_boxes(DrawablePtr src, DrawablePtr dst, GCPtr gc,
 		if (!DAMAGE_IS_ALL(priv->gpu_damage)) {
 			assert(!priv->clear);
 			if (priv->cpu_bo == NULL) {
-				sna_damage_all(&priv->gpu_damage,
-						pixmap->drawable.width,
-						pixmap->drawable.height);
+				sna_damage_all(&priv->gpu_damage, pixmap);
 			} else {
 				RegionTranslate(region, tx, ty);
 				sna_damage_add(&priv->gpu_damage, region);
@@ -6073,9 +6025,7 @@ sna_copy_boxes(DrawablePtr src, DrawablePtr dst, GCPtr gc,
 					     src_priv->clear_color));
 					dst_priv->clear = true;
 					dst_priv->clear_color = color;
-					sna_damage_all(&dst_priv->gpu_damage,
-						       dst_pixmap->drawable.width,
-						       dst_pixmap->drawable.height);
+					sna_damage_all(&dst_priv->gpu_damage, dst_pixmap);
 					sna_damage_destroy(&dst_priv->cpu_damage);
 					list_del(&dst_priv->flush_list);
 					return;
@@ -6116,9 +6066,7 @@ sna_copy_boxes(DrawablePtr src, DrawablePtr dst, GCPtr gc,
 				assert(src_pixmap->drawable.bitsPerPixel == dst_pixmap->drawable.bitsPerPixel);
 				if (sna_pixmap_make_cow(sna, src_priv, dst_priv)) {
 					assert(dst_priv->gpu_bo == src_priv->gpu_bo);
-					sna_damage_all(&dst_priv->gpu_damage,
-						       dst_pixmap->drawable.width,
-						       dst_pixmap->drawable.height);
+					sna_damage_all(&dst_priv->gpu_damage, dst_pixmap);
 					sna_damage_destroy(&dst_priv->cpu_damage);
 					list_del(&dst_priv->flush_list);
 					if (dst_priv->shm)
@@ -6386,9 +6334,7 @@ sna_copy_boxes(DrawablePtr src, DrawablePtr dst, GCPtr gc,
 				assert(*damage == dst_priv->gpu_damage);
 				if (replaces) {
 					sna_damage_destroy(&dst_priv->cpu_damage);
-					sna_damage_all(&dst_priv->gpu_damage,
-						       dst_pixmap->drawable.width,
-						       dst_pixmap->drawable.height);
+					sna_damage_all(&dst_priv->gpu_damage, dst_pixmap);
 					list_del(&dst_priv->flush_list);
 				} else
 					sna_damage_add(&dst_priv->gpu_damage,
@@ -11374,11 +11320,9 @@ sna_poly_fill_rect_blt(DrawablePtr drawable,
 				if (damage) {
 					assert_pixmap_contains_box(pixmap, &r);
 					if (r.x2 - r.x1 == pixmap->drawable.width &&
-					    r.y2 - r.y1 == pixmap->drawable.height) {
-						sna_damage_all(damage,
-							       pixmap->drawable.width,
-							       pixmap->drawable.height);
-					} else
+					    r.y2 - r.y1 == pixmap->drawable.height)
+						sna_damage_all(damage, pixmap);
+					else
 						sna_damage_add_box(damage, &r);
 				}
 				assert_pixmap_damage(pixmap);
@@ -11389,9 +11333,7 @@ sna_poly_fill_rect_blt(DrawablePtr drawable,
 					struct sna_pixmap *priv = sna_pixmap(pixmap);
 					if (bo == priv->gpu_bo) {
 						assert(priv->gpu_bo->proxy == NULL);
-						sna_damage_all(&priv->gpu_damage,
-							       pixmap->drawable.width,
-							       pixmap->drawable.height);
+						sna_damage_all(&priv->gpu_damage, pixmap);
 						sna_damage_destroy(&priv->cpu_damage);
 						list_del(&priv->flush_list);
 						priv->clear = true;
@@ -14337,9 +14279,7 @@ sna_poly_fill_rect(DrawablePtr draw, GCPtr gc, int n, xRectangle *rect)
 				DBG(("%s: promoting to full GPU\n",
 				     __FUNCTION__));
 				assert(priv->gpu_bo->proxy == NULL);
-				sna_damage_all(&priv->gpu_damage,
-					       pixmap->drawable.width,
-					       pixmap->drawable.height);
+				sna_damage_all(&priv->gpu_damage, pixmap);
 			}
 			DBG(("%s: dropping last-cpu hint\n", __FUNCTION__));
 			priv->cpu = false;
diff --git a/src/sna/sna_composite.c b/src/sna/sna_composite.c
index 070faf4..8b4aa80 100644
--- a/src/sna/sna_composite.c
+++ b/src/sna/sna_composite.c
@@ -418,9 +418,12 @@ static void apply_damage(struct sna_composite_op *op, RegionPtr region)
 	assert_pixmap_contains_box(op->dst.pixmap, RegionExtents(region));
 	if (region->data == NULL &&
 	    region->extents.x2 - region->extents.x1 == op->dst.width &&
-	    region->extents.y2 - region->extents.y1 == op->dst.height)
-		sna_damage_all(op->damage, op->dst.width, op->dst.height);
-	else
+	    region->extents.y2 - region->extents.y1 == op->dst.height) {
+		*op->damage = _sna_damage_all(*op->damage,
+					      op->dst.width,
+					      op->dst.height);
+		op->damage = NULL;
+	} else
 		sna_damage_add(op->damage, region);
 }
 
@@ -964,9 +967,7 @@ sna_composite_rectangles(CARD8		 op,
 				if (priv->gpu_bo && priv->cpu_damage == NULL) {
 					DBG(("%s: promoting to full GPU\n", __FUNCTION__));
 					assert(priv->gpu_bo->proxy == NULL);
-					sna_damage_all(&priv->gpu_damage,
-						       pixmap->drawable.width,
-						       pixmap->drawable.height);
+					sna_damage_all(&priv->gpu_damage, pixmap);
 				}
 			}
 		}
@@ -1037,9 +1038,7 @@ sna_composite_rectangles(CARD8		 op,
 	 */
 	if (region_subsumes_drawable(&region, &pixmap->drawable)) {
 		if (damage) {
-			sna_damage_all(damage,
-				       pixmap->drawable.width,
-				       pixmap->drawable.height);
+			sna_damage_all(damage, pixmap);
 			sna_damage_destroy(damage == &priv->gpu_damage ?
 					   &priv->cpu_damage : &priv->gpu_damage);
 		}
diff --git a/src/sna/sna_damage.h b/src/sna/sna_damage.h
index 187d312..42edb10 100644
--- a/src/sna/sna_damage.h
+++ b/src/sna/sna_damage.h
@@ -141,10 +141,12 @@ _sna_damage_all(struct sna_damage *damage,
 }
 
 static inline void sna_damage_all(struct sna_damage **damage,
-				  int width, int height)
+				  PixmapPtr pixmap)
 {
 	if (!DAMAGE_IS_ALL(*damage))
-		*damage = _sna_damage_all(*damage, width, height);
+		*damage = _sna_damage_all(*damage,
+					  pixmap->drawable.width,
+					  pixmap->drawable.height);
 }
 
 fastcall struct sna_damage *_sna_damage_subtract(struct sna_damage *damage,
@@ -262,7 +264,7 @@ static inline void sna_damage_reduce(struct sna_damage **damage)
 }
 
 static inline void sna_damage_reduce_all(struct sna_damage **_damage,
-					 int width, int height)
+					 PixmapPtr pixmap)
 {
 	struct sna_damage *damage = *_damage;
 
@@ -274,8 +276,8 @@ static inline void sna_damage_reduce_all(struct sna_damage **_damage,
 	if (damage->mode == DAMAGE_ADD) {
 		if (damage->extents.x1 <= 0 &&
 		    damage->extents.y1 <= 0 &&
-		    damage->extents.x2 >= width &&
-		    damage->extents.y2 >= height) {
+		    damage->extents.x2 >= pixmap->drawable.width &&
+		    damage->extents.y2 >= pixmap->drawable.height) {
 			if (damage->dirty) {
 				damage = *_damage = _sna_damage_reduce(damage);
 				if (damage == NULL)
@@ -283,7 +285,9 @@ static inline void sna_damage_reduce_all(struct sna_damage **_damage,
 			}
 
 			if (damage->region.data == NULL)
-				*_damage = _sna_damage_all(damage, width, height);
+				*_damage = _sna_damage_all(damage,
+							   pixmap->drawable.width,
+							   pixmap->drawable.height);
 		}
 	} else
 		*_damage = _sna_damage_reduce(damage);
diff --git a/src/sna/sna_display.c b/src/sna/sna_display.c
index 470db86..46fc22c 100644
--- a/src/sna/sna_display.c
+++ b/src/sna/sna_display.c
@@ -3306,10 +3306,7 @@ static void copy_front(struct sna *sna, PixmapPtr old, PixmapPtr new)
 		}
 	}
 
-	if (!DAMAGE_IS_ALL(new_priv->gpu_damage))
-		sna_damage_all(&new_priv->gpu_damage,
-			       new->drawable.width,
-			       new->drawable.height);
+	sna_damage_all(&new_priv->gpu_damage, new);
 }
 
 static Bool
diff --git a/src/sna/sna_dri3.c b/src/sna/sna_dri3.c
index 0e58e79..5d51b41 100644
--- a/src/sna/sna_dri3.c
+++ b/src/sna/sna_dri3.c
@@ -61,9 +61,7 @@ static void sna_sync_flush(struct sna *sna, struct sna_pixmap *priv)
 		assert(priv->pinned & PIN_DRI3);
 		DBG(("%s: flushing prime GPU bo, handle=%ld\n", __FUNCTION__, priv->gpu_bo->handle));
 		if (sna_pixmap_move_to_gpu(priv->pixmap, MOVE_READ | MOVE_WRITE | MOVE_ASYNC_HINT | __MOVE_FORCE)) {
-			sna_damage_all(&priv->gpu_damage,
-				       priv->pixmap->drawable.width,
-				       priv->pixmap->drawable.height);
+			sna_damage_all(&priv->gpu_damage, priv->pixmap);
 			bo = priv->gpu_bo;
 		}
 	} else {
@@ -313,9 +311,7 @@ static int sna_dri3_fd_from_pixmap(ScreenPtr screen,
 	} else {
 		priv = sna_pixmap_move_to_gpu(pixmap, MOVE_READ | MOVE_WRITE | MOVE_ASYNC_HINT | __MOVE_FORCE | __MOVE_DRI);
 		if (priv != NULL) {
-			sna_damage_all(&priv->gpu_damage,
-					pixmap->drawable.width,
-					pixmap->drawable.height);
+			sna_damage_all(&priv->gpu_damage, pixmap);
 			bo = priv->gpu_bo;
 		}
 	}
diff --git a/src/sna/sna_trapezoids.c b/src/sna/sna_trapezoids.c
index 40663bb..c11a141 100644
--- a/src/sna/sna_trapezoids.c
+++ b/src/sna/sna_trapezoids.c
@@ -725,9 +725,7 @@ static void mark_damaged(PixmapPtr pixmap, struct sna_pixmap *priv,
 	    box->x2 >= pixmap->drawable.width &&
 	    box->y2 >= pixmap->drawable.height) {
 		sna_damage_destroy(&priv->cpu_damage);
-		sna_damage_all(&priv->gpu_damage,
-			       pixmap->drawable.width,
-			       pixmap->drawable.height);
+		sna_damage_all(&priv->gpu_damage, pixmap);
 		list_del(&priv->flush_list);
 	} else {
 		sna_damage_add_box(&priv->gpu_damage, box);
diff --git a/src/sna/sna_trapezoids_imprecise.c b/src/sna/sna_trapezoids_imprecise.c
index a6da2d8..115d2b4 100644
--- a/src/sna/sna_trapezoids_imprecise.c
+++ b/src/sna/sna_trapezoids_imprecise.c
@@ -3395,9 +3395,7 @@ static void mark_damaged(PixmapPtr pixmap, struct sna_pixmap *priv,
 	    box->x2 >= pixmap->drawable.width &&
 	    box->y2 >= pixmap->drawable.height) {
 		sna_damage_destroy(&priv->cpu_damage);
-		sna_damage_all(&priv->gpu_damage,
-			       pixmap->drawable.width,
-			       pixmap->drawable.height);
+		sna_damage_all(&priv->gpu_damage, pixmap);
 		list_del(&priv->flush_list);
 	} else {
 		sna_damage_add_box(&priv->gpu_damage, box);
commit 1327918f91dce2051b809594db5a3ed555934612
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Jun 5 12:04:29 2014 +0100

    sna: Mark CPU as all damaged when discarding a busy GPU bo
    
    When we discard a GPU bo, we need to make sure that the remaining
    content is marked as only accessible via the CPU shadow pointer.
    
    Regression from commit 65301412ecf2d55ab55a2d7faeaa048d4ee8b1d0
    Author: Chris Wilson <chris at chris-wilson.co.uk>
    Date:   Sat May 17 20:59:38 2014 +0100
    
        sna: Discard active GPU buffers before uploading into them
    
    Reported-by: Jan Alexander Steffens <jan.steffens at gmail.com>
    Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=79517
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index f77b4c6..70211eb 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -4602,6 +4602,10 @@ try_upload_tiled_x(PixmapPtr pixmap, RegionRec *region,
 			     kgem_bo_can_map__cpu(&sna->kgem, priv->gpu_bo, true)));
 			sna_pixmap_free_gpu(sna, priv);
 			ignore_cpu = priv->cpu_damage == NULL;
+			if (priv->ptr)
+				sna_damage_all(&priv->cpu_damage,
+						pixmap->drawable.width,
+						pixmap->drawable.height);
 		}
 	}
 
commit 8369166349c92a20d9a2e7d0256e63f66fe2682b
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Jun 4 08:29:51 2014 +0100

    sna/dri2: Enable immediate buffer exchanges
    
    The primary benefit of this is avoid the extra blit when using a
    compositor and instead propagate the compositor flip on the frontbuffer
    to the scanout, or equivalently allows a fullscreen game to flip onto
    the scanout without intervention by TearFree.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/kgem.c b/src/sna/kgem.c
index ea114f4..a45dedd 100644
--- a/src/sna/kgem.c
+++ b/src/sna/kgem.c
@@ -2222,6 +2222,7 @@ static void __kgem_bo_destroy(struct kgem *kgem, struct kgem_bo *bo)
 	assert(list_is_empty(&bo->list));
 	assert(bo->refcnt == 0);
 	assert(bo->proxy == NULL);
+	assert(bo->active_scanout == 0);
 	assert_tiling(kgem, bo);
 
 	bo->binding.offset = 0;
@@ -5832,13 +5833,6 @@ uint32_t kgem_bo_flink(struct kgem *kgem, struct kgem_bo *bo)
 
 	kgem_bo_unclean(kgem, bo);
 
-	/* Henceforth, we need to broadcast all updates to clients and
-	 * flush our rendering before doing so.
-	 */
-	bo->flush = true;
-	if (bo->exec)
-		kgem->flush = 1;
-
 	return flink.name;
 }
 
diff --git a/src/sna/kgem.h b/src/sna/kgem.h
index ae25821..370894b 100644
--- a/src/sna/kgem.h
+++ b/src/sna/kgem.h
@@ -74,6 +74,7 @@ struct kgem_bo {
 	uint32_t handle;
 	uint32_t target_handle;
 	uint32_t delta;
+	uint32_t active_scanout;
 	union {
 		struct {
 			uint32_t count:27;
diff --git a/src/sna/sna.h b/src/sna/sna.h
index b9a40db..7845cd1 100644
--- a/src/sna/sna.h
+++ b/src/sna/sna.h
@@ -111,6 +111,7 @@ void LogF(const char *f, ...);
 #include "fb/fb.h"
 
 struct sna_cursor;
+struct sna_crtc;
 
 struct sna_client {
 	int is_compositor; /* only 4 bits used */
@@ -283,9 +284,10 @@ struct sna {
 	struct sna_mode {
 		DamagePtr shadow_damage;
 		struct kgem_bo *shadow;
-		int shadow_active;
-		int shadow_flip;
-		int front_active;
+		unsigned front_active;
+		unsigned shadow_active;
+		unsigned flip_active;
+		bool dirty;
 
 		int max_crtc_width, max_crtc_height;
 
@@ -324,10 +326,6 @@ struct sna {
 
 #if HAVE_DRI2
 		void *flip_pending;
-		struct {
-			struct kgem_bo *bo;
-			uint32_t name;
-		} scanout[2];
 #endif
 	} dri2;
 
@@ -416,7 +414,7 @@ bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna);
 bool sna_mode_fake_init(struct sna *sna, int num_fake);
 void sna_mode_adjust_frame(struct sna *sna, int x, int y);
 extern void sna_mode_discover(struct sna *sna);
-extern void sna_mode_update(struct sna *sna);
+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);
@@ -425,12 +423,18 @@ extern void sna_pixmap_discard_shadow_damage(struct sna_pixmap *priv,
 extern void sna_mode_close(struct sna *sna);
 extern void sna_mode_fini(struct sna *sna);
 
+extern void sna_crtc_config_notify(ScreenPtr screen);
+
 extern bool sna_cursors_init(ScreenPtr screen, struct sna *sna);
 
+typedef void (*sna_flip_handler_t)(struct sna *sna,
+				   struct drm_event_vblank *e,
+				   void *data);
+
 extern int sna_page_flip(struct sna *sna,
 			 struct kgem_bo *bo,
-			 void *data,
-			 int ref_crtc_hw_id);
+			 sna_flip_handler_t handler,
+			 void *data);
 
 pure static inline struct sna *
 to_sna(ScrnInfoPtr scrn)
@@ -523,17 +527,15 @@ static inline uint64_t ust64(int tv_sec, int tv_usec)
 bool sna_dri2_open(struct sna *sna, ScreenPtr pScreen);
 void sna_dri2_page_flip_handler(struct sna *sna, struct drm_event_vblank *event);
 void sna_dri2_vblank_handler(struct sna *sna, struct drm_event_vblank *event);
-void sna_dri2_pixmap_update_bo(struct sna *sna, PixmapPtr pixmap);
+void sna_dri2_pixmap_update_bo(struct sna *sna, PixmapPtr pixmap, struct kgem_bo *bo);
 void sna_dri2_destroy_window(WindowPtr win);
-void sna_dri2_reset_scanout(struct sna *sna);
 void sna_dri2_close(struct sna *sna, ScreenPtr pScreen);
 #else
 static inline bool sna_dri2_open(struct sna *sna, ScreenPtr pScreen) { return false; }
 static inline void sna_dri2_page_flip_handler(struct sna *sna, struct drm_event_vblank *event) { }
 static inline void sna_dri2_vblank_handler(struct sna *sna, struct drm_event_vblank *event) { }
-static inline void sna_dri2_pixmap_update_bo(struct sna *sna, PixmapPtr pixmap) { }
+static inline void sna_dri2_pixmap_update_bo(struct sna *sna, PixmapPtr pixmap, struct kgem_bo *bo) { }
 static inline void sna_dri2_destroy_window(WindowPtr win) { }
-static inline void sna_dri2_reset_scanout(struct sna *sna) { }
 static inline void sna_dri2_close(struct sna *sna, ScreenPtr pScreen) { }
 #endif
 
@@ -549,15 +551,12 @@ static inline void sna_dri3_close(struct sna *sna, ScreenPtr pScreen) { }
 bool sna_present_open(struct sna *sna, ScreenPtr pScreen);
 void sna_present_update(struct sna *sna);
 void sna_present_close(struct sna *sna, ScreenPtr pScreen);
-void sna_present_flip_handler(struct sna *sna,
-			      struct drm_event_vblank *event);
 void sna_present_vblank_handler(struct sna *sna,
 				struct drm_event_vblank *event);
 #else
 static inline bool sna_present_open(struct sna *sna, ScreenPtr pScreen) { return false; }
 static inline void sna_present_update(struct sna *sna) { }
 static inline void sna_present_close(struct sna *sna, ScreenPtr pScreen) { }
-static inline void sna_present_flip_handler(struct sna *sna, struct drm_event_vblank *event) { }
 static inline void sna_present_vblank_handler(struct sna *sna, struct drm_event_vblank *event) { }
 #endif
 
@@ -832,7 +831,6 @@ region_subsumes_damage(const RegionRec *region, struct sna_damage *damage)
 						(BoxPtr)de) == PIXMAN_REGION_IN;
 }
 
-
 static inline bool
 sna_drawable_is_clear(DrawablePtr d)
 {
@@ -855,7 +853,7 @@ static inline struct kgem_bo *sna_pixmap_pin(PixmapPtr pixmap, unsigned flags)
 {
 	struct sna_pixmap *priv;
 
-	priv = sna_pixmap_force_to_gpu(pixmap, MOVE_READ | MOVE_WRITE);
+	priv = sna_pixmap_force_to_gpu(pixmap, MOVE_READ);
 	if (!priv)
 		return NULL;
 
diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index 85c1c35..f77b4c6 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -1825,6 +1825,9 @@ sna_pixmap_undo_cow(struct sna *sna, struct sna_pixmap *priv, unsigned flags)
 	assert(priv->gpu_bo == cow->bo);
 	assert(cow->refcnt);
 
+	if (flags && (flags & MOVE_WRITE) == 0 && IS_COW_OWNER(priv->cow))
+		return true;
+
 	if (!IS_COW_OWNER(priv->cow))
 		list_del(&priv->cow_list);
 
@@ -3189,25 +3192,32 @@ sna_pixmap_move_area_to_gpu(PixmapPtr pixmap, const BoxRec *box, unsigned int fl
 		}
 	}
 
-	if (priv->cow && (flags & MOVE_WRITE || priv->cpu_damage)) {
-		unsigned cow = MOVE_READ;
+	if (priv->cow) {
+		unsigned cow = flags & (MOVE_READ | MOVE_WRITE | __MOVE_FORCE);
 
 		if ((flags & MOVE_READ) == 0) {
 			if (priv->gpu_damage) {
 				r.extents = *box;
 				r.data = NULL;
-				if (region_subsumes_damage(&r,
-							   priv->gpu_damage))
-					cow = 0;
-			} else
-				cow = 0;
+				if (!region_subsumes_damage(&r, priv->gpu_damage))
+					cow |= MOVE_READ;
+			}
+		} else {
+			if (priv->cpu_damage) {
+				r.extents = *box;
+				r.data = NULL;
+				if (region_overlaps_damage(&r, priv->cpu_damage, 0, 0))
+					cow |= MOVE_WRITE;
+			}
 		}
 
-		if (!sna_pixmap_undo_cow(sna, priv, cow))
-			return NULL;
+		if (cow) {
+			if (!sna_pixmap_undo_cow(sna, priv, cow))
+				return NULL;
 
-		if (priv->gpu_bo == NULL)
-			sna_damage_destroy(&priv->gpu_damage);
+			if (priv->gpu_bo == NULL)
+				sna_damage_destroy(&priv->gpu_damage);
+		}
 	}
 
 	if (sna_damage_is_all(&priv->gpu_damage,
@@ -3221,7 +3231,7 @@ sna_pixmap_move_area_to_gpu(PixmapPtr pixmap, const BoxRec *box, unsigned int fl
 	}
 
 	if ((priv->gpu_bo && priv->gpu_bo->proxy) &&
-	    (flags & MOVE_WRITE || priv->gpu_bo->proxy->rq == NULL)) {
+	    (flags & (MOVE_WRITE | __MOVE_FORCE) || priv->gpu_bo->proxy->rq == NULL)) {
 		DBG(("%s: discarding cached upload buffer\n", __FUNCTION__));
 		assert(priv->gpu_damage == NULL);
 		assert(!priv->pinned);
@@ -3437,7 +3447,7 @@ sna_drawable_use_bo(DrawablePtr drawable, unsigned flags, const BoxRec *box,
 	}
 
 	if (priv->cow) {
-		unsigned cow = MOVE_READ;
+		unsigned cow = MOVE_WRITE | MOVE_READ;
 
 		if (flags & IGNORE_CPU) {
 			if (priv->gpu_damage) {
@@ -3451,9 +3461,9 @@ sna_drawable_use_bo(DrawablePtr drawable, unsigned flags, const BoxRec *box,
 				region.data = NULL;
 				if (region_subsumes_damage(&region,
 							   priv->gpu_damage))
-					cow = 0;
+					cow &= ~MOVE_READ;
 			} else
-				cow = 0;
+				cow &= ~MOVE_READ;
 		}
 
 		if (!sna_pixmap_undo_cow(to_sna_from_pixmap(pixmap), priv, cow))
@@ -3933,12 +3943,17 @@ sna_pixmap_move_to_gpu(PixmapPtr pixmap, unsigned flags)
 	if ((flags & MOVE_READ) == 0 && UNDO)
 		kgem_bo_pair_undo(&sna->kgem, priv->gpu_bo, priv->cpu_bo);
 
-	if (priv->cow && (flags & MOVE_WRITE || priv->cpu_damage)) {
-		if (!sna_pixmap_undo_cow(sna, priv, flags & MOVE_READ))
-			return NULL;
+	if (priv->cow) {
+		unsigned cow = flags & (MOVE_READ | MOVE_WRITE | __MOVE_FORCE);
+		if (flags & MOVE_READ && priv->cpu_damage)
+			cow |= MOVE_WRITE;
+		if (cow) {
+			if (!sna_pixmap_undo_cow(sna, priv, cow))
+				return NULL;
 
-		if (priv->gpu_bo == NULL)
-			sna_damage_destroy(&priv->gpu_damage);
+			if (priv->gpu_bo == NULL)
+				sna_damage_destroy(&priv->gpu_damage);
+		}
 	}
 
 	if (sna_damage_is_all(&priv->gpu_damage,
@@ -3955,7 +3970,7 @@ sna_pixmap_move_to_gpu(PixmapPtr pixmap, unsigned flags)
 	}
 
 	if ((priv->gpu_bo && priv->gpu_bo->proxy) &&
-	    (flags & MOVE_WRITE || priv->gpu_bo->proxy->rq == NULL)) {
+	    (flags & (MOVE_WRITE | __MOVE_FORCE) || priv->gpu_bo->proxy->rq == NULL)) {
 		DBG(("%s: discarding cached upload buffer\n", __FUNCTION__));
 		assert(priv->gpu_damage == NULL);
 		assert(!priv->pinned);
@@ -11716,7 +11731,7 @@ sna_pixmap_get_source_bo(PixmapPtr pixmap)
 	if (priv->cpu_damage && priv->cpu_bo)
 		return kgem_bo_reference(priv->cpu_bo);
 
-	if (!sna_pixmap_force_to_gpu(pixmap, MOVE_READ | MOVE_ASYNC_HINT))
+	if (!sna_pixmap_move_to_gpu(pixmap, MOVE_READ | MOVE_ASYNC_HINT))
 		return NULL;
 
 	return kgem_bo_reference(priv->gpu_bo);
@@ -16643,7 +16658,7 @@ static bool has_shadow(struct sna *sna)
 	DBG(("%s: has pending damage? %d, outstanding flips: %d\n",
 	     __FUNCTION__,
 	     RegionNotEmpty(DamageRegion(damage)),
-	     sna->mode.shadow_flip));
+	     sna->mode.flip_active));
 
 	return RegionNotEmpty(DamageRegion(damage));
 }
diff --git a/src/sna/sna_display.c b/src/sna/sna_display.c
index 13dbf64..470db86 100644
--- a/src/sna/sna_display.c
+++ b/src/sna/sna_display.c
@@ -98,13 +98,6 @@ union compat_mode_get_connector{
 
 extern XF86ConfigPtr xf86configptr;
 
-struct rotation {
-	uint32_t obj_id, obj_type;
-	uint32_t prop_id;
-	uint32_t supported;
-	uint32_t current;
-};
-
 struct sna_crtc {
 	struct drm_mode_modeinfo kmode;
 	int dpms_mode;
@@ -119,11 +112,19 @@ struct sna_crtc {
 	uint8_t pipe;
 
 	uint32_t rotation;
-	struct rotation primary_rotation;
-	struct rotation sprite_rotation;
+	struct rotation {
+		uint32_t obj_id, obj_type;
+		uint32_t prop_id;
+		uint32_t supported;
+		uint32_t current;
+	} primary_rotation, sprite_rotation;
 
 	uint32_t last_seq, wrap_seq;
 	struct ust_msc swap;
+
+	sna_flip_handler_t flip_handler;
+	struct kgem_bo *flip_bo;
+	void *flip_data;
 };
 
 struct sna_property {
@@ -802,7 +803,7 @@ sna_crtc_apply(xf86CrtcPtr crtc)
 	int output_count = 0;
 	int i;
 
-	DBG(("%s CRTC:%d [pipe=%d]\n", __FUNCTION__, sna_crtc->id, sna_crtc->pipe));
+	DBG(("%s CRTC:%d [pipe=%d], handle=%d\n", __FUNCTION__, sna_crtc->id, sna_crtc->pipe, sna_crtc->bo->handle));
 
 	assert(config->num_output < ARRAY_SIZE(output_ids));
 
@@ -880,8 +881,8 @@ static bool wait_for_shadow(struct sna *sna, struct sna_pixmap *priv, unsigned f
 	DamagePtr damage;
 	bool ret = true;
 
-	DBG(("%s: flags=%x, shadow_flip=%d, handle=%d, wait=%d, old=%d\n",
-	     __FUNCTION__, flags, sna->mode.shadow_flip,
+	DBG(("%s: flags=%x, flips=%d, handle=%d, wait=%d, old=%d\n",
+	     __FUNCTION__, flags, sna->mode.flip_active,
 	     priv->gpu_bo->handle, wait->bo->handle, sna->mode.shadow->handle));
 
 	assert(wait->bo != priv->gpu_bo);
@@ -893,18 +894,14 @@ static bool wait_for_shadow(struct sna *sna, struct sna_pixmap *priv, unsigned f
 		return true;
 
 	assert(sna->mode.shadow_active);
-	assert(bo == sna->mode.shadow);
-
-	assert(priv->gpu_bo->refcnt >= 1);
-	sna->mode.shadow = priv->gpu_bo;
 
 	damage = sna->mode.shadow_damage;
 	sna->mode.shadow_damage = NULL;
 
-	while (sna->mode.shadow_flip && sna_mode_has_pending_events(sna))
+	while (sna->mode.flip_active && sna_mode_has_pending_events(sna))
 		sna_mode_wakeup(sna);
 
-	if (sna->mode.shadow_flip) {
+	if (sna->mode.flip_active) {
 		bo = kgem_create_2d(&sna->kgem,
 				    pixmap->drawable.width,
 				    pixmap->drawable.height,
@@ -915,16 +912,14 @@ static bool wait_for_shadow(struct sna *sna, struct sna_pixmap *priv, unsigned f
 			DBG(("%s: replacing still-attached GPU bo\n",
 			     __FUNCTION__));
 
-			kgem_bo_destroy(&sna->kgem, wait->bo);
 			RegionUninit(&wait->region);
-
 			wait->region.extents.x1 = 0;
 			wait->region.extents.y1 = 0;
 			wait->region.extents.x2 = pixmap->drawable.width;
 			wait->region.extents.y2 = pixmap->drawable.height;
 			wait->region.data = NULL;
 		} else {
-			while (sna->mode.shadow_flip &&
+			while (sna->mode.flip_active &&
 			       sna_mode_wait_for_event(sna))
 				sna_mode_wakeup(sna);
 
@@ -932,6 +927,27 @@ static bool wait_for_shadow(struct sna *sna, struct sna_pixmap *priv, unsigned f
 		}
 	}
 
+	if (bo->refcnt > 1) {
+		bo = kgem_create_2d(&sna->kgem,
+				    pixmap->drawable.width,
+				    pixmap->drawable.height,
+				    pixmap->drawable.bitsPerPixel,
+				    priv->gpu_bo->tiling,
+				    CREATE_EXACT | CREATE_SCANOUT);
+		if (bo != NULL) {
+			DBG(("%s: replacing exported GPU bo\n",
+			     __FUNCTION__));
+
+			RegionUninit(&wait->region);
+			wait->region.extents.x1 = 0;
+			wait->region.extents.y1 = 0;
+			wait->region.extents.x2 = pixmap->drawable.width;
+			wait->region.extents.y2 = pixmap->drawable.height;
+			wait->region.data = NULL;
+		} else
+			bo = wait->bo;
+	}
+
 	sna->mode.shadow_damage = damage;
 
 	if (flags & MOVE_READ && RegionNotEmpty(&wait->region)) {
@@ -951,9 +967,12 @@ static bool wait_for_shadow(struct sna *sna, struct sna_pixmap *priv, unsigned f
 		sna_pixmap_undo_cow(sna, priv, 0);
 
 	sna_pixmap_unmap(pixmap, priv);
-	priv->gpu_bo = bo;
 
-	sna_dri2_pixmap_update_bo(sna, pixmap);
+	DBG(("%s: setting front pixmap to handle=%d\n", __FUNCTION__, bo->handle));
+	kgem_bo_destroy(&sna->kgem, priv->gpu_bo);
+	priv->gpu_bo = bo == wait->bo ? kgem_bo_reference(bo) : bo;
+
+	sna_dri2_pixmap_update_bo(sna, pixmap, bo);
 
 done:
 	kgem_bo_destroy(&sna->kgem, wait->bo);
@@ -1099,8 +1118,14 @@ sna_crtc_disable(xf86CrtcPtr crtc)
 	sna_crtc_disable_shadow(sna, sna_crtc);
 
 	if (sna_crtc->bo) {
+		assert(sna_crtc->bo->active_scanout);
+		assert(sna_crtc->bo->refcnt >= sna_crtc->bo->active_scanout);
+		sna_crtc->bo->active_scanout--;
 		kgem_bo_destroy(&sna->kgem, sna_crtc->bo);
 		sna_crtc->bo = NULL;
+
+		sna->mode.front_active--;
+		sna->mode.dirty = true;
 	}
 
 	assert(sna_crtc->dpms_mode == DPMSModeOff);
@@ -1301,7 +1326,7 @@ static bool use_shadow(struct sna *sna, xf86CrtcPtr crtc)
 		return true;
 	}
 
-	priv = sna_pixmap_force_to_gpu(sna->front, MOVE_READ | MOVE_WRITE);
+	priv = sna_pixmap_force_to_gpu(sna->front, MOVE_READ);
 	if (priv == NULL)
 		return true; /* maybe we can create a bo for the scanout? */
 
@@ -1362,6 +1387,45 @@ static bool use_shadow(struct sna *sna, xf86CrtcPtr crtc)
 	return false;
 }
 
+static void set_shadow(PixmapPtr pixmap, struct kgem_bo *bo, RegionPtr region)
+{
+	struct sna_pixmap *priv = sna_pixmap(pixmap);
+	struct wait_for_shadow *wait;
+
+	DBG(("%s: waiting for region %dx[(%d, %d), (%d, %d)] on handle=%d\n",
+	     __FUNCTION__,
+	     RegionNumRects(region),
+	     region->extents.x1, region->extents.y1,
+	     region->extents.x2, region->extents.y2,
+	     bo->handle));
+
+	assert((priv->pinned & PIN_PRIME) == 0);
+	assert(bo != priv->gpu_bo);
+	assert(priv->gpu_bo);
+
+	wait = priv->move_to_gpu_data;
+	if (wait != NULL) {
+		assert(priv->move_to_gpu = wait_for_shadow);
+		assert(priv->pinned & PIN_SCANOUT);
+		assert(wait->bo == bo);
+		RegionUnion(&wait->region, &wait->region, region);
+		return;
+	}
+
+	assert(priv->move_to_gpu == NULL);
+	wait = malloc(sizeof(*wait));
+	if (wait != NULL) {
+		wait->bo = kgem_bo_reference(bo);
+		RegionNull(&wait->region);
+		RegionCopy(&wait->region, region);
+
+		priv->move_to_gpu = wait_for_shadow;
+		priv->move_to_gpu_data = wait;
+	}
+
+	priv->pinned |= PIN_SCANOUT;
+}
+
 static struct kgem_bo *sna_crtc_attach(xf86CrtcPtr crtc)
 {
 	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
@@ -1422,51 +1486,8 @@ static struct kgem_bo *sna_crtc_attach(xf86CrtcPtr crtc)
 
 		sna_crtc->transform = true;
 		return bo;
-	} else if (sna->flags & SNA_TEAR_FREE) {
-		DBG(("%s: tear-free updates requested\n", __FUNCTION__));
-
-		if (!sna_crtc_enable_shadow(sna, sna_crtc))
-			return NULL;
-
-		DBG(("%s: attaching to single shadow pixmap\n", __FUNCTION__));
-		if (sna->mode.shadow == NULL) {
-			BoxRec box;
-
-			bo = kgem_create_2d(&sna->kgem,
-					    sna->scrn->virtualX,
-					    sna->scrn->virtualY,
-					    scrn->bitsPerPixel,
-					    I915_TILING_X,
-					    CREATE_SCANOUT);
-			if (bo == NULL)
-				return NULL;
-
-			box.x1 = box.y1 = 0;
-			box.x2 = sna->scrn->virtualX;
-			box.y2 = sna->scrn->virtualY;
-
-			if (!sna->render.copy_boxes(sna, GXcopy,
-						    sna->front, sna_pixmap(sna->front)->gpu_bo, 0, 0,
-						    sna->front, bo, 0, 0,
-						    &box, 1, COPY_LAST)) {
-				kgem_bo_destroy(&sna->kgem, bo);
-				return NULL;
-			}
-
-			if (!get_fb(sna, bo,
-				    sna->scrn->virtualX,
-				    sna->scrn->virtualY)) {
-				kgem_bo_destroy(&sna->kgem, bo);
-				return NULL;
-			}
-
-			sna->mode.shadow = bo;
-		}
-
-		return kgem_bo_reference(sna->mode.shadow);
 	} else {
 		DBG(("%s: attaching to framebuffer\n", __FUNCTION__));
-		sna_crtc_disable_shadow(sna, sna_crtc);
 		bo = sna_pixmap_pin(sna->front, PIN_SCANOUT);
 		if (bo == NULL)
 			return NULL;
@@ -1474,7 +1495,43 @@ static struct kgem_bo *sna_crtc_attach(xf86CrtcPtr crtc)
 		if (!get_fb(sna, bo, scrn->virtualX, scrn->virtualY))
 			return NULL;
 
-		assert(!sna_crtc->shadow);
+		if (sna->flags & SNA_TEAR_FREE) {
+			DBG(("%s: creating TearFree shadow\n", __FUNCTION__));
+			if (!sna_crtc_enable_shadow(sna, sna_crtc))
+				return NULL;
+
+			if (sna->mode.shadow == NULL) {
+				RegionRec region;
+				struct kgem_bo *shadow;
+
+				region.extents.x1 = 0;
+				region.extents.y1 = 0;
+				region.extents.x2 = sna->scrn->virtualX;
+				region.extents.y2 = sna->scrn->virtualY;
+				region.data = NULL;
+
+				shadow = kgem_create_2d(&sna->kgem,
+							region.extents.x2,
+							region.extents.y2,
+							scrn->bitsPerPixel,
+							I915_TILING_X,
+							CREATE_SCANOUT);
+				if (shadow == NULL)
+					return NULL;
+
+				if (!get_fb(sna, shadow,
+					    region.extents.x2,
+					    region.extents.y2)) {
+					kgem_bo_destroy(&sna->kgem, shadow);
+					return NULL;
+				}
+
+				sna->mode.shadow = shadow;
+				set_shadow(sna->front, shadow, &region);
+			}
+		} else
+			sna_crtc_disable_shadow(sna, sna_crtc);
+
 		assert(sna_crtc->primary_rotation.supported & crtc->rotation);
 		sna_crtc->rotation = crtc->rotation;
 		return kgem_bo_reference(bo);
@@ -1690,12 +1747,19 @@ retry: /* Attach per-crtc pixmap or direct */
 		sna_crtc->kmode = saved_kmode;
 		return FALSE;
 	}
-	if (saved_bo)
+	bo->active_scanout++;
+	if (saved_bo) {
+		assert(saved_bo->active_scanout);
+		assert(saved_bo->refcnt >= saved_bo->active_scanout);
+		saved_bo->active_scanout--;
 		kgem_bo_destroy(&sna->kgem, saved_bo);
+	}
 
 	sna_crtc_randr(crtc);
 	if (sna_crtc->shadow)
 		sna_crtc_damage(crtc);
+	sna->mode.front_active += saved_bo == NULL;
+	sna->mode.dirty = true;
 
 	return TRUE;
 }
@@ -1731,23 +1795,28 @@ void sna_mode_adjust_frame(struct sna *sna, int x, int y)
 {
 	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
 	xf86CrtcPtr crtc;
+	int saved_x, saved_y;
 
 	if ((unsigned)config->compat_output >= config->num_output)
 		return;
 
 	crtc = config->output[config->compat_output]->crtc;
-	if (crtc && crtc->enabled) {
-		int saved_x = crtc->x;
-		int saved_y = crtc->y;
-
-		crtc->x = x;
-		crtc->y = y;
-		if (to_sna_crtc(crtc) &&
-		    !sna_crtc_set_mode_major(crtc, &crtc->mode,
-					     crtc->rotation, x, y)) {
-			crtc->x = saved_x;
-			crtc->y = saved_y;
-		}
+	if (crtc == NULL || !crtc->enabled)
+		return;
+
+	if (crtc->x == x && crtc->y == y)
+		return;
+
+	saved_x = crtc->x;
+	saved_y = crtc->y;
+
+	crtc->x = x;
+	crtc->y = y;
+	if (to_sna_crtc(crtc) &&
+	    !sna_crtc_set_mode_major(crtc, &crtc->mode,
+				     crtc->rotation, x, y)) {
+		crtc->x = saved_x;
+		crtc->y = saved_y;
 	}
 }
 
@@ -3298,10 +3367,10 @@ sna_mode_resize(ScrnInfoPtr scrn, int width, int height)
 	scrn->displayWidth = width;
 
 	/* Flush pending shadow updates */
-	if (sna->mode.shadow_flip) {
+	if (sna->mode.flip_active) {
 		DBG(("%s: waiting for %d outstanding TearFree flips\n",
-		     __FUNCTION__, sna->mode.shadow_flip));
-		while (sna->mode.shadow_flip && sna_mode_wait_for_event(sna))
+		     __FUNCTION__, sna->mode.flip_active));
+		while (sna->mode.flip_active && sna_mode_wait_for_event(sna))
 			sna_mode_wakeup(sna);
 	}
 
@@ -4090,7 +4159,7 @@ sna_cursors_fini(struct sna *sna)
 }
 
 static int do_page_flip(struct sna *sna, struct kgem_bo *bo,
-			void *data, int pipe)
+			sna_flip_handler_t handler, void *data)
 {
 	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
 	int width = sna->scrn->virtualX;
@@ -4098,6 +4167,9 @@ static int do_page_flip(struct sna *sna, struct kgem_bo *bo,
 	int count = 0;
 	int i;
 
+	assert((sna->flags & SNA_TEAR_FREE) == 0);
+	assert(sna->mode.flip_active == 0);
+
 	if ((sna->flags & (data ? SNA_HAS_FLIP : SNA_HAS_ASYNC_FLIP)) == 0)
 		return 0;
 
@@ -4116,10 +4188,10 @@ static int do_page_flip(struct sna *sna, struct kgem_bo *bo,
 
 		DBG(("%s: crtc %d id=%d, pipe=%d active? %d\n",
 		     __FUNCTION__, i, crtc->id, crtc->pipe, crtc->bo != NULL));
-		if (crtc->bo == NULL) {
-			assert(crtc->pipe != pipe);
+		if (crtc->bo == NULL)
 			continue;
-		}
+		assert(crtc->bo->active_scanout);
+		assert(crtc->bo->refcnt >= crtc->bo->active_scanout);
 
 		arg.crtc_id = crtc->id;
 		arg.fb_id = get_fb(sna, bo, width, height);
@@ -4132,8 +4204,7 @@ static int do_page_flip(struct sna *sna, struct kgem_bo *bo,
 		 * completion event. All other crtc's events will be discarded.
 		 */
 		if (data) {
-			arg.user_data = (uintptr_t)data;
-			arg.user_data |= crtc->pipe == pipe;
+			arg.user_data = (uintptr_t)crtc;
 			arg.flags = DRM_MODE_PAGE_FLIP_EVENT;
 		} else {
 			arg.user_data = 0;
@@ -4141,9 +4212,8 @@ static int do_page_flip(struct sna *sna, struct kgem_bo *bo,
 		}
 		arg.reserved = 0;
 
-		DBG(("%s: crtc %d id=%d, pipe=%d, [ref? %d] --> fb %d\n",
-		     __FUNCTION__, i, crtc->id, crtc->pipe,
-		     crtc->pipe == pipe, arg.fb_id));
+		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));
@@ -4161,9 +4231,13 @@ static int do_page_flip(struct sna *sna, struct kgem_bo *bo,
 			return 0;
 		}
 
-		if (crtc->bo != bo) {
-			kgem_bo_destroy(&sna->kgem, crtc->bo);
-			crtc->bo = kgem_bo_reference(bo);
+		if (data) {
+			assert(crtc->flip_bo == NULL);
+			crtc->flip_handler = handler;
+			crtc->flip_data = data;
+			crtc->flip_bo = kgem_bo_reference(bo);
+			crtc->flip_bo->active_scanout++;
+			sna->mode.flip_active++;
 		}
 
 		count++;
@@ -4175,8 +4249,8 @@ static int do_page_flip(struct sna *sna, struct kgem_bo *bo,
 int
 sna_page_flip(struct sna *sna,
 	      struct kgem_bo *bo,
-	      void *data,
-	      int pipe)
+	      sna_flip_handler_t handler,
+	      void *data)
 {
 	int count;
 
@@ -4195,7 +4269,7 @@ sna_page_flip(struct sna *sna,
 	 * Also, flips queued on disabled or incorrectly configured displays
 	 * may never complete; this is a configuration error.
 	 */
-	count = do_page_flip(sna, bo, data, pipe);
+	count = do_page_flip(sna, bo, handler, data);
 	DBG(("%s: page flipped %d crtcs\n", __FUNCTION__, count));
 
 	return count;
@@ -4589,21 +4663,22 @@ probe_capabilities(struct sna *sna)
 	     sna->flags & SNA_HAS_ASYNC_FLIP ? "enabled" : "disabled"));
 }
 
-static void
+void
 sna_crtc_config_notify(ScreenPtr screen)
 {
 	struct sna *sna = to_sna_from_screen(screen);
 
-	DBG(("%s\n", __FUNCTION__));
+	DBG(("%s(dirty?=%d)\n", __FUNCTION__, sna->mode.dirty));
+	if (!sna->mode.dirty)
+		return;
 
 	probe_capabilities(sna);
 
-	sna_dri2_reset_scanout(sna);
-
-	sna_mode_update(sna);
 	sna_cursors_reload(sna);
 
 	sna_present_update(sna);
+
+	sna->mode.dirty = false;
 }
 
 #if HAS_PIXMAP_SHARING
@@ -5129,7 +5204,7 @@ sna_wait_for_scanline(struct sna *sna,
 	return ret;
 }
 
-void sna_mode_update(struct sna *sna)
+void sna_mode_check(struct sna *sna)
 {
 	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
 	int i;
@@ -5140,35 +5215,32 @@ void sna_mode_update(struct sna *sna)
 	DBG(("%s\n", __FUNCTION__));
 
 	/* Validate CRTC attachments and force consistency upon the kernel */
-	sna->mode.front_active = 0;
 	for (i = 0; i < sna->mode.num_real_crtc; i++) {
 		xf86CrtcPtr crtc = config->crtc[i];
 		struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
 		struct drm_mode_crtc mode;
-		uint32_t expected;
+		uint32_t expected[2];
 
 		assert(sna_crtc);
 
 #if XF86_CRTC_VERSION >= 3
 		assert(sna_crtc->bo == NULL || crtc->active);
 #endif
-		expected = sna_crtc->bo ? fb_id(sna_crtc->bo) : 0;
+		expected[0] = sna_crtc->bo ? fb_id(sna_crtc->bo) : 0;
+		expected[1] = sna_crtc->flip_bo ? fb_id(sna_crtc->flip_bo) : -1;
 
 		VG_CLEAR(mode);
 		mode.crtc_id = sna_crtc->id;
 		if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCRTC, &mode))
 			continue;
 
-		DBG(("%s: crtc=%d, valid?=%d, fb attached?=%d, expected=%d\n",
+		DBG(("%s: crtc=%d, valid?=%d, fb attached?=%d, expected=(%d or %d)\n",
 		     __FUNCTION__,
 		     mode.crtc_id, mode.mode_valid,
-		     mode.fb_id, expected));
+		     mode.fb_id, expected[0], expected[1]));
 
-		if (mode.fb_id != expected)
+		if (mode.fb_id != expected[0] && mode.fb_id != expected[1])
 			sna_crtc_disable(crtc);
-
-		if (sna->front && sna_crtc->bo == __sna_pixmap_get_bo(sna->front))
-			sna->mode.front_active++;
 	}
 
 	for (i = 0; i < config->num_output; i++) {
@@ -5474,28 +5546,7 @@ sna_crtc_redisplay(xf86CrtcPtr crtc, RegionPtr region)
 		sna_crtc_redisplay__fallback(crtc, region, sna_crtc->bo);
 }
 
-static void set_bo(PixmapPtr pixmap, struct kgem_bo *bo, RegionPtr region)
-{
-	struct sna_pixmap *priv = sna_pixmap(pixmap);
-	struct wait_for_shadow *wait;
-
-	assert((priv->pinned & PIN_PRIME) == 0);
-	assert(bo != priv->gpu_bo);
-	assert(priv->gpu_bo);
-
-	assert(priv->move_to_gpu == NULL);
-	wait = malloc(sizeof(*wait));
-	if (wait != NULL) {
-		wait->bo = kgem_bo_reference(bo);
-		RegionNull(&wait->region);
-		RegionCopy(&wait->region, region);
-
-		priv->move_to_gpu = wait_for_shadow;
-		priv->move_to_gpu_data = wait;
-	}
-
-	priv->pinned |= PIN_SCANOUT;
-}
+#define shadow_flip_handler (sna_flip_handler_t)sna_mode_redisplay
 
 void sna_mode_redisplay(struct sna *sna)
 {
@@ -5509,7 +5560,7 @@ void sna_mode_redisplay(struct sna *sna)
 	DBG(("%s: posting shadow damage? %d (flips pending? %d)\n",
 	     __FUNCTION__,
 	     !RegionNil(DamageRegion(sna->mode.shadow_damage)),
-	     sna->mode.shadow_flip));
+	     sna->mode.flip_active));
 	assert((sna->flags & SNA_IS_HOSTED) == 0);
 	assert(sna->mode.shadow_active);
 
@@ -5522,19 +5573,19 @@ void sna_mode_redisplay(struct sna *sna)
 	     region->extents.x1, region->extents.y1,
 	     region->extents.x2, region->extents.y2));
 
-	if (sna->mode.shadow_flip) {
+	if (sna->mode.flip_active) {
 		DamagePtr damage;
 
 		damage = sna->mode.shadow_damage;
 		sna->mode.shadow_damage = NULL;
 
-		while (sna->mode.shadow_flip && sna_mode_has_pending_events(sna))
+		while (sna->mode.flip_active && sna_mode_has_pending_events(sna))
 			sna_mode_wakeup(sna);
 
 		sna->mode.shadow_damage = damage;
 	}
 
-	if (sna->mode.shadow_flip)
+	if (sna->mode.flip_active)
 		return;
 
 	if (wedged(sna) || !sna_pixmap_move_to_gpu(sna->front, MOVE_READ | MOVE_ASYNC_HINT)) {
@@ -5594,16 +5645,13 @@ void sna_mode_redisplay(struct sna *sna)
 		RegionRec damage;
 
 		assert(sna_crtc != NULL);
-		DBG(("%s: crtc[%d] shadow? %d, transformed? %d\n",
-		     __FUNCTION__, i,
-		     sna_crtc->shadow,
-		     sna_crtc->bo != sna->mode.shadow));
+		DBG(("%s: crtc[%d] transformed? %d\n",
+		     __FUNCTION__, i, sna_crtc->transform));
 
-		if (!sna_crtc->shadow || sna_crtc->bo == sna->mode.shadow)
+		if (!sna_crtc->transform)
 			continue;
 
 		assert(crtc->enabled);
-		assert(sna_crtc->transform);
 
 		damage.extents = crtc->bounds;
 		damage.data = NULL;
@@ -5639,7 +5687,7 @@ void sna_mode_redisplay(struct sna *sna)
 				if (arg.fb_id == 0)
 					goto disable1;
 
-				arg.user_data = 0;
+				arg.user_data = (uintptr_t)crtc;
 				arg.flags = DRM_MODE_PAGE_FLIP_EVENT;
 				arg.reserved = 0;
 
@@ -5666,11 +5714,14 @@ disable1:
 
 					continue;
 				}
+				sna->mode.flip_active++;
 
-				sna_crtc->shadow_bo = sna_crtc->bo;
-				sna_crtc->bo = bo;
+				assert(sna_crtc->flip_bo == NULL);
+				sna_crtc->flip_handler = shadow_flip_handler;
+				sna_crtc->flip_bo = bo;
+				sna_crtc->flip_bo->active_scanout++;
 
-				sna->mode.shadow_flip++;
+				sna_crtc->shadow_bo = sna_crtc->bo;
 			} else {
 				sna_crtc_redisplay(crtc, &damage);
 				kgem_scanout_flush(&sna->kgem, sna_crtc->bo);
@@ -5682,56 +5733,62 @@ disable1:
 	if (sna->mode.shadow) {
 		struct kgem_bo *new = __sna_pixmap_get_bo(sna->front);
 		struct kgem_bo *old = sna->mode.shadow;
+		struct drm_mode_crtc_page_flip arg;
+
+		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;
+
+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);
+			}
+
+			return;
+		}
+
+		arg.flags = DRM_MODE_PAGE_FLIP_EVENT;
+		arg.reserved = 0;
 
-		DBG(("%s: flipping tear-free outputs\n", __FUNCTION__));
 		kgem_bo_submit(&sna->kgem, new);
 
 		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;
 
 			assert(crtc != NULL);
-			DBG(("%s: crtc %d [%d, pipe=%d] active? %d\n",
-			     __FUNCTION__, i, crtc->id, crtc->pipe, crtc && crtc->bo));
-			if (crtc->bo != old)
+			DBG(("%s: crtc %d [%d, pipe=%d] active? %d, transformed? %d\n",
+			     __FUNCTION__, i, crtc->id, crtc->pipe, crtc->bo ? crtc->bo->handle : 0, crtc->transform));
+			if (crtc->bo == NULL || crtc->transform)
 				continue;
 
 			assert(config->crtc[i]->enabled);
 			assert(crtc->dpms_mode <= DPMSModeOn);
 
 			arg.crtc_id = crtc->id;
-			arg.fb_id = get_fb(sna, new,
-					   sna->scrn->virtualX,
-					   sna->scrn->virtualY);
-			if (arg.fb_id == 0)
-				goto disable2;
-
-			arg.user_data = 0;
-			arg.flags = DRM_MODE_PAGE_FLIP_EVENT;
-			arg.reserved = 0;
+			arg.user_data = (uintptr_t)crtc;
 
 			if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_PAGE_FLIP, &arg)) {
-				DBG(("%s: flip [fb=%d] on crtc %d [%d, pipe=%d] failed - %d\n",
+				ERR(("%s: flip [fb=%d] on crtc %d [%d, pipe=%d] failed - %d\n",
 				     __FUNCTION__, arg.fb_id, i, crtc->id, crtc->pipe, errno));
-disable2:
-				if (sna->mode.shadow_flip == 0) {
-					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, new, 0, 0,
-								    sna->front, old, 0, 0,
-								    &box, 1, COPY_LAST)) {
-						kgem_submit(&sna->kgem);
-						RegionEmpty(region);
-					}
-
-					return;
-				}
+				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",
@@ -5739,21 +5796,27 @@ disable2:
 				sna_crtc_disable(config->crtc[i]);
 				continue;
 			}
-			sna->mode.shadow_flip++;
+			sna->mode.flip_active++;
 
-			kgem_bo_destroy(&sna->kgem, old);
-			crtc->bo = kgem_bo_reference(new);
+			assert(crtc->flip_bo == NULL);
+			crtc->flip_handler = shadow_flip_handler;
+			crtc->flip_bo = kgem_bo_reference(new);
+			crtc->flip_bo->active_scanout++;
 		}
 
 		DBG(("%s: flipped %d outputs, shadow active? %d\n",
 		     __FUNCTION__,
-		     sna->mode.shadow_flip,
+		     sna->mode.flip_active,
 		     sna->mode.shadow ? sna->mode.shadow->handle : 0));
 
-		if (sna->mode.shadow) {
+		if (sna->mode.flip_active) {
 			assert(old == sna->mode.shadow);
 			assert(old->refcnt >= 1);
-			set_bo(sna->front, old, region);
+			set_shadow(sna->front, old, region);
+
+			assert(new->refcnt >= 1);
+			kgem_bo_destroy(&sna->kgem, old);
+			sna->mode.shadow = kgem_bo_reference(new);
 		}
 	} else
 		kgem_submit(&sna->kgem);
@@ -5786,14 +5849,35 @@ void sna_mode_wakeup(struct sna *sna)
 				sna_dri2_vblank_handler(sna, (struct drm_event_vblank *)e);
 			break;
 		case DRM_EVENT_FLIP_COMPLETE:
-			if (((struct drm_event_vblank *)e)->user_data) {
-				if (((uintptr_t)((struct drm_event_vblank *)e)->user_data) & 2)
-					sna_present_flip_handler(sna, (struct drm_event_vblank *)e);
-				else
-					sna_dri2_page_flip_handler(sna, (struct drm_event_vblank *)e);
-			} else {
-				if (!--sna->mode.shadow_flip)
-					sna_mode_redisplay(sna);
+			{
+				struct drm_event_vblank *vbl = (struct drm_event_vblank *)e;
+				struct sna_crtc *crtc = (void *)(uintptr_t)vbl->user_data;
+
+				crtc->swap.tv_sec = vbl->tv_sec;
+				crtc->swap.tv_usec = vbl->tv_usec;
+				crtc->swap.msc = msc64(crtc, vbl->sequence);
+
+				if (crtc->bo) {
+					assert(crtc->bo->active_scanout);
+					assert(crtc->bo->refcnt >= crtc->bo->active_scanout);
+					crtc->bo->active_scanout--;
+					kgem_bo_destroy(&sna->kgem, crtc->bo);
+
+					assert(crtc->flip_bo->active_scanout >= 1);
+					assert(crtc->flip_bo->refcnt >= crtc->flip_bo->active_scanout);
+					crtc->bo = crtc->flip_bo;
+					crtc->flip_bo = NULL;
+				} else {
+					assert(crtc->flip_bo->active_scanout);
+					assert(crtc->flip_bo->refcnt >= crtc->flip_bo->active_scanout);
+					crtc->flip_bo->active_scanout--;
+					kgem_bo_destroy(&sna->kgem, crtc->flip_bo);
+					crtc->flip_bo = NULL;
+				}
+
+				assert(sna->mode.flip_active);
+				if (--sna->mode.flip_active == 0)
+					crtc->flip_handler(sna, vbl, crtc->flip_data);
 			}
 			break;
 		default:
diff --git a/src/sna/sna_dri2.c b/src/sna/sna_dri2.c
index 317f598..9e591be 100644
--- a/src/sna/sna_dri2.c
+++ b/src/sna/sna_dri2.c
@@ -72,6 +72,7 @@ struct sna_dri2_private {
 	PixmapPtr pixmap;
 	struct kgem_bo *bo;
 	bool scanout;
+	bool stale;
 	uint32_t size;
 	int refcnt;
 };
@@ -83,7 +84,7 @@ get_private(void *buffer)
 }
 
 #if DRI2INFOREC_VERSION >= 4
-enum frame_event_type {
+enum event_type {
 	WAITMSC = 0,
 	SWAP,
 	SWAP_WAIT,
@@ -100,13 +101,12 @@ struct dri_bo {
 	uint32_t name;
 };
 
-struct sna_dri2_frame_event {
+struct sna_dri2_event {
 	DrawablePtr draw;
 	ClientPtr client;
-	enum frame_event_type type;
+	enum event_type type;
 	xf86CrtcPtr crtc;
 	int pipe;
-	int count;
 	bool queued;
 
 	/* for swaps & flips only */
@@ -116,53 +116,54 @@ struct sna_dri2_frame_event {
 	DRI2BufferPtr back;
 	struct kgem_bo *bo;
 
-	struct sna_dri2_frame_event *chain;
+	struct sna_dri2_event *chain;
 
 	struct list cache;
 
 	int mode;
 };
 
+static void sna_dri2_flip_event(struct sna *sna,
+				struct sna_dri2_event *flip);
+
 static void
-sna_dri2_get_back(struct sna *sna, struct sna_dri2_frame_event *info)
+sna_dri2_get_back(struct sna *sna,
+		  DRI2BufferPtr back,
+		  struct sna_dri2_event *info)
 {
 	struct kgem_bo *bo;
 	uint32_t name;
 
-	DBG(("%s: scanout=(%d, %d), back=%d, cache?=%d\n",
-	     __FUNCTION__,
-	     sna->dri2.scanout[0].bo ? sna->dri2.scanout[0].bo->handle : 0,
-	     sna->dri2.scanout[1].bo ? sna->dri2.scanout[1].bo->handle : 0,
-	     get_private(info->back)->bo->handle,
-	     !list_is_empty(&info->cache)));
+	get_private(back)->stale = false;
 
-	bo = get_private(info->back)->bo;
+	bo = get_private(back)->bo;
 	assert(bo->refcnt);
-	assert(bo->flush);
-	if (!(bo == sna->dri2.scanout[0].bo || bo == sna->dri2.scanout[1].bo)) {
+	DBG(("%s: back buffer handle=%d, scanout?=%d\n",
+	     __FUNCTION__, bo->handle, bo->active_scanout));
+	if (bo->active_scanout == 0) {
 		DBG(("%s: reuse unattached back\n", __FUNCTION__));
 		return;
 	}
 
 	bo = NULL;
-	if (!list_is_empty(&info->cache)) {
-		struct dri_bo *c = list_first_entry(&info->cache, struct dri_bo, link);
-		if (c->bo) {
-			bo = c->bo;
-			name = c->name;
-			DBG(("%s: reuse cache handle=%d,name=%d\n", __FUNCTION__,
-			     bo->handle, name));
-			list_move_tail(&c->link, &info->cache);
-			c->bo = NULL;
+	if (info) {
+		struct dri_bo *c;
+		list_for_each_entry(c, &info->cache, link) {
+			if (c->bo && c->bo->scanout == 0) {
+				bo = c->bo;
+				name = c->name;
+				DBG(("%s: reuse cache handle=%d\n", __FUNCTION__, bo->handle));
+				list_move_tail(&c->link, &info->cache);
+				c->bo = NULL;
+			}
 		}
 	}
 	if (bo == NULL) {
+		DrawablePtr draw = &sna->front->drawable;
 		DBG(("%s: allocating new backbuffer\n", __FUNCTION__));
 		bo = kgem_create_2d(&sna->kgem,
-				    info->draw->width,
-				    info->draw->height,
-				    info->draw->bitsPerPixel,
-				    get_private(info->front)->bo->tiling,
+				    draw->width, draw->height, draw->bitsPerPixel,
+				    get_private(back)->bo->tiling,
 				    CREATE_SCANOUT);
 		if (bo == NULL)
 			return;
@@ -173,29 +174,35 @@ sna_dri2_get_back(struct sna *sna, struct sna_dri2_frame_event *info)
 			return;
 		}
 	}
+	assert(bo->active_scanout == 0);
 
-	assert(!(bo == sna->dri2.scanout[0].bo || bo == sna->dri2.scanout[1].bo));
-	assert(name);
-
-	unref(get_private(info->back)->bo);
-	get_private(info->back)->bo = bo;
-	info->back->name = name;
-
-	assert(get_private(info->back)->bo != sna->dri2.scanout[0].bo);
-	assert(get_private(info->back)->bo != sna->dri2.scanout[1].bo);
+	if (info) {
+		bool found = false;
+		struct dri_bo *c;
 
-	assert(bo->refcnt == 1);
-	assert(bo->flush);
-}
+		list_for_each_entry_reverse(c, &info->cache, link) {
+			if (c->bo == NULL) {
+				found = true;
+				_list_del(&c->link);
+				break;
+			}
+		}
+		if (!found)
+			c = malloc(sizeof(*c));
+		if (c != NULL) {
+			c->bo = kgem_bo_reference(get_private(back)->bo);
+			c->name = back->name;
+			list_add(&c->link, &info->cache);
+		}
+	}
 
-static inline struct sna_dri2_frame_event *
-to_frame_event(uintptr_t  data)
-{
-	 return (struct sna_dri2_frame_event *)(data & ~3);
+	kgem_bo_destroy(&sna->kgem, get_private(back)->bo);
+	get_private(back)->bo = bo;
+	back->name = name;
 }
 
 struct dri2_window {
-	struct sna_dri2_frame_event *chain;
+	struct sna_dri2_event *chain;
 	xf86CrtcPtr crtc;
 	int64_t msc_delta;
 };
@@ -205,7 +212,7 @@ static struct dri2_window *dri2_window(WindowPtr win)
 	return ((void **)__get_private(win, sna_window_key))[1];
 }
 
-static struct sna_dri2_frame_event *
+static struct sna_dri2_event *
 sna_dri2_window_get_chain(WindowPtr win)
 {
 	struct dri2_window *priv = dri2_window(win);
@@ -238,19 +245,12 @@ sna_dri2_reuse_buffer(DrawablePtr draw, DRI2BufferPtr buffer)
 
 	if (buffer->attachment == DRI2BufferBackLeft &&
 	    draw->type != DRAWABLE_PIXMAP) {
-		struct sna_dri2_frame_event *info;
-
-		info = sna_dri2_window_get_chain((WindowPtr)draw);
-		DBG(("%s: draw->id=%lu, active? %d, current back? %d\n",
-		     __FUNCTION__, (long)draw->id, info!=NULL, info && info->back == buffer));
-		if (info && info->back == buffer) {
-			DBG(("%s: replacing back buffer\n", __FUNCTION__));
-			sna_dri2_get_back(to_sna_from_drawable(draw), info);
-		}
+		DBG(("%s: replacing back buffer\n", __FUNCTION__));
+		sna_dri2_get_back(to_sna_from_drawable(draw), buffer,
+				  sna_dri2_window_get_chain((WindowPtr)draw));
 
 		assert(kgem_bo_flink(&to_sna_from_drawable(draw)->kgem, get_private(buffer)->bo) == buffer->name);
-		assert(get_private(buffer)->bo != to_sna_from_drawable(draw)->dri2.scanout[0].bo);
-		assert(get_private(buffer)->bo != to_sna_from_drawable(draw)->dri2.scanout[1].bo);
+		assert(get_private(buffer)->bo->active_scanout == 0);
 	}
 }
 
@@ -315,19 +315,25 @@ static struct kgem_bo *sna_pixmap_set_dri(struct sna *sna,
 	}
 
 	priv = sna_pixmap_move_to_gpu(pixmap,
-				      MOVE_READ | MOVE_WRITE | __MOVE_FORCE | __MOVE_DRI);
+				      MOVE_READ | __MOVE_FORCE | __MOVE_DRI);
 	if (priv == NULL) {
 		DBG(("%s: failed to move to GPU, BadAlloc\n", __FUNCTION__));
 		return NULL;
 	}
 
 	assert(priv->flush == false);
-	assert(priv->cow == NULL);
 	assert(priv->cpu_damage == NULL);
 	assert(priv->gpu_bo);
 	assert(priv->gpu_bo->proxy == NULL);
 	assert(priv->gpu_bo->flush == false);
 
+	/* Henceforth, we need to broadcast all updates to clients and
+	 * flush our rendering before doing so.
+	 */
+	priv->gpu_bo->flush = true;
+	if (priv->gpu_bo->exec)
+		sna->kgem.flush = 1;
+
 	tiling = color_tiling(sna, &pixmap->drawable);
 	if (tiling < 0)
 		tiling = -tiling;
@@ -350,11 +356,10 @@ static inline void sna_pixmap_set_buffer(PixmapPtr pixmap, void *ptr)
 }
 
 void
-sna_dri2_pixmap_update_bo(struct sna *sna, PixmapPtr pixmap)
+sna_dri2_pixmap_update_bo(struct sna *sna, PixmapPtr pixmap, struct kgem_bo *bo)
 {
 	DRI2BufferPtr buffer;
 	struct sna_dri2_private *private;
-	struct kgem_bo *bo;
 
 	buffer = sna_pixmap_get_buffer(pixmap);
 	if (buffer == NULL)
@@ -368,7 +373,7 @@ sna_dri2_pixmap_update_bo(struct sna *sna, PixmapPtr pixmap)
 	private = get_private(buffer);
 	assert(private->pixmap == pixmap);
 
-	bo = sna_pixmap(pixmap)->gpu_bo;
+	assert(bo != private->bo);
 	if (private->bo == bo)
 		return;
 
@@ -378,7 +383,8 @@ sna_dri2_pixmap_update_bo(struct sna *sna, PixmapPtr pixmap)
 	buffer->name = kgem_bo_flink(&sna->kgem, bo);
 	private->bo = ref(bo);
 
-	assert(bo->flush);
+	bo->flush = true;
+	assert(sna_pixmap(pixmap)->flush);
 
 	/* XXX DRI2InvalidateDrawable(&pixmap->drawable); */
 }
@@ -562,8 +568,6 @@ sna_dri2_create_buffer(DrawablePtr draw,
 		sna_accel_watch_flush(sna, 1);
 	}
 
-	assert(bo->flush == true);
-
 	return buffer;
 
 err:
@@ -667,14 +671,10 @@ static void set_bo(PixmapPtr pixmap, struct kgem_bo *bo)
 	assert(pixmap->drawable.width * pixmap->drawable.bitsPerPixel <= 8*bo->pitch);
 	assert(pixmap->drawable.height * bo->pitch <= kgem_bo_size(bo));
 	assert(bo->proxy == NULL);
-	assert(bo->flush);
 	assert(priv->pinned & PIN_DRI2);
 	assert((priv->pinned & (PIN_PRIME | PIN_DRI3)) == 0);
 	assert(priv->flush);
 
-	if (priv->cow && priv->gpu_bo != bo)
-		sna_pixmap_undo_cow(sna, priv, 0);
-
 	/* Post damage on the new front buffer so that listeners, such
 	 * as DisplayLink know take a copy and shove it over the USB,
 	 * also for software cursors and the like.
@@ -688,11 +688,19 @@ static void set_bo(PixmapPtr pixmap, struct kgem_bo *bo)
 	damage(pixmap, priv, NULL);
 
 	assert(bo->refcnt);
+	if (priv->move_to_gpu)
+		priv->move_to_gpu(sna, priv, 0);
 	if (priv->gpu_bo != bo) {
+		priv->gpu_bo->flush = false;
+		if (priv->cow)
+			sna_pixmap_undo_cow(sna, priv, 0);
 		if (priv->gpu_bo) {
 			sna_pixmap_unmap(pixmap, priv);
 			kgem_bo_destroy(&sna->kgem, priv->gpu_bo);
 		}
+		bo->flush = true;
+		if (bo->exec)
+			sna->kgem.flush = 1;
 		priv->gpu_bo = ref(bo);
 	}
 	if (bo->domain != DOMAIN_GPU)
@@ -1005,10 +1013,7 @@ sna_dri2_copy_region(DrawablePtr draw,
 	assert(get_private(dst)->refcnt);
 
 	assert(get_private(src)->bo->refcnt);
-	assert(get_private(src)->bo->flush);
-
 	assert(get_private(dst)->bo->refcnt);
-	assert(get_private(dst)->bo->flush);
 
 	DBG(("%s: region (%d, %d), (%d, %d) x %ld\n",
 	     __FUNCTION__,
@@ -1119,11 +1124,10 @@ sna_dri2_get_crtc(DrawablePtr draw)
 }
 
 static void
-sna_dri2_remove_frame_event(WindowPtr win,
-			    struct sna_dri2_frame_event *info)
+sna_dri2_remove_event(WindowPtr win, struct sna_dri2_event *info)
 {
 	struct dri2_window *priv;
-	struct sna_dri2_frame_event *chain;
+	struct sna_dri2_event *chain;
 
 	assert(win->drawable.type == DRAWABLE_WINDOW);
 	DBG(("%s: remove[%p] from window %ld)\n",
@@ -1146,11 +1150,11 @@ sna_dri2_remove_frame_event(WindowPtr win,
 	chain->chain = info->chain;
 }
 
-static struct sna_dri2_frame_event *
-sna_dri2_add_frame_event(DrawablePtr draw, ClientPtr client)
+static struct sna_dri2_event *
+sna_dri2_add_event(DrawablePtr draw, ClientPtr client)
 {
 	struct dri2_window *priv;
-	struct sna_dri2_frame_event *info, *chain;
+	struct sna_dri2_event *info, *chain;
 
 	assert(draw->type == DRAWABLE_WINDOW);
 	DBG(("%s: adding event to window %ld)\n",
@@ -1160,7 +1164,7 @@ sna_dri2_add_frame_event(DrawablePtr draw, ClientPtr client)
 	if (priv == NULL)
 		return NULL;
 
-	info = calloc(1, sizeof(struct sna_dri2_frame_event));
+	info = calloc(1, sizeof(struct sna_dri2_event));
 	if (info == NULL)
 		return NULL;
 
@@ -1187,12 +1191,12 @@ sna_dri2_add_frame_event(DrawablePtr draw, ClientPtr client)
 }
 
 static void
-sna_dri2_frame_event_info_free(struct sna *sna,
-			       DrawablePtr draw,
-			       struct sna_dri2_frame_event *info)
+sna_dri2_event_free(struct sna *sna,
+		    DrawablePtr draw,
+		    struct sna_dri2_event *info)
 {
 	if (draw && draw->type == DRAWABLE_WINDOW)
-		sna_dri2_remove_frame_event((WindowPtr)draw, info);
+		sna_dri2_remove_event((WindowPtr)draw, info);
 	_sna_dri2_destroy_buffer(sna, info->front);
 	_sna_dri2_destroy_buffer(sna, info->back);
 
@@ -1202,10 +1206,8 @@ sna_dri2_frame_event_info_free(struct sna *sna,
 		c = list_first_entry(&info->cache, struct dri_bo, link);
 		list_del(&c->link);
 
-		if (c->bo) {
-			assert(c->bo->refcnt == 1);
+		if (c->bo)
 			kgem_bo_destroy(&sna->kgem, c->bo);
-		}
 
 		free(c);
 	}
@@ -1228,7 +1230,7 @@ void sna_dri2_destroy_window(WindowPtr win)
 
 	if (priv->chain) {
 		struct sna *sna = to_sna_from_drawable(&win->drawable);
-		struct sna_dri2_frame_event *info, *chain;
+		struct sna_dri2_event *info, *chain;
 
 		DBG(("%s: freeing chain\n", __FUNCTION__));
 
@@ -1238,13 +1240,13 @@ void sna_dri2_destroy_window(WindowPtr win)
 		chain = info->chain;
 		info->chain = NULL;
 
+		assert(info->queued);
 		while ((info = chain)) {
 			chain = info->chain;
-			if (info->queued) {
-				info->draw = NULL;
-				info->chain = NULL;
-			} else
-				sna_dri2_frame_event_info_free(sna, NULL, info);
+			info->chain = NULL;
+			info->draw = NULL;
+			if (!info->queued)
+				sna_dri2_event_free(sna, NULL, info);
 		}
 	}
 
@@ -1252,95 +1254,33 @@ void sna_dri2_destroy_window(WindowPtr win)
 }
 
 static void
-update_scanout(struct sna *sna, struct kgem_bo *bo, int name)
-{
-	assert(sna->dri2.scanout[1].bo == NULL);
-	assert(sna->dri2.scanout[0].bo->scanout);
-	assert(sna->dri2.scanout[0].bo->refcnt);
-	sna->dri2.scanout[1] = sna->dri2.scanout[0];
-	sna->dri2.scanout[0].bo = ref(bo);
-	sna->dri2.scanout[0].name = name;
-	assert(sna->dri2.scanout[0].bo->scanout);
-
-	DBG(("%s: pending scanout handle=%d, active scanout handle=%d\n",
-	     __FUNCTION__, sna->dri2.scanout[0].bo->handle, sna->dri2.scanout[1].bo->handle));
-	assert(sna->dri2.scanout[0].bo->handle != sna->dri2.scanout[1].bo->handle);
-}
-
-static void
-retire_scanout(struct sna *sna,
-	       struct sna_dri2_frame_event *flip)
+sna_dri2_flip_handler(struct sna *sna,
+		      struct drm_event_vblank *event,
+		      void *data)
 {
-	struct dri_bo *c = NULL;
-
-	if (sna->dri2.scanout[1].bo == NULL)
-		return;
-
-	DBG(("%s: retiring previous scanout handle=%d, name=%d, refcnt=%d (current scanout handle=%d)\n",
-	     __FUNCTION__,
-	     sna->dri2.scanout[1].bo->handle,
-	     sna->dri2.scanout[1].name,
-	     sna->dri2.scanout[1].bo->refcnt,
-	     sna->dri2.scanout[0].bo->handle));
-
-	if (flip &&
-	    sna->dri2.scanout[1].bo != sna->dri2.scanout[0].bo &&
-	    sna->dri2.scanout[1].bo->refcnt == 1) {
-		DBG(("%s: adding old scanout to flip cache\n", __FUNCTION__));
-		if (!list_is_empty(&flip->cache))
-			c = list_last_entry(&flip->cache, struct dri_bo, link);
-		if (c) {
-			if (c->bo == NULL)
-				_list_del(&c->link);
-			else
-				c = NULL;
-		}
-		if (c == NULL)
-			c = malloc(sizeof(*c));
-		if (c != NULL) {
-			c->bo = sna->dri2.scanout[1].bo;
-			c->name = sna->dri2.scanout[1].name;
-			list_add(&c->link, &flip->cache);
-		}
-	}
-
-	if (c == NULL) {
-		DBG(("%s: not caching old scanout handle=%d, still busy\n",
-		     __FUNCTION__, sna->dri2.scanout[1].bo->handle));
-		kgem_bo_destroy(&sna->kgem, sna->dri2.scanout[1].bo);
-	}
-
-	sna->dri2.scanout[1].bo = NULL;
+	DBG(("%s: sequence=%d\n", __FUNCTION__, event->sequence));
+	sna_dri2_flip_event(sna, data);
 }
 
 static bool
-sna_dri2_page_flip(struct sna *sna, struct sna_dri2_frame_event *info)
+sna_dri2_flip(struct sna *sna, struct sna_dri2_event *info)
 {
 	struct kgem_bo *bo = get_private(info->back)->bo;
-	struct dri_bo tmp;
+	struct kgem_bo *tmp_bo;
+	uint32_t tmp_name;
 
 	DBG(("%s(type=%d)\n", __FUNCTION__, info->type));
 
 	assert(sna_pixmap_get_buffer(sna->front) == info->front);
 	assert(get_drawable_pixmap(info->draw)->drawable.height * bo->pitch <= kgem_bo_size(bo));
-	assert(sna->dri2.scanout[0].bo);
-	assert(sna->dri2.scanout[0].bo->scanout);
-	assert(sna->dri2.scanout[1].bo == NULL);
 	assert(bo->refcnt);
 
-	if (info->type == FLIP_ASYNC)
-		info->count = sna_page_flip(sna, bo, NULL, -1);
-	else
-		info->count = sna_page_flip(sna, bo, info, info->pipe);
-	if (!info->count)
+	if (!sna_page_flip(sna, bo, sna_dri2_flip_handler,
+			   info->type == FLIP_ASYNC ? NULL : info))
 		return false;
 
-	update_scanout(sna, bo, info->back->name);
-
 	assert(sna->dri2.flip_pending == NULL || sna->dri2.flip_pending == info);
-	if (info->type == FLIP_ASYNC)
-		retire_scanout(sna, NULL);
-	else
+	if (info->type != FLIP_ASYNC)
 		sna->dri2.flip_pending = info;
 
 	DBG(("%s: marked handle=%d as scanout, swap front (handle=%d, name=%d) and back (handle=%d, name=%d)\n",
@@ -1348,16 +1288,17 @@ sna_dri2_page_flip(struct sna *sna, struct sna_dri2_frame_event *info)
 	     get_private(info->front)->bo->handle, info->front->name,
 	     get_private(info->back)->bo->handle, info->back->name));
 
-	tmp.bo = get_private(info->front)->bo;
-	tmp.name = info->front->name;
+	tmp_bo = get_private(info->front)->bo;
+	tmp_name = info->front->name;
 
 	set_bo(sna->front, bo);
 
 	info->front->name = info->back->name;
 	get_private(info->front)->bo = bo;
 
-	info->back->name = tmp.name;
-	get_private(info->back)->bo = tmp.bo;
+	info->back->name = tmp_name;
+	get_private(info->back)->bo = tmp_bo;
+	get_private(info->back)->stale = true;
 
 	info->queued = true;
 	return true;
@@ -1495,6 +1436,74 @@ can_flip(struct sna * sna,
 	return true;
 }
 
+static bool
+can_xchg(struct sna * sna,
+	 DrawablePtr draw,
+	 DRI2BufferPtr front,
+	 DRI2BufferPtr back)
+{
+	WindowPtr win = (WindowPtr)draw;
+	PixmapPtr pixmap;
+
+	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;
+	}
+
+	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, draw)) {
+		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 (get_private(back)->size != get_private(front)->size) {
+		DBG(("%s: no, back buffer %dx%d does not match front buffer %dx%d\n",
+		     __FUNCTION__,
+		     get_private(back)->size & 0x7fff, (get_private(back)->size >> 16) & 0x7fff,
+		     get_private(front)->size & 0x7fff, (get_private(front)->size >> 16) & 0x7fff));
+		return false;
+	}
+
+	pixmap = get_window_pixmap(win);
+	if (pixmap == sna->front && !(sna->flags & SNA_TEAR_FREE)) {
+		DBG(("%s: no, front buffer, requires flipping\n",
+		     __FUNCTION__));
+		return false;
+	}
+
+	if (sna_pixmap(pixmap)->pinned & ~(PIN_DRI2 | PIN_SCANOUT)) {
+		DBG(("%s: no, pinned: %x\n",
+		     __FUNCTION__, sna_pixmap(pixmap)->pinned));
+		return false;
+	}
+
+	DBG(("%s: yes\n", __FUNCTION__));
+	return true;
+}
+
 static void
 sna_dri2_exchange_buffers(DrawablePtr draw,
 			  DRI2BufferPtr front,
@@ -1517,14 +1526,15 @@ sna_dri2_exchange_buffers(DrawablePtr draw,
 	     pixmap->drawable.width,
 	     pixmap->drawable.height));
 
-	DBG(("%s: back_bo pitch=%d, size=%d, ref=%d\n",
-	     __FUNCTION__, back_bo->pitch, kgem_bo_size(back_bo), back_bo->refcnt));
-	DBG(("%s: front_bo pitch=%d, size=%d, ref=%d\n",
-	     __FUNCTION__, front_bo->pitch, kgem_bo_size(front_bo), front_bo->refcnt));
+	DBG(("%s: back_bo pitch=%d, size=%d, ref=%d, active_scanout?=%d\n",
+	     __FUNCTION__, back_bo->pitch, kgem_bo_size(back_bo), back_bo->refcnt, back_bo->active_scanout));
+	DBG(("%s: front_bo pitch=%d, size=%d, ref=%d, active_scanout?=%d\n",
+	     __FUNCTION__, front_bo->pitch, kgem_bo_size(front_bo), front_bo->refcnt, front_bo->active_scanout));
 	assert(front_bo->refcnt);
 	assert(back_bo->refcnt);
 
 	assert(sna_pixmap_get_buffer(pixmap) == front);
+
 	assert(pixmap->drawable.height * back_bo->pitch <= kgem_bo_size(back_bo));
 	assert(pixmap->drawable.height * front_bo->pitch <= kgem_bo_size(front_bo));
 
@@ -1532,14 +1542,20 @@ sna_dri2_exchange_buffers(DrawablePtr draw,
 
 	get_private(front)->bo = back_bo;
 	get_private(back)->bo = front_bo;
+	get_private(back)->stale = true;
 
 	tmp = front->name;
 	front->name = back->name;
 	back->name = tmp;
+
+	assert(front_bo->refcnt);
+	assert(back_bo->refcnt);
+
+	assert(get_private(front)->bo == sna_pixmap(pixmap)->gpu_bo);
 }
 
 static void frame_swap_complete(struct sna *sna,
-				struct sna_dri2_frame_event *frame,
+				struct sna_dri2_event *frame,
 				int type)
 {
 	const struct ust_msc *swap;
@@ -1579,17 +1595,22 @@ static void fake_swap_complete(struct sna *sna, ClientPtr client,
 			 type, func, data);
 }
 
-static void chain_swap(struct sna *sna, DrawablePtr draw, struct sna_dri2_frame_event *chain)
+static void chain_swap(struct sna *sna, struct sna_dri2_event *chain)
 {
 	union drm_wait_vblank vbl;
 
-	assert(chain == sna_dri2_window_get_chain((WindowPtr)draw));
+	if (chain->draw == NULL) {
+		sna_dri2_event_free(sna, NULL, chain);
+		return;
+	}
+
+	assert(chain == sna_dri2_window_get_chain((WindowPtr)chain->draw));
 	DBG(("%s: chaining draw=%ld, type=%d\n",
-	     __FUNCTION__, (long)draw->id, chain->type));
+	     __FUNCTION__, (long)chain->draw->id, chain->type));
 	switch (chain->type) {
 	case SWAP_THROTTLE:
 		DBG(("%s: emitting chained vsync'ed blit\n", __FUNCTION__));
-		if (sna->mode.shadow_flip && !sna->mode.shadow_damage) {
+		if (sna->mode.shadow && !sna->mode.shadow_damage) {
 			/* recursed from wait_for_shadow(), simply requeue */
 			DBG(("%s -- recursed from wait_for_shadow(), requeuing\n", __FUNCTION__));
 			VG_CLEAR(vbl);
@@ -1597,7 +1618,7 @@ static void chain_swap(struct sna *sna, DrawablePtr draw, struct sna_dri2_frame_
 				DRM_VBLANK_RELATIVE |
 				DRM_VBLANK_EVENT;
 			vbl.request.sequence = 1;
-			vbl.request.signal = (unsigned long)chain;
+			vbl.request.signal = (uintptr_t)chain;
 
 			if (!sna_wait_vblank(sna, &vbl, chain->pipe)) {
 				chain->queued = true;
@@ -1607,9 +1628,13 @@ static void chain_swap(struct sna *sna, DrawablePtr draw, struct sna_dri2_frame_
 			DBG(("%s -- requeue failed, errno=%d\n", __FUNCTION__, errno));
 		}
 
-		chain->bo = __sna_dri2_copy_region(sna, draw, NULL,
-						   chain->back, chain->front,
-						   true);
+		if (can_xchg(sna, chain->draw, chain->front, chain->back)) {
+			sna_dri2_exchange_buffers(chain->draw, chain->front, chain->back);
+		} else {
+			chain->bo = __sna_dri2_copy_region(sna, chain->draw, NULL,
+							   chain->back, chain->front,
+							   true);
+		}
 	case SWAP:
 		break;
 	default:
@@ -1621,14 +1646,14 @@ static void chain_swap(struct sna *sna, DrawablePtr draw, struct sna_dri2_frame_
 		DRM_VBLANK_RELATIVE |
 		DRM_VBLANK_EVENT;
 	vbl.request.sequence = 1;
-	vbl.request.signal = (unsigned long)chain;
+	vbl.request.signal = (uintptr_t)chain;
 	chain->queued = true;
 	if (sna_wait_vblank(sna, &vbl, chain->pipe)) {
 		DBG(("%s: vblank wait failed, unblocking client\n", __FUNCTION__));
 		frame_swap_complete(sna, chain, DRI2_BLIT_COMPLETE);
-		sna_dri2_frame_event_info_free(sna, draw, chain);
+		sna_dri2_event_free(sna, chain->draw, chain);
 	} else {
-		if (chain->type == SWAP_THROTTLE && !swap_limit(draw, 2)) {
+		if (chain->type == SWAP_THROTTLE && !swap_limit(chain->draw, 2)) {
 			DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__));
 			frame_swap_complete(sna, chain, DRI2_BLIT_COMPLETE);
 		}
@@ -1654,7 +1679,7 @@ static inline bool rq_is_busy(struct kgem *kgem, struct kgem_bo *bo)
 }
 
 static bool sna_dri2_blit_complete(struct sna *sna,
-				   struct sna_dri2_frame_event *info)
+				   struct sna_dri2_event *info)
 {
 	if (rq_is_busy(&sna->kgem, info->bo)) {
 		union drm_wait_vblank vbl;
@@ -1667,7 +1692,7 @@ static bool sna_dri2_blit_complete(struct sna *sna,
 			DRM_VBLANK_RELATIVE |
 			DRM_VBLANK_EVENT;
 		vbl.request.sequence = 1;
-		vbl.request.signal = (unsigned long)info;
+		vbl.request.signal = (uintptr_t)info;
 		assert(info->queued);
 		if (!sna_wait_vblank(sna, &vbl, info->pipe))
 			return false;
@@ -1679,7 +1704,7 @@ static bool sna_dri2_blit_complete(struct sna *sna,
 
 void sna_dri2_vblank_handler(struct sna *sna, struct drm_event_vblank *event)
 {
-	struct sna_dri2_frame_event *info = (void *)(uintptr_t)event->user_data;
+	struct sna_dri2_event *info = (void *)(uintptr_t)event->user_data;
 	DrawablePtr draw;
 	union drm_wait_vblank vbl;
 	uint64_t msc;
@@ -1698,18 +1723,21 @@ void sna_dri2_vblank_handler(struct sna *sna, struct drm_event_vblank *event)
 	case FLIP:
 		/* If we can still flip... */
 		if (can_flip(sna, draw, info->front, info->back, info->crtc) &&
-		    sna_dri2_page_flip(sna, info))
+		    sna_dri2_flip(sna, info))
 			return;
 
 		/* else fall through to blit */
 	case SWAP:
-		if (sna->mode.shadow_flip && !sna->mode.shadow_damage) {
+		if (sna->mode.shadow && !sna->mode.shadow_damage) {
 			/* recursed from wait_for_shadow(), simply requeue */
 			DBG(("%s -- recursed from wait_for_shadow(), requeuing\n", __FUNCTION__));
 
-		} else {
+		} else if (can_xchg(sna, draw, info->front, info->back)) {
+			sna_dri2_exchange_buffers(draw, info->front, info->back);
+			info->type = SWAP_WAIT;
+		}  else {
 			info->bo = __sna_dri2_copy_region(sna, draw, NULL,
-							 info->back, info->front, true);
+							  info->back, info->front, true);
 			info->type = SWAP_WAIT;
 		}
 
@@ -1718,7 +1746,7 @@ void sna_dri2_vblank_handler(struct sna *sna, struct drm_event_vblank *event)
 			DRM_VBLANK_RELATIVE |
 			DRM_VBLANK_EVENT;
 		vbl.request.sequence = 1;
-		vbl.request.signal = (unsigned long)info;
+		vbl.request.signal = (uintptr_t)info;
 
 		assert(info->queued);
 		if (!sna_wait_vblank(sna, &vbl, info->pipe))
@@ -1763,18 +1791,19 @@ void sna_dri2_vblank_handler(struct sna *sna, struct drm_event_vblank *event)
 
 	if (info->chain) {
 		assert(info->chain != info);
-		sna_dri2_remove_frame_event((WindowPtr)draw, info);
-		chain_swap(sna, draw, info->chain);
+		assert(info->draw == draw);
+		sna_dri2_remove_event((WindowPtr)draw, info);
+		chain_swap(sna, info->chain);
 		draw = NULL;
 	}
 
 done:
-	sna_dri2_frame_event_info_free(sna, draw, info);
+	sna_dri2_event_free(sna, draw, info);
 }
 
 static bool
 sna_dri2_immediate_blit(struct sna *sna,
-			struct sna_dri2_frame_event *info,
+			struct sna_dri2_event *info,
 			bool sync, bool event)
 {
 	DrawablePtr draw = info->draw;
@@ -1805,7 +1834,7 @@ sna_dri2_immediate_blit(struct sna *sna,
 					DRM_VBLANK_RELATIVE |
 					DRM_VBLANK_EVENT;
 				vbl.request.sequence = 1;
-				vbl.request.signal = (unsigned long)info;
+				vbl.request.signal = (uintptr_t)info;
 				ret = !sna_wait_vblank(sna, &vbl, info->pipe);
 				if (ret) {
 					info->queued = true;
@@ -1827,7 +1856,7 @@ sna_dri2_immediate_blit(struct sna *sna,
 }
 
 static bool
-sna_dri2_flip_continue(struct sna *sna, struct sna_dri2_frame_event *info)
+sna_dri2_flip_continue(struct sna *sna, struct sna_dri2_event *info)
 {
 	DBG(("%s(mode=%d)\n", __FUNCTION__, info->mode));
 
@@ -1839,27 +1868,11 @@ sna_dri2_flip_continue(struct sna *sna, struct sna_dri2_frame_event *info)
 		if (bo != sna_pixmap(sna->front)->gpu_bo)
 			return false;
 
-		if (bo == sna->dri2.scanout[0].bo) {
-			DBG(("%s: flip chain already complete\n", __FUNCTION__));
-
-			if (info->draw) {
-				sna_dri2_remove_frame_event((WindowPtr)info->draw, info);
-				if (info->chain)
-					chain_swap(sna, info->draw, info->chain);
-			}
-
-			info->draw = NULL;
+		if (!sna_page_flip(sna, bo, sna_dri2_flip_handler, info))
 			return false;
-		} else {
-			info->count = sna_page_flip(sna, bo, info, info->pipe);
-			if (!info->count)
-				return false;
-
-			update_scanout(sna, bo, info->front->name);
 
-			assert(sna->dri2.flip_pending == NULL || sna->dri2.flip_pending == info);
-			sna->dri2.flip_pending = info;
-		}
+		assert(sna->dri2.flip_pending == NULL || sna->dri2.flip_pending == info);
+		sna->dri2.flip_pending = info;
 	} else {
 		info->type = -info->mode;
 
@@ -1870,11 +1883,11 @@ sna_dri2_flip_continue(struct sna *sna, struct sna_dri2_frame_event *info)
 			return false;
 
 		assert(sna_pixmap_get_buffer(get_drawable_pixmap(info->draw)) == info->front);
-		if (!sna_dri2_page_flip(sna, info))
+		if (!sna_dri2_flip(sna, info))
 			return false;
 
 		if (!XORG_CAN_TRIPLE_BUFFER) {
-			sna_dri2_get_back(sna, info);
+			sna_dri2_get_back(sna, info->back, info);
 			DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__));
 			frame_swap_complete(sna, info, DRI2_FLIP_COMPLETE);
 		}
@@ -1886,7 +1899,7 @@ sna_dri2_flip_continue(struct sna *sna, struct sna_dri2_frame_event *info)
 
 static void chain_flip(struct sna *sna)
 {
-	struct sna_dri2_frame_event *chain = sna->dri2.flip_pending;
+	struct sna_dri2_event *chain = sna->dri2.flip_pending;
 
 	assert(chain->type == FLIP);
 	DBG(("%s: chaining type=%d, cancelled?=%d\n",
@@ -1894,14 +1907,14 @@ static void chain_flip(struct sna *sna)
 
 	sna->dri2.flip_pending = NULL;
 	if (chain->draw == NULL) {
-		sna_dri2_frame_event_info_free(sna, NULL, chain);
+		sna_dri2_event_free(sna, NULL, chain);
 		return;
 	}
 
 	assert(chain == sna_dri2_window_get_chain((WindowPtr)chain->draw));
 
 	if (can_flip(sna, chain->draw, chain->front, chain->back, chain->crtc) &&
-	    sna_dri2_page_flip(sna, chain)) {
+	    sna_dri2_flip(sna, chain)) {
 		DBG(("%s: performing chained flip\n", __FUNCTION__));
 	} else {
 		DBG(("%s: emitting chained vsync'ed blit\n", __FUNCTION__));
@@ -1919,7 +1932,7 @@ static void chain_flip(struct sna *sna)
 				DRM_VBLANK_RELATIVE |
 				DRM_VBLANK_EVENT;
 			vbl.request.sequence = 1;
-			vbl.request.signal = (unsigned long)chain;
+			vbl.request.signal = (uintptr_t)chain;
 
 			if (!sna_wait_vblank(sna, &vbl, chain->pipe)) {
 				chain->queued = true;
@@ -1929,18 +1942,15 @@ static void chain_flip(struct sna *sna)
 
 		DBG(("%s: fake triple buffering (or vblank wait failed), unblocking client\n", __FUNCTION__));
 		frame_swap_complete(sna, chain, DRI2_BLIT_COMPLETE);
-		sna_dri2_frame_event_info_free(sna, chain->draw, chain);
+		sna_dri2_event_free(sna, chain->draw, chain);
 	}
 }
 
 static void sna_dri2_flip_event(struct sna *sna,
-				struct sna_dri2_frame_event *flip)
+				struct sna_dri2_event *flip)
 {
 	DBG(("%s(pipe=%d, event=%d)\n", __FUNCTION__, flip->pipe, flip->type));
 
-	assert(!sna->mode.shadow_flip);
-
-	retire_scanout(sna, flip);
 	if (sna->dri2.flip_pending == flip)
 		sna->dri2.flip_pending = NULL;
 
@@ -1949,7 +1959,7 @@ static void sna_dri2_flip_event(struct sna *sna,
 	case FLIP:
 		DBG(("%s: swap complete, unblocking client\n", __FUNCTION__));
 		frame_swap_complete(sna, flip, DRI2_FLIP_COMPLETE);
-		sna_dri2_frame_event_info_free(sna, flip->draw, flip);
+		sna_dri2_event_free(sna, flip->draw, flip);
 
 		if (sna->dri2.flip_pending)
 			chain_flip(sna);
@@ -1960,56 +1970,36 @@ static void sna_dri2_flip_event(struct sna *sna,
 		frame_swap_complete(sna, flip, DRI2_FLIP_COMPLETE);
 	case FLIP_COMPLETE:
 		if (sna->dri2.flip_pending) {
-			sna_dri2_frame_event_info_free(sna, flip->draw, flip);
+			sna_dri2_event_free(sna, flip->draw, flip);
 			chain_flip(sna);
 		} else if (!flip->mode) {
 			DBG(("%s: flip chain complete\n", __FUNCTION__));
 
 			if (flip->chain) {
-				sna_dri2_remove_frame_event((WindowPtr)flip->draw,
-							   flip);
-				chain_swap(sna, flip->draw, flip->chain);
+				sna_dri2_remove_event((WindowPtr)flip->draw,
+						      flip);
+				chain_swap(sna, flip->chain);
 				flip->draw = NULL;
 			}
 
-			sna_dri2_frame_event_info_free(sna, flip->draw, flip);
+			sna_dri2_event_free(sna, flip->draw, flip);
 		} else if (!sna_dri2_flip_continue(sna, flip)) {
 			DBG(("%s: no longer able to flip\n", __FUNCTION__));
 			if (flip->draw == NULL || !sna_dri2_immediate_blit(sna, flip, false, flip->mode < 0))
-				sna_dri2_frame_event_info_free(sna, flip->draw, flip);
+				sna_dri2_event_free(sna, flip->draw, flip);
 		}
 		break;
 
 	default: /* Unknown type */
 		xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING,
 			   "%s: unknown vblank event received\n", __func__);
-		sna_dri2_frame_event_info_free(sna, flip->draw, flip);
+		sna_dri2_event_free(sna, flip->draw, flip);
 		if (sna->dri2.flip_pending)
 			chain_flip(sna);
 		break;
 	}
 }
 
-void
-sna_dri2_page_flip_handler(struct sna *sna,
-			   struct drm_event_vblank *event)
-{
-	struct sna_dri2_frame_event *info = to_frame_event(event->user_data);
-
-	DBG(("%s: pending flip_count=%d\n", __FUNCTION__, info->count));
-	assert(info->count > 0);
-
-	/* Is this the event whose info shall be delivered to higher level? */
-	if (event->user_data & 1)
-		sna_crtc_record_event(info->crtc, event);
-
-	if (--info->count)
-		return;
-
-	DBG(("%s: sequence=%d\n", __FUNCTION__, event->sequence));
-	sna_dri2_flip_event(sna, info);
-}
-
 static uint64_t
 get_current_msc(struct sna *sna, DrawablePtr draw, xf86CrtcPtr crtc)
 {
@@ -2105,7 +2095,7 @@ sna_dri2_schedule_flip(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc,
 		       DRI2SwapEventPtr func, void *data)
 {
 	struct sna *sna = to_sna_from_drawable(draw);
-	struct sna_dri2_frame_event *info;
+	struct sna_dri2_event *info;
 	uint64_t current_msc;
 
 	if (immediate_swap(sna, *target_msc, divisor, draw, crtc, &current_msc)) {
@@ -2142,7 +2132,7 @@ sna_dri2_schedule_flip(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc,
 			}
 		}
 
-		info = sna_dri2_add_frame_event(draw, client);
+		info = sna_dri2_add_event(draw, client);
 		if (info == NULL)
 			return false;
 
@@ -2153,13 +2143,6 @@ sna_dri2_schedule_flip(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc,
 		info->front = sna_dri2_reference_buffer(front);
 		info->back = sna_dri2_reference_buffer(back);
 
-		if (sna->dri2.scanout[0].bo == NULL) {
-			sna->dri2.scanout[0].bo = ref(get_private(front)->bo);
-			sna->dri2.scanout[0].name = info->front->name;
-		}
-		assert(sna->dri2.scanout[0].bo == get_private(front)->bo);
-		assert(sna->dri2.scanout[0].bo->scanout);
-
 		if (sna->dri2.flip_pending) {
 			/* We need to first wait (one vblank) for the
 			 * async flips to complete before this client
@@ -2172,9 +2155,9 @@ sna_dri2_schedule_flip(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc,
 			current_msc++;
 		} else {
 			info->type = type = use_triple_buffer(sna, client, *target_msc == 0);
-			if (!sna_dri2_page_flip(sna, info)) {
+			if (!sna_dri2_flip(sna, info)) {
 				DBG(("%s: flip failed, falling back\n", __FUNCTION__));
-				sna_dri2_frame_event_info_free(sna, draw, info);
+				sna_dri2_event_free(sna, draw, info);
 				return false;
 			}
 		}
@@ -2183,11 +2166,11 @@ 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, info);
+				sna_dri2_get_back(sna, back, info);
 			DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__));
 			frame_swap_complete(sna, info, DRI2_EXCHANGE_COMPLETE);
 			if (info->type == FLIP_ASYNC)
-				sna_dri2_frame_event_info_free(sna, draw, info);
+				sna_dri2_event_free(sna, draw, info);
 		}
 out:
 		DBG(("%s: target_msc=%llu\n", __FUNCTION__, current_msc + 1));
@@ -2195,7 +2178,7 @@ out:
 		return true;
 	}
 
-	info = sna_dri2_add_frame_event(draw, client);
+	info = sna_dri2_add_event(draw, client);
 	if (info == NULL)
 		return false;
 
@@ -2207,13 +2190,6 @@ out:
 	info->front = sna_dri2_reference_buffer(front);
 	info->back = sna_dri2_reference_buffer(back);
 
-	if (sna->dri2.scanout[0].bo == NULL) {
-		sna->dri2.scanout[0].bo = ref(get_private(front)->bo);
-		sna->dri2.scanout[0].name = info->front->name;
-	}
-	assert(sna->dri2.scanout[0].bo == get_private(front)->bo);
-	assert(sna->dri2.scanout[0].bo->scanout);
-
 	/*
 	 * If divisor is zero, or current_msc is smaller than target_msc
 	 * we just need to make sure target_msc passes before initiating
@@ -2233,8 +2209,8 @@ out:
 	}
 
 	if (*target_msc <= current_msc + 1) {
-		if (!sna_dri2_page_flip(sna, info)) {
-			sna_dri2_frame_event_info_free(sna, draw, info);
+		if (!sna_dri2_flip(sna, info)) {
+			sna_dri2_event_free(sna, draw, info);
 			return false;
 		}
 		*target_msc = current_msc + 1;
@@ -2249,10 +2225,10 @@ out:
 
 		/* Account for 1 frame extra pageflip delay */
 		vbl.reply.sequence = draw_target_seq(draw, *target_msc - 1);
-		vbl.request.signal = (unsigned long)info;
+		vbl.request.signal = (uintptr_t)info;
 
 		if (sna_wait_vblank(sna, &vbl, info->pipe)) {
-			sna_dri2_frame_event_info_free(sna, draw, info);
+			sna_dri2_event_free(sna, draw, info);
 			return false;
 		}
 	}
@@ -2263,6 +2239,67 @@ out:
 	return true;
 }
 
+static bool
+sna_dri2_schedule_xchg(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 = sna_dri2_window_get_chain((WindowPtr)draw) == NULL;
+	if (!sync || event) {
+		DBG(("%s: performing immediate xchg on pipe %d\n",
+		     __FUNCTION__, sna_crtc_to_pipe(crtc)));
+		sna_dri2_exchange_buffers(draw, front, 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;
+
+			if (sna_wait_vblank(sna, &vbl, info->pipe)) {
+				sna_dri2_event_free(sna, draw, info);
+				goto complete;
+			}
+
+			info->queued = true;
+			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;
+}
+
 /*
  * ScheduleSwap is responsible for requesting a DRM vblank event for the
  * appropriate frame.
@@ -2291,7 +2328,7 @@ sna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	struct sna *sna = to_sna_from_drawable(draw);
 	union drm_wait_vblank vbl;
 	xf86CrtcPtr crtc = NULL;
-	struct sna_dri2_frame_event *info = NULL;
+	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",
@@ -2319,11 +2356,13 @@ sna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	assert(get_private(front)->bo->flush);
 
 	assert(get_private(back)->bo->refcnt);
-	assert(get_private(back)->bo->flush);
 
 	if (get_private(front)->pixmap != get_drawable_pixmap(draw))
 		goto skip;
 
+	if (get_private(back)->stale)
+		goto skip;
+
 	assert(sna_pixmap_from_drawable(draw)->flush);
 
 	/* Drawable not displayed... just complete the swap */
@@ -2334,6 +2373,12 @@ sna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 		goto blit;
 	}
 
+	if (can_xchg(sna, draw, front, back) &&
+	    sna_dri2_schedule_xchg(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,
@@ -2342,7 +2387,7 @@ sna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 
 	VG_CLEAR(vbl);
 
-	info = sna_dri2_add_frame_event(draw, client);
+	info = sna_dri2_add_event(draw, client);
 	if (!info)
 		goto blit;
 
@@ -2353,21 +2398,18 @@ sna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	info->front = sna_dri2_reference_buffer(front);
 	info->back = sna_dri2_reference_buffer(back);
 
-	info->type = SWAP;
-
 	if (immediate_swap(sna, *target_msc, divisor, draw, crtc, &current_msc)) {
 		bool sync = current_msc < *target_msc;
 		if (!sna_dri2_immediate_blit(sna, info, sync, true))
-			sna_dri2_frame_event_info_free(sna, draw, info);
-		if (*target_msc)
-			*target_msc = current_msc + sync;
+			sna_dri2_event_free(sna, draw, info);
+		*target_msc = current_msc + sync;
 		return TRUE;
 	}
 
 	vbl.request.type =
 		DRM_VBLANK_ABSOLUTE |
 		DRM_VBLANK_EVENT;
-	vbl.request.signal = (unsigned long)info;
+	vbl.request.signal = (uintptr_t)info;
 
 	/*
 	 * If divisor is zero, or current_msc is smaller than target_msc
@@ -2391,7 +2433,7 @@ sna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	if (*target_msc <= current_msc + 1) {
 		DBG(("%s: performing blit before queueing\n", __FUNCTION__));
 		info->bo = __sna_dri2_copy_region(sna, draw, NULL,
-						  info->back, info->front,
+						  back, front,
 						  true);
 		info->type = SWAP_WAIT;
 
@@ -2414,7 +2456,7 @@ blit:
 	DBG(("%s -- blit\n", __FUNCTION__));
 	__sna_dri2_copy_region(sna, draw, NULL, back, front, false);
 	if (info)
-		sna_dri2_frame_event_info_free(sna, draw, info);
+		sna_dri2_event_free(sna, draw, info);
 skip:
 	DBG(("%s: unable to show frame, unblocking client\n", __FUNCTION__));
 	if (crtc == NULL)
@@ -2469,7 +2511,7 @@ sna_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc
 			   CARD64 divisor, CARD64 remainder)
 {
 	struct sna *sna = to_sna_from_drawable(draw);
-	struct sna_dri2_frame_event *info = NULL;
+	struct sna_dri2_event *info = NULL;
 	xf86CrtcPtr crtc;
 	CARD64 current_msc;
 	union drm_wait_vblank vbl;
@@ -2508,14 +2550,14 @@ sna_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc
 	if (divisor == 0 && current_msc >= target_msc)
 		goto out_complete;
 
-	info = sna_dri2_add_frame_event(draw, client);
+	info = sna_dri2_add_event(draw, client);
 	if (!info)
 		goto out_complete;
 
 	assert(info->crtc == crtc);
 	info->type = WAITMSC;
 
-	vbl.request.signal = (unsigned long)info;
+	vbl.request.signal = (uintptr_t)info;
 	vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
 	/*
 	 * If divisor is zero, or current_msc is smaller than target_msc,
@@ -2543,7 +2585,7 @@ sna_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc
 	return TRUE;
 
 out_free_info:
-	sna_dri2_frame_event_info_free(sna, draw, info);
+	sna_dri2_event_free(sna, draw, info);
 out_complete:
 	if (crtc == NULL)
 		crtc = sna_mode_first_crtc(sna);
@@ -2719,18 +2761,6 @@ bool sna_dri2_open(struct sna *sna, ScreenPtr screen)
 	return DRI2ScreenInit(screen, &info);
 }
 
-void sna_dri2_reset_scanout(struct sna *sna)
-{
-	if (sna->dri2.scanout[1].bo != NULL) {
-		kgem_bo_destroy(&sna->kgem, sna->dri2.scanout[1].bo);
-		sna->dri2.scanout[1].bo = NULL;
-	}
-	if (sna->dri2.scanout[0].bo != NULL) {
-		kgem_bo_destroy(&sna->kgem, sna->dri2.scanout[0].bo);
-		sna->dri2.scanout[0].bo = NULL;
-	}
-}
-
 void sna_dri2_close(struct sna *sna, ScreenPtr screen)
 {
 	DBG(("%s()\n", __FUNCTION__));
diff --git a/src/sna/sna_driver.c b/src/sna/sna_driver.c
index 7a58a86..b923236 100644
--- a/src/sna/sna_driver.c
+++ b/src/sna/sna_driver.c
@@ -196,7 +196,7 @@ static Bool sna_set_desired_mode(struct sna *sna)
 		sna_set_fallback_mode(scrn);
 	}
 
-	sna_mode_update(sna);
+	sna_mode_check(sna);
 	return TRUE;
 }
 
@@ -281,6 +281,19 @@ static Bool sna_create_screen_resources(ScreenPtr screen)
 	return TRUE;
 }
 
+static Bool sna_save_screen(ScreenPtr screen, int mode)
+{
+	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+
+	DBG(("%s(mode=%d)\n", __FUNCTION__, mode));
+	if (!scrn->vtSema)
+		return FALSE;
+
+	xf86SaveScreen(screen, mode);
+	sna_crtc_config_notify(screen);
+	return TRUE;
+}
+
 static void sna_selftest(void)
 {
 	sna_damage_selftest();
@@ -671,7 +684,7 @@ static bool has_shadow(struct sna *sna)
 	if (RegionNil(DamageRegion(sna->mode.shadow_damage)))
 		return false;
 
-	return sna->mode.shadow_flip == 0;
+	return sna->mode.flip_active == 0;
 }
 
 static void
@@ -756,7 +769,7 @@ sna_handle_uevents(int fd, void *closure)
 		DBG(("%s: hotplug event (vtSema?=%d)\n",
 		     __FUNCTION__, sna->scrn->vtSema));
 		if (sna->scrn->vtSema) {
-			sna_mode_update(sna);
+			sna_mode_check(sna);
 			RRGetInfo(xf86ScrnToScreen(scrn), TRUE);
 		} else
 			sna->flags |= SNA_REPROBE;
@@ -1084,7 +1097,7 @@ sna_screen_init(SCREEN_INIT_ARGS_DECL)
 	sna->WakeupHandler = screen->WakeupHandler;
 	screen->WakeupHandler = sna_wakeup_handler;
 
-	screen->SaveScreen = xf86SaveScreen;
+	screen->SaveScreen = sna_save_screen;
 	screen->CreateScreenResources = sna_create_screen_resources;
 
 	sna->CloseScreen = screen->CloseScreen;
diff --git a/src/sna/sna_present.c b/src/sna/sna_present.c
index cacef78..5379d1e 100644
--- a/src/sna/sna_present.c
+++ b/src/sna/sna_present.c
@@ -39,10 +39,7 @@ static present_screen_info_rec present_info;
 
 struct sna_present_event {
 	uint64_t event_id;
-	unsigned int count;
 	xf86CrtcPtr crtc;
-	uint64_t msc;
-	uint64_t ust;
 };
 
 static inline struct sna_present_event *
@@ -287,7 +284,7 @@ page_flip__async(RRCrtcPtr crtc,
 	     (long long)event_id,
 	     bo->handle));
 
-	if (!sna_page_flip(to_sna_from_screen(crtc->pScreen), bo, NULL, -1)) {
+	if (!sna_page_flip(to_sna_from_screen(crtc->pScreen), bo, NULL, NULL)) {
 		DBG(("%s: async pageflip failed\n", __FUNCTION__));
 		present_info.capabilities &= ~PresentCapabilityAsync;
 		return FALSE;
@@ -297,28 +294,27 @@ page_flip__async(RRCrtcPtr crtc,
 	return TRUE;
 }
 
-void
-sna_present_flip_handler(struct sna *sna, struct drm_event_vblank *event)
+static void
+present_flip_handler(struct sna *sna,
+		     struct drm_event_vblank *event,
+		     void *data)
 {
-	struct sna_present_event *info = to_present_event(event->user_data);
+	struct sna_present_event *info = data;
+	struct ust_msc swap;
 
-	DBG(("%s(count=%d, ref-pipe?=%d)\n", __FUNCTION__,
-	     info->count, event->user_data & 1));
-
-	if (event->user_data & 1) {
-		info->msc = sna_crtc_record_event(info->crtc, event);
-		info->ust = ust64(event->tv_sec, event->tv_usec);
-	}
+	DBG(("%s(sequence=%d)\n", __FUNCTION__, event->sequence));
 
-	if (--info->count)
-		return;
+	if (info->crtc == NULL) {
+		swap.tv_sec = event->tv_sec;
+		swap.tv_usec = event->tv_usec;
+		swap.msc = event->sequence;
+	} else
+		swap = *sna_crtc_last_swap(info->crtc);
 
 	DBG(("%s: pipe=%d, tv=%d.%06d msc %lld, complete\n", __FUNCTION__,
 	     info->crtc ? sna_crtc_to_pipe(info->crtc) : -1,
-	     info->crtc ? (int)(info->ust / 1000000) : event->tv_sec,
-	     info->crtc ? (int)(info->ust % 1000000) : event->tv_usec,
-	     info->crtc ? (long long)info->msc : (long long)event->sequence));
-	present_event_notify(info->event_id, info->ust, info->msc);
+	     swap.tv_sec, swap.tv_usec, (long long)swap.msc));
+	present_event_notify(info->event_id, ust64(swap.tv_sec, swap.tv_usec), swap.msc);
 	free(info);
 }
 
@@ -343,10 +339,7 @@ page_flip(ScreenPtr screen,
 
 	event->event_id = event_id;
 	event->crtc = crtc ? crtc->devPrivate : NULL;
-	event->count = sna_page_flip(sna, bo,
-				     MARK_PRESENT(event),
-				     crtc ? sna_crtc_to_pipe(event->crtc) : -1);
-	if (event->count == 0) {
+	if (!sna_page_flip(sna, bo, present_flip_handler, event)) {
 		DBG(("%s: pageflip failed\n", __FUNCTION__));
 		free(event);
 		return FALSE;
diff --git a/src/sna/sna_render.c b/src/sna/sna_render.c
index bf17069..b03134d 100644
--- a/src/sna/sna_render.c
+++ b/src/sna/sna_render.c
@@ -777,7 +777,7 @@ static int sna_render_picture_downsample(struct sna *sna,
 		goto fixup;
 	}
 
-	if (!sna_pixmap_force_to_gpu(pixmap, MOVE_SOURCE_HINT | MOVE_READ)) {
+	if (!sna_pixmap_move_to_gpu(pixmap, MOVE_SOURCE_HINT | MOVE_READ)) {
 fixup:
 		DBG(("%s: unable to create GPU bo for target or temporary pixmaps\n",
 		     __FUNCTION__));


More information about the xorg-commit mailing list