xf86-video-intel: 3 commits - src/intel_options.c src/intel_options.h src/sna/gen3_render.c src/sna/gen4_render.c src/sna/gen5_render.c src/sna/gen6_render.c src/sna/gen7_render.c src/sna/kgem.c src/sna/kgem.h src/sna/sna_accel.c src/sna/sna_display.c src/sna/sna_dri.c src/sna/sna_driver.c src/sna/sna.h src/sna/sna_render.c src/sna/sna_video.c src/sna/sna_video_textured.c

Chris Wilson ickle at kemper.freedesktop.org
Sat Jun 23 07:23:29 PDT 2012


 src/intel_options.c          |    1 
 src/intel_options.h          |    1 
 src/sna/gen3_render.c        |    6 
 src/sna/gen4_render.c        |    6 
 src/sna/gen5_render.c        |    6 
 src/sna/gen6_render.c        |    6 
 src/sna/gen7_render.c        |    6 
 src/sna/kgem.c               |   25 
 src/sna/kgem.h               |    2 
 src/sna/sna.h                |   36 -
 src/sna/sna_accel.c          |  108 ++-
 src/sna/sna_display.c        | 1179 +++++++++++++++++++++++++++++++------------
 src/sna/sna_dri.c            |  297 +++++++---
 src/sna/sna_driver.c         |   42 +
 src/sna/sna_render.c         |   14 
 src/sna/sna_video.c          |    6 
 src/sna/sna_video_textured.c |    2 
 17 files changed, 1223 insertions(+), 520 deletions(-)

New commits:
commit 53d735ddb16b0204662b8584aa22998ba53deec1
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Jun 23 10:16:44 2012 +0100

    sna/dri: Queue windowed swaps
    
    Implement "tripple-buffering" for windowed SwapBuffers by allowing the
    client to submit one extra frame before throttling. That is we emit the
    vsync'ed blit and immediately unblock the client so that it renders to
    the GPU (which is guaranteed to be executed after the blit so that its
    Front/Back buffers are still correct) and requests another SwapBuffers.
    The subsequent swapbuffers are appended to the vsync chain with the
    blit/unblock then executed on the vblank following the original blit.
    That is both the client and xserver render concurrently.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_dri.c b/src/sna/sna_dri.c
index 7cf1d1c..a323b4e 100644
--- a/src/sna/sna_dri.c
+++ b/src/sna/sna_dri.c
@@ -66,12 +66,6 @@ enum frame_event_type {
 	DRI2_WAITMSC,
 };
 
-struct sna_dri_private {
-	int refcnt;
-	PixmapPtr pixmap;
-	struct kgem_bo *bo;
-};
-
 struct sna_dri_frame_event {
 	XID drawable_id;
 	ClientPtr client;
@@ -90,6 +84,8 @@ struct sna_dri_frame_event {
 	DRI2BufferPtr back;
 	struct kgem_bo *bo;
 
+	struct sna_dri_frame_event *chain;
+
 	unsigned int fe_frame;
 	unsigned int fe_tv_sec;
 	unsigned int fe_tv_usec;
@@ -103,8 +99,20 @@ struct sna_dri_frame_event {
 	int off_delay;
 };
 
+struct sna_dri_private {
+	int refcnt;
+	PixmapPtr pixmap;
+	int width, height;
+	struct kgem_bo *bo;
+	struct sna_dri_frame_event *chain;
+};
+
 static DevPrivateKeyRec sna_client_key;
 
+static RESTYPE frame_event_client_type;
+static RESTYPE frame_event_drawable_type;
+static RESTYPE dri_drawable_type;
+
 static inline struct sna_dri_frame_event *
 to_frame_event(uintptr_t  data)
 {
@@ -204,6 +212,25 @@ sna_dri_create_buffer(DrawablePtr drawable,
 	switch (attachment) {
 	case DRI2BufferFrontLeft:
 		pixmap = get_drawable_pixmap(drawable);
+
+		buffer = NULL;
+		dixLookupResourceByType((void **)&buffer, drawable->id,
+					dri_drawable_type, NULL, DixWriteAccess);
+		if (buffer) {
+			private = get_private(buffer);
+			if (private->pixmap == pixmap &&
+			    private->width  == pixmap->drawable.width &&
+			    private->height == pixmap->drawable.height)  {
+				DBG(("%s: reusing front buffer attachment\n",
+				     __FUNCTION__));
+				private->refcnt++;
+				return buffer;
+			}
+			FreeResourceByType(drawable->id,
+					   dri_drawable_type,
+					   TRUE);
+		}
+
 		bo = sna_pixmap_set_dri(sna, pixmap);
 		if (bo == NULL)
 			return NULL;
@@ -292,6 +319,10 @@ sna_dri_create_buffer(DrawablePtr drawable,
 	buffer->name = kgem_bo_flink(&sna->kgem, bo);
 	private->refcnt = 1;
 	private->pixmap = pixmap;
+	if (pixmap) {
+		private->width  = pixmap->drawable.width;
+		private->height = pixmap->drawable.height;
+	}
 	private->bo = bo;
 
 	if (buffer->name == 0)
@@ -300,6 +331,9 @@ sna_dri_create_buffer(DrawablePtr drawable,
 	if (pixmap)
 		pixmap->refcnt++;
 
+	if (attachment == DRI2BufferFrontLeft)
+		(void)AddResource(drawable->id, dri_drawable_type, buffer);
+
 	return buffer;
 
 err:
@@ -727,8 +761,6 @@ sna_dri_get_pipe(DrawablePtr pDraw)
 	return pipe;
 }
 
-static RESTYPE frame_event_client_type, frame_event_drawable_type;
-
 static struct list *
 get_resource(XID id, RESTYPE type)
 {
@@ -806,6 +838,13 @@ sna_dri_frame_event_drawable_gone(void *data, XID id)
 	return Success;
 }
 
+static int
+sna_dri_drawable_gone(void *data, XID id)
+{
+	DBG(("%s(%ld)\n", __FUNCTION__, (long)id));
+	return Success;
+}
+
 static Bool
 sna_dri_register_frame_event_resource_types(void)
 {
@@ -815,12 +854,26 @@ sna_dri_register_frame_event_resource_types(void)
 	if (!frame_event_client_type)
 		return FALSE;
 
+	DBG(("%s: frame_event_client_type=%d\n",
+	     __FUNCTION__, frame_event_client_type));
+
 	frame_event_drawable_type =
 		CreateNewResourceType(sna_dri_frame_event_drawable_gone,
 				      "Frame Event Drawable");
 	if (!frame_event_drawable_type)
 		return FALSE;
 
+	DBG(("%s: frame_event_drawable_type=%d\n",
+	     __FUNCTION__, frame_event_drawable_type));
+
+	dri_drawable_type =
+		CreateNewResourceType(sna_dri_drawable_gone,
+				      "DRI2 Drawable");
+	if (!dri_drawable_type)
+		return FALSE;
+
+	DBG(("%s: dri_drawable_type=%d\n", __FUNCTION__, dri_drawable_type));
+
 	return TRUE;
 }
 
@@ -1083,6 +1136,9 @@ void sna_dri_vblank_handler(struct sna *sna, struct drm_event_vblank *event)
 			if (kgem_bo_is_busy(info->bo)) {
 				drmVBlank vbl;
 
+				DBG(("%s: vsync'ed blit is still busy, postponing\n",
+				     __FUNCTION__));
+
 				VG_CLEAR(vbl);
 				vbl.request.type =
 					DRM_VBLANK_RELATIVE |
@@ -1095,12 +1151,53 @@ void sna_dri_vblank_handler(struct sna *sna, struct drm_event_vblank *event)
 			}
 		}
 
-		DRI2SwapComplete(info->client,
-				 draw, event->sequence,
-				 event->tv_sec, event->tv_usec,
-				 DRI2_BLIT_COMPLETE,
-				 info->client ? info->event_complete : NULL,
-				 info->event_data);
+		if (info->chain) {
+			struct sna_dri_frame_event *chain = info->chain;
+			drmVBlank vbl;
+
+			DBG(("%s: emitting chained vsync'ed blit\n",
+			     __FUNCTION__));
+
+			assert(get_private(info->front)->chain == info);
+			get_private(info->front)->chain = chain;
+
+			chain->bo = sna_dri_copy_to_front(sna, draw, NULL,
+							  get_private(chain->front)->bo,
+							  get_private(chain->back)->bo,
+							  true);
+
+			DRI2SwapComplete(chain->client,
+					 draw, event->sequence,
+					 event->tv_sec, event->tv_usec,
+					 DRI2_BLIT_COMPLETE,
+					 chain->client ? chain->event_complete : NULL,
+					 chain->event_data);
+
+			VG_CLEAR(vbl);
+			vbl.request.type =
+				DRM_VBLANK_RELATIVE |
+				DRM_VBLANK_NEXTONMISS |
+				DRM_VBLANK_EVENT |
+				pipe_select(chain->pipe);
+			vbl.request.sequence = 0;
+			vbl.request.signal = (unsigned long)chain;
+			if (sna_wait_vblank(sna, &vbl))
+				sna_dri_frame_event_info_free(sna, chain);
+
+			info->chain = NULL;
+		} else if (get_private(info->front)->chain == info) {
+			DBG(("%s: chain complete\n", __FUNCTION__));
+			get_private(info->front)->chain = NULL;
+		} else {
+			DBG(("%s: deferred blit complete, unblock client\n",
+			     __FUNCTION__));
+			DRI2SwapComplete(info->client,
+					 draw, event->sequence,
+					 event->tv_sec, event->tv_usec,
+					 DRI2_BLIT_COMPLETE,
+					 info->client ? info->event_complete : NULL,
+					 info->event_data);
+		}
 		break;
 
 	case DRI2_WAITMSC:
@@ -1596,21 +1693,44 @@ sna_dri_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 		DBG(("%s: emitting immediate vsync'ed blit, throttling client\n",
 		     __FUNCTION__));
 
-		info->bo = sna_dri_copy_to_front(sna, draw, NULL,
-						 get_private(front)->bo,
-						 get_private(back)->bo,
-						 true);
 		if ((sna->flags & SNA_NO_WAIT) == 0) {
+			struct sna_dri_private *priv = get_private(front);
+
 			info->type = DRI2_SWAP_THROTTLE;
-			vbl.request.type =
-				DRM_VBLANK_RELATIVE |
-				DRM_VBLANK_NEXTONMISS |
-				DRM_VBLANK_EVENT |
-				pipe_select(pipe);
-			vbl.request.sequence = 0;
-			vbl.request.signal = (unsigned long)info;
-			if (sna_wait_vblank(sna, &vbl) == 0)
+			if (priv->chain == NULL) {
+				DBG(("%s: no pending blit, starting chain\n",
+				     __FUNCTION__));
+
+				info->bo = sna_dri_copy_to_front(sna, draw, NULL,
+								 get_private(front)->bo,
+								 get_private(back)->bo,
+								 true);
+
+				DRI2SwapComplete(client, draw, 0, 0, 0,
+						 DRI2_BLIT_COMPLETE, func, data);
+				vbl.request.type =
+					DRM_VBLANK_RELATIVE |
+					DRM_VBLANK_NEXTONMISS |
+					DRM_VBLANK_EVENT |
+					pipe_select(pipe);
+				vbl.request.sequence = 0;
+				vbl.request.signal = (unsigned long)info;
+				if (sna_wait_vblank(sna, &vbl) == 0) {
+					priv->chain = info;
+					return TRUE;
+				}
+			} else {
+				DBG(("%s: attaching to vsync chain\n",
+				     __FUNCTION__));
+				assert(priv->chain->chain == NULL);
+				priv->chain->chain = info;
 				return TRUE;
+			}
+		} else {
+			info->bo = sna_dri_copy_to_front(sna, draw, NULL,
+							 get_private(front)->bo,
+							 get_private(back)->bo,
+							 true);
 		}
 
 		sna_dri_frame_event_info_free(sna, info);
commit 1e9319d5f56583be99f573f208cebb0ee3b5cc26
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Jun 22 11:22:16 2012 +0100

    sna: extend RandR to support super sized monitor configurations
    
    With the introduction of the third pipe on IvyBridge it is possible to
    encounter situations where the combination of the three monitors exceed
    the limits of the scanout engine and so prevent them being used at their
    native resolutions. (It is conceivable to hit similar issues on earlier
    generation, especially gen2/3.) One workaround, this patch, is to extend
    the RandR shadow support to break the extended framebuffer into per-crtc
    pixmaps.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/intel_options.c b/src/intel_options.c
index 78575a6..d8455f9 100644
--- a/src/intel_options.c
+++ b/src/intel_options.c
@@ -22,6 +22,7 @@ const OptionInfoRec intel_options[] = {
 	{OPTION_THROTTLE,	"Throttle",	OPTV_BOOLEAN,	{0},	1},
 	{OPTION_ZAPHOD,	"ZaphodHeads",	OPTV_STRING,	{0},	0},
 	{OPTION_DELAYED_FLUSH,	"DelayedFlush",	OPTV_BOOLEAN,	{0},	1},
+	{OPTION_TEAR_FREE,	"TearFree",	OPTV_BOOLEAN,	{0},	1},
 #endif
 #ifdef USE_UXA
 	{OPTION_FALLBACKDEBUG,	"FallbackDebug",OPTV_BOOLEAN,	{0},	0},
diff --git a/src/intel_options.h b/src/intel_options.h
index 05a2ad1..c3e4999 100644
--- a/src/intel_options.h
+++ b/src/intel_options.h
@@ -28,6 +28,7 @@ enum intel_options {
 	OPTION_THROTTLE,
 	OPTION_ZAPHOD,
 	OPTION_DELAYED_FLUSH,
+	OPTION_TEAR_FREE,
 #endif
 #ifdef USE_UXA
 	OPTION_FALLBACKDEBUG,
diff --git a/src/sna/kgem.c b/src/sna/kgem.c
index 9fe3661..5b58e0e 100644
--- a/src/sna/kgem.c
+++ b/src/sna/kgem.c
@@ -1990,7 +1990,6 @@ void _kgem_submit(struct kgem *kgem)
 	if (kgem->wedged)
 		kgem_cleanup(kgem);
 
-	kgem->flush_now = kgem->scanout;
 	kgem_reset(kgem);
 
 	assert(kgem->next_request != NULL);
@@ -2486,14 +2485,30 @@ done:
 	return tiling;
 }
 
+static int bits_per_pixel(int depth)
+{
+	switch (depth) {
+	case 8: return 8;
+	case 15:
+	case 16: return 16;
+	case 24:
+	case 30:
+	case 32: return 32;
+	default: return 0;
+	}
+}
+
 unsigned kgem_can_create_2d(struct kgem *kgem,
 			    int width, int height, int depth)
 {
-	int bpp = BitsPerPixel(depth);
 	uint32_t pitch, size;
 	unsigned flags = 0;
+	int bpp;
+
+	DBG(("%s: %dx%d @ %d\n", __FUNCTION__, width, height, depth));
 
-	if (depth < 8) {
+	bpp = bits_per_pixel(depth);
+	if (bpp == 0) {
 		DBG(("%s: unhandled depth %d\n", __FUNCTION__, depth));
 		return 0;
 	}
@@ -2509,6 +2524,8 @@ unsigned kgem_can_create_2d(struct kgem *kgem,
 				 I915_TILING_NONE, &pitch);
 	if (size > 0 && size <= kgem->max_cpu_size)
 		flags |= KGEM_CAN_CREATE_CPU | KGEM_CAN_CREATE_GPU;
+	if (size > 0 && size <= kgem->aperture_mappable/4)
+		flags |= KGEM_CAN_CREATE_GTT;
 	if (size > kgem->large_object_size)
 		flags |= KGEM_CAN_CREATE_LARGE;
 	if (size > kgem->max_object_size) {
@@ -2524,6 +2541,8 @@ unsigned kgem_can_create_2d(struct kgem *kgem,
 				 &pitch);
 	if (size > 0 && size <= kgem->max_gpu_size)
 		flags |= KGEM_CAN_CREATE_GPU;
+	if (size > 0 && size <= kgem->aperture_mappable/4)
+		flags |= KGEM_CAN_CREATE_GTT;
 	if (size > kgem->large_object_size)
 		flags |= KGEM_CAN_CREATE_LARGE;
 	if (size > kgem->max_object_size) {
diff --git a/src/sna/kgem.h b/src/sna/kgem.h
index c154be5..2d8def8 100644
--- a/src/sna/kgem.h
+++ b/src/sna/kgem.h
@@ -150,7 +150,6 @@ struct kgem {
 	uint32_t need_retire:1;
 	uint32_t need_throttle:1;
 	uint32_t scanout:1;
-	uint32_t flush_now:1;
 	uint32_t busy:1;
 
 	uint32_t has_vmap :1;
@@ -218,6 +217,7 @@ unsigned kgem_can_create_2d(struct kgem *kgem, int width, int height, int depth)
 #define KGEM_CAN_CREATE_GPU	0x1
 #define KGEM_CAN_CREATE_CPU	0x2
 #define KGEM_CAN_CREATE_LARGE	0x4
+#define KGEM_CAN_CREATE_GTT	0x8
 
 struct kgem_bo *
 kgem_replace_bo(struct kgem *kgem,
diff --git a/src/sna/sna.h b/src/sna/sna.h
index ee8273c..2e8925b 100644
--- a/src/sna/sna.h
+++ b/src/sna/sna.h
@@ -46,6 +46,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include "compiler.h"
 
 #include <xf86Crtc.h>
+#include <xf86str.h>
 #include <windowstr.h>
 #include <glyphstr.h>
 #include <picturestr.h>
@@ -58,6 +59,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 #include "../compat-api.h"
 #define _XF86DRI_SERVER_
+#include <drm.h>
 #include <dri2.h>
 #include <i915_drm.h>
 
@@ -209,6 +211,7 @@ struct sna {
 #define SNA_NO_DELAYED_FLUSH	0x2
 #define SNA_NO_WAIT		0x4
 #define SNA_NO_FLIP		0x8
+#define SNA_TEAR_FREE		0x10
 
 	unsigned watch_flush;
 	unsigned flush;
@@ -226,14 +229,16 @@ struct sna {
 	struct list active_pixmaps;
 	struct list inactive_clock[2];
 
-	PixmapPtr front, shadow;
+	PixmapPtr front;
 	PixmapPtr freed_pixmap;
 
 	struct sna_mode {
-		uint32_t fb_id;
-		uint32_t fb_pixmap;
-		drmModeResPtr mode_res;
-		int cpp;
+		drmModeResPtr kmode;
+
+		int shadow_active;
+		DamagePtr shadow_damage;
+		struct kgem_bo *shadow;
+		int shadow_flip;
 
 		struct list outputs;
 		struct list crtcs;
@@ -256,6 +261,7 @@ struct sna {
 	ScreenBlockHandlerProcPtr BlockHandler;
 	ScreenWakeupHandlerProcPtr WakeupHandler;
 	CloseScreenProcPtr CloseScreen;
+	xf86ModeSetProc *ModeSet;
 
 	PicturePtr clear;
 	struct {
@@ -302,9 +308,10 @@ struct sna {
 
 Bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna);
 void sna_mode_adjust_frame(struct sna *sna, int x, int y);
-extern void sna_mode_remove_fb(struct sna *sna);
 extern void sna_mode_update(struct sna *sna);
 extern void sna_mode_disable_unused(struct sna *sna);
+extern void sna_mode_wakeup(struct sna *sna);
+extern void sna_mode_redisplay(struct sna *sna);
 extern void sna_mode_fini(struct sna *sna);
 
 extern int sna_crtc_id(xf86CrtcPtr crtc);
@@ -356,17 +363,17 @@ to_sna_from_kgem(struct kgem *kgem)
 
 extern xf86CrtcPtr sna_covering_crtc(ScrnInfoPtr scrn,
 				     const BoxRec *box,
-				     xf86CrtcPtr desired,
-				     BoxPtr crtc_box_ret);
+				     xf86CrtcPtr desired);
 
 extern bool sna_wait_for_scanline(struct sna *sna, PixmapPtr pixmap,
 				  xf86CrtcPtr crtc, const BoxRec *clip);
 
 Bool sna_dri_open(struct sna *sna, ScreenPtr pScreen);
-void sna_dri_wakeup(struct sna *sna);
+void sna_dri_page_flip_handler(struct sna *sna, struct drm_event_vblank *event);
+void sna_dri_vblank_handler(struct sna *sna, struct drm_event_vblank *event);
 void sna_dri_close(struct sna *sna, ScreenPtr pScreen);
 
-extern Bool sna_crtc_on(xf86CrtcPtr crtc);
+extern bool sna_crtc_on(xf86CrtcPtr crtc);
 int sna_crtc_to_pipe(xf86CrtcPtr crtc);
 int sna_crtc_to_plane(xf86CrtcPtr crtc);
 
@@ -408,10 +415,12 @@ get_drawable_dy(DrawablePtr drawable)
 	return 0;
 }
 
-static inline Bool pixmap_is_scanout(PixmapPtr pixmap)
+bool sna_pixmap_attach_to_bo(PixmapPtr pixmap, struct kgem_bo *bo);
+static inline bool sna_pixmap_is_scanout(struct sna *sna, PixmapPtr pixmap)
 {
-	ScreenPtr screen = pixmap->drawable.pScreen;
-	return pixmap == screen->GetScreenPixmap(screen);
+	return (pixmap == sna->front &&
+		!sna->mode.shadow_active &&
+		(sna->flags & SNA_NO_WAIT) == 0);
 }
 
 PixmapPtr sna_pixmap_create_upload(ScreenPtr screen,
@@ -429,6 +438,7 @@ struct kgem_bo *sna_pixmap_change_tiling(PixmapPtr pixmap, uint32_t tiling);
 #define MOVE_INPLACE_HINT 0x4
 #define MOVE_ASYNC_HINT 0x8
 #define MOVE_SOURCE_HINT 0x10
+#define __MOVE_FORCE 0x20
 bool must_check _sna_pixmap_move_to_cpu(PixmapPtr pixmap, unsigned flags);
 static inline bool must_check sna_pixmap_move_to_cpu(PixmapPtr pixmap, unsigned flags)
 {
diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index 5b0b33d..0f52a27 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -645,7 +645,7 @@ _sna_pixmap_reset(PixmapPtr pixmap)
 	return _sna_pixmap_init(priv, pixmap);
 }
 
-static struct sna_pixmap *sna_pixmap_attach(struct sna *sna, PixmapPtr pixmap)
+static struct sna_pixmap *sna_pixmap_attach(PixmapPtr pixmap)
 {
 	struct sna_pixmap *priv;
 
@@ -657,6 +657,22 @@ static struct sna_pixmap *sna_pixmap_attach(struct sna *sna, PixmapPtr pixmap)
 	return _sna_pixmap_init(priv, pixmap);
 }
 
+bool sna_pixmap_attach_to_bo(PixmapPtr pixmap, struct kgem_bo *bo)
+{
+	struct sna_pixmap *priv;
+
+	priv = sna_pixmap_attach(pixmap);
+	if (!priv)
+		return false;
+
+	priv->gpu_bo = kgem_bo_reference(bo);
+	sna_damage_all(&priv->gpu_damage,
+		       pixmap->drawable.width,
+		       pixmap->drawable.height);
+
+	return true;
+}
+
 static inline PixmapPtr
 create_pixmap(struct sna *sna, ScreenPtr screen,
 	      int width, int height, int depth,
@@ -724,7 +740,7 @@ sna_pixmap_create_shm(ScreenPtr screen,
 		pixmap->drawable.depth = depth;
 		pixmap->drawable.bitsPerPixel = bpp;
 
-		priv = sna_pixmap_attach(sna, pixmap);
+		priv = sna_pixmap_attach(pixmap);
 		if (!priv) {
 			fbDestroyPixmap(pixmap);
 			return NullPixmap;
@@ -816,7 +832,7 @@ sna_pixmap_create_scratch(ScreenPtr screen,
 		pixmap->drawable.depth = depth;
 		pixmap->drawable.bitsPerPixel = bpp;
 
-		priv = sna_pixmap_attach(sna, pixmap);
+		priv = sna_pixmap_attach(pixmap);
 		if (!priv) {
 			fbDestroyPixmap(pixmap);
 			return NullPixmap;
@@ -907,7 +923,7 @@ force_create:
 		if (pixmap == NullPixmap)
 			return NullPixmap;
 
-		sna_pixmap_attach(sna, pixmap);
+		sna_pixmap_attach(pixmap);
 	} else {
 		struct sna_pixmap *priv;
 
@@ -923,7 +939,7 @@ force_create:
 		pixmap->devKind = pad;
 		pixmap->devPrivate.ptr = NULL;
 
-		priv = sna_pixmap_attach(sna, pixmap);
+		priv = sna_pixmap_attach(pixmap);
 		if (priv == NULL) {
 			free(pixmap);
 			goto fallback;
@@ -2508,7 +2524,7 @@ sna_pixmap_force_to_gpu(PixmapPtr pixmap, unsigned flags)
 		}
 	}
 
-	if (!sna_pixmap_move_to_gpu(pixmap, flags))
+	if (!sna_pixmap_move_to_gpu(pixmap, flags | __MOVE_FORCE))
 		return NULL;
 
 	/* For large bo, try to keep only a single copy around */
@@ -2537,6 +2553,9 @@ sna_pixmap_move_to_gpu(PixmapPtr pixmap, unsigned flags)
 	DBG(("%s(pixmap=%ld, usage=%d)\n",
 	     __FUNCTION__, pixmap->drawable.serialNumber, pixmap->usage_hint));
 
+	if ((flags & __MOVE_FORCE) == 0 && wedged(sna))
+		return NULL;
+
 	priv = sna_pixmap(pixmap);
 	if (priv == NULL) {
 		DBG(("%s: not attached\n", __FUNCTION__));
@@ -12277,7 +12296,6 @@ sna_accel_flush_callback(CallbackListPtr *list,
 	}
 
 	kgem_submit(&sna->kgem);
-	sna->kgem.flush_now = 0;
 
 	kgem_sync(&sna->kgem);
 
@@ -12286,8 +12304,7 @@ sna_accel_flush_callback(CallbackListPtr *list,
 
 static struct sna_pixmap *sna_accel_scanout(struct sna *sna)
 {
-	PixmapPtr front = sna->shadow ? sna->shadow : sna->front;
-	struct sna_pixmap *priv = sna_pixmap(front);
+	struct sna_pixmap *priv = sna_pixmap(sna->front);
 	return priv && priv->gpu_bo ? priv : NULL;
 }
 
@@ -12298,12 +12315,48 @@ static void sna_accel_disarm_timer(struct sna *sna, int id)
 	sna->timer_ready &= ~(1<<id);
 }
 
+static bool has_shadow(struct sna *sna)
+{
+	DamagePtr damage = sna->mode.shadow_damage;
+
+	if (!(damage && RegionNotEmpty(DamageRegion(damage))))
+		return false;
+
+	DBG(("%s: has pending damage\n", __FUNCTION__));
+	if ((sna->flags & SNA_TEAR_FREE) == 0)
+		return true;
+
+	DBG(("%s: outstanding flips: %d\n",
+	     __FUNCTION__, sna->mode.shadow_flip));
+	return !sna->mode.shadow_flip;
+}
+
+static bool need_flush(struct sna *sna, struct sna_pixmap *scanout)
+{
+	DBG(("%s: scanout=%d shadow?=%d || (cpu?=%d || gpu?=%d) && !busy=%d)\n",
+	     __FUNCTION__,
+	     scanout && scanout->gpu_bo ? scanout->gpu_bo->handle : 0,
+	     has_shadow(sna),
+	     scanout && scanout->cpu_damage != NULL,
+	     scanout && scanout->gpu_bo && scanout->gpu_bo->exec != NULL,
+	     scanout && scanout->gpu_bo && __kgem_flush(&sna->kgem, scanout->gpu_bo)));
+
+	if (has_shadow(sna))
+		return true;
+
+	if (!scanout)
+		return false;
+
+	return (scanout->cpu_damage || scanout->gpu_bo->exec) &&
+		!__kgem_flush(&sna->kgem, scanout->gpu_bo);
+}
+
 static bool sna_accel_do_flush(struct sna *sna)
 {
 	struct sna_pixmap *priv;
 
 	priv = sna_accel_scanout(sna);
-	if (priv == NULL || priv->gpu_bo == NULL) {
+	if (priv == NULL && !sna->mode.shadow_active) {
 		DBG(("%s -- no scanout attached\n", __FUNCTION__));
 		sna_accel_disarm_timer(sna, FLUSH_TIMER);
 		return false;
@@ -12313,27 +12366,19 @@ static bool sna_accel_do_flush(struct sna *sna)
 		return true;
 
 	if (sna->timer_active & (1<<(FLUSH_TIMER))) {
-		if (sna->kgem.flush_now) {
-			sna->kgem.flush_now = 0;
-			if (priv->gpu_bo->exec) {
-				DBG(("%s -- forcing flush\n", __FUNCTION__));
-				sna->timer_ready |= 1 << FLUSH_TIMER;
-			}
-		}
-
+		DBG(("%s: flush timer active\n", __FUNCTION__));
 		if (sna->timer_ready & (1<<(FLUSH_TIMER))) {
 			DBG(("%s (time=%ld), triggered\n", __FUNCTION__, (long)sna->time));
 			sna->timer_expire[FLUSH_TIMER] =
 				sna->time + sna->vblank_interval;
-			return priv->cpu_damage || !__kgem_flush(&sna->kgem, priv->gpu_bo);
+			return true;
 		}
 	} else {
-		if (priv->cpu_damage == NULL &&
-		    !__kgem_flush(&sna->kgem, priv->gpu_bo)) {
+		if (!need_flush(sna, priv)) {
 			DBG(("%s -- no pending write to scanout\n", __FUNCTION__));
 		} else {
 			sna->timer_active |= 1 << FLUSH_TIMER;
-			sna->timer_ready  |= 1 << FLUSH_TIMER;
+			sna->timer_ready |= 1 << FLUSH_TIMER;
 			sna->timer_expire[FLUSH_TIMER] =
 				sna->time + sna->vblank_interval / 2;
 			DBG(("%s (time=%ld), starting\n", __FUNCTION__, (long)sna->time));
@@ -12447,24 +12492,24 @@ static void sna_accel_flush(struct sna *sna)
 	struct sna_pixmap *priv = sna_accel_scanout(sna);
 	bool busy;
 
-	assert(priv != NULL);
 	DBG(("%s (time=%ld), cpu damage? %p, exec? %d nbatch=%d, busy? %d\n",
 	     __FUNCTION__, (long)sna->time,
-	     priv->cpu_damage,
-	     priv->gpu_bo->exec != NULL,
+	     priv && priv->cpu_damage,
+	     priv && priv->gpu_bo->exec != NULL,
 	     sna->kgem.nbatch,
 	     sna->kgem.busy));
 
-	busy = priv->cpu_damage || priv->gpu_bo->rq;
+	busy = need_flush(sna, priv);
 	if (!sna->kgem.busy && !busy)
 		sna_accel_disarm_timer(sna, FLUSH_TIMER);
 	sna->kgem.busy = busy;
 
-	if (priv->cpu_damage)
-		sna_pixmap_move_to_gpu(priv->pixmap, MOVE_READ);
+	if (priv) {
+		sna_pixmap_force_to_gpu(priv->pixmap, MOVE_READ);
+		kgem_bo_flush(&sna->kgem, priv->gpu_bo);
+	}
 
-	kgem_bo_flush(&sna->kgem, priv->gpu_bo);
-	sna->kgem.flush_now = 0;
+	sna_mode_redisplay(sna);
 }
 
 static void sna_accel_throttle(struct sna *sna)
@@ -12811,10 +12856,9 @@ void sna_accel_wakeup_handler(struct sna *sna, fd_set *ready)
 	DBG(("%s\n", __FUNCTION__));
 	if (sna->kgem.need_retire)
 		kgem_retire(&sna->kgem);
-	if (!sna->kgem.need_retire) {
+	if (!sna->mode.shadow_active && !sna->kgem.need_retire) {
 		DBG(("%s: GPU idle, flushing\n", __FUNCTION__));
 		kgem_submit(&sna->kgem);
-		sna->kgem.flush_now = 0;
 	}
 	if (sna->kgem.need_purge)
 		kgem_purge_cache(&sna->kgem);
diff --git a/src/sna/sna_display.c b/src/sna/sna_display.c
index de834ae..0896af4 100644
--- a/src/sna/sna_display.c
+++ b/src/sna/sna_display.c
@@ -43,6 +43,9 @@
 #include <xf86drm.h>
 #include <xf86DDC.h> /* for xf86InterpretEDID */
 
+#include <fb.h>
+#include <fbpict.h>
+
 #include "sna.h"
 #include "sna_reg.h"
 
@@ -61,13 +64,12 @@
 
 struct sna_crtc {
 	struct drm_mode_modeinfo kmode;
-	PixmapPtr shadow;
-	uint32_t shadow_fb_id;
+	struct kgem_bo *bo;
 	uint32_t cursor;
+	bool shadow;
 	uint8_t id;
 	uint8_t pipe;
 	uint8_t plane;
-	uint8_t active;
 	struct list link;
 };
 
@@ -126,10 +128,9 @@ static const char *backlight_interfaces[] = {
 /* Enough for 10 digits of backlight + '\n' + '\0' */
 #define BACKLIGHT_VALUE_LEN 12
 
-static inline int
-crtc_id(struct sna_crtc *crtc)
+static inline uint32_t fb_id(struct kgem_bo *bo)
 {
-	return crtc->id;
+	return bo->delta;
 }
 
 int sna_crtc_id(xf86CrtcPtr crtc)
@@ -137,9 +138,9 @@ int sna_crtc_id(xf86CrtcPtr crtc)
 	return to_sna_crtc(crtc)->id;
 }
 
-int sna_crtc_on(xf86CrtcPtr crtc)
+bool sna_crtc_on(xf86CrtcPtr crtc)
 {
-	return to_sna_crtc(crtc)->active;
+	return to_sna_crtc(crtc)->bo != NULL;
 }
 
 int sna_crtc_to_pipe(xf86CrtcPtr crtc)
@@ -159,7 +160,6 @@ static unsigned get_fb(struct sna *sna, struct kgem_bo *bo,
 {
 	ScrnInfoPtr scrn = sna->scrn;
 	struct drm_mode_fb_cmd arg;
-	int ret;
 
 	assert(bo->proxy == NULL);
 	if (bo->delta) {
@@ -181,11 +181,11 @@ static unsigned get_fb(struct sna *sna, struct kgem_bo *bo,
 	arg.depth = scrn->depth;
 	arg.handle = bo->handle;
 
-	if ((ret = drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_ADDFB, &arg))) {
+	if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_ADDFB, &arg)) {
 		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
 			   "%s: failed to add fb: %dx%d depth=%d, bpp=%d, pitch=%d: %d\n",
 			   __FUNCTION__, width, height,
-			   scrn->depth, scrn->bitsPerPixel, bo->pitch, ret);
+			   scrn->depth, scrn->bitsPerPixel, bo->pitch, errno);
 		return 0;
 	}
 
@@ -397,16 +397,20 @@ mode_to_kmode(struct drm_mode_modeinfo *kmode, DisplayModePtr mode)
 
 bool sna_crtc_is_bound(struct sna *sna, xf86CrtcPtr crtc)
 {
+	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
 	struct drm_mode_crtc mode;
 
+	if (!sna_crtc->bo)
+		return false;
+
 	VG_CLEAR(mode);
-	mode.crtc_id = to_sna_crtc(crtc)->id;
+	mode.crtc_id = sna_crtc->id;
 	if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_GETCRTC, &mode))
 		return false;
 
 	DBG(("%s: crtc=%d, mode valid?=%d, fb attached?=%d\n", __FUNCTION__,
-	     mode.crtc_id, mode.mode_valid, sna->mode.fb_id == mode.fb_id));
-	return mode.mode_valid && sna->mode.fb_id == mode.fb_id;
+	     mode.crtc_id, mode.mode_valid, fb_id(sna_crtc->bo) == mode.fb_id));
+	return mode.mode_valid && fb_id(sna_crtc->bo) == mode.fb_id;
 }
 
 static Bool
@@ -415,11 +419,9 @@ sna_crtc_apply(xf86CrtcPtr crtc)
 	struct sna *sna = to_sna(crtc->scrn);
 	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
 	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
-	struct sna_mode *mode = &sna->mode;
 	struct drm_mode_crtc arg;
 	uint32_t output_ids[16];
 	int output_count = 0;
-	int fb_id, x, y;
 	int i, ret = FALSE;
 
 	DBG(("%s\n", __FUNCTION__));
@@ -439,22 +441,23 @@ sna_crtc_apply(xf86CrtcPtr crtc)
 		output_count++;
 	}
 
-	if (!xf86CrtcRotate(crtc)) {
-		DBG(("%s: failed to rotate crtc\n", __FUNCTION__));
-		return FALSE;
-	}
-
 	crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
 			       crtc->gamma_blue, crtc->gamma_size);
 
-	x = crtc->x;
-	y = crtc->y;
-	fb_id = mode->fb_id;
-	if (sna_crtc->shadow_fb_id) {
-		fb_id = sna_crtc->shadow_fb_id;
-		x = 0;
-		y = 0;
+	VG_CLEAR(arg);
+	arg.crtc_id = sna_crtc->id;
+	arg.fb_id = fb_id(sna_crtc->bo);
+	if (sna_crtc->shadow) {
+		arg.x = 0;
+		arg.y = 0;
+	} else {
+		arg.x = crtc->x;
+		arg.y = crtc->y;
 	}
+	arg.set_connectors_ptr = (uintptr_t)output_ids;
+	arg.count_connectors = output_count;
+	arg.mode = sna_crtc->kmode;
+	arg.mode_valid = 1;
 
 	xf86DrvMsg(crtc->scrn->scrnIndex, X_INFO,
 		   "switch to mode %dx%d on crtc %d (pipe %d)\n",
@@ -467,22 +470,14 @@ sna_crtc_apply(xf86CrtcPtr crtc)
 	     sna_crtc->kmode.hdisplay,
 	     sna_crtc->kmode.vdisplay,
 	     sna_crtc->kmode.clock,
-	     fb_id, sna_crtc->shadow_fb_id ? " [shadow]" : "",
+	     arg.fb_id,
+	     sna_crtc->shadow ? " [shadow]" : "",
 	     output_count));
 
-	VG_CLEAR(arg);
-	arg.x = x;
-	arg.y = y;
-	arg.crtc_id = sna_crtc->id;
-	arg.fb_id = fb_id;
-	arg.set_connectors_ptr = (uintptr_t)output_ids;
-	arg.count_connectors = output_count;
-	arg.mode = sna_crtc->kmode;
-	arg.mode_valid = 1;
 	ret = drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_SETCRTC, &arg);
 	if (ret) {
 		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
-			   "failed to set mode: %s\n", strerror(-ret));
+			   "failed to set mode: %s\n", strerror(errno));
 		ret = FALSE;
 	} else
 		ret = TRUE;
@@ -493,6 +488,79 @@ sna_crtc_apply(xf86CrtcPtr crtc)
 	return ret;
 }
 
+static bool sna_mode_enable_shadow(struct sna *sna)
+{
+	ScreenPtr screen = sna->scrn->pScreen;
+
+	DBG(("%s\n", __FUNCTION__));
+	assert(sna->mode.shadow == NULL);
+	assert(sna->mode.shadow_damage == NULL);
+	assert(sna->mode.shadow_active == 0);
+
+	sna->mode.shadow_damage = DamageCreate(NULL, NULL,
+					       DamageReportNone, TRUE,
+					       screen, screen);
+	if (!sna->mode.shadow_damage)
+		return false;
+
+	DamageRegister(&sna->front->drawable, sna->mode.shadow_damage);
+	return true;
+}
+
+static void sna_mode_disable_shadow(struct sna *sna)
+{
+	if (!sna->mode.shadow_damage)
+		return;
+
+	DBG(("%s\n", __FUNCTION__));
+
+	DamageUnregister(&sna->front->drawable, sna->mode.shadow_damage);
+	DamageDestroy(sna->mode.shadow_damage);
+	sna->mode.shadow_damage = NULL;
+
+	if (sna->mode.shadow) {
+		kgem_bo_destroy(&sna->kgem, sna->mode.shadow);
+		sna->mode.shadow = NULL;
+	}
+
+	sna->mode.shadow_active = 0;
+}
+
+static bool sna_crtc_enable_shadow(struct sna *sna, struct sna_crtc *crtc)
+{
+	if (crtc->shadow) {
+		assert(sna->mode.shadow_damage && sna->mode.shadow_active);
+		return true;
+	}
+
+	DBG(("%s: enabling for crtc %d\n", __FUNCTION__, crtc->id));
+
+	if (!sna->mode.shadow_active) {
+		if (!sna_mode_enable_shadow(sna))
+			return false;
+		assert(sna->mode.shadow_damage);
+		assert(sna->mode.shadow == NULL);
+	}
+
+	crtc->shadow = true;
+	sna->mode.shadow_active++;
+	return true;
+}
+
+static void sna_crtc_disable_shadow(struct sna *sna, struct sna_crtc *crtc)
+{
+	if (!crtc->shadow)
+		return;
+
+	DBG(("%s: disabling for crtc %d\n", __FUNCTION__, crtc->id));
+	assert(sna->mode.shadow_active > 0);
+
+	if (!--sna->mode.shadow_active)
+		sna_mode_disable_shadow(sna);
+
+	crtc->shadow = false;
+}
+
 static void
 sna_crtc_disable(xf86CrtcPtr crtc)
 {
@@ -507,7 +575,13 @@ sna_crtc_disable(xf86CrtcPtr crtc)
 	arg.fb_id = 0;
 	arg.mode_valid = 0;
 	(void)drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_SETCRTC, &arg);
-	sna_crtc->active = false;
+
+	sna_crtc_disable_shadow(sna, sna_crtc);
+
+	if (sna_crtc->bo) {
+		kgem_bo_destroy(&sna->kgem, sna_crtc->bo);
+		sna_crtc->bo = NULL;
+	}
 }
 
 static void
@@ -675,70 +749,267 @@ static void update_flush_interval(struct sna *sna)
 	       max_vrefresh, sna->vblank_interval));
 }
 
-static Bool
-sna_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
-			Rotation rotation, int x, int y)
+static bool use_shadow(struct sna *sna, xf86CrtcPtr crtc)
 {
+	RRTransformPtr transform;
+	PictTransform crtc_to_fb;
+	struct pict_f_transform f_crtc_to_fb, f_fb_to_crtc;
+	BoxRec b;
+
+	assert(sna->scrn->virtualX && sna->scrn->virtualY);
+
+	if (sna->scrn->virtualX > sna->mode.kmode->max_width ||
+	    sna->scrn->virtualY > sna->mode.kmode->max_height) {
+		DBG(("%s: framebuffer too large (%dx%d) > (%dx%d)\n",
+		    __FUNCTION__,
+		    sna->scrn->virtualX, sna->scrn->virtualY,
+		    sna->mode.kmode->max_width,
+		    sna->mode.kmode->max_height));
+		return true;
+	}
+
+	transform = NULL;
+	if (crtc->transformPresent)
+		transform = &crtc->transform;
+	if (RRTransformCompute(crtc->x, crtc->y,
+			       crtc->mode.HDisplay, crtc->mode.VDisplay,
+			       crtc->rotation, transform,
+			       &crtc_to_fb,
+			       &f_crtc_to_fb,
+			       &f_fb_to_crtc)) {
+		DBG(("%s: RandR transform present\n", __FUNCTION__));
+		return true;
+	}
+
+	/* And finally check that it is entirely visible */
+	b.x1 = b.y1 = 0;
+	b.x2 = crtc->mode.HDisplay;
+	b.y2 = crtc->mode.VDisplay;
+	pixman_f_transform_bounds(&f_crtc_to_fb, &b);
+	DBG(("%s? bounds (%d, %d), (%d, %d), framebufer %dx%d\n",
+	     __FUNCTION__, b.x1, b.y1, b.x2, b.y2,
+		 sna->scrn->virtualX, sna->scrn->virtualY));
+
+	if  (b.x1 < 0 || b.y1 < 0 ||
+	     b.x2 > sna->scrn->virtualX ||
+	     b.y2 > sna->scrn->virtualY) {
+		DBG(("%s: scanout is partly outside the framebuffer\n",
+		     __FUNCTION__));
+		return true;
+	}
+
+	return false;
+}
+
+static struct kgem_bo *sna_crtc_attach(xf86CrtcPtr crtc)
+{
+	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
 	ScrnInfoPtr scrn = crtc->scrn;
 	struct sna *sna = to_sna(scrn);
-	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
-	struct sna_mode *sna_mode = &sna->mode;
-	int saved_x, saved_y;
-	Rotation saved_rotation;
-	DisplayModeRec saved_mode;
+	struct kgem_bo *bo;
 
-	DBG(("%s(rotation=%d, x=%d, y=%d, mode=%dx%d@%d)\n",
-	     __FUNCTION__, rotation, x, y,
-	     mode->HDisplay, mode->VDisplay, mode->Clock));
+	if (use_shadow(sna, crtc)) {
+		if (!sna_crtc_enable_shadow(sna, sna_crtc))
+			return NULL;
 
-	DBG(("%s: current fb pixmap = %d, front is %lu\n",
-	     __FUNCTION__,
-	     sna_mode->fb_pixmap,
-	     sna->front->drawable.serialNumber));
+		DBG(("%s: attaching to per-crtc pixmap %dx%d\n",
+		     __FUNCTION__, crtc->mode.HDisplay, crtc->mode.VDisplay));
 
-	if (sna_mode->fb_pixmap != sna->front->drawable.serialNumber) {
-		kgem_submit(&sna->kgem);
-		sna_mode_remove_fb(sna);
+		bo = kgem_create_2d(&sna->kgem,
+				    crtc->mode.HDisplay, crtc->mode.VDisplay,
+				    scrn->bitsPerPixel,
+				    I915_TILING_X, CREATE_SCANOUT);
+		if (bo == NULL)
+			return NULL;
+
+		if (!get_fb(sna, bo, crtc->mode.HDisplay, crtc->mode.VDisplay)) {
+			kgem_bo_destroy(&sna->kgem, bo);
+			return NULL;
+		}
+
+		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) {
+			bo = kgem_create_2d(&sna->kgem,
+					    sna->scrn->virtualX,
+					    sna->scrn->virtualY,
+					    scrn->bitsPerPixel,
+					    I915_TILING_X,
+					    CREATE_SCANOUT);
+			if (bo == NULL)
+				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);
+		if (!get_fb(sna, bo, scrn->virtualX, scrn->virtualY))
+			return NULL;
+
+		return kgem_bo_reference(bo);
 	}
+}
 
-	if (sna_mode->fb_id == 0) {
-		struct kgem_bo *bo = sna_pixmap_pin(sna->front);
-		if (!bo)
-			return FALSE;
+static void sna_crtc_randr(xf86CrtcPtr crtc)
+{
+	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
+	struct pict_f_transform f_crtc_to_fb, f_fb_to_crtc;
+	PictTransform crtc_to_fb;
+	PictFilterPtr filter;
+	xFixed *params;
+	int nparams;
+	RRTransformPtr transform;
+
+	transform = NULL;
+	if (crtc->transformPresent)
+		transform = &crtc->transform;
+
+	RRTransformCompute(crtc->x, crtc->y,
+			   crtc->mode.HDisplay, crtc->mode.VDisplay,
+			   crtc->rotation, transform,
+			   &crtc_to_fb,
+			   &f_crtc_to_fb,
+			   &f_fb_to_crtc);
+
+	filter = NULL;
+	params = NULL;
+	nparams = 0;
+	if (sna_crtc->shadow) {
+#ifdef RANDR_12_INTERFACE
+		if (transform) {
+			if (transform->nparams) {
+				params = malloc(transform->nparams * sizeof(xFixed));
+				if (params) {
+					memcpy(params, transform->params,
+					       transform->nparams * sizeof(xFixed));
+					nparams = transform->nparams;
+					filter = transform->filter;
+				}
+			} else
+				filter = transform->filter;
+		}
+#endif
+		crtc->transform_in_use = TRUE;
+	} else
+		crtc->transform_in_use = FALSE;
 
-		/* XXX recreate the fb in case the size has changed? */
-		sna_mode->fb_id = get_fb(sna, bo,
-					 scrn->virtualX, scrn->virtualY);
-		if (sna_mode->fb_id == 0)
-			return FALSE;
+	crtc->crtc_to_framebuffer = crtc_to_fb;
+	crtc->f_crtc_to_framebuffer = f_crtc_to_fb;
+	crtc->f_framebuffer_to_crtc = f_fb_to_crtc;
 
-		DBG(("%s: handle %d attached to fb %d\n",
-		     __FUNCTION__, bo->handle, sna_mode->fb_id));
+	free(crtc->params);
+	crtc->params  = params;
+	crtc->nparams = nparams;
 
-		sna_mode->fb_pixmap = sna->front->drawable.serialNumber;
+	crtc->filter = filter;
+	if (filter) {
+		crtc->filter_width  = filter->width;
+		crtc->filter_height = filter->height;
+	} else {
+		crtc->filter_width  = 0;
+		crtc->filter_height = 0;
 	}
 
-	saved_mode = crtc->mode;
-	saved_x = crtc->x;
-	saved_y = crtc->y;
-	saved_rotation = crtc->rotation;
+	crtc->bounds.x1 = 0;
+	crtc->bounds.x2 = crtc->mode.HDisplay;
+	crtc->bounds.y1 = 0;
+	crtc->bounds.y2 = crtc->mode.VDisplay;
+	pixman_f_transform_bounds(&f_crtc_to_fb, &crtc->bounds);
+
+	DBG(("%s: transform? %d, bounds (%d, %d), (%d, %d)\n",
+	     __FUNCTION__, crtc->transform_in_use,
+	     crtc->bounds.x1, crtc->bounds.y1,
+	     crtc->bounds.x2, crtc->bounds.y2));
+}
+
+static void
+sna_crtc_damage(xf86CrtcPtr crtc)
+{
+	ScreenPtr screen = crtc->scrn->pScreen;
+	struct sna *sna = to_sna(crtc->scrn);
+	RegionRec region, *damage;
+
+	region.extents = crtc->bounds;
+	region.data = NULL;
 
-	crtc->mode = *mode;
-	crtc->x = x;
-	crtc->y = y;
-	crtc->rotation = rotation;
+	if (region.extents.x1 < 0)
+		region.extents.x1 = 0;
+	if (region.extents.y1 < 0)
+		region.extents.y1 = 0;
+	if (region.extents.x2 > screen->width)
+		region.extents.x2 = screen->width;
+	if (region.extents.y2 > screen->width)
+		region.extents.y2 = screen->height;
 
+	DBG(("%s: marking crtc %d as completely damaged (%d, %d), (%d, %d)\n",
+	     __FUNCTION__, to_sna_crtc(crtc)->id,
+	     region.extents.x1, region.extents.y1,
+	     region.extents.x2, region.extents.y2));
+
+	assert(sna->mode.shadow_damage && sna->mode.shadow_active);
+	damage = DamageRegion(sna->mode.shadow_damage);
+	RegionUnion(damage, damage, &region);
+}
+
+static Bool
+sna_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
+			Rotation rotation, int x, int y)
+{
+	ScrnInfoPtr scrn = crtc->scrn;
+	struct sna *sna = to_sna(scrn);
+	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
+	struct kgem_bo *saved_bo, *bo;
+	struct drm_mode_modeinfo saved_kmode;
+
+	DBG(("%s(crtc=%d [pipe=%d] rotation=%d, x=%d, y=%d, mode=%dx%d@%d)\n",
+	     __FUNCTION__, sna_crtc->id, sna_crtc->pipe, rotation, x, y,
+	     mode->HDisplay, mode->VDisplay, mode->Clock));
+
+	assert(mode->HDisplay <= sna->mode.kmode->max_width &&
+	       mode->VDisplay <= sna->mode.kmode->max_height);
+
+	/* Attach per-crtc pixmap or direct */
+	bo = sna_crtc_attach(crtc);
+	if (bo == NULL)
+		return FALSE;
+
+	saved_kmode = sna_crtc->kmode;
+	saved_bo = sna_crtc->bo;
+	sna_crtc->bo = bo;
 	mode_to_kmode(&sna_crtc->kmode, mode);
+
 	if (!sna_crtc_apply(crtc)) {
-		crtc->x = saved_x;
-		crtc->y = saved_y;
-		crtc->rotation = saved_rotation;
-		crtc->mode = saved_mode;
+		sna_crtc->bo = saved_bo;
+		sna_crtc->kmode = saved_kmode;
+		kgem_bo_destroy(&sna->kgem, bo);
 		return FALSE;
 	}
-	sna_mode_update(sna);
+	if (saved_bo)
+		kgem_bo_destroy(&sna->kgem, saved_bo);
 
 	update_flush_interval(sna);
+
+	sna_crtc_randr(crtc);
+	if (sna_crtc->shadow)
+		sna_crtc_damage(crtc);
+
 	return TRUE;
 }
 
@@ -748,8 +1019,18 @@ void sna_mode_adjust_frame(struct sna *sna, int x, int y)
 	xf86OutputPtr output = config->output[config->compat_output];
 	xf86CrtcPtr crtc = output->crtc;
 
-	if (crtc && crtc->enabled)
-		sna_crtc_set_mode_major(crtc, &crtc->mode, crtc->rotation, x, y);
+	if (crtc && crtc->enabled) {
+		int saved_x = crtc->x;
+		int saved_y = crtc->y;
+
+		crtc->x = x;
+		crtc->y = y;
+		if (!sna_crtc_set_mode_major(crtc, &crtc->mode,
+					     crtc->rotation, x, y)) {
+			crtc->x = saved_x;
+			crtc->y = saved_y;
+		}
+	}
 }
 
 static void
@@ -831,65 +1112,6 @@ sna_crtc_load_cursor_argb(xf86CrtcPtr crtc, CARD32 *image)
 	(void)drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GEM_PWRITE, &pwrite);
 }
 
-static void *
-sna_crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
-{
-	ScrnInfoPtr scrn = crtc->scrn;
-	struct sna *sna = to_sna(scrn);
-	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
-	PixmapPtr shadow;
-	struct kgem_bo *bo;
-
-	DBG(("%s(%d, %d)\n", __FUNCTION__, width, height));
-
-	shadow = scrn->pScreen->CreatePixmap(scrn->pScreen,
-					     width, height, scrn->depth,
-					     SNA_CREATE_FB);
-	if (!shadow)
-		return NULL;
-
-	bo = sna_pixmap_pin(shadow);
-	if (!bo) {
-		scrn->pScreen->DestroyPixmap(shadow);
-		return NULL;
-	}
-
-	sna_crtc->shadow_fb_id = get_fb(sna, bo, width, height);
-	if (sna_crtc->shadow_fb_id == 0) {
-		scrn->pScreen->DestroyPixmap(shadow);
-		return NULL;
-	}
-
-	DBG(("%s: attached handle %d to fb %d\n",
-	     __FUNCTION__, bo->handle, sna_crtc->shadow_fb_id));
-	return sna_crtc->shadow = shadow;
-}
-
-static PixmapPtr
-sna_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
-{
-	return data;
-}
-
-static void
-sna_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr pixmap, void *data)
-{
-	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
-
-	/* We may have not called shadow_create() on the data yet and
-	 * be cleaning up a NULL shadow_pixmap.
-	 */
-	pixmap = data;
-
-	DBG(("%s(fb=%d, handle=%d)\n", __FUNCTION__,
-	     sna_crtc->shadow_fb_id, sna_pixmap_get_bo(pixmap)->handle));
-
-	sna_crtc->shadow_fb_id = 0;
-
-	pixmap->drawable.pScreen->DestroyPixmap(pixmap);
-	sna_crtc->shadow = NULL;
-}
-
 static void
 sna_crtc_gamma_set(xf86CrtcPtr crtc,
 		       CARD16 *red, CARD16 *green, CARD16 *blue, int size)
@@ -924,9 +1146,6 @@ static const xf86CrtcFuncsRec sna_crtc_funcs = {
 	.show_cursor = sna_crtc_show_cursor,
 	.hide_cursor = sna_crtc_hide_cursor,
 	.load_cursor_argb = sna_crtc_load_cursor_argb,
-	.shadow_create = sna_crtc_shadow_create,
-	.shadow_allocate = sna_crtc_shadow_allocate,
-	.shadow_destroy = sna_crtc_shadow_destroy,
 	.gamma_set = sna_crtc_gamma_set,
 	.destroy = sna_crtc_destroy,
 };
@@ -986,7 +1205,7 @@ sna_crtc_init(ScrnInfoPtr scrn, struct sna_mode *mode, int num)
 	if (sna_crtc == NULL)
 		return;
 
-	sna_crtc->id = mode->mode_res->crtcs[num];
+	sna_crtc->id = mode->kmode->crtcs[num];
 
 	VG_CLEAR(get_pipe);
 	get_pipe.pipe = 0;
@@ -1061,17 +1280,29 @@ sna_output_detect(xf86OutputPtr output)
 }
 
 static Bool
-sna_output_mode_valid(xf86OutputPtr output, DisplayModePtr pModes)
+sna_output_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
 {
 	struct sna_output *sna_output = output->driver_private;
+	struct sna *sna = to_sna(output->scrn);
+
+	if (mode->HDisplay > sna->mode.kmode->max_width)
+		return MODE_VIRTUAL_X;
+	if (mode->VDisplay > sna->mode.kmode->max_height)
+		return MODE_VIRTUAL_Y;
+
+	/* Check that we can successfully pin this into the global GTT */
+	if ((kgem_can_create_2d(&sna->kgem,
+				mode->HDisplay, mode->VDisplay,
+				sna->scrn->bitsPerPixel) & KGEM_CAN_CREATE_GTT) == 0)
+		return MODE_MEM_VIRT;
 
 	/*
 	 * If the connector type is a panel, we will use the panel limit to
 	 * verfiy whether the mode is valid.
 	 */
 	if (sna_output->has_panel_limits) {
-		if (pModes->HDisplay > sna_output->panel_hdisplay ||
-		    pModes->VDisplay > sna_output->panel_vdisplay)
+		if (mode->HDisplay > sna_output->panel_hdisplay ||
+		    mode->VDisplay > sna_output->panel_vdisplay)
 			return MODE_PANEL;
 	}
 
@@ -1684,7 +1915,7 @@ sna_output_init(ScrnInfoPtr scrn, struct sna_mode *mode, int num)
 	char name[32];
 
 	koutput = drmModeGetConnector(sna->kgem.fd,
-				      mode->mode_res->connectors[num]);
+				      mode->kmode->connectors[num]);
 	if (!koutput)
 		return;
 
@@ -1713,7 +1944,7 @@ sna_output_init(ScrnInfoPtr scrn, struct sna_mode *mode, int num)
 	if (!sna_output)
 		goto cleanup_output;
 
-	sna_output->id = mode->mode_res->connectors[num];
+	sna_output->id = mode->kmode->connectors[num];
 	sna_output->mode_output = koutput;
 
 	output->mm_width = koutput->mmWidth;
@@ -1773,12 +2004,9 @@ sna_redirect_screen_pixmap(ScrnInfoPtr scrn, PixmapPtr old, PixmapPtr new)
 static Bool
 sna_crtc_resize(ScrnInfoPtr scrn, int width, int height)
 {
-	struct sna *sna = to_sna(scrn);
 	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
-	struct sna_mode *mode = &sna->mode;
-	PixmapPtr old_front;
-	uint32_t old_fb_id;
-	struct kgem_bo *bo;
+	struct sna *sna = to_sna(scrn);
+	PixmapPtr old_front, new_front;
 	int i;
 
 	DBG(("%s (%d, %d) -> (%d, %d)\n",
@@ -1791,32 +2019,27 @@ sna_crtc_resize(ScrnInfoPtr scrn, int width, int height)
 
 	assert(scrn->pScreen->GetScreenPixmap(scrn->pScreen) == sna->front);
 	assert(scrn->pScreen->GetWindowPixmap(scrn->pScreen->root) == sna->front);
+	DBG(("%s: creating new framebuffer %dx%d\n",
+	     __FUNCTION__, width, height));
 
-	kgem_submit(&sna->kgem);
-
-	old_fb_id = mode->fb_id;
 	old_front = sna->front;
-
-	sna->front = scrn->pScreen->CreatePixmap(scrn->pScreen,
+	new_front = scrn->pScreen->CreatePixmap(scrn->pScreen,
 						 width, height,
 						 scrn->depth,
 						 SNA_CREATE_FB);
-	if (!sna->front)
-		goto fail;
-
-	bo = sna_pixmap_pin(sna->front);
-	if (!bo)
-		goto fail;
-
-	assert(bo->delta == 0);
+	if (!new_front)
+		return FALSE;
 
-	mode->fb_id = get_fb(sna, bo, width, height);
-	if (mode->fb_id == 0)
-		goto fail;
+	for (i = 0; i < xf86_config->num_crtc; i++)
+		sna_crtc_disable_shadow(sna, to_sna_crtc(xf86_config->crtc[i]));
+	assert(sna->mode.shadow_active == 0);
+	assert(sna->mode.shadow_damage == NULL);
+	assert(sna->mode.shadow == NULL);
 
-	DBG(("%s: handle %d, pixmap serial %lu attached to fb %d\n",
-	     __FUNCTION__, bo->handle,
-	     sna->front->drawable.serialNumber, mode->fb_id));
+	sna->front = new_front;
+	scrn->virtualX = width;
+	scrn->virtualY = height;
+	scrn->displayWidth = width;
 
 	for (i = 0; i < xf86_config->num_crtc; i++) {
 		xf86CrtcPtr crtc = xf86_config->crtc[i];
@@ -1824,18 +2047,12 @@ sna_crtc_resize(ScrnInfoPtr scrn, int width, int height)
 		if (!crtc->enabled)
 			continue;
 
-		if (!sna_crtc_apply(crtc))
-			goto fail;
+		if (!sna_crtc_set_mode_major(crtc,
+					     &crtc->mode, crtc->rotation,
+					     crtc->x, crtc->y))
+			sna_crtc_disable(crtc);
 	}
-	sna_mode_update(sna);
 
-	kgem_bo_retire(&sna->kgem, bo);
-
-	scrn->virtualX = width;
-	scrn->virtualY = height;
-	scrn->displayWidth = bo->pitch / sna->mode.cpp;
-
-	sna->mode.fb_pixmap = sna->front->drawable.serialNumber;
 	sna_redirect_screen_pixmap(scrn, old_front, sna->front);
 	assert(scrn->pScreen->GetScreenPixmap(scrn->pScreen) == sna->front);
 	assert(scrn->pScreen->GetWindowPixmap(scrn->pScreen->root) == sna->front);
@@ -1843,20 +2060,14 @@ sna_crtc_resize(ScrnInfoPtr scrn, int width, int height)
 	scrn->pScreen->DestroyPixmap(old_front);
 
 	return TRUE;
-
-fail:
-	DBG(("%s: restoring original front pixmap and fb\n", __FUNCTION__));
-	mode->fb_id = old_fb_id;
-
-	if (sna->front)
-		scrn->pScreen->DestroyPixmap(sna->front);
-	sna->front = old_front;
-	return FALSE;
 }
 
-static int do_page_flip(struct sna *sna, void *data, int ref_crtc_hw_id)
+static int do_page_flip(struct sna *sna, struct kgem_bo *bo,
+			void *data, int ref_crtc_hw_id)
 {
 	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
+	int width = sna->scrn->virtualX;
+	int height = sna->scrn->virtualY;
 	int count = 0;
 	int i;
 
@@ -1871,36 +2082,40 @@ static int do_page_flip(struct sna *sna, void *data, int ref_crtc_hw_id)
 	 */
 	for (i = 0; i < config->num_crtc; i++) {
 		struct sna_crtc *crtc = config->crtc[i]->driver_private;
-		uintptr_t evdata;
+		struct drm_mode_crtc_page_flip arg;
 
-		DBG(("%s: crtc %d active? %d\n",__FUNCTION__, i,crtc->active));
-		if (!crtc->active)
+		DBG(("%s: crtc %d active? %d\n",
+		     __FUNCTION__, i, crtc->bo != NULL));
+		if (crtc->bo == NULL)
 			continue;
 
+		arg.crtc_id = crtc->id;
+		arg.fb_id = get_fb(sna, bo, width, height);
+		if (arg.fb_id == 0)
+			goto disable;
+
 		/* Only the reference crtc will finally deliver its page flip
 		 * completion event. All other crtc's events will be discarded.
 		 */
-		evdata = (uintptr_t)data;
-		evdata |= crtc->pipe == ref_crtc_hw_id;
+		arg.user_data = (uintptr_t)data;
+		arg.user_data |= crtc->pipe == ref_crtc_hw_id;
+		arg.flags = DRM_MODE_PAGE_FLIP_EVENT;
+		arg.reserved = 0;
 
 		DBG(("%s: crtc %d [ref? %d] --> fb %d\n",
 		     __FUNCTION__, crtc->id,
-		     crtc->pipe == ref_crtc_hw_id,
-		     sna->mode.fb_id));
-		if (drmModePageFlip(sna->kgem.fd,
-				    crtc->id,
-				    sna->mode.fb_id,
-				    DRM_MODE_PAGE_FLIP_EVENT,
-				    (void*)evdata)) {
-			int err = errno;
+		     crtc->pipe == ref_crtc_hw_id, arg.fb_id));
+		if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_PAGE_FLIP, &arg)) {
 			DBG(("%s: flip [fb=%d] on crtc %d [%d] failed - %d\n",
-			     __FUNCTION__, sna->mode.fb_id,
-			     i, crtc->id, err));
-			xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING,
-				   "flip queue failed: %s\n", strerror(err));
+			     __FUNCTION__, arg.fb_id, i, crtc->id, errno));
+disable:
+			sna_crtc_disable(config->crtc[i]);
 			continue;
 		}
 
+		kgem_bo_destroy(&sna->kgem, crtc->bo);
+		crtc->bo = kgem_bo_reference(bo);
+
 		count++;
 	}
 
@@ -1914,23 +2129,9 @@ sna_page_flip(struct sna *sna,
 	      int ref_crtc_hw_id,
 	      uint32_t *old_fb)
 {
-	ScrnInfoPtr scrn = sna->scrn;
-	struct sna_mode *mode = &sna->mode;
 	int count;
 
-	*old_fb = mode->fb_id;
-
-	/*
-	 * Create a new handle for the back buffer
-	 */
-	mode->fb_id = get_fb(sna, bo, scrn->virtualX, scrn->virtualY);
-	if (mode->fb_id == 0) {
-		mode->fb_id = *old_fb;
-		return 0;
-	}
-
-	DBG(("%s: handle %d attached to fb %d\n",
-	     __FUNCTION__, bo->handle, mode->fb_id));
+	DBG(("%s: handle %d attached\n", __FUNCTION__, bo->handle));
 
 	kgem_submit(&sna->kgem);
 
@@ -1943,10 +2144,8 @@ 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, data, ref_crtc_hw_id);
+	count = do_page_flip(sna, bo, data, ref_crtc_hw_id);
 	DBG(("%s: page flipped %d crtcs\n", __FUNCTION__, count));
-	if (count == 0)
-		mode->fb_id = *old_fb;
 
 	return count;
 }
@@ -1955,6 +2154,15 @@ static const xf86CrtcConfigFuncsRec sna_crtc_config_funcs = {
 	sna_crtc_resize
 };
 
+static void set_size_range(struct sna *sna)
+{
+	/* We lie slightly as we expect no single monitor to exceed the
+	 * crtc limits, so if the mode exceeds the scanout restrictions,
+	 * we will quietly convert that to per-crtc pixmaps.
+	 */
+	xf86CrtcSetSizeRange(sna->scrn, 320, 200, INT16_MAX, INT16_MAX);
+}
+
 Bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna)
 {
 	struct sna_mode *mode = &sna->mode;
@@ -1965,21 +2173,19 @@ Bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna)
 
 	xf86CrtcConfigInit(scrn, &sna_crtc_config_funcs);
 
-	mode->mode_res = drmModeGetResources(sna->kgem.fd);
-	if (!mode->mode_res) {
+	mode->kmode = drmModeGetResources(sna->kgem.fd);
+	if (!mode->kmode) {
 		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
 			   "failed to get resources: %s\n", strerror(errno));
 		return FALSE;
 	}
 
-	xf86CrtcSetSizeRange(scrn,
-			     320, 200,
-			     mode->mode_res->max_width,
-			     mode->mode_res->max_height);
-	for (i = 0; i < mode->mode_res->count_crtcs; i++)
+	set_size_range(sna);
+
+	for (i = 0; i < mode->kmode->count_crtcs; i++)
 		sna_crtc_init(scrn, mode, i);
 
-	for (i = 0; i < mode->mode_res->count_connectors; i++)
+	for (i = 0; i < mode->kmode->count_connectors; i++)
 		sna_output_init(scrn, mode, i);
 
 	xf86InitialConfiguration(scrn, TRUE);
@@ -1988,18 +2194,6 @@ Bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna)
 }
 
 void
-sna_mode_remove_fb(struct sna *sna)
-{
-	struct sna_mode *mode = &sna->mode;
-
-	DBG(("%s: deleting fb id %d for pixmap serial %d\n",
-	     __FUNCTION__, mode->fb_id,mode->fb_pixmap));
-
-	mode->fb_id = 0;
-	mode->fb_pixmap = 0;
-}
-
-void
 sna_mode_fini(struct sna *sna)
 {
 #if 0
@@ -2015,38 +2209,9 @@ sna_mode_fini(struct sna *sna)
 						   link)->output);
 	}
 #endif
-
-	sna_mode_remove_fb(sna);
-
-	/* mode->shadow_fb_id should have been destroyed already */
-}
-
-static void sna_crtc_box(xf86CrtcPtr crtc, BoxPtr crtc_box)
-{
-	if (crtc->enabled) {
-		crtc_box->x1 = crtc->x;
-		crtc_box->y1 = crtc->y;
-
-		switch (crtc->rotation & 0xf) {
-		default:
-			assert(0);
-		case RR_Rotate_0:
-		case RR_Rotate_180:
-			crtc_box->x2 = crtc->x + crtc->mode.HDisplay;
-			crtc_box->y2 = crtc->y + crtc->mode.VDisplay;
-			break;
-
-		case RR_Rotate_90:
-		case RR_Rotate_270:
-			crtc_box->x2 = crtc->x + crtc->mode.VDisplay;
-			crtc_box->y2 = crtc->y + crtc->mode.HDisplay;
-			break;
-		}
-	} else
-		crtc_box->x1 = crtc_box->x2 = crtc_box->y1 = crtc_box->y2 = 0;
 }
 
-static void sna_box_intersect(BoxPtr r, const BoxRec *a, const BoxRec *b)
+static bool sna_box_intersect(BoxPtr r, const BoxRec *a, const BoxRec *b)
 {
 	r->x1 = a->x1 > b->x1 ? a->x1 : b->x1;
 	r->x2 = a->x2 < b->x2 ? a->x2 : b->x2;
@@ -2057,8 +2222,7 @@ static void sna_box_intersect(BoxPtr r, const BoxRec *a, const BoxRec *b)
 	     a->x1, a->y1, a->x2, a->y2,
 	     b->x1, b->y1, b->x2, b->y2,
 	     r->x1, r->y1, r->x2, r->y2));
-	if (r->x1 >= r->x2 || r->y1 >= r->y2)
-		r->x1 = r->x2 = r->y1 = r->y2 = 0;
+	return r->x2 > r->x1 && r->y2 > r->y1;
 }
 
 static int sna_box_area(const BoxRec *box)
@@ -2074,13 +2238,11 @@ static int sna_box_area(const BoxRec *box)
 xf86CrtcPtr
 sna_covering_crtc(ScrnInfoPtr scrn,
 		  const BoxRec *box,
-		  xf86CrtcPtr desired,
-		  BoxPtr crtc_box_ret)
+		  xf86CrtcPtr desired)
 {
 	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
 	xf86CrtcPtr best_crtc;
 	int best_coverage, c;
-	BoxRec best_crtc_box;
 
 	/* If we do not own the VT, we do not own the CRTC either */
 	if (!scrn->vtSema)
@@ -2091,51 +2253,46 @@ sna_covering_crtc(ScrnInfoPtr scrn,
 
 	best_crtc = NULL;
 	best_coverage = 0;
-	best_crtc_box.x1 = 0;
-	best_crtc_box.x2 = 0;
-	best_crtc_box.y1 = 0;
-	best_crtc_box.y2 = 0;
 	for (c = 0; c < xf86_config->num_crtc; c++) {
 		xf86CrtcPtr crtc = xf86_config->crtc[c];
-		BoxRec crtc_box, cover_box;
+		BoxRec cover_box;
 		int coverage;
 
 		/* If the CRTC is off, treat it as not covering */
-		if (!sna_crtc_on(crtc)) {
+		if (to_sna_crtc(crtc)->bo == NULL) {
 			DBG(("%s: crtc %d off, skipping\n", __FUNCTION__, c));
 			continue;
 		}
 
-		sna_crtc_box(crtc, &crtc_box);
 		DBG(("%s: crtc %d: (%d, %d), (%d, %d)\n",
 		     __FUNCTION__, c,
-		     crtc_box.x1, crtc_box.y1,
-		     crtc_box.x2, crtc_box.y2));
+		     crtc->bounds.x1, crtc->bounds.y1,
+		     crtc->bounds.x2, crtc->bounds.y2));
+
+		if (!sna_box_intersect(&cover_box, &crtc->bounds, box))
+			continue;
 
-		sna_box_intersect(&cover_box, &crtc_box, box);
 		DBG(("%s: box instersects (%d, %d), (%d, %d) of crtc %d\n",
 		     __FUNCTION__,
 		     cover_box.x1, cover_box.y1,
 		     cover_box.x2, cover_box.y2,
 		     c));
-		coverage = sna_box_area(&cover_box);
-		DBG(("%s: box covers %d of crtc %d\n",
-		     __FUNCTION__, coverage, c));
-		if (coverage && crtc == desired) {
+		if (crtc == desired) {
 			DBG(("%s: box is on desired crtc [%p]\n",
 			     __FUNCTION__, crtc));
-			*crtc_box_ret = crtc_box;
 			return crtc;
 		}
+
+		coverage = sna_box_area(&cover_box);
+		DBG(("%s: box covers %d of crtc %d\n",
+		     __FUNCTION__, coverage, c));
 		if (coverage > best_coverage) {
-			best_crtc_box = crtc_box;
 			best_crtc = crtc;
 			best_coverage = coverage;
 		}
 	}
 	DBG(("%s: best crtc = %p, coverage = %d\n",
 	     __FUNCTION__, best_crtc, best_coverage));
-	*crtc_box_ret = best_crtc_box;
 	return best_crtc;
 }
 
@@ -2235,40 +2392,32 @@ sna_wait_for_scanline(struct sna *sna,
 		      xf86CrtcPtr crtc,
 		      const BoxRec *clip)
 {
-	pixman_box16_t box, crtc_box;
 	Bool full_height;
 	int y1, y2, pipe;
 
 	assert(crtc);
 	assert(sna_crtc_on(crtc));
-	assert(pixmap_is_scanout(pixmap));
+	assert(pixmap == sna->front);
 
 	/* XXX WAIT_EVENT is still causing hangs on SNB */
 	if (sna->kgem.gen >= 60)
 		return false;
 
-	sna_crtc_box(crtc, &crtc_box);
-	if (crtc->transform_in_use) {
-		box = *clip;
-		pixman_f_transform_bounds(&crtc->f_framebuffer_to_crtc, &box);
-		clip = &box;
-	}
-
 	/*
 	 * Make sure we don't wait for a scanline that will
 	 * never occur
 	 */
-	y1 = clip->y1 - crtc_box.y1;
+	y1 = clip->y1 - crtc->bounds.y1;
 	if (y1 < 0)
 		y1 = 0;
-	y2 = clip->y2 - crtc_box.y1;
-	if (y2 > crtc_box.y2 - crtc_box.y1)
-		y2 = crtc_box.y2 - crtc_box.y1;
+	y2 = clip->y2 - crtc->bounds.y1;
+	if (y2 > crtc->bounds.y2 - crtc->bounds.y1)
+		y2 = crtc->bounds.y2 - crtc->bounds.y1;
 	DBG(("%s: clipped range = %d, %d\n", __FUNCTION__, y1, y2));
 	if (y2 <= y1)
 		return false;
 
-	full_height = y1 == 0 && y2 == crtc_box.y2 - crtc_box.y1;
+	full_height = y1 == 0 && y2 == crtc->bounds.y2 - crtc->bounds.y1;
 
 	if (crtc->mode.Flags & V_INTERLACE) {
 		/* DSL count field lines */
@@ -2298,10 +2447,396 @@ void sna_mode_update(struct sna *sna)
 	/* Validate CRTC attachments */
 	for (i = 0; i < xf86_config->num_crtc; i++) {
 		xf86CrtcPtr crtc = xf86_config->crtc[i];
+		if (!crtc->active || !sna_crtc_is_bound(sna, crtc))
+			sna_crtc_disable(crtc);
+	}
+}
+
+static void
+sna_crtc_redisplay__fallback(xf86CrtcPtr crtc, RegionPtr region)
+{
+	struct sna *sna = to_sna(crtc->scrn);
+	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
+	ScreenPtr screen = sna->scrn->pScreen;
+	PictFormatPtr format;
+	PicturePtr src, dst;
+	PixmapPtr pixmap;
+	BoxPtr b;
+	int n, error;
+	void *ptr;
+
+	DBG(("%s: compositing transformed damage boxes\n", __FUNCTION__));
+
+	ptr = kgem_bo_map__gtt(&sna->kgem, sna_crtc->bo);
+	if (ptr == NULL)
+		return;
+
+	pixmap = fbCreatePixmap(screen, 0, 0, sna->front->drawable.depth, 0);
+	if (pixmap == NullPixmap)
+		return;
+
+	if (!screen->ModifyPixmapHeader(pixmap,
+					crtc->mode.HDisplay,
+					crtc->mode.VDisplay,
+					sna->front->drawable.depth,
+					sna->front->drawable.bitsPerPixel,
+					sna_crtc->bo->pitch, ptr))
+		goto free_pixmap;
+
+	error = sna_render_format_for_depth(sna->front->drawable.depth);
+	format = PictureMatchFormat(screen,
+				    PIXMAN_FORMAT_DEPTH(error), error);
+	if (format == NULL) {
+		DBG(("%s: can't find format for depth=%d [%08x]\n",
+		     __FUNCTION__, sna->front->drawable.depth,
+		     (int)sna_render_format_for_depth(sna->front->drawable.depth)));
+		goto free_pixmap;
+	}
+
+	src = CreatePicture(None, &sna->front->drawable, format,
+			    0, NULL, serverClient, &error);
+	if (!src)
+		goto free_pixmap;
+
+	error = SetPictureTransform(src, &crtc->crtc_to_framebuffer);
+	if (error)
+		goto free_src;
+
+	if (crtc->filter)
+		SetPicturePictFilter(src, crtc->filter,
+				     crtc->params, crtc->nparams);
+
+	dst = CreatePicture(None, &pixmap->drawable, format,
+			    0, NULL, serverClient, &error);
+	if (!dst)
+		goto free_src;
+
+	kgem_bo_sync__gtt(&sna->kgem, sna_crtc->bo);
+	n = REGION_NUM_RECTS(region);
+	b = REGION_RECTS(region);
+	do {
+		BoxRec box;
+
+		box = *b++;
+		box.x1 -= crtc->filter_width >> 1;
+		box.x2 += crtc->filter_width >> 1;
+		box.y1 -= crtc->filter_height >> 1;
+		box.y2 += crtc->filter_height >> 1;
+		pixman_f_transform_bounds(&crtc->f_framebuffer_to_crtc, & box);
+
+		DBG(("%s: (%d, %d)x(%d, %d) -> (%d, %d), (%d, %d)\n",
+		     __FUNCTION__,
+		     b[-1].x1, b[-1].y1, b[-1].x2-b[-1].x1, b[-1].y2-b[-1].y1,
+		     box.x1, box.y1, box.x2, box.y2));
+
+		fbComposite(PictOpSrc, src, NULL, dst,
+			    box.x1, box.y1,
+			    0, 0,
+			    box.x1, box.y1,
+			    box.x2 - box.x1, box.y2 - box.y1);
+	} while (--n);
+
+	FreePicture(dst, None);
+free_src:
+	FreePicture(src, None);
+free_pixmap:
+	screen->DestroyPixmap(pixmap);
+}
+
+static void
+sna_crtc_redisplay__composite(xf86CrtcPtr crtc, RegionPtr region)
+{
+	struct sna *sna = to_sna(crtc->scrn);
+	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
+	ScreenPtr screen = sna->scrn->pScreen;
+	struct sna_composite_op tmp;
+	PictFormatPtr format;
+	PicturePtr src, dst;
+	PixmapPtr pixmap;
+	BoxPtr b;
+	int n, error;
+
+	DBG(("%s: compositing transformed damage boxes\n", __FUNCTION__));
+
+	pixmap = sna_pixmap_create_unattached(screen,
+					      crtc->mode.HDisplay,
+					      crtc->mode.VDisplay,
+					      sna->front->drawable.depth);
+	if (pixmap == NullPixmap)
+		return;
+
+	if (!sna_pixmap_attach_to_bo(pixmap, sna_crtc->bo))
+		goto free_pixmap;
+
+	error = sna_render_format_for_depth(sna->front->drawable.depth);
+	format = PictureMatchFormat(screen,
+				    PIXMAN_FORMAT_DEPTH(error), error);
+	if (format == NULL) {
+		DBG(("%s: can't find format for depth=%d [%08x]\n",
+		     __FUNCTION__, sna->front->drawable.depth,
+		     (int)sna_render_format_for_depth(sna->front->drawable.depth)));
+		goto free_pixmap;
+	}
+
+	src = CreatePicture(None, &sna->front->drawable, format,
+			    0, NULL, serverClient, &error);
+	if (!src)
+		goto free_pixmap;
+
+	error = SetPictureTransform(src, &crtc->crtc_to_framebuffer);
+	if (error)
+		goto free_src;
+
+	if (crtc->filter)
+		SetPicturePictFilter(src, crtc->filter,
+				     crtc->params, crtc->nparams);
+
+	dst = CreatePicture(None, &pixmap->drawable, format,
+			    0, NULL, serverClient, &error);
+	if (!dst)
+		goto free_src;
+
+	if (!sna->render.composite(sna,
+				   PictOpSrc, src, NULL, dst,
+				   0, 0,
+				   0, 0,
+				   0, 0,
+				   0, 0,
+				   memset(&tmp, 0, sizeof(tmp)))) {
+		DBG(("%s: unsupported operation!\n", __FUNCTION__));
+		sna_crtc_redisplay__fallback(crtc, region);
+		goto free_dst;
+	}
+
+	n = REGION_NUM_RECTS(region);
+	b = REGION_RECTS(region);
+	do {
+		BoxRec box;
+
+		box = *b++;
+		box.x1 -= crtc->filter_width >> 1;
+		box.x2 += crtc->filter_width >> 1;
+		box.y1 -= crtc->filter_height >> 1;
+		box.y2 += crtc->filter_height >> 1;
+		pixman_f_transform_bounds(&crtc->f_framebuffer_to_crtc, & box);
+
+		DBG(("%s: (%d, %d)x(%d, %d) -> (%d, %d), (%d, %d)\n",
+		     __FUNCTION__,
+		     b[-1].x1, b[-1].y1, b[-1].x2-b[-1].x1, b[-1].y2-b[-1].y1,
+		     box.x1, box.y1, box.x2, box.y2));
+
+		tmp.box(sna, &tmp, &box);
+	} while (--n);
+	tmp.done(sna, &tmp);
+
+free_dst:
+	FreePicture(dst, None);
+free_src:
+	FreePicture(src, None);
+free_pixmap:
+	screen->DestroyPixmap(pixmap);
+}
+
+static void
+sna_crtc_redisplay(xf86CrtcPtr crtc, RegionPtr region)
+{
+	struct sna *sna = to_sna(crtc->scrn);
+	struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
+	int16_t tx, ty;
+
+	DBG(("%s: crtc %d [pipe=%d], damage (%d, %d), (%d, %d) x %d\n",
+	     __FUNCTION__, sna_crtc->id, sna_crtc->pipe,
+	     region->extents.x1, region->extents.y1,
+	     region->extents.x2, region->extents.y2,
+	     REGION_NUM_RECTS(region)));
+
+	assert(!wedged(sna));
+
+	if (crtc->filter == NULL &&
+	    sna_transform_is_integer_translation(&crtc->crtc_to_framebuffer,
+						 &tx, &ty)) {
+		PixmapRec tmp;
+
+		DBG(("%s: copy damage boxes\n", __FUNCTION__));
+
+		tmp.drawable.width = crtc->mode.HDisplay;
+		tmp.drawable.height = crtc->mode.VDisplay;
+		tmp.drawable.depth = sna->front->drawable.depth;
+		tmp.drawable.bitsPerPixel = sna->front->drawable.bitsPerPixel;
+
+		/* XXX for tear-free we may want to try copying to a back
+		 * and flipping.
+		 */
+
+		if (sna->render.copy_boxes(sna, GXcopy,
+					   sna->front, sna_pixmap_get_bo(sna->front), tx, ty,
+					   &tmp, sna_crtc->bo, 0, 0,
+					   REGION_RECTS(region), REGION_NUM_RECTS(region)))
+			return;
+	}
+
+	sna_crtc_redisplay__composite(crtc, region);
+}
+
+void sna_mode_redisplay(struct sna *sna)
+{
+	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(sna->scrn);
+	RegionPtr region;
+	int i;
+
+	if (!sna->mode.shadow_damage)
+		return;
+
+	DBG(("%s: posting shadow damage\n", __FUNCTION__));
+	assert(sna->mode.shadow_active);
+
+	region = DamageRegion(sna->mode.shadow_damage);
+	if (!RegionNotEmpty(region))
+		return;
+
+	if (!sna_pixmap_move_to_gpu(sna->front, MOVE_READ)) {
+		if (!sna_pixmap_move_to_cpu(sna->front, MOVE_READ))
+			return;
+
+		for (i = 0; i < config->num_crtc; i++) {
+			xf86CrtcPtr crtc = config->crtc[i];
+			struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
+			RegionRec damage;
+
+			if (!sna_crtc->shadow)
+				continue;
+
+			assert(crtc->enabled);
+			assert(crtc->transform_in_use);
+
+			damage.extents = crtc->bounds;
+			damage.data = NULL;
+			RegionIntersect(&damage, &damage, region);
+			if (RegionNotEmpty(&damage))
+				sna_crtc_redisplay__fallback(crtc, &damage);
+			RegionUninit(&damage);
+		}
+
+		RegionEmpty(region);
+		return;
+	}
+
+	for (i = 0; i < config->num_crtc; i++) {
+		xf86CrtcPtr crtc = config->crtc[i];
 		struct sna_crtc *sna_crtc = to_sna_crtc(crtc);
-		if (crtc->enabled)
-			sna_crtc->active = sna_crtc_is_bound(sna, crtc);
-		else
-			sna_crtc->active = false;
+		RegionRec damage;
+
+		if (!sna_crtc->shadow || sna_crtc->bo == sna->mode.shadow)
+			continue;
+
+		assert(crtc->enabled);
+		assert(crtc->transform_in_use);
+
+		damage.extents = crtc->bounds;
+		damage.data = NULL;
+		RegionIntersect(&damage, &damage, region);
+		if (RegionNotEmpty(&damage))
+			sna_crtc_redisplay(crtc, &damage);
+		RegionUninit(&damage);
+	}
+
+	if (!sna->mode.shadow) {
+		kgem_submit(&sna->kgem);
+		RegionEmpty(region);
+		return;
+	}
+
+	if (sna->mode.shadow_flip == 0) {
+		struct kgem_bo *new = sna_pixmap_get_bo(sna->front);
+		struct kgem_bo *old = sna->mode.shadow;
+
+		DBG(("%s: flipping tear-free outputs\n", __FUNCTION__));
+		kgem_bo_submit(&sna->kgem, new);
+
+		for (i = 0; i < config->num_crtc; i++) {
+			struct sna_crtc *crtc = config->crtc[i]->driver_private;
+			struct drm_mode_crtc_page_flip arg;
+
+			DBG(("%s: crtc %d active? %d\n",
+			     __FUNCTION__, i, crtc->bo != NULL));
+			if (crtc->bo != old)
+				continue;
+
+			arg.crtc_id = crtc->id;
+			arg.fb_id = get_fb(sna, new,
+					   sna->scrn->virtualX,
+					   sna->scrn->virtualY);
+			if (arg.fb_id == 0)
+				goto disable;
+
+			/* Only the reference crtc will finally deliver its page flip
+			 * completion event. All other crtc's events will be discarded.
+			 */
+			arg.user_data = 0;
+			arg.flags = DRM_MODE_PAGE_FLIP_EVENT;
+			arg.reserved = 0;
+
+			if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_PAGE_FLIP, &arg)) {
+				DBG(("%s: flip [fb=%d] on crtc %d [%d] failed - %d\n",
+				     __FUNCTION__, arg.fb_id, i, crtc->id, errno));
+disable:
+				sna_crtc_disable(config->crtc[i]);
+				continue;
+			}
+
+			kgem_bo_destroy(&sna->kgem, old);
+			crtc->bo = kgem_bo_reference(new);
+			sna->mode.shadow_flip++;
+		}
+
+		/* XXX only works if the kernel stalls fwrites to the current
+		 * scanout whilst the flip is pending
+		 */
+		(void)sna->render.copy_boxes(sna, GXcopy,
+					     sna->front, new, 0, 0,
+					     sna->front, old, 0, 0,
+					     REGION_RECTS(region),
+					     REGION_NUM_RECTS(region));
+
+		sna_pixmap(sna->front)->gpu_bo = old;
+		sna->mode.shadow = new;
+
+		new->flush = old->flush;
+
+		RegionEmpty(region);
+	}
+}
+
+void sna_mode_wakeup(struct sna *sna)
+{
+	char buffer[1024];
+	int len, i;
+
+	/* The DRM read semantics guarantees that we always get only
+	 * complete events.
+	 */
+	len = read(sna->kgem.fd, buffer, sizeof (buffer));
+	if (len < (int)sizeof(struct drm_event))
+		return;
+
+	DBG(("%s: len=%d\n", __FUNCTION__, len));
+
+	i = 0;
+	while (i < len) {
+		struct drm_event *e = (struct drm_event *)&buffer[i];
+		switch (e->type) {
+		case DRM_EVENT_VBLANK:
+			sna_dri_vblank_handler(sna, (struct drm_event_vblank *)e);
+			break;
+		case DRM_EVENT_FLIP_COMPLETE:
+			if (((struct drm_event_vblank *)e)->user_data)
+				sna_dri_page_flip_handler(sna, (struct drm_event_vblank *)e);
+			else
+				sna->mode.shadow_flip--;
+			break;
+		default:
+			break;
+		}
+		i += e->length;
 	}
 }
diff --git a/src/sna/sna_dri.c b/src/sna/sna_dri.c
index 46a43e9..7cf1d1c 100644
--- a/src/sna/sna_dri.c
+++ b/src/sna/sna_dri.c
@@ -73,7 +73,6 @@ struct sna_dri_private {
 };
 
 struct sna_dri_frame_event {
-	struct sna *sna;
 	XID drawable_id;
 	ClientPtr client;
 	enum frame_event_type type;
@@ -107,9 +106,9 @@ struct sna_dri_frame_event {
 static DevPrivateKeyRec sna_client_key;
 
 static inline struct sna_dri_frame_event *
-to_frame_event(void *data)
+to_frame_event(uintptr_t  data)
 {
-	 return (struct sna_dri_frame_event *)((uintptr_t)data & ~1);
+	 return (struct sna_dri_frame_event *)(data & ~1);
 }
 
 static inline struct sna_dri_private *
@@ -316,7 +315,7 @@ static void _sna_dri_destroy_buffer(struct sna *sna, DRI2Buffer2Ptr buffer)
 	if (buffer == NULL)
 		return;
 
-	DBG(("%s: %p [handle=%d] -- refcnt=%d, pixmap=%d\n",
+	DBG(("%s: %p [handle=%d] -- refcnt=%d, pixmap=%ld\n",
 	     __FUNCTION__, buffer, private->bo->handle, private->refcnt,
 	     private->pixmap ? private->pixmap->drawable.serialNumber : 0));
 
@@ -458,12 +457,8 @@ sna_dri_copy_to_front(struct sna *sna, DrawablePtr draw, RegionPtr region,
 			return NULL;
 		}
 
-		if (pixmap == sna->front && sync &&
-		    (sna->flags & SNA_NO_WAIT) == 0) {
-			BoxRec crtc_box;
-
-			crtc = sna_covering_crtc(sna->scrn, &region->extents,
-						 NULL, &crtc_box);
+		if (sync && sna_pixmap_is_scanout(sna, pixmap)) {
+			crtc = sna_covering_crtc(sna->scrn, &region->extents, NULL);
 			if (crtc)
 				flush = sna_wait_for_scanline(sna, pixmap, crtc,
 							      &region->extents);
@@ -707,8 +702,8 @@ static int
 sna_dri_get_pipe(DrawablePtr pDraw)
 {
 	ScrnInfoPtr pScrn = xf86ScreenToScrn(pDraw->pScreen);
-	BoxRec box, crtcbox;
 	xf86CrtcPtr crtc;
+	BoxRec box;
 	int pipe;
 
 	if (pDraw->type == DRAWABLE_PIXMAP)
@@ -719,18 +714,15 @@ sna_dri_get_pipe(DrawablePtr pDraw)
 	box.x2 = box.x1 + pDraw->width;
 	box.y2 = box.y1 + pDraw->height;
 
-	crtc = sna_covering_crtc(pScrn, &box, NULL, &crtcbox);
+	crtc = sna_covering_crtc(pScrn, &box, NULL);
 
 	/* Make sure the CRTC is valid and this is the real front buffer */
 	pipe = -1;
-	if (crtc != NULL && !crtc->rotatedData)
+	if (crtc != NULL)
 		pipe = sna_crtc_to_pipe(crtc);
 
-	DBG(("%s(box=((%d, %d), (%d, %d)), crtcbox=((%d, %d), (%d, %d)), pipe=%d)\n",
-	     __FUNCTION__,
-	     box.x1, box.y1, box.x2, box.y2,
-	     crtcbox.x1, crtcbox.y1, crtcbox.x2, crtcbox.y2,
-	     pipe));
+	DBG(("%s(box=((%d, %d), (%d, %d)), pipe=%d)\n",
+	     __FUNCTION__, box.x1, box.y1, box.x2, box.y2, pipe));
 
 	return pipe;
 }
@@ -882,7 +874,8 @@ sna_dri_frame_event_release_bo(struct kgem *kgem, struct kgem_bo *bo)
 }
 
 static void
-sna_dri_frame_event_info_free(struct sna_dri_frame_event *info)
+sna_dri_frame_event_info_free(struct sna *sna,
+			      struct sna_dri_frame_event *info)
 {
 	DBG(("%s: del[%p] (%p, %ld)\n", __FUNCTION__,
 	     info, info->client, (long)info->drawable_id));
@@ -890,23 +883,20 @@ sna_dri_frame_event_info_free(struct sna_dri_frame_event *info)
 	list_del(&info->client_resource);
 	list_del(&info->drawable_resource);
 
-	_sna_dri_destroy_buffer(info->sna, info->front);
-	_sna_dri_destroy_buffer(info->sna, info->back);
+	_sna_dri_destroy_buffer(sna, info->front);
+	_sna_dri_destroy_buffer(sna, info->back);
 
 	if (info->old_front.bo)
-		sna_dri_frame_event_release_bo(&info->sna->kgem,
-					       info->old_front.bo);
+		sna_dri_frame_event_release_bo(&sna->kgem, info->old_front.bo);
 
 	if (info->next_front.bo)
-		sna_dri_frame_event_release_bo(&info->sna->kgem,
-					       info->next_front.bo);
+		sna_dri_frame_event_release_bo(&sna->kgem, info->next_front.bo);
 
 	if (info->cache.bo)
-		sna_dri_frame_event_release_bo(&info->sna->kgem,
-					       info->cache.bo);
+		sna_dri_frame_event_release_bo(&sna->kgem, info->cache.bo);
 
 	if (info->bo)
-		kgem_bo_destroy(&info->sna->kgem, info->bo);
+		kgem_bo_destroy(&sna->kgem, info->bo);
 
 	free(info);
 }
@@ -974,7 +964,7 @@ can_flip(struct sna * sna,
 		return FALSE;
 	}
 
-	if (sna->shadow) {
+	if (sna->mode.shadow_active) {
 		DBG(("%s: no, shadow enabled\n", __FUNCTION__));
 		return FALSE;
 	}
@@ -1047,14 +1037,10 @@ inline static uint32_t pipe_select(int pipe)
 		return 0;
 }
 
-static void sna_dri_vblank_handle(int fd,
-				  unsigned int frame, unsigned int tv_sec,
-				  unsigned int tv_usec,
-				  void *data)
+void sna_dri_vblank_handler(struct sna *sna, struct drm_event_vblank *event)
 {
-	struct sna_dri_frame_event *info = data;
+	struct sna_dri_frame_event *info = (void *)(uintptr_t)event->user_data;
 	DrawablePtr draw;
-	struct sna *sna;
 	int status;
 
 	DBG(("%s(id=%d, type=%d)\n", __FUNCTION__,
@@ -1069,8 +1055,6 @@ static void sna_dri_vblank_handle(int fd,
 	if (status != Success)
 		goto done;
 
-	sna = to_sna_from_drawable(draw);
-
 	switch (info->type) {
 	case DRI2_FLIP:
 		/* If we can still flip... */
@@ -1091,7 +1075,8 @@ static void sna_dri_vblank_handle(int fd,
 		/* fall through to SwapComplete */
 	case DRI2_SWAP_THROTTLE:
 		DBG(("%s: %d complete, frame=%d tv=%d.%06d\n",
-		     __FUNCTION__, info->type, frame, tv_sec, tv_usec));
+		     __FUNCTION__, info->type,
+		     event->sequence, event->tv_sec, event->tv_usec));
 
 		if (info->bo && kgem_bo_is_busy(info->bo)) {
 			kgem_retire(&sna->kgem);
@@ -1111,8 +1096,8 @@ static void sna_dri_vblank_handle(int fd,
 		}
 
 		DRI2SwapComplete(info->client,
-				 draw, frame,
-				 tv_sec, tv_usec,
+				 draw, event->sequence,
+				 event->tv_sec, event->tv_usec,
 				 DRI2_BLIT_COMPLETE,
 				 info->client ? info->event_complete : NULL,
 				 info->event_data);
@@ -1121,7 +1106,9 @@ static void sna_dri_vblank_handle(int fd,
 	case DRI2_WAITMSC:
 		if (info->client)
 			DRI2WaitMSCComplete(info->client, draw,
-					    frame, tv_sec, tv_usec);
+					    event->sequence,
+					    event->tv_sec,
+					    event->tv_usec);
 		break;
 	default:
 		xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING,
@@ -1131,7 +1118,7 @@ static void sna_dri_vblank_handle(int fd,
 	}
 
 done:
-	sna_dri_frame_event_info_free(info);
+	sna_dri_frame_event_info_free(sna, info);
 }
 
 static int
@@ -1222,7 +1209,7 @@ static void sna_dri_flip_event(struct sna *sna,
 					 flip->event_data);
 		}
 
-		sna_dri_frame_event_info_free(flip);
+		sna_dri_frame_event_info_free(sna, flip);
 		break;
 
 	case DRI2_FLIP_THROTTLE:
@@ -1251,10 +1238,10 @@ static void sna_dri_flip_event(struct sna *sna,
 						DRI2_EXCHANGE_COMPLETE,
 						flip->client ? flip->event_complete : NULL,
 						flip->event_data);
-				sna_dri_frame_event_info_free(flip);
+				sna_dri_frame_event_info_free(sna, flip);
 			}
 		} else
-			sna_dri_frame_event_info_free(flip);
+			sna_dri_frame_event_info_free(sna, flip);
 		break;
 
 #if USE_ASYNC_SWAP && DRI2INFOREC_VERSION >= 7
@@ -1298,7 +1285,7 @@ finish_async_flip:
 
 			DBG(("%s: async flip completed\n", __FUNCTION__));
 			sna->dri.flip_pending = NULL;
-			sna_dri_frame_event_info_free(flip);
+			sna_dri_frame_event_info_free(fsna, lip);
 		}
 		break;
 #endif
@@ -1311,26 +1298,26 @@ finish_async_flip:
 	}
 }
 
-static void
-sna_dri_page_flip_handler(int fd, unsigned int frame, unsigned int tv_sec,
-			  unsigned int tv_usec, void *data)
+void
+sna_dri_page_flip_handler(struct sna *sna,
+			  struct drm_event_vblank *event)
 {
-	struct sna_dri_frame_event *info = to_frame_event(data);
+	struct sna_dri_frame_event *info = to_frame_event(event->user_data);
 
 	DBG(("%s: pending flip_count=%d\n", __FUNCTION__, info->count));
 
 	/* Is this the event whose info shall be delivered to higher level? */
-	if ((uintptr_t)data & 1) {
+	if (event->user_data & 1) {
 		/* Yes: Cache msc, ust for later delivery. */
-		info->fe_frame = frame;
-		info->fe_tv_sec = tv_sec;
-		info->fe_tv_usec = tv_usec;
+		info->fe_frame = event->sequence;
+		info->fe_tv_sec = event->tv_sec;
+		info->fe_tv_usec = event->tv_usec;
 	}
 
 	if (--info->count)
 		return;
 
-	sna_dri_flip_event(info->sna, info);
+	sna_dri_flip_event(sna, info);
 }
 
 static int
@@ -1387,7 +1374,6 @@ sna_dri_schedule_flip(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 
 		info->type = type;
 
-		info->sna = sna;
 		info->drawable_id = draw->id;
 		info->client = client;
 		info->event_complete = func;
@@ -1407,7 +1393,7 @@ sna_dri_schedule_flip(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 
 		if (!sna_dri_page_flip(sna, info)) {
 			DBG(("%s: failed to queue page flip\n", __FUNCTION__));
-			sna_dri_frame_event_info_free(info);
+			sna_dri_frame_event_info_free(sna, info);
 			return FALSE;
 		}
 
@@ -1431,7 +1417,6 @@ sna_dri_schedule_flip(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 		if (info == NULL)
 			return FALSE;
 
-		info->sna = sna;
 		info->drawable_id = draw->id;
 		info->client = client;
 		info->event_complete = func;
@@ -1454,7 +1439,7 @@ sna_dri_schedule_flip(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 		vbl.request.type = DRM_VBLANK_RELATIVE | pipe_select(pipe);
 		vbl.request.sequence = 0;
 		if (sna_wait_vblank(sna, &vbl)) {
-			sna_dri_frame_event_info_free(info);
+			sna_dri_frame_event_info_free(sna, info);
 			return FALSE;
 		}
 
@@ -1509,7 +1494,7 @@ sna_dri_schedule_flip(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 		vbl.request.sequence -= 1;
 		vbl.request.signal = (unsigned long)info;
 		if (sna_wait_vblank(sna, &vbl)) {
-			sna_dri_frame_event_info_free(info);
+			sna_dri_frame_event_info_free(sna, info);
 			return FALSE;
 		}
 
@@ -1588,7 +1573,6 @@ sna_dri_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	if (!info)
 		goto blit_fallback;
 
-	info->sna = sna;
 	info->drawable_id = draw->id;
 	info->client = client;
 	info->event_complete = func;
@@ -1629,7 +1613,7 @@ sna_dri_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 				return TRUE;
 		}
 
-		sna_dri_frame_event_info_free(info);
+		sna_dri_frame_event_info_free(sna, info);
 		DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data);
 		return TRUE;
 	}
@@ -1713,7 +1697,7 @@ blit_fallback:
 			      get_private(back)->bo,
 			      false);
 	if (info)
-		sna_dri_frame_event_info_free(info);
+		sna_dri_frame_event_info_free(sna, info);
 	DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data);
 	*target_msc = 0; /* offscreen, so zero out target vblank count */
 	return TRUE;
@@ -1812,7 +1796,7 @@ blit:
 		sna_dri_reference_buffer(back);
 
 		if (!sna_dri_page_flip(sna, info)) {
-			sna_dri_frame_event_info_free(info);
+			sna_dri_frame_event_info_free(sna, info);
 			goto blit;
 		}
 
@@ -1952,7 +1936,6 @@ sna_dri_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 	if (!info)
 		goto out_complete;
 
-	info->sna = sna;
 	info->drawable_id = draw->id;
 	info->client = client;
 	info->type = DRI2_WAITMSC;
@@ -2009,7 +1992,7 @@ sna_dri_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 	return TRUE;
 
 out_free_info:
-	sna_dri_frame_event_info_free(info);
+	sna_dri_frame_event_info_free(sna, info);
 out_complete:
 	DRI2WaitMSCComplete(client, draw, target_msc, 0, 0);
 	return TRUE;
@@ -2093,20 +2076,6 @@ Bool sna_dri_open(struct sna *sna, ScreenPtr screen)
 	return DRI2ScreenInit(screen, &info);
 }
 
-void
-sna_dri_wakeup(struct sna *sna)
-{
-	drmEventContext ctx;
-
-	DBG(("%s\n", __FUNCTION__));
-
-	ctx.version = DRM_EVENT_CONTEXT_VERSION;
-	ctx.vblank_handler = sna_dri_vblank_handle;
-	ctx.page_flip_handler = sna_dri_page_flip_handler;
-
-	drmHandleEvent(sna->kgem.fd, &ctx);
-}
-
 void sna_dri_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 a02ff76..3b3b93f 100644
--- a/src/sna/sna_driver.c
+++ b/src/sna/sna_driver.c
@@ -47,6 +47,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 #include <xf86cmap.h>
 #include <xf86drm.h>
+#include <xf86RandR12.h>
 #include <micmap.h>
 #include <fb.h>
 
@@ -492,8 +493,6 @@ static Bool sna_pre_init(ScrnInfoPtr scrn, int flags)
 	if (!xf86SetDefaultVisual(scrn, -1))
 		return FALSE;
 
-	sna->mode.cpp = scrn->bitsPerPixel / 8;
-
 	if (!sna_get_early_options(scrn))
 		return FALSE;
 
@@ -527,7 +526,10 @@ static Bool sna_pre_init(ScrnInfoPtr scrn, int flags)
 		sna->flags |= SNA_NO_DELAYED_FLUSH;
 	if (!xf86ReturnOptValBool(sna->Options, OPTION_SWAPBUFFERS_WAIT, TRUE))
 		sna->flags |= SNA_NO_WAIT;
-	if (!has_pageflipping(sna))
+	if (has_pageflipping(sna)) {
+		if (xf86ReturnOptValBool(sna->Options, OPTION_TEAR_FREE, FALSE))
+			sna->flags |= SNA_TEAR_FREE;
+	} else
 		sna->flags |= SNA_NO_FLIP;
 
 	xf86DrvMsg(scrn->scrnIndex, X_CONFIG, "Framebuffer %s\n",
@@ -540,6 +542,8 @@ static Bool sna_pre_init(ScrnInfoPtr scrn, int flags)
 		   sna->flags & SNA_NO_THROTTLE ? "dis" : "en");
 	xf86DrvMsg(scrn->scrnIndex, X_CONFIG, "Delayed flush %sabled\n",
 		   sna->flags & SNA_NO_DELAYED_FLUSH ? "dis" : "en");
+	xf86DrvMsg(scrn->scrnIndex, X_CONFIG, "\"Tear free\" %sabled\n",
+		   sna->flags & SNA_TEAR_FREE ? "en" : "dis");
 
 	if (!sna_mode_pre_init(scrn, sna)) {
 		PreInitCleanup(scrn);
@@ -608,7 +612,7 @@ sna_wakeup_handler(WAKEUPHANDLER_ARGS_DECL)
 	sna_accel_wakeup_handler(sna, read_mask);
 
 	if (FD_ISSET(sna->kgem.fd, (fd_set*)read_mask))
-		sna_dri_wakeup(sna);
+		sna_mode_wakeup(sna);
 }
 
 #if HAVE_UDEV
@@ -735,7 +739,6 @@ static void sna_leave_vt(VT_FUNC_ARGS_DECL)
 
 	xf86RotateFreeShadow(scrn);
 	xf86_hide_cursors(scrn);
-	sna_mode_remove_fb(sna);
 
 	ret = drmDropMaster(sna->kgem.fd);
 	if (ret)
@@ -747,7 +750,7 @@ static void sna_leave_vt(VT_FUNC_ARGS_DECL)
  * check that the fd is readable before attempting to read the next
  * event from drm.
  */
-static Bool sna_dri_has_pending_events(struct sna *sna)
+static Bool sna_mode_has_pending_events(struct sna *sna)
 {
 	struct pollfd pfd;
 	pfd.fd = sna->kgem.fd;
@@ -767,8 +770,8 @@ static Bool sna_close_screen(CLOSE_SCREEN_ARGS_DECL)
 #endif
 
 	/* drain the event queues */
-	if (sna_dri_has_pending_events(sna))
-		sna_dri_wakeup(sna);
+	if (sna_mode_has_pending_events(sna))
+		sna_mode_wakeup(sna);
 
 	if (scrn->vtSema == TRUE)
 		sna_leave_vt(VT_FUNC_ARGS(0));
@@ -788,7 +791,6 @@ static Bool sna_close_screen(CLOSE_SCREEN_ARGS_DECL)
 		sna->directRenderingOpen = FALSE;
 	}
 
-	sna_mode_remove_fb(sna);
 	if (sna->front) {
 		screen->DestroyPixmap(sna->front);
 		sna->front = NULL;
@@ -799,6 +801,20 @@ static Bool sna_close_screen(CLOSE_SCREEN_ARGS_DECL)
 	return TRUE;
 }
 
+static void sna_mode_set(ScrnInfoPtr scrn)
+{
+	struct sna *sna = to_sna(scrn);
+
+	DBG(("%s\n", __FUNCTION__));
+
+	if (sna->ModeSet) {
+		scrn->ModeSet = sna->ModeSet;
+		scrn->ModeSet(scrn);
+		scrn->ModeSet = sna_mode_set;
+	}
+	sna_mode_update(sna);
+}
+
 static Bool
 sna_register_all_privates(void)
 {
@@ -917,9 +933,17 @@ sna_screen_init(SCREEN_INIT_ARGS_DECL)
 	screen->CloseScreen = sna_close_screen;
 	screen->CreateScreenResources = sna_create_screen_resources;
 
+	sna->ModeSet = scrn->ModeSet;
+	scrn->ModeSet = sna_mode_set;
+
 	if (!xf86CrtcScreenInit(screen))
 		return FALSE;
 
+	xf86RandR12SetRotations(screen,
+				RR_Rotate_0 | RR_Rotate_90 | RR_Rotate_180 | RR_Rotate_270 |
+				RR_Reflect_X | RR_Reflect_Y);
+	xf86RandR12SetTransformSupport(screen, TRUE);
+
 	if (!miCreateDefColormap(screen))
 		return FALSE;
 
diff --git a/src/sna/sna_render.c b/src/sna/sna_render.c
index 6ddf6f3..a072994 100644
--- a/src/sna/sna_render.c
+++ b/src/sna/sna_render.c
@@ -64,15 +64,15 @@ CARD32
 sna_render_format_for_depth(int depth)
 {
 	switch (depth) {
-	case 1: return PICT_a1;
-	case 4: return PICT_a4;
-	case 8: return PICT_a8;
-	case 15: return PICT_a1r5g5b5;
-	case 16: return PICT_r5g6b5;
-	case 30: return PICT_a2r10g10b10;
+	case 1: return PIXMAN_a1;
+	case 4: return PIXMAN_a4;
+	case 8: return PIXMAN_a8;
+	case 15: return PIXMAN_a1r5g5b5;
+	case 16: return PIXMAN_r5g6b5;
+	case 30: return PIXMAN_a2r10g10b10;
 	default: assert(0);
 	case 24:
-	case 32: return PICT_a8r8g8b8;
+	case 32: return PIXMAN_a8r8g8b8;
 	}
 }
 
diff --git a/src/sna/sna_video.c b/src/sna/sna_video.c
index 08b848b..6ad81c3 100644
--- a/src/sna/sna_video.c
+++ b/src/sna/sna_video.c
@@ -143,7 +143,6 @@ sna_video_clip_helper(ScrnInfoPtr scrn,
 	Bool ret;
 	RegionRec crtc_region_local;
 	RegionPtr crtc_region = reg;
-	BoxRec crtc_box;
 	INT32 x1, x2, y1, y2;
 	xf86CrtcPtr crtc;
 
@@ -161,11 +160,12 @@ sna_video_clip_helper(ScrnInfoPtr scrn,
 	 * For overlay video, compute the relevant CRTC and
 	 * clip video to that
 	 */
-	crtc = sna_covering_crtc(scrn, dst, video->desired_crtc, &crtc_box);
+	crtc = sna_covering_crtc(scrn, dst, video->desired_crtc);
 
 	/* For textured video, we don't actually want to clip at all. */
 	if (crtc && !video->textured) {
-		RegionInit(&crtc_region_local, &crtc_box, 0);
+		crtc_region_local.extents = crtc->bounds;
+		crtc_region_local.data = NULL;
 		crtc_region = &crtc_region_local;
 		RegionIntersect(crtc_region, crtc_region, reg);
 	}
diff --git a/src/sna/sna_video_textured.c b/src/sna/sna_video_textured.c
index 33f3f71..1b3b3af 100644
--- a/src/sna/sna_video_textured.c
+++ b/src/sna/sna_video_textured.c
@@ -281,7 +281,7 @@ sna_video_textured_put_image(ScrnInfoPtr scrn,
 	}
 
 	if (crtc && video->SyncToVblank != 0 &&
-	    pixmap == sna->front && !sna->shadow)
+	    sna_pixmap_is_scanout(sna, pixmap))
 		flush = sna_wait_for_scanline(sna, pixmap, crtc,
 					      &clip->extents);
 
commit e8b090902e788257610374deae659f01a91888f3
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Jun 22 22:01:37 2012 +0100

    sna/gen3+: Remove stale assertions for cached vbo
    
    Following the previous commit, we reset the vbo when it becomes idle
    rather than discard it. As such, the assertions to check that we are
    discarding the vbo are now bogus.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/gen3_render.c b/src/sna/gen3_render.c
index b66e0e0..63bbd76 100644
--- a/src/sna/gen3_render.c
+++ b/src/sna/gen3_render.c
@@ -1678,12 +1678,8 @@ static void gen3_vertex_close(struct sna *sna)
 	     __FUNCTION__, sna->render.vertex_used, sna->render.vertex_size,
 	     sna->render.vbo ? sna->render.vbo->handle : 0));
 
-	if (sna->render.vertex_used == 0) {
-		assert(sna->render.vbo == NULL);
-		assert(sna->render.vertices == sna->render.vertex_data);
-		assert(sna->render.vertex_size == ARRAY_SIZE(sna->render.vertex_data));
+	if (sna->render.vertex_used == 0)
 		return;
-	}
 
 	bo = sna->render.vbo;
 	if (bo) {
diff --git a/src/sna/gen4_render.c b/src/sna/gen4_render.c
index 6379a18..cd4ca36 100644
--- a/src/sna/gen4_render.c
+++ b/src/sna/gen4_render.c
@@ -426,12 +426,8 @@ static void gen4_vertex_close(struct sna *sna)
 	DBG(("%s: used=%d, vbo active? %d\n",
 	     __FUNCTION__, sna->render.vertex_used, sna->render.vbo != NULL));
 
-	if (!sna->render.vertex_used) {
-		assert(sna->render.vbo == NULL);
-		assert(sna->render.vertices == sna->render.vertex_data);
-		assert(sna->render.vertex_size == ARRAY_SIZE(sna->render.vertex_data));
+	if (!sna->render.vertex_used)
 		return;
-	}
 
 	bo = sna->render.vbo;
 	if (bo) {
diff --git a/src/sna/gen5_render.c b/src/sna/gen5_render.c
index 7d424aa..27ba04d 100644
--- a/src/sna/gen5_render.c
+++ b/src/sna/gen5_render.c
@@ -418,12 +418,8 @@ static void gen5_vertex_close(struct sna *sna)
 	DBG(("%s: used=%d, vbo active? %d\n",
 	     __FUNCTION__, sna->render.vertex_used, sna->render.vbo != NULL));
 
-	if (!sna->render.vertex_used) {
-		assert(sna->render.vbo == NULL);
-		assert(sna->render.vertices == sna->render.vertex_data);
-		assert(sna->render.vertex_size == ARRAY_SIZE(sna->render.vertex_data));
+	if (!sna->render.vertex_used)
 		return;
-	}
 
 	bo = sna->render.vbo;
 	if (bo) {
diff --git a/src/sna/gen6_render.c b/src/sna/gen6_render.c
index 896693c..ecc8dfb 100644
--- a/src/sna/gen6_render.c
+++ b/src/sna/gen6_render.c
@@ -997,12 +997,8 @@ static void gen6_vertex_close(struct sna *sna)
 	DBG(("%s: used=%d, vbo active? %d\n",
 	     __FUNCTION__, sna->render.vertex_used, sna->render.vbo != NULL));
 
-	if (!sna->render.vertex_used) {
-		assert(sna->render.vbo == NULL);
-		assert(sna->render.vertices == sna->render.vertex_data);
-		assert(sna->render.vertex_size == ARRAY_SIZE(sna->render.vertex_data));
+	if (!sna->render.vertex_used)
 		return;
-	}
 
 	bo = sna->render.vbo;
 	if (bo) {
diff --git a/src/sna/gen7_render.c b/src/sna/gen7_render.c
index ea9b01a..6cf75eb 100644
--- a/src/sna/gen7_render.c
+++ b/src/sna/gen7_render.c
@@ -1153,12 +1153,8 @@ static void gen7_vertex_close(struct sna *sna)
 	DBG(("%s: used=%d, vbo active? %d\n",
 	     __FUNCTION__, sna->render.vertex_used, sna->render.vbo != NULL));
 
-	if (!sna->render.vertex_used) {
-		assert(sna->render.vbo == NULL);
-		assert(sna->render.vertices == sna->render.vertex_data);
-		assert(sna->render.vertex_size == ARRAY_SIZE(sna->render.vertex_data));
+	if (!sna->render.vertex_used)
 		return;
-	}
 
 	bo = sna->render.vbo;
 	if (bo) {


More information about the xorg-commit mailing list