xf86-video-intel: 2 commits - 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_video_textured.c

Chris Wilson ickle at kemper.freedesktop.org
Sun Jun 12 02:50:38 PDT 2011


 src/sna/kgem.c               |   23 -
 src/sna/kgem.h               |    6 
 src/sna/sna.h                |   16 -
 src/sna/sna_accel.c          |    2 
 src/sna/sna_display.c        |  203 +++++++++------
 src/sna/sna_dri.c            |  571 ++++++++++++++++++++-----------------------
 src/sna/sna_driver.c         |   33 --
 src/sna/sna_video_textured.c |    7 
 8 files changed, 418 insertions(+), 443 deletions(-)

New commits:
commit ff262aca9c46b7616c59d8e8f04d33a5bbc6e324
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Jun 11 10:15:14 2011 +0100

    sna/dri: Fix tripple-buffering for vblank_mode=0
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/kgem.c b/src/sna/kgem.c
index d600936..8a8bca2 100644
--- a/src/sna/kgem.c
+++ b/src/sna/kgem.c
@@ -515,6 +515,7 @@ static void __kgem_bo_destroy(struct kgem *kgem, struct kgem_bo *bo)
 		bo->deleted = 1;
 	}
 
+	kgem->need_expire = true;
 	list_move(&bo->list, (bo->rq || bo->needs_flush) ? &kgem->active : inactive(kgem, bo->size));
 	if (bo->rq == NULL && bo->needs_flush) {
 		assert(list_is_empty(&bo->request));
@@ -943,21 +944,6 @@ void kgem_throttle(struct kgem *kgem)
 	}
 }
 
-bool kgem_needs_expire(struct kgem *kgem)
-{
-	int i;
-
-	if (!list_is_empty(&kgem->active))
-		return true;
-
-	for (i = 0; i < ARRAY_SIZE(kgem->inactive); i++) {
-		if (!list_is_empty(&kgem->inactive[i]))
-			return true;
-	}
-
-	return false;
-}
-
 bool kgem_expire_cache(struct kgem *kgem)
 {
 	time_t now, expire;
@@ -1018,6 +1004,7 @@ bool kgem_expire_cache(struct kgem *kgem)
 
 	DBG(("%s: purge? %d -- expired %d objects, %d bytes\n", __FUNCTION__, kgem->need_purge,  count, size));
 
+	kgem->need_expire = !idle;
 	kgem->need_purge = false;
 	return idle;
 	(void)count;
@@ -1539,7 +1526,11 @@ uint32_t kgem_bo_flink(struct kgem *kgem, struct kgem_bo *bo)
 	if (ret)
 		return 0;
 
-	bo->reusable = false;
+	/* Ordinarily giving the name aware makes the buffer non-reusable.
+	 * However, we track the lifetime of all clients and their hold
+	 * on the buffer, and *presuming* they do not pass it on to a third
+	 * party, we track the lifetime accurately.
+	 */
 	return flink.name;
 }
 
diff --git a/src/sna/kgem.h b/src/sna/kgem.h
index fc1c38c..eb16ceb 100644
--- a/src/sna/kgem.h
+++ b/src/sna/kgem.h
@@ -97,8 +97,9 @@ struct kgem {
 	uint16_t nreloc;
 	uint16_t nfence;
 
-	uint32_t flush;
-	uint32_t need_purge;
+	uint32_t flush:1;
+	uint32_t need_expire:1;
+	uint32_t need_purge:1;
 
 	uint32_t has_vmap :1;
 	uint32_t has_relaxed_fencing :1;
@@ -312,7 +313,6 @@ struct kgem_bo *kgem_create_buffer(struct kgem *kgem,
 void kgem_buffer_sync(struct kgem *kgem, struct kgem_bo *bo);
 
 void kgem_throttle(struct kgem *kgem);
-bool kgem_needs_expire(struct kgem *kgem);
 bool kgem_expire_cache(struct kgem *kgem);
 
 #if HAS_EXTRA_DEBUG
diff --git a/src/sna/sna.h b/src/sna/sna.h
index 844705d..1173b7b 100644
--- a/src/sna/sna.h
+++ b/src/sna/sna.h
@@ -120,13 +120,6 @@ static inline void list_add_tail(struct list *new, struct list *head)
 	__list_add(new, head->prev, head);
 }
 
-enum DRI2FrameEventType {
-	DRI2_SWAP,
-	DRI2_ASYNC_SWAP,
-	DRI2_FLIP,
-	DRI2_WAITMSC,
-};
-
 #ifndef CREATE_PIXMAP_USAGE_SCRATCH_HEADER
 #define CREATE_PIXMAP_USAGE_SCRATCH_HEADER -1
 #endif
@@ -182,7 +175,6 @@ enum {
 	OPTION_PREFER_OVERLAY,
 	OPTION_COLOR_KEY,
 	OPTION_VIDEO_KEY,
-	OPTION_SWAPBUFFERS_WAIT,
 	OPTION_HOTPLUG,
 	OPTION_THROTTLE,
 	OPTION_RELAXED_FENCING,
@@ -201,7 +193,6 @@ struct sna {
 
 	unsigned flags;
 #define SNA_NO_THROTTLE		0x1
-#define SNA_SWAP_WAIT		0x2
 
 	int timer[NUM_TIMERS];
 	int timer_active;
@@ -222,9 +213,6 @@ struct sna {
 
 	struct sna_dri {
 		int flip_pending[2];
-		unsigned int fe_frame;
-		unsigned int fe_tv_sec;
-		unsigned int fe_tv_usec;
 	} dri;
 
 	unsigned int tiling;
@@ -239,7 +227,9 @@ struct sna {
 	struct intel_chipset chipset;
 
 	ScreenBlockHandlerProcPtr BlockHandler;
+	void *BlockData;
 	ScreenWakeupHandlerProcPtr WakeupHandler;
+	void *WakeupData;
 	CloseScreenProcPtr CloseScreen;
 
 	union {
@@ -315,7 +305,7 @@ extern xf86CrtcPtr sna_covering_crtc(ScrnInfoPtr scrn,
 				     BoxPtr crtc_box_ret);
 
 extern bool sna_wait_for_scanline(struct sna *sna, PixmapPtr pixmap,
-				  xf86CrtcPtr crtc, RegionPtr clip);
+				  xf86CrtcPtr crtc, const BoxRec *clip);
 
 Bool sna_dri_open(struct sna *sna, ScreenPtr pScreen);
 void sna_dri_wakeup(struct sna *sna);
diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index d84d703..8a8165e 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -3131,7 +3131,7 @@ static Bool sna_accel_do_expire(struct sna *sna)
 
 	return_if_timer_active(EXPIRE_TIMER);
 
-	if (!kgem_needs_expire(&sna->kgem))
+	if (!sna->kgem.need_expire)
 		return FALSE;
 
 	if (sna->timer[EXPIRE_TIMER] == -1)
diff --git a/src/sna/sna_display.c b/src/sna/sna_display.c
index 6131be1..bf0b98f 100644
--- a/src/sna/sna_display.c
+++ b/src/sna/sna_display.c
@@ -1310,11 +1310,8 @@ sna_output_init(ScrnInfoPtr scrn, struct sna_mode *mode, int num)
 
 	if (xf86IsEntityShared(scrn->entityList[0])) {
 		s = xf86GetOptValString(sna->Options, OPTION_ZAPHOD);
-		if (s && !sna_zaphod_match(scrn, s, name)) {
-			ErrorF("output '%s' not matched for zaphod '%s'\n",
-			       name, s);
+		if (s && !sna_zaphod_match(scrn, s, name))
 			goto cleanup_encoder;
-		}
 	}
 
 	output = xf86OutputCreate(scrn, &sna_output_funcs, name);
@@ -1492,7 +1489,8 @@ static int do_page_flip(struct sna *sna, void *data, int ref_crtc_hw_id)
 		struct sna_crtc *crtc = config->crtc[i]->driver_private;
 		uintptr_t evdata;
 
-		if (!config->crtc[i]->enabled)
+		DBG(("%s: crtc %d active? %d\n",__FUNCTION__, i,crtc->active));
+		if (!crtc->active)
 			continue;
 
 		/* Only the reference crtc will finally deliver its page flip
@@ -1510,8 +1508,12 @@ static int do_page_flip(struct sna *sna, void *data, int ref_crtc_hw_id)
 				    sna->mode.fb_id,
 				    DRM_MODE_PAGE_FLIP_EVENT,
 				    (void*)evdata)) {
+			int err = errno;
+			DBG(("%s: flip [fb=%d] on crtc %d [%d] failed - %d\n",
+			     __FUNCTION__, sna->mode.fb_id,
+			     i, crtc_id(crtc), err));
 			xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING,
-				   "flip queue failed: %s\n", strerror(errno));
+				   "flip queue failed: %s\n", strerror(err));
 			continue;
 		}
 
@@ -1531,11 +1533,12 @@ sna_do_pageflip(struct sna *sna,
 {
 	ScrnInfoPtr scrn = sna->scrn;
 	struct sna_mode *mode = &sna->mode;
-	struct kgem_bo *bo = sna_pixmap_pin(pixmap);
+	struct kgem_bo *bo;
 	int count;
 
 	assert(pixmap != sna->front);
 
+	bo = sna_pixmap_pin(pixmap);
 	if (!bo)
 		return 0;
 
@@ -1574,20 +1577,13 @@ sna_do_pageflip(struct sna *sna,
 	 * may never complete; this is a configuration error.
 	 */
 	count = do_page_flip(sna, data, ref_crtc_hw_id);
+	DBG(("%s: page flipped %d crtcs\n", __FUNCTION__, count));
 	if (count > 0) {
-		int id = sna->front->drawable.serialNumber;
-
 		sna->front = pixmap;
 		pixmap->refcnt++;
 
 		sna_redirect_screen_pixmap(scrn, *old_front, sna->front);
 		scrn->displayWidth = bo->pitch / sna->mode.cpp;
-
-		/* DRI2 uses the serialNumber as a means for detecting
-		 * when to revoke its buffers after a reconfigureatin event.
-		 * For the ScreenPixmap this means set_size.
-		  */
-		pixmap->drawable.serialNumber = id;
 	} else {
 		drmModeRmFB(sna->kgem.fd, mode->fb_id);
 		mode->fb_id = *old_fb;
@@ -1683,11 +1679,23 @@ static void sna_crtc_box(xf86CrtcPtr crtc, BoxPtr crtc_box)
 {
 	if (crtc->enabled) {
 		crtc_box->x1 = crtc->x;
-		crtc_box->x2 =
-		    crtc->x + xf86ModeWidth(&crtc->mode, crtc->rotation);
 		crtc_box->y1 = crtc->y;
-		crtc_box->y2 =
-		    crtc->y + xf86ModeHeight(&crtc->mode, crtc->rotation);
+
+		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;
 }
@@ -1781,74 +1789,51 @@ sna_covering_crtc(ScrnInfoPtr scrn,
 	return best_crtc;
 }
 
-bool
-sna_wait_for_scanline(struct sna *sna, PixmapPtr pixmap,
-		      xf86CrtcPtr crtc, RegionPtr clip)
-{
-	pixman_box16_t box, crtc_box;
-	int pipe, event;
-	Bool full_height;
-	int y1, y2;
-	uint32_t *b;
+/* Gen6 wait for scan line support */
+#define MI_LOAD_REGISTER_IMM			(0x22<<23)
 
-	/* XXX no wait for scanline support on SNB? */
-	if (sna->kgem.gen >= 60)
-		return false;
+/* gen6: Scan lines register */
+#define GEN6_PIPEA_SLC			(0x7004)
+#define GEN6_PIPEB_SLC			(0x7104)
 
-	if (!pixmap_is_scanout(pixmap))
-		return false;
+static void sna_emit_wait_for_scanline_gen6(struct sna *sna,
+					    int pipe, int y1, int y2,
+					    bool full_height)
+{
+	uint32_t event;
+	uint32_t *b;
 
-	if (crtc == NULL) {
-		if (clip) {
-			crtc_box = *REGION_EXTENTS(NULL, clip);
-		} else {
-			crtc_box.x1 = 0; /* XXX drawable offsets? */
-			crtc_box.y1 = 0;
-			crtc_box.x2 = pixmap->drawable.width;
-			crtc_box.y2 = pixmap->drawable.height;
-		}
-		crtc = sna_covering_crtc(sna->scrn, &crtc_box, NULL, &crtc_box);
+	/* We just wait until the trace passes the roi */
+	if (pipe == 0) {
+		pipe = GEN6_PIPEA_SLC;
+		event = MI_WAIT_FOR_PIPEA_SCAN_LINE_WINDOW;
+	} else {
+		pipe = GEN6_PIPEB_SLC;
+		event = MI_WAIT_FOR_PIPEB_SCAN_LINE_WINDOW;
 	}
 
-	if (crtc == NULL)
-		return false;
-
-	if (clip) {
-		box = *REGION_EXTENTS(unused, clip);
-
-		if (crtc->transform_in_use)
-			pixman_f_transform_bounds(&crtc->f_framebuffer_to_crtc, &box);
-
-		/* We could presume the clip was correctly computed... */
-		sna_crtc_box(crtc, &crtc_box);
-		sna_box_intersect(&box, &crtc_box, &box);
+	kgem_set_mode(&sna->kgem, KGEM_RENDER);
+	b = kgem_get_batch(&sna->kgem, 4);
+	b[0] = MI_LOAD_REGISTER_IMM | 1;
+	b[1] = pipe;
+	b[2] = y2 - 1;
+	b[3] = MI_WAIT_FOR_EVENT | event;
+	kgem_advance_batch(&sna->kgem, 4);
+}
 
-		/*
-		 * Make sure we don't wait for a scanline that will
-		 * never occur
-		 */
-		y1 = (crtc_box.y1 <= box.y1) ? box.y1 - crtc_box.y1 : 0;
-		y2 = (box.y2 <= crtc_box.y2) ?
-			box.y2 - crtc_box.y1 : crtc_box.y2 - crtc_box.y1;
-		if (y2 <= y1)
-			return false;
-
-		full_height = FALSE;
-		if (y1 == 0 && y2 == (crtc_box.y2 - crtc_box.y1))
-			full_height = TRUE;
-	} else {
-		sna_crtc_box(crtc, &crtc_box);
-		y1 = crtc_box.y1;
-		y2 = crtc_box.y2;
-		full_height = TRUE;
-	}
+static void sna_emit_wait_for_scanline_gen2(struct sna *sna,
+					    int pipe, int y1, int y2,
+					    bool full_height)
+{
+	uint32_t event;
+	uint32_t *b;
 
 	/*
 	 * Pre-965 doesn't have SVBLANK, so we need a bit
 	 * of extra time for the blitter to start up and
 	 * do its job for a full height blit
 	 */
-	if (sna_crtc_to_pipe(crtc) == 0) {
+	if (pipe == 0) {
 		pipe = MI_LOAD_SCAN_LINES_DISPLAY_PIPEA;
 		event = MI_WAIT_FOR_PIPEA_SCAN_LINE_WINDOW;
 		if (full_height)
@@ -1860,11 +1845,8 @@ sna_wait_for_scanline(struct sna *sna, PixmapPtr pixmap,
 			event = MI_WAIT_FOR_PIPEB_SVBLANK;
 	}
 
-	if (crtc->mode.Flags & V_INTERLACE) {
-		/* DSL count field lines */
-		y1 /= 2;
-		y2 /= 2;
-	}
+	if (sna->kgem.mode == KGEM_NONE)
+		kgem_set_mode(&sna->kgem, KGEM_BLT);
 
 	b = kgem_get_batch(&sna->kgem, 5);
 	/* The documentation says that the LOAD_SCAN_LINES command
@@ -1875,5 +1857,66 @@ sna_wait_for_scanline(struct sna *sna, PixmapPtr pixmap,
 	b[3] = (y1 << 16) | (y2-1);
 	b[4] = MI_WAIT_FOR_EVENT | event;
 	kgem_advance_batch(&sna->kgem, 5);
+}
+
+bool
+sna_wait_for_scanline(struct sna *sna,
+		      PixmapPtr pixmap,
+		      xf86CrtcPtr crtc,
+		      const BoxRec *clip)
+{
+	pixman_box16_t box, crtc_box;
+	Bool full_height;
+	int y1, y2, pipe;
+
+	if (sna->kgem.gen >= 60)
+		return false;
+
+	if (!pixmap_is_scanout(pixmap))
+		return false;
+
+	if (crtc == NULL) {
+		crtc = sna_covering_crtc(sna->scrn, clip, NULL, &crtc_box);
+		assert(crtc);
+	} else
+		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;
+	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;
+	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;
+
+	if (crtc->mode.Flags & V_INTERLACE) {
+		/* DSL count field lines */
+		y1 /= 2;
+		y2 /= 2;
+	}
+
+	pipe = sna_crtc_to_pipe(crtc);
+	DBG(("%s: pipe=%d, y1=%d, y2=%d, full_height?=%d\n",
+	     __FUNCTION__, pipe, y1, y2, full_height));
+
+	if (sna->kgem.gen >= 60)
+		sna_emit_wait_for_scanline_gen6(sna, pipe, y1, y2, full_height);
+	else
+		sna_emit_wait_for_scanline_gen2(sna, pipe, y1, y2, full_height);
+
 	return true;
 }
diff --git a/src/sna/sna_dri.c b/src/sna/sna_dri.c
index 94cd205..14a2f3d 100644
--- a/src/sna/sna_dri.c
+++ b/src/sna/sna_dri.c
@@ -77,6 +77,14 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
 #define NDEBUG 1
 #endif
 
+enum frame_event_type {
+	DRI2_SWAP,
+	DRI2_SWAP_THROTTLE,
+	DRI2_ASYNC_FLIP,
+	DRI2_FLIP,
+	DRI2_WAITMSC,
+};
+
 struct sna_dri_private {
 	int refcnt;
 	PixmapPtr pixmap;
@@ -88,7 +96,7 @@ struct sna_dri_frame_event {
 	XID drawable_id;
 	XID client_id;	/* fake client ID to track client destruction */
 	ClientPtr client;
-	enum DRI2FrameEventType type;
+	enum frame_event_type type;
 	int frame;
 	int pipe;
 	int count;
@@ -99,6 +107,10 @@ struct sna_dri_frame_event {
 	DRI2BufferPtr front;
 	DRI2BufferPtr back;
 
+	unsigned int fe_frame;
+	unsigned int fe_tv_sec;
+	unsigned int fe_tv_usec;
+
 	PixmapPtr old_front;
 	uint32_t old_fb;
 };
@@ -291,13 +303,11 @@ static void sna_dri_reference_buffer(DRI2Buffer2Ptr buffer)
 	}
 }
 
-static void damage(DrawablePtr drawable, RegionPtr region)
+static void damage(DrawablePtr drawable, PixmapPtr pixmap, RegionPtr region)
 {
-	PixmapPtr pixmap;
 	struct sna_pixmap *priv;
 	int16_t dx, dy;
 
-	pixmap = get_drawable_pixmap(drawable);
 	get_drawable_deltas(drawable, pixmap, &dx, &dy);
 
 	priv = sna_pixmap(pixmap);
@@ -350,51 +360,65 @@ static void damage(DrawablePtr drawable, RegionPtr region)
 }
 
 static void
-sna_dri_copy_region(DrawablePtr drawable, RegionPtr region,
-		     DRI2BufferPtr dst_buffer, DRI2BufferPtr src_buffer)
+sna_dri_copy_region(DrawablePtr draw,
+		    RegionPtr region,
+		    DRI2BufferPtr dst_buffer,
+		    DRI2BufferPtr src_buffer)
 {
-	struct sna *sna = to_sna_from_drawable(drawable);
-	struct sna_dri_private *srcPrivate = src_buffer->driverPrivate;
-	struct sna_dri_private *dstPrivate = dst_buffer->driverPrivate;
-	ScreenPtr screen = drawable->pScreen;
-	DrawablePtr src = (src_buffer->attachment == DRI2BufferFrontLeft)
-		? drawable : &srcPrivate->pixmap->drawable;
-	DrawablePtr dst = (dst_buffer->attachment == DRI2BufferFrontLeft)
-		? drawable : &dstPrivate->pixmap->drawable;
-	GCPtr gc;
-	bool flush = false;
+	struct sna *sna = to_sna_from_drawable(draw);
+	struct sna_dri_private *src_priv = src_buffer->driverPrivate;
+	struct sna_dri_private *dst_priv = dst_buffer->driverPrivate;
+	PixmapPtr src = src_priv->pixmap;
+	PixmapPtr dst = dst_priv->pixmap;
+	pixman_region16_t clip;
 
 	DBG(("%s(region=(%d, %d), (%d, %d)))\n", __FUNCTION__,
 	     region ? REGION_EXTENTS(NULL, region)->x1 : 0,
 	     region ? REGION_EXTENTS(NULL, region)->y1 : 0,
-	     region ? REGION_EXTENTS(NULL, region)->x2 : dst->width,
-	     region ? REGION_EXTENTS(NULL, region)->y2 : dst->height));
+	     region ? REGION_EXTENTS(NULL, region)->x2 : draw->width,
+	     region ? REGION_EXTENTS(NULL, region)->y2 : draw->height));
 
 	DBG(("%s: dst -- attachment=%d, name=%d, handle=%d\n",
 	     __FUNCTION__,
 	     dst_buffer->attachment,
 	     dst_buffer->name,
-	     dstPrivate->bo->handle));
+	     dst_priv->bo->handle));
 	DBG(("%s: src -- attachment=%d, name=%d, handle=%d\n",
 	     __FUNCTION__,
 	     src_buffer->attachment,
 	     src_buffer->name,
-	     srcPrivate->bo->handle));
+	     src_priv->bo->handle));
 
-	gc = GetScratchGC(dst->depth, screen);
-	if (!gc)
-		return;
-
-	if (region) {
-		RegionPtr clip;
+	if (draw->type == DRAWABLE_WINDOW) {
+		WindowPtr win = (WindowPtr)draw;
 
-		clip = REGION_CREATE(screen, NULL, 0);
-		pixman_region_intersect_rect(clip, region,
-					     0, 0, dst->width, dst->height);
-		(*gc->funcs->ChangeClip)(gc, CT_REGION, clip, 0);
-		region = clip;
+		pixman_region_init(&clip);
+		pixman_region_intersect(&clip, &win->clipList, region);
+		if (!pixman_region_not_empty(&clip)) {
+			DBG(("%s: all clipped\n", __FUNCTION__));
+			return;
+		}
+		region = &clip;
 	}
-	ValidateGC(dst, gc);
+
+	assert(sna_pixmap(src)->cpu_damage == NULL);
+	assert(sna_pixmap(dst)->cpu_damage == NULL);
+
+	/* It's important that this copy gets submitted before the
+	 * direct rendering client submits rendering for the next
+	 * frame, but we don't actually need to submit right now.  The
+	 * client will wait for the DRI2CopyRegion reply or the swap
+	 * buffer event before rendering, and we'll hit the flush
+	 * callback chain before those messages are sent.  We submit
+	 * our batch buffers from the flush callback chain so we know
+	 * that will happen before the client tries to render
+	 * again.
+	 */
+	sna->render.copy_boxes(sna, GXcopy,
+			       src, src_priv->bo, draw->x, draw->y,
+			       dst, dst_priv->bo, draw->x, draw->y,
+			       REGION_RECTS(region),
+			       REGION_NUM_RECTS(region));
 
 	/* Invalidate src to reflect unknown modifications made by the client
 	 *
@@ -402,12 +426,46 @@ sna_dri_copy_region(DrawablePtr drawable, RegionPtr region,
 	 * between the last flush and this request? Hopefully nobody will
 	 * hit that race window to find out...
 	 */
-	damage(src, region);
+	damage(draw, src, region);
+	damage(draw, dst, region);
+	if (region == &clip)
+		pixman_region_fini(&clip);
+}
+
+static void
+sna_dri_swap_blit(struct sna *sna, DrawablePtr draw, DRI2BufferPtr back)
+{
+	struct sna_dri_private *src_priv = back->driverPrivate;
+	PixmapPtr src = src_priv->pixmap;
+	PixmapPtr dst = sna->front;
+	bool flush = false;
+	BoxRec box, *boxes;
+	int n;
+
+	DBG(("%s: back -- attachment=%d, name=%d, handle=%d\n",
+	     __FUNCTION__,
+	     back->attachment,
+	     back->name,
+	     src_priv->bo->handle));
+
+	if (draw->type == DRAWABLE_PIXMAP) {
+		box.x1 = box.y1 = 0;
+		box.x2 = draw->width;
+		box.y2 = draw->height;
+
+		boxes = &box;
+		n = 1;
+	} else {
+		WindowPtr win = (WindowPtr)draw;
+
+		boxes = REGION_RECTS(&win->clipList);
+		n = REGION_NUM_RECTS(&win->clipList);
+		if (n == 0)
+			return;
 
-	/* Wait for the scanline to be outside the region to be copied */
-	if (sna->flags & SNA_SWAP_WAIT)
-		flush = sna_wait_for_scanline(sna, get_drawable_pixmap(dst),
-					      NULL, region);
+		flush = sna_wait_for_scanline(sna, sna->front,
+					      NULL, &win->clipList.extents);
+	}
 
 	/* It's important that this copy gets submitted before the
 	 * direct rendering client submits rendering for the next
@@ -419,11 +477,19 @@ sna_dri_copy_region(DrawablePtr drawable, RegionPtr region,
 	 * that will happen before the client tries to render
 	 * again.
 	 */
-	gc->ops->CopyArea(src, dst, gc,
-			  0, 0,
-			  drawable->width, drawable->height,
-			  0, 0);
-	FreeScratchGC(gc);
+	sna->render.copy_boxes(sna, GXcopy,
+			       src, src_priv->bo, draw->x, draw->y,
+			       dst, sna_pixmap_get_bo(dst), draw->x, draw->y,
+			       boxes, n);
+
+	/* Invalidate src to reflect unknown modifications made by the client
+	 *
+	 * XXX But what about any conflicting shadow writes made by others
+	 * between the last flush and this request? Hopefully nobody will
+	 * hit that race window to find out...
+	 */
+	damage(draw, src, NULL);
+	damage(draw, dst, NULL);
 
 	DBG(("%s: flushing? %d\n", __FUNCTION__, flush));
 	if (flush) /* STAT! */
@@ -432,12 +498,10 @@ sna_dri_copy_region(DrawablePtr drawable, RegionPtr region,
 
 #if DRI2INFOREC_VERSION >= 4
 
-
 static int
 sna_dri_get_pipe(DrawablePtr pDraw)
 {
-	ScreenPtr pScreen = pDraw->pScreen;
-	ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
+	ScrnInfoPtr pScrn = xf86Screens[pDraw->pScreen->myNum];
 	BoxRec box, crtcbox;
 	xf86CrtcPtr crtc;
 	int pipe;
@@ -533,7 +597,7 @@ sna_dri_add_frame_event(struct sna_dri_frame_event *frame_event)
 }
 
 static void
-sna_dri_frame_event_info(struct sna_dri_frame_event *info)
+sna_dri_frame_event_info_free(struct sna_dri_frame_event *info)
 {
 	if (info->client_id)
 		FreeResourceByType(info->client_id,
@@ -559,7 +623,8 @@ sna_dri_exchange_buffers(DrawablePtr draw,
 {
 	int tmp;
 
-	DBG(("%s()\n", __FUNCTION__));
+	DBG(("%s(%d <--> %d)\n",
+	     __FUNCTION__, front->attachment, back->attachment));
 
 	assert(front->format == back->format);
 
@@ -577,83 +642,21 @@ sna_dri_schedule_flip(struct sna *sna,
 		      DrawablePtr draw,
 		      struct sna_dri_frame_event *info)
 {
-	struct sna_dri *dri = &sna->dri;
 	struct sna_dri_private *back_priv;
 
-	/* Main crtc for this drawable shall finally deliver pageflip event. */
-	int ref_crtc_hw_id = sna_dri_get_pipe(draw);
-
 	DBG(("%s()\n", __FUNCTION__));
 
-	dri->fe_frame = 0;
-	dri->fe_tv_sec = 0;
-	dri->fe_tv_usec = 0;
-
 	/* Page flip the full screen buffer */
 	back_priv = info->back->driverPrivate;
 	info->count = sna_do_pageflip(sna,
 				      back_priv->pixmap,
-				      info, ref_crtc_hw_id,
+				      info, info->pipe,
 				      &info->old_front,
 				      &info->old_fb);
 	return info->count != 0;
 }
 
 static Bool
-can_exchange(DRI2BufferPtr front,
-	     DRI2BufferPtr back)
-{
-	struct sna_dri_private *front_priv = front->driverPrivate;
-	struct sna_dri_private *back_priv = back->driverPrivate;
-	PixmapPtr front_pixmap = front_priv->pixmap;
-	PixmapPtr back_pixmap = back_priv->pixmap;
-	struct sna_pixmap *front_sna, *back_sna;
-
-	if (front_pixmap->drawable.width != back_pixmap->drawable.width) {
-		DBG(("%s -- no, size mismatch: front width=%d, back=%d\n",
-		     __FUNCTION__,
-		     front_pixmap->drawable.width,
-		     back_pixmap->drawable.width));
-		return FALSE;
-	}
-
-	if (front_pixmap->drawable.height != back_pixmap->drawable.height) {
-		DBG(("%s -- no, size mismatch: front height=%d, back=%d\n",
-		     __FUNCTION__,
-		     front_pixmap->drawable.height,
-		     back_pixmap->drawable.height));
-		return FALSE;
-	}
-
-	if (front_pixmap->drawable.bitsPerPixel != back_pixmap->drawable.bitsPerPixel) {
-		DBG(("%s -- no, depth mismatch: front bpp=%d, back=%d\n",
-		     __FUNCTION__,
-		     front_pixmap->drawable.bitsPerPixel,
-		     back_pixmap->drawable.bitsPerPixel));
-		return FALSE;
-	}
-
-	/* prevent an implicit tiling mode change */
-	front_sna = sna_pixmap(front_pixmap);
-	back_sna = sna_pixmap(back_pixmap);
-	if (front_sna->gpu_bo->tiling != back_sna->gpu_bo->tiling) {
-		DBG(("%s -- no, tiling mismatch: front %d, back=%d\n",
-		     __FUNCTION__,
-		     front_sna->gpu_bo->tiling,
-		     back_sna->gpu_bo->tiling));
-		return FALSE;
-	}
-
-	if (front_sna->gpu_only != back_sna->gpu_only) {
-		DBG(("%s -- no, mismatch in gpu_only: front %d, back=%d\n",
-		     __FUNCTION__, front_sna->gpu_only, back_sna->gpu_only));
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static Bool
 can_flip(struct sna * sna,
 	 DrawablePtr draw,
 	 DRI2BufferPtr front,
@@ -773,78 +776,59 @@ static void sna_dri_vblank_handle(int fd,
 				  unsigned int tv_usec,
 				  void *data)
 {
-	struct sna_dri_frame_event *swap_info = data;
+	struct sna_dri_frame_event *info = data;
 	DrawablePtr draw;
-	ScreenPtr screen;
-	ScrnInfoPtr scrn;
 	struct sna *sna;
 	int status;
 
 	DBG(("%s(id=%d, type=%d)\n", __FUNCTION__,
-	     (int)swap_info->drawable_id, swap_info->type));
+	     (int)info->drawable_id, info->type));
 
 	status = BadDrawable;
-	if (swap_info->drawable_id)
+	if (info->drawable_id)
 		status = dixLookupDrawable(&draw,
-					   swap_info->drawable_id,
+					   info->drawable_id,
 					   serverClient,
 					   M_ANY, DixWriteAccess);
 	if (status != Success)
 		goto done;
 
-	screen = draw->pScreen;
-	scrn = xf86Screens[screen->myNum];
-	sna = to_sna(scrn);
+	sna = to_sna_from_drawable(draw);
 
-	switch (swap_info->type) {
+	switch (info->type) {
 	case DRI2_FLIP:
 		/* If we can still flip... */
-		if (can_flip(sna, draw,
-			     swap_info->front, swap_info->back) &&
-		    sna_dri_schedule_flip(sna, draw, swap_info)) {
-			sna_dri_exchange_buffers(draw,
-						 swap_info->front,
-						 swap_info->back);
+		if (can_flip(sna, draw, info->front, info->back) &&
+		    sna_dri_schedule_flip(sna, draw, info)) {
+			sna_dri_exchange_buffers(draw, info->front, info->back);
 			return;
 		}
 		/* else fall through to exchange/blit */
-	case DRI2_SWAP: {
-		int swap_type;
-
-		if (DRI2CanExchange(draw) &&
-		    can_exchange(swap_info->front, swap_info->back)) {
-			sna_dri_exchange_buffers(draw,
-						 swap_info->front,
-						 swap_info->back);
-			swap_type = DRI2_EXCHANGE_COMPLETE;
-		} else {
-			sna_dri_copy_region(draw, NULL,
-					    swap_info->front,
-					    swap_info->back);
-			swap_type = DRI2_BLIT_COMPLETE;
-		}
-		DRI2SwapComplete(swap_info->client,
+	case DRI2_SWAP:
+		sna_dri_swap_blit(sna, draw, info->back);
+	case DRI2_SWAP_THROTTLE:
+		DRI2SwapComplete(info->client,
 				 draw, frame,
 				 tv_sec, tv_usec,
-				 swap_type,
-				 swap_info->client ? swap_info->event_complete : NULL,
-				 swap_info->event_data);
+				 DRI2_BLIT_COMPLETE,
+				 info->client ? info->event_complete : NULL,
+				 info->event_data);
 		break;
-	}
+
 	case DRI2_WAITMSC:
-		if (swap_info->client)
-			DRI2WaitMSCComplete(swap_info->client, draw,
+		if (info->client)
+			DRI2WaitMSCComplete(info->client, draw,
 					    frame, tv_sec, tv_usec);
 		break;
 	default:
-		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+		xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING,
 			   "%s: unknown vblank event received\n", __func__);
 		/* Unknown type */
 		break;
 	}
 
 done:
-	sna_dri_frame_event_info(swap_info);
+	sna_dri_frame_event_info_free(info);
 }
 
 static void sna_dri_flip_event(struct sna *sna,
@@ -855,9 +839,9 @@ static void sna_dri_flip_event(struct sna *sna,
 
 	DBG(("%s(frame=%d, tv=%d.%06d, type=%d)\n",
 	     __FUNCTION__,
-	     sna->dri.fe_frame,
-	     sna->dri.fe_tv_sec,
-	     sna->dri.fe_tv_usec,
+	     flip->fe_frame,
+	     flip->fe_tv_sec,
+	     flip->fe_tv_usec,
 	     flip->type));
 
 	if (!flip->drawable_id)
@@ -878,8 +862,8 @@ static void sna_dri_flip_event(struct sna *sna,
 		 * into account. This usually means some defective kms pageflip completion,
 		 * causing wrong (msc, ust) return values and possible visual corruption.
 		 */
-		if ((sna->dri.fe_frame < flip->frame) &&
-		    (flip->frame - sna->dri.fe_frame < 5)) {
+		if ((flip->fe_frame < flip->frame) &&
+		    (flip->frame - flip->fe_frame < 5)) {
 			static int limit = 5;
 
 			/* XXX we are currently hitting this path with older
@@ -888,25 +872,25 @@ static void sna_dri_flip_event(struct sna *sna,
 			if (limit) {
 				xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING,
 					   "%s: Pageflip completion has impossible msc %d < target_msc %d\n",
-					   __func__, sna->dri.fe_frame, flip->frame);
+					   __func__, flip->fe_frame, flip->frame);
 				limit--;
 			}
 
 			/* All-0 values signal timestamping failure. */
-			sna->dri.fe_frame = sna->dri.fe_tv_sec = sna->dri.fe_tv_usec = 0;
+			flip->fe_frame = flip->fe_tv_sec = flip->fe_tv_usec = 0;
 		}
 
 		DBG(("%s: flip complete\n", __FUNCTION__));
 		DRI2SwapComplete(flip->client, drawable,
-				 sna->dri.fe_frame,
-				 sna->dri.fe_tv_sec,
-				 sna->dri.fe_tv_usec,
+				 flip->fe_frame,
+				 flip->fe_tv_sec,
+				 flip->fe_tv_usec,
 				 DRI2_FLIP_COMPLETE,
 				 flip->client ? flip->event_complete : NULL,
 				 flip->event_data);
 		break;
 
-	case DRI2_ASYNC_SWAP:
+	case DRI2_ASYNC_FLIP:
 		DBG(("%s: async swap flip completed on pipe %d, pending %d\n",
 		     __FUNCTION__, flip->pipe, sna->dri.flip_pending[flip->pipe]));
 		sna->dri.flip_pending[flip->pipe]--;
@@ -925,16 +909,15 @@ sna_dri_page_flip_handler(int fd, unsigned int frame, unsigned int tv_sec,
 			  unsigned int tv_usec, void *data)
 {
 	struct sna_dri_frame_event *info = to_frame_event(data);
-	struct sna_dri *dri = &info->sna->dri;
 
 	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) {
 		/* Yes: Cache msc, ust for later delivery. */
-		dri->fe_frame = frame;
-		dri->fe_tv_sec = tv_sec;
-		dri->fe_tv_usec = tv_usec;
+		info->fe_frame = frame;
+		info->fe_tv_sec = tv_sec;
+		info->fe_tv_usec = tv_usec;
 	}
 
 	if (--info->count)
@@ -943,7 +926,7 @@ sna_dri_page_flip_handler(int fd, unsigned int frame, unsigned int tv_sec,
 	sna_dri_flip_event(info->sna, info);
 
 	sna_mode_delete_fb(info->sna, info->old_front, info->old_fb);
-	sna_dri_frame_event_info(info);
+	sna_dri_frame_event_info_free(info);
 }
 
 /*
@@ -976,16 +959,20 @@ sna_dri_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	struct sna *sna = to_sna(scrn);
 	drmVBlank vbl;
 	int pipe, flip;
-	struct sna_dri_frame_event *info;
-	enum DRI2FrameEventType swap_type = DRI2_SWAP;
+	struct sna_dri_frame_event *info = NULL;
+	enum frame_event_type swap_type = DRI2_SWAP;
 	CARD64 current_msc;
 
-	DBG(("%s(target_msc=%llu)\n", __FUNCTION__, (long long)*target_msc));
+	DBG(("%s(target_msc=%llu, divisor=%llu, remainder=%llu)\n",
+	     __FUNCTION__,
+	     (long long)*target_msc,
+	     (long long)divisor,
+	     (long long)remainder));
 
 	/* Drawable not displayed... just complete the swap */
 	pipe = sna_dri_get_pipe(draw);
 	if (pipe == -1)
-		goto xchg_fallback;
+		goto blit_fallback;
 
 	/* Truncate to match kernel interfaces; means occasional overflow
 	 * misses, but that's generally not a big deal */
@@ -995,7 +982,7 @@ sna_dri_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 
 	info = calloc(1, sizeof(struct sna_dri_frame_event));
 	if (!info)
-		goto xchg_fallback;
+		goto blit_fallback;
 
 	info->sna = sna;
 	info->drawable_id = draw->id;
@@ -1004,10 +991,12 @@ sna_dri_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	info->event_data = data;
 	info->front = front;
 	info->back = back;
+	info->pipe = pipe;
 
 	if (!sna_dri_add_frame_event(info)) {
 		free(info);
-		goto xchg_fallback;
+		info = NULL;
+		goto blit_fallback;
 	}
 
 	sna_dri_reference_buffer(front);
@@ -1064,8 +1053,23 @@ immediate:
 			return TRUE;
 		}
 
-		/* Similarly, the CopyRegion blit is coupled to vsync. */
-		goto blit_fallback;
+		DBG(("%s: emitting immediate vsync'ed blit, throttling client\n"));
+
+		 info->type = DRI2_SWAP_THROTTLE;
+
+		 vbl.request.type =
+			 DRM_VBLANK_RELATIVE |
+			 DRM_VBLANK_EVENT |
+			 DRM_VBLANK_NEXTONMISS;
+		 if (pipe > 0)
+			 vbl.request.type |= DRM_VBLANK_SECONDARY;
+		 vbl.request.sequence = 0;
+		 vbl.request.signal = (unsigned long)info;
+		 if (drmWaitVBlank(sna->kgem.fd, &vbl))
+			 sna_dri_frame_event_info_free(info);
+
+		 sna_dri_swap_blit(sna, draw, back);
+		 return TRUE;
 	}
 
 	/* Correct target_msc by 'flip' if swap_type == DRI2_FLIP.
@@ -1126,15 +1130,10 @@ immediate:
 
 blit_fallback:
 	DBG(("%s -- blit\n", __FUNCTION__));
-	sna_dri_copy_region(draw, NULL, front, back);
-	sna_dri_frame_event_info(info);
-	swap_type = DRI2_BLIT_COMPLETE;
-	goto fallback;
-xchg_fallback:
-	swap_type = DRI2_EXCHANGE_COMPLETE;
-	sna_dri_exchange_buffers(draw, front, back);
-fallback:
-	DRI2SwapComplete(client, draw, 0, 0, 0, swap_type, func, data);
+	sna_dri_swap_blit(sna, draw, back);
+	if (info)
+		sna_dri_frame_event_info_free(info);
+	DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data);
 	*target_msc = 0; /* offscreen, so zero out target vblank count */
 	return TRUE;
 }
@@ -1142,53 +1141,53 @@ fallback:
 #if DRI2INFOREC_VERSION >= 6
 static void
 sna_dri_async_swap(ClientPtr client, DrawablePtr draw,
-		  DRI2BufferPtr front, DRI2BufferPtr back,
-		  DRI2SwapEventPtr func, void *data)
+		   DRI2BufferPtr front, DRI2BufferPtr back,
+		   DRI2SwapEventPtr func, void *data)
 {
 	ScreenPtr screen = draw->pScreen;
 	ScrnInfoPtr scrn = xf86Screens[screen->myNum];
 	struct sna *sna = to_sna(scrn);
-	int pipe = sna_dri_get_pipe(draw);
 	int type = DRI2_EXCHANGE_COMPLETE;
+	struct sna_dri_private *back_priv = back->driverPrivate;
+	struct sna_dri_private *front_priv = front->driverPrivate;
+	PixmapPtr pixmap;
+	int pipe;
 
 	DBG(("%s()\n", __FUNCTION__));
 
 	/* Drawable not displayed... just complete the swap */
-	if (pipe == -1)
-		goto exchange;
-
-	if (!can_flip(sna, draw, front, back)) {
-		/* Do an synchronous copy instead */
-		struct sna_dri_private *front_priv = front->driverPrivate;
-		struct sna_dri_private *back_priv = back->driverPrivate;
+	pipe = sna_dri_get_pipe(draw);
+	if (pipe == -1 || !can_flip(sna, draw, front, back)) {
 		BoxRec box, *boxes;
-		PixmapPtr dst;
 		int n;
 
-		DBG(("%s: fallback blit: %dx%d\n",
-		     __FUNCTION__, draw->width, draw->height));
-
-		/* XXX clipping */
 		if (draw->type == DRAWABLE_PIXMAP) {
 			box.x1 = box.y1 = 0;
 			box.x2 = draw->width;
 			box.y2 = draw->height;
 
-			dst = front_priv->pixmap;
 			boxes = &box;
 			n = 1;
 		} else {
 			WindowPtr win = (WindowPtr)draw;
 
-			dst = sna->front;
 			boxes = REGION_RECTS(&win->clipList);
 			n = REGION_NUM_RECTS(&win->clipList);
 		}
 
-		sna->render.copy_boxes(sna, GXcopy,
-				       back_priv->pixmap, back_priv->bo, 0, 0,
-				       dst, sna_pixmap_get_bo(dst), 0, 0,
-				       boxes, n);
+		DBG(("%s: fallback blit: %dx%d\n",
+		     __FUNCTION__, draw->width, draw->height));
+
+		if (n) {
+			sna->render.copy_boxes(sna, GXcopy,
+					       back_priv->pixmap,
+					       back_priv->bo,
+					       0, 0,
+					       front_priv->pixmap,
+					       front_priv->bo,
+					       0, 0,
+					       boxes, n);
+		}
 
 		DRI2SwapComplete(client, draw, 0, 0, 0,
 				 DRI2_BLIT_COMPLETE, func, data);
@@ -1197,72 +1196,60 @@ sna_dri_async_swap(ClientPtr client, DrawablePtr draw,
 
 	if (!sna->dri.flip_pending[pipe]) {
 		struct sna_dri_frame_event *info;
-		struct sna_dri_private *back_priv = back->driverPrivate;
-		struct sna_pixmap *priv;
-		PixmapPtr src = back_priv->pixmap;
-		PixmapPtr copy;
-		BoxRec box;
+		DRI2BufferPtr t;
 
 		DBG(("%s: no pending flip on pipe %d, so updating scanout\n",
 		     __FUNCTION__, pipe));
 
-		copy = screen->CreatePixmap(screen,
-					    src->drawable.width,
-					    src->drawable.height,
-					    src->drawable.depth,
-					    SNA_CREATE_FB);
-		if (copy == NullPixmap)
-			goto exchange;
-
-		priv = sna_pixmap_force_to_gpu(copy);
-		if (priv == NULL) {
-			screen->DestroyPixmap(copy);
-			goto exchange;
-		}
-
-		box.x1 = box.y1 = 0;
-		box.x2 = src->drawable.width;
-		box.y2 = src->drawable.height;
-		if (!sna->render.copy_boxes(sna, GXcopy,
-					    src, back_priv->bo, 0, 0,
-					    copy, priv->gpu_bo, 0, 0,
-					    &box, 1)) {
-			screen->DestroyPixmap(copy);
-			goto exchange;
-		}
-		sna_damage_all(&priv->gpu_damage,
-			       src->drawable.width, src->drawable.height);
-		assert(priv->cpu_damage == NULL);
-
 		info = calloc(1, sizeof(struct sna_dri_frame_event));
 		if (!info) {
-			screen->DestroyPixmap(copy);
 			goto exchange;
 		}
 
 		info->sna = sna;
 		info->drawable_id = draw->id;
 		info->client = client;
-		info->type = DRI2_ASYNC_SWAP;
+		info->type = DRI2_ASYNC_FLIP;
 		info->pipe = pipe;
 
 		if (!sna_dri_add_frame_event(info)) {
 			free(info);
-			screen->DestroyPixmap(copy);
 			goto exchange;
 		}
 
-		info->count = sna_do_pageflip(sna, copy, info, pipe,
+		info->count = sna_do_pageflip(sna, back_priv->pixmap,
+					      info, pipe,
 					      &info->old_front, &info->old_fb);
-		screen->DestroyPixmap(copy);
 
 		if (info->count == 0) {
+			DBG(("%s: pageflip failed\n", __FUNCTION__));
 			free(info);
 			goto exchange;
 		}
 
 		type = DRI2_FLIP_COMPLETE;
 		sna->dri.flip_pending[pipe]++;
+
+		/* and flip the pointers */
+		t = front;
+		front = back;
+		back = t;
+
+		front_priv = front->driverPrivate;
+		back_priv = back->driverPrivate;
+	}
+
+	if (front_priv->pixmap == sna->front &&
+	    (pixmap = screen->CreatePixmap(screen,
+					   draw->width,
+					   draw->height,
+					   draw->depth,
+					   SNA_CREATE_FB))) {
+		screen->DestroyPixmap(front_priv->pixmap);
+		front_priv->pixmap = pixmap;
+		front_priv->bo = sna_pixmap_set_dri(sna, pixmap);
+		front->name = kgem_bo_flink(&sna->kgem, front_priv->bo);
+		front->pitch = front_priv->bo->pitch;
 	}
 
 exchange:
@@ -1278,11 +1265,9 @@ exchange:
 static int
 sna_dri_get_msc(DrawablePtr draw, CARD64 *ust, CARD64 *msc)
 {
-	ScreenPtr screen = draw->pScreen;
-	ScrnInfoPtr scrn = xf86Screens[screen->myNum];
-	struct sna *sna = to_sna(scrn);
+	struct sna *sna = to_sna_from_drawable(draw);
 	drmVBlank vbl;
-	int ret, pipe = sna_dri_get_pipe(draw);
+	int pipe = sna_dri_get_pipe(draw);
 
 	DBG(("%s()\n", __FUNCTION__));
 
@@ -1298,22 +1283,21 @@ sna_dri_get_msc(DrawablePtr draw, CARD64 *ust, CARD64 *msc)
 		vbl.request.type |= DRM_VBLANK_SECONDARY;
 	vbl.request.sequence = 0;
 
-	ret = drmWaitVBlank(sna->kgem.fd, &vbl);
-	if (ret) {
+	if (drmWaitVBlank(sna->kgem.fd, &vbl)) {
 		static int limit = 5;
 		if (limit) {
-			xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+			xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING,
 				   "%s:%d get vblank counter failed: %s\n",
 				   __FUNCTION__, __LINE__,
 				   strerror(errno));
 			limit--;
 		}
+		DBG(("%s: failed on pipe %d\n", __FUNCTION__, pipe));
 		return FALSE;
 	}
 
 	*ust = ((CARD64)vbl.reply.tval_sec * 1000000) + vbl.reply.tval_usec;
 	*msc = vbl.reply.sequence;
-
 	return TRUE;
 }
 
@@ -1330,9 +1314,9 @@ sna_dri_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 	ScreenPtr screen = draw->pScreen;
 	ScrnInfoPtr scrn = xf86Screens[screen->myNum];
 	struct sna *sna = to_sna(scrn);
-	struct sna_dri_frame_event *wait_info;
+	struct sna_dri_frame_event *info;
 	drmVBlank vbl;
-	int ret, pipe = sna_dri_get_pipe(draw);
+	int pipe = sna_dri_get_pipe(draw);
 	CARD64 current_msc;
 
 	DBG(("%s(target_msc=%llu, divisor=%llu, rem=%llu)\n",
@@ -1351,22 +1335,21 @@ sna_dri_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 	if (pipe == -1)
 		goto out_complete;
 
-	wait_info = calloc(1, sizeof(struct sna_dri_frame_event));
-	if (!wait_info)
+	info = calloc(1, sizeof(struct sna_dri_frame_event));
+	if (!info)
 		goto out_complete;
 
-	wait_info->sna = sna;
-	wait_info->drawable_id = draw->id;
-	wait_info->client = client;
-	wait_info->type = DRI2_WAITMSC;
+	info->sna = sna;
+	info->drawable_id = draw->id;
+	info->client = client;
+	info->type = DRI2_WAITMSC;
 
 	/* Get current count */
 	vbl.request.type = DRM_VBLANK_RELATIVE;
 	if (pipe > 0)
 		vbl.request.type |= DRM_VBLANK_SECONDARY;
 	vbl.request.sequence = 0;
-	ret = drmWaitVBlank(sna->kgem.fd, &vbl);
-	if (ret) {
+	if (drmWaitVBlank(sna->kgem.fd, &vbl)) {
 		static int limit = 5;
 		if (limit) {
 			xf86DrvMsg(scrn->scrnIndex, X_WARNING,
@@ -1398,9 +1381,8 @@ sna_dri_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 		if (pipe > 0)
 			vbl.request.type |= DRM_VBLANK_SECONDARY;
 		vbl.request.sequence = target_msc;
-		vbl.request.signal = (unsigned long)wait_info;
-		ret = drmWaitVBlank(sna->kgem.fd, &vbl);
-		if (ret) {
+		vbl.request.signal = (unsigned long)info;
+		if (drmWaitVBlank(sna->kgem.fd, &vbl)) {
 			static int limit = 5;
 			if (limit) {
 				xf86DrvMsg(scrn->scrnIndex, X_WARNING,
@@ -1412,7 +1394,7 @@ sna_dri_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 			goto out_complete;
 		}
 
-		wait_info->frame = vbl.reply.sequence;
+		info->frame = vbl.reply.sequence;
 		DRI2BlockClient(client, draw);
 		return TRUE;
 	}
@@ -1437,9 +1419,8 @@ sna_dri_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 	if ((current_msc % divisor) >= remainder)
 	    vbl.request.sequence += divisor;
 
-	vbl.request.signal = (unsigned long)wait_info;
-	ret = drmWaitVBlank(sna->kgem.fd, &vbl);
-	if (ret) {
+	vbl.request.signal = (unsigned long)info;
+	if (drmWaitVBlank(sna->kgem.fd, &vbl)) {
 		static int limit = 5;
 		if (limit) {
 			xf86DrvMsg(scrn->scrnIndex, X_WARNING,
@@ -1451,7 +1432,7 @@ sna_dri_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 		goto out_complete;
 	}
 
-	wait_info->frame = vbl.reply.sequence;
+	info->frame = vbl.reply.sequence;
 	DRI2BlockClient(client, draw);
 
 	return TRUE;
diff --git a/src/sna/sna_driver.c b/src/sna/sna_driver.c
index b9402e7..9970177 100644
--- a/src/sna/sna_driver.c
+++ b/src/sna/sna_driver.c
@@ -84,7 +84,6 @@ static OptionInfoRec sna_options[] = {
    {OPTION_PREFER_OVERLAY, "XvPreferOverlay", OPTV_BOOLEAN, {0}, FALSE},
    {OPTION_COLOR_KEY,	"ColorKey",	OPTV_INTEGER,	{0},	FALSE},
    {OPTION_VIDEO_KEY,	"VideoKey",	OPTV_INTEGER,	{0},	FALSE},
-   {OPTION_SWAPBUFFERS_WAIT, "SwapbuffersWait", OPTV_BOOLEAN,	{0},	TRUE},
    {OPTION_HOTPLUG,	"HotPlug",	OPTV_BOOLEAN,	{0},	TRUE},
    {OPTION_THROTTLE,	"Throttle",	OPTV_BOOLEAN,	{0},	TRUE},
    {OPTION_RELAXED_FENCING,	"UseRelaxedFencing",	OPTV_BOOLEAN,	{0},	TRUE},
@@ -502,8 +501,6 @@ static Bool sna_pre_init(ScrnInfoPtr scrn, int flags)
 	sna->flags = 0;
 	if (!xf86ReturnOptValBool(sna->Options, OPTION_THROTTLE, TRUE))
 		sna->flags |= SNA_NO_THROTTLE;
-	if (xf86ReturnOptValBool(sna->Options, OPTION_SWAPBUFFERS_WAIT, TRUE))
-		sna->flags |= SNA_SWAP_WAIT;
 
 	xf86DrvMsg(scrn->scrnIndex, X_CONFIG, "Framebuffer %s\n",
 		   sna->tiling & SNA_TILING_FB ? "tiled" : "linear");
@@ -511,8 +508,6 @@ static Bool sna_pre_init(ScrnInfoPtr scrn, int flags)
 		   sna->tiling & SNA_TILING_2D ? "tiled" : "linear");
 	xf86DrvMsg(scrn->scrnIndex, X_CONFIG, "3D buffers %s\n",
 		   sna->tiling & SNA_TILING_3D ? "tiled" : "linear");
-	xf86DrvMsg(scrn->scrnIndex, X_CONFIG, "SwapBuffers wait %sabled\n",
-		   sna->flags & SNA_SWAP_WAIT ? "en" : "dis");
 	xf86DrvMsg(scrn->scrnIndex, X_CONFIG, "Throttling %sabled\n",
 		   sna->flags & SNA_NO_THROTTLE ? "dis" : "en");
 
@@ -558,18 +553,11 @@ static Bool sna_pre_init(ScrnInfoPtr scrn, int flags)
 static void
 sna_block_handler(int i, pointer data, pointer timeout, pointer read_mask)
 {
-	ScreenPtr screen = screenInfo.screens[i];
-	ScrnInfoPtr scrn = xf86Screens[i];
-	struct sna *sna = to_sna(scrn);
+	struct sna *sna = data;
 
 	DBG(("%s\n", __FUNCTION__));
 
-	screen->BlockHandler = sna->BlockHandler;
-
-	(*screen->BlockHandler) (i, data, timeout, read_mask);
-
-	sna->BlockHandler = screen->BlockHandler;
-	screen->BlockHandler = sna_block_handler;
+	sna->BlockHandler(i, sna->BlockData, timeout, read_mask);
 
 	sna_accel_block_handler(sna);
 }
@@ -577,19 +565,10 @@ sna_block_handler(int i, pointer data, pointer timeout, pointer read_mask)
 static void
 sna_wakeup_handler(int i, pointer data, unsigned long result, pointer read_mask)
 {
-	ScreenPtr screen = screenInfo.screens[i];
-	ScrnInfoPtr scrn = xf86Screens[i];
-	struct sna *sna = to_sna(scrn);
+	struct sna *sna = data;
 
 	DBG(("%s\n", __FUNCTION__));
 
-	screen->WakeupHandler = sna->WakeupHandler;
-
-	(*screen->WakeupHandler) (i, data, result, read_mask);
-
-	sna->WakeupHandler = screen->WakeupHandler;
-	screen->WakeupHandler = sna_wakeup_handler;
-
 	/* despite all appearances, result is just a signed int */
 	if ((int)result < 0)
 		return;
@@ -597,6 +576,8 @@ sna_wakeup_handler(int i, pointer data, unsigned long result, pointer read_mask)
 	if (FD_ISSET(sna->kgem.fd, (fd_set*)read_mask))
 		sna_dri_wakeup(sna);
 
+	sna->WakeupHandler(i, sna->WakeupData, result, read_mask);
+
 	sna_accel_wakeup_handler(sna);
 }
 
@@ -852,10 +833,14 @@ sna_screen_init(int scrnIndex, ScreenPtr screen, int argc, char **argv)
 	scrn->vtSema = TRUE;
 
 	sna->BlockHandler = screen->BlockHandler;
+	sna->BlockData = screen->blockData;
 	screen->BlockHandler = sna_block_handler;
+	screen->blockData = sna;
 
 	sna->WakeupHandler = screen->WakeupHandler;
+	sna->WakeupData = screen->wakeupData;
 	screen->WakeupHandler = sna_wakeup_handler;
+	screen->wakeupData = sna;
 
 	screen->SaveScreen = xf86SaveScreen;
 	sna->CloseScreen = screen->CloseScreen;
diff --git a/src/sna/sna_video_textured.c b/src/sna/sna_video_textured.c
index 66c70d4..a10c676 100644
--- a/src/sna/sna_video_textured.c
+++ b/src/sna/sna_video_textured.c
@@ -238,6 +238,7 @@ sna_video_textured_put_image(ScrnInfoPtr scrn,
 	BoxRec dstBox;
 	xf86CrtcPtr crtc;
 	int top, left, npixels, nlines;
+	Bool flush = false;
 
 	if (!sna_video_clip_helper(scrn, video, &crtc, &dstBox,
 				   src_x, src_y, drw_x, drw_y,
@@ -267,7 +268,8 @@ sna_video_textured_put_image(ScrnInfoPtr scrn,
 	}
 
 	if (crtc && video->SyncToVblank != 0)
-		sna_wait_for_scanline(sna, pixmap, crtc, clip);
+		flush = sna_wait_for_scanline(sna, pixmap, crtc,
+					      &clip->extents);
 
 	sna->render.video(sna, video, &frame, clip,
 			  src_w, src_h,
@@ -281,7 +283,8 @@ sna_video_textured_put_image(ScrnInfoPtr scrn,
 	/* Push the frame to the GPU as soon as possible so
 	 * we can hit the next vsync.
 	 */
-	kgem_submit(&sna->kgem);
+	if (flush)
+		kgem_submit(&sna->kgem);
 
 	return Success;
 }
commit b199bc2b415fd0f06e456e0b1f44dd25ba8dbe02
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Jun 11 09:57:46 2011 +0100

    sna/dri: Skip querying current msc for immediate swaps
    
    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 72962ed..94cd205 100644
--- a/src/sna/sna_dri.c
+++ b/src/sna/sna_dri.c
@@ -975,7 +975,7 @@ sna_dri_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	ScrnInfoPtr scrn = xf86Screens[screen->myNum];
 	struct sna *sna = to_sna(scrn);
 	drmVBlank vbl;
-	int ret, pipe, flip = 0;
+	int pipe, flip;
 	struct sna_dri_frame_event *info;
 	enum DRI2FrameEventType swap_type = DRI2_SWAP;
 	CARD64 current_msc;
@@ -1013,13 +1013,23 @@ sna_dri_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	sna_dri_reference_buffer(front);
 	sna_dri_reference_buffer(back);
 
+	flip = 0;
+	if (can_flip(sna, draw, front, back)) {
+		DBG(("%s: can flip\n", __FUNCTION__));
+		swap_type = DRI2_FLIP;
+		flip = 1;
+	}
+
+	info->type = swap_type;
+	if (divisor == 0)
+		goto immediate;
+
 	/* Get current count */
 	vbl.request.type = DRM_VBLANK_RELATIVE;
 	if (pipe > 0)
 		vbl.request.type |= DRM_VBLANK_SECONDARY;
 	vbl.request.sequence = 0;
-	ret = drmWaitVBlank(sna->kgem.fd, &vbl);
-	if (ret) {
+	if (drmWaitVBlank(sna->kgem.fd, &vbl)) {
 		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
 			   "first get vblank counter failed: %s\n",
 			   strerror(errno));
@@ -1028,21 +1038,18 @@ sna_dri_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 
 	current_msc = vbl.reply.sequence;
 
-	/* Flips need to be submitted one frame before */
-	if (can_flip(sna, draw, front, back)) {
-		DBG(("%s: can flip\n", __FUNCTION__));
-		swap_type = DRI2_FLIP;
-		flip = 1;
-	}
-
-	info->type = swap_type;
-
 	/*
 	 * If divisor is zero, or current_msc is smaller than target_msc
 	 * we just need to make sure target_msc passes before initiating
 	 * the swap.
 	 */
-	if (divisor == 0 || current_msc < *target_msc) {
+	if (current_msc < *target_msc) {
+		DBG(("%s: performing immediate swap: current=%d, target=%d,  divisor=%d\n",
+		     __FUNCTION__,
+		     (int)current_msc,
+		     (int)*target_msc,
+		     (int)divisor));
+
 		/* If target_msc already reached or passed, set it to
 		 * current_msc to ensure we return a reasonable value back
 		 * to the caller. This makes swap_interval logic more robust.
@@ -1050,40 +1057,15 @@ sna_dri_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 		if (current_msc >= *target_msc)
 			*target_msc = current_msc;
 
+immediate:
 		info->frame = *target_msc;
 		if (flip && sna_dri_schedule_flip(sna, draw, info)) {
 			sna_dri_exchange_buffers(draw, front, back);
 			return TRUE;
 		}
 
-		DBG(("%s: waiting before swapping: current=%d, target=%d,  divisor=%d\n",
-		     __FUNCTION__,
-		     (int)current_msc,
-		     (int)*target_msc,
-		     (int)divisor));
-
-		vbl.request.type =  DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
-		if (pipe > 0)
-			vbl.request.type |= DRM_VBLANK_SECONDARY;
-
-		/* If non-pageflipping, but blitting/exchanging, we need to use
-		 * DRM_VBLANK_NEXTONMISS to avoid unreliable timestamping later
-		 * on.
-		 */
-		if (flip == 0)
-			vbl.request.type |= DRM_VBLANK_NEXTONMISS;
-		if (pipe > 0)
-			vbl.request.type |= DRM_VBLANK_SECONDARY;
-
-		vbl.request.sequence = *target_msc;
-		vbl.request.signal = (unsigned long)info;
-		if (drmWaitVBlank(sna->kgem.fd, &vbl)) {
-			xf86DrvMsg(scrn->scrnIndex, X_WARNING,
-				   "divisor 0 get vblank counter failed: %s\n",
-				   strerror(errno));
-			goto blit_fallback;
-		}
-		return TRUE;
+		/* Similarly, the CopyRegion blit is coupled to vsync. */
+		goto blit_fallback;
 	}
 
 	/* Correct target_msc by 'flip' if swap_type == DRI2_FLIP.


More information about the xorg-commit mailing list