xf86-video-intel: 3 commits - src/sna/gen6_render.c src/sna/gen7_render.c src/sna/kgem.c src/sna/kgem.h src/sna/sna_dri.c src/sna/sna_video.c

Chris Wilson ickle at kemper.freedesktop.org
Mon Dec 31 08:32:57 PST 2012


 src/sna/gen6_render.c |    2 
 src/sna/gen7_render.c |    2 
 src/sna/kgem.c        |   45 ++
 src/sna/kgem.h        |    1 
 src/sna/sna_dri.c     |  795 +++++++++++++++++++++-----------------------------
 src/sna/sna_video.c   |    3 
 6 files changed, 395 insertions(+), 453 deletions(-)

New commits:
commit e4f6ba6b47c41645a40e314f14047ba0b5f93a01
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Dec 31 14:06:36 2012 +0000

    sna/gen6+: Hint that we prefer to use the BLT with uncached scanouts
    
    Once again balancing the trade-off of faster smaller copies with the BLT
    versus the faster larger copies the RENDER ring.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/gen6_render.c b/src/sna/gen6_render.c
index dd8f0dc..65b4dc4 100644
--- a/src/sna/gen6_render.c
+++ b/src/sna/gen6_render.c
@@ -1975,7 +1975,7 @@ static int prefer_blt_bo(struct sna *sna, struct kgem_bo *bo)
 	if (bo->rq)
 		return RQ_IS_BLT(bo->rq) ? 1 : -1;
 
-	return bo->tiling == I915_TILING_NONE;
+	return bo->tiling == I915_TILING_NONE || bo->scanout;
 }
 
 inline static bool prefer_blt_ring(struct sna *sna,
diff --git a/src/sna/gen7_render.c b/src/sna/gen7_render.c
index edab5db..173a558 100644
--- a/src/sna/gen7_render.c
+++ b/src/sna/gen7_render.c
@@ -2098,7 +2098,7 @@ static int prefer_blt_bo(struct sna *sna, struct kgem_bo *bo)
 	if (bo->rq)
 		return RQ_IS_BLT(bo->rq) ? 1 : -1;
 
-	return bo->tiling == I915_TILING_NONE;
+	return bo->tiling == I915_TILING_NONE || bo->scanout;
 }
 
 inline static bool prefer_blt_ring(struct sna *sna,
commit 6e87e7ddfe0c21e0fb6b3c2cb940a40aa7d4e061
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Dec 31 14:03:16 2012 +0000

    sna/dri: Use the default choice of backend for copying the region
    
    Notably, if everything is idle, using the BLT is a win as we can emit
    them so much faster than a rendercopy, and as the target is uncached we
    do not benefit as much from the rendercache.
    
    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 9886f37..503e53b 100644
--- a/src/sna/sna_dri.c
+++ b/src/sna/sna_dri.c
@@ -522,8 +522,7 @@ static void sna_dri_select_mode(struct sna *sna, struct kgem_bo *dst, struct kge
 	}
 
 	if (sna->kgem.has_semaphores) {
-		DBG(("%s: have sempahores, prefering RENDER\n", __FUNCTION__));
-		kgem_set_mode(&sna->kgem, KGEM_RENDER, dst);
+		DBG(("%s: have sempahores, prefering defaults\n", __FUNCTION__));
 		return;
 	}
 
commit a7988bf77f5a106a48b6e39b6eaf60ef2f8bec11
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Dec 30 14:50:49 2012 +0000

    sna/dri: Fix triple buffering to not penalise missed frames
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/kgem.c b/src/sna/kgem.c
index 21d5d1c..f4c2b1c 100644
--- a/src/sna/kgem.c
+++ b/src/sna/kgem.c
@@ -942,6 +942,7 @@ void kgem_init(struct kgem *kgem, int fd, struct pci_device *dev, unsigned gen)
 	list_init(&kgem->large);
 	list_init(&kgem->large_inactive);
 	list_init(&kgem->snoop);
+	list_init(&kgem->scanout);
 	for (i = 0; i < ARRAY_SIZE(kgem->pinned_batches); i++)
 		list_init(&kgem->pinned_batches[i]);
 	for (i = 0; i < ARRAY_SIZE(kgem->inactive); i++)
@@ -1666,7 +1667,6 @@ static void __kgem_bo_destroy(struct kgem *kgem, struct kgem_bo *bo)
 	assert(bo->proxy == NULL);
 
 	bo->binding.offset = 0;
-	kgem_bo_clear_scanout(kgem, bo);
 
 	if (DBG_NO_CACHE)
 		goto destroy;
@@ -1708,6 +1708,12 @@ static void __kgem_bo_destroy(struct kgem *kgem, struct kgem_bo *bo)
 			bo->reusable = false;
 	}
 
+	if (bo->scanout) {
+		DBG(("%s: handle=%d -> scanout\n", __FUNCTION__, bo->handle));
+		list_add(&bo->list, &kgem->scanout);
+		return;
+	}
+
 	if (!bo->reusable) {
 		DBG(("%s: handle=%d, not reusable\n",
 		     __FUNCTION__, bo->handle));
@@ -2705,6 +2711,13 @@ bool kgem_expire_cache(struct kgem *kgem)
 
 	}
 
+	while (!list_is_empty(&kgem->scanout)) {
+		bo = list_first_entry(&kgem->scanout, struct kgem_bo, list);
+		list_del(&bo->list);
+		kgem_bo_clear_scanout(kgem, bo);
+		__kgem_bo_destroy(kgem, bo);
+	}
+
 	expire = 0;
 	list_for_each_entry(bo, &kgem->snoop, list) {
 		if (bo->delta) {
@@ -3393,6 +3406,36 @@ struct kgem_bo *kgem_create_2d(struct kgem *kgem,
 	size /= PAGE_SIZE;
 	bucket = cache_bucket(size);
 
+	if (flags & CREATE_SCANOUT) {
+		list_for_each_entry(bo, &kgem->scanout, list) {
+			assert(bo->scanout);
+			assert(bo->delta);
+			assert(!bo->purged);
+
+			if (size > num_pages(bo) || num_pages(bo) > 2*size)
+				continue;
+
+			if (bo->tiling != tiling ||
+			    (tiling != I915_TILING_NONE && bo->pitch != pitch)) {
+				if (!gem_set_tiling(kgem->fd, bo->handle,
+						    tiling, pitch))
+					continue;
+
+				bo->tiling = tiling;
+				bo->pitch = pitch;
+			}
+
+			list_del(&bo->list);
+
+			bo->unique_id = kgem_get_unique_id(kgem);
+			DBG(("  1:from scanout: pitch=%d, tiling=%d, handle=%d, id=%d\n",
+			     bo->pitch, bo->tiling, bo->handle, bo->unique_id));
+			assert(bo->pitch*kgem_aligned_height(kgem, height, bo->tiling) <= kgem_bo_size(bo));
+			bo->refcnt = 1;
+			return bo;
+		}
+	}
+
 	if (bucket >= NUM_CACHE_BUCKETS) {
 		DBG(("%s: large bo num pages=%d, bucket=%d\n",
 		     __FUNCTION__, size, bucket));
diff --git a/src/sna/kgem.h b/src/sna/kgem.h
index bf45793..eed4132 100644
--- a/src/sna/kgem.h
+++ b/src/sna/kgem.h
@@ -133,6 +133,7 @@ struct kgem {
 	struct list inactive[NUM_CACHE_BUCKETS];
 	struct list pinned_batches[2];
 	struct list snoop;
+	struct list scanout;
 	struct list batch_buffers, active_buffers;
 
 	struct list requests[2];
diff --git a/src/sna/sna_dri.c b/src/sna/sna_dri.c
index af75a55..9886f37 100644
--- a/src/sna/sna_dri.c
+++ b/src/sna/sna_dri.c
@@ -52,18 +52,17 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
 #endif
 
 #if DRI2INFOREC_VERSION < 10
+#undef USE_ASYNC_SWAP
 #define USE_ASYNC_SWAP 0
 #endif
 
 #define COLOR_PREFER_TILING_Y 0
-#define FLIP_OFF_DELAY 5
 
 enum frame_event_type {
 	DRI2_SWAP,
 	DRI2_SWAP_WAIT,
 	DRI2_SWAP_THROTTLE,
 	DRI2_XCHG_THROTTLE,
-	DRI2_ASYNC_FLIP,
 	DRI2_FLIP,
 	DRI2_FLIP_THROTTLE,
 	DRI2_WAITMSC,
@@ -92,9 +91,9 @@ struct sna_dri_frame_event {
 	struct dri_bo {
 		struct kgem_bo *bo;
 		uint32_t name;
-	} old_front, next_front, cache;
+	} scanout[2], cache;
 
-	int off_delay;
+	int mode;
 };
 
 struct sna_dri_private {
@@ -1019,8 +1018,10 @@ sna_dri_frame_event_info_free(struct sna *sna,
 	_sna_dri_destroy_buffer(sna, info->front);
 	_sna_dri_destroy_buffer(sna, info->back);
 
-	if (info->old_front.bo)
-		kgem_bo_destroy(&sna->kgem, info->old_front.bo);
+	assert(info->scanout[1].bo == NULL);
+
+	if (info->scanout[0].bo)
+		kgem_bo_destroy(&sna->kgem, info->scanout[0].bo);
 
 	if (info->cache.bo)
 		kgem_bo_destroy(&sna->kgem, info->cache.bo);
@@ -1035,23 +1036,34 @@ static bool
 sna_dri_page_flip(struct sna *sna, struct sna_dri_frame_event *info)
 {
 	struct kgem_bo *bo = get_private(info->back)->bo;
+	struct dri_bo tmp;
 
 	DBG(("%s()\n", __FUNCTION__));
 
 	assert(sna_pixmap_get_buffer(sna->front) == info->front);
 	assert(get_drawable_pixmap(info->draw)->drawable.height * bo->pitch <= kgem_bo_size(bo));
+	assert(info->scanout[0].bo);
 
 	info->count = sna_page_flip(sna, bo, info, info->pipe);
 	if (!info->count)
 		return false;
 
-	info->old_front.name = info->front->name;
-	info->old_front.bo = get_private(info->front)->bo;
+	info->scanout[1] = info->scanout[0];
+	info->scanout[0].bo = ref(bo);
+	info->scanout[0].name = info->back->name;
+
+	tmp.bo = get_private(info->front)->bo;
+	tmp.name = info->front->name;
 
 	set_bo(sna->front, bo);
 
 	info->front->name = info->back->name;
 	get_private(info->front)->bo = bo;
+
+	info->back->name = tmp.name;
+	get_private(info->back)->bo = tmp.bo;
+
+	sna->dri.flip_pending = info;
 	return true;
 }
 
@@ -1117,6 +1129,7 @@ can_flip(struct sna * sna,
 		return false;
 	}
 	assert(get_private(front)->pixmap == sna->front);
+	assert(sna_pixmap(sna->front)->gpu_bo == get_private(front)->bo);
 
 	if (!get_private(back)->scanout) {
 		DBG(("%s: no, DRI2 drawable was too small at time of creation)\n",
@@ -1274,10 +1287,12 @@ sna_dri_exchange_buffers(DrawablePtr draw,
 	     pixmap->drawable.width,
 	     pixmap->drawable.height));
 
-	DBG(("%s: back_bo pitch=%d, size=%d\n",
-	     __FUNCTION__, back_bo->pitch, kgem_bo_size(back_bo)));
-	DBG(("%s: front_bo pitch=%d, size=%d\n",
-	     __FUNCTION__, front_bo->pitch, kgem_bo_size(front_bo)));
+	DBG(("%s: back_bo pitch=%d, size=%d, ref=%d\n",
+	     __FUNCTION__, back_bo->pitch, kgem_bo_size(back_bo), back_bo->refcnt));
+	DBG(("%s: front_bo pitch=%d, size=%d, ref=%d\n",
+	     __FUNCTION__, front_bo->pitch, kgem_bo_size(front_bo), front_bo->refcnt));
+	assert(front_bo->refcnt);
+	assert(back_bo->refcnt);
 
 	assert(sna_pixmap_get_buffer(pixmap) == front);
 	assert(pixmap->drawable.height * back_bo->pitch <= kgem_bo_size(back_bo));
@@ -1387,12 +1402,9 @@ void sna_dri_vblank_handler(struct sna *sna, struct drm_event_vblank *event)
 	case DRI2_FLIP:
 		/* If we can still flip... */
 		if (can_flip(sna, draw, info->front, info->back) &&
-		    sna_dri_page_flip(sna, info)) {
-			info->back->name = info->old_front.name;
-			get_private(info->back)->bo = info->old_front.bo;
-			info->old_front.bo = NULL;
+		    sna_dri_page_flip(sna, info))
 			return;
-		}
+
 		/* else fall through to blit */
 	case DRI2_SWAP:
 		if (can_blit(sna, draw, info->front, info->back))
@@ -1449,24 +1461,91 @@ done:
 	sna_dri_frame_event_info_free(sna, draw, info);
 }
 
+static void
+sna_dri_flip_get_back(struct sna *sna, struct sna_dri_frame_event *info)
+{
+	struct kgem_bo *bo;
+	uint32_t name;
+
+	DBG(("%s: scanout=(%d, %d), back=%d, cache=%d\n",
+	     __FUNCTION__,
+	     info->scanout[0].bo ? info->scanout[0].bo->handle : 0,
+	     info->scanout[1].bo ? info->scanout[1].bo->handle : 0,
+	     get_private(info->back)->bo->handle,
+	     info->cache.bo ? info->cache.bo->handle : 0));
+
+	bo = get_private(info->back)->bo;
+	if (!(bo == info->scanout[0].bo || bo == info->scanout[1].bo))
+		return;
+
+	bo = info->cache.bo;
+	name = info->cache.name;
+	if (bo == NULL ||
+	    bo == info->scanout[0].bo ||
+	    bo == info->scanout[1].bo) {
+		if (bo) {
+			DBG(("%s: discarding old backbuffer\n", __FUNCTION__));
+			kgem_bo_destroy(&sna->kgem, bo);
+		}
+		DBG(("%s: allocating new backbuffer\n", __FUNCTION__));
+		bo = kgem_create_2d(&sna->kgem,
+				    info->draw->width,
+				    info->draw->height,
+				    info->draw->bitsPerPixel,
+				    get_private(info->front)->bo->tiling,
+				    CREATE_SCANOUT | CREATE_EXACT);
+		name = kgem_bo_flink(&sna->kgem, bo);
+	}
+
+	info->cache.bo = get_private(info->back)->bo;
+	info->cache.name = info->back->name;
+
+	get_private(info->back)->bo = bo;
+	info->back->name = name;
+
+	assert(get_private(info->back)->bo != info->scanout[0].bo);
+	assert(get_private(info->back)->bo != info->scanout[1].bo);
+}
+
 static bool
 sna_dri_flip_continue(struct sna *sna, struct sna_dri_frame_event *info)
 {
-	struct dri_bo tmp;
+	DBG(("%s(mode=%d)\n", __FUNCTION__, info->mode));
 
-	DBG(("%s()\n", __FUNCTION__));
+	if (info->mode > 1){
+		if (get_private(info->front)->bo != sna_pixmap(sna->front)->gpu_bo)
+			return false;
 
-	assert(sna_pixmap_get_buffer(get_drawable_pixmap(info->draw)) == info->front);
+		info->count = sna_page_flip(sna,
+					    get_private(info->front)->bo,
+					    info, info->pipe);
+		if (!info->count)
+			return false;
 
-	tmp = info->old_front;
+		info->scanout[1] = info->scanout[0];
+		info->scanout[0].bo = ref(get_private(info->front)->bo);
+		info->scanout[0].name = info->front->name;
+		sna->dri.flip_pending = info;
+	} else {
+		if (!info->draw)
+			return false;
 
-	if (!sna_dri_page_flip(sna, info))
-		return false;
+		assert(sna_pixmap_get_buffer(get_drawable_pixmap(info->draw)) == info->front);
+		if (!can_flip(sna, info->draw, info->front, info->back))
+			return false;
 
-	get_private(info->back)->bo = tmp.bo;
-	info->back->name = tmp.name;
+		if (!sna_dri_page_flip(sna, info))
+			return false;
 
-	info->next_front.name = 0;
+		sna_dri_flip_get_back(sna, info);
+		DRI2SwapComplete(info->client, info->draw,
+				 0, 0, 0,
+				 DRI2_FLIP_COMPLETE,
+				 info->client ? info->event_complete : NULL,
+				 info->event_data);
+	}
+
+	info->mode = 0;
 	return true;
 }
 
@@ -1488,23 +1567,9 @@ static void chain_flip(struct sna *sna)
 	    can_flip(sna, chain->draw, chain->front, chain->back) &&
 	    sna_dri_page_flip(sna, chain)) {
 		DBG(("%s: performing chained flip\n", __FUNCTION__));
-
-		chain->back->name = chain->old_front.name;
-		get_private(chain->back)->bo = chain->old_front.bo;
-		chain->old_front.bo = NULL;
-
-		if (chain->count == 0) {
-			DRI2SwapComplete(chain->client, chain->draw, 0, 0, 0,
-					 DRI2_EXCHANGE_COMPLETE,
-					 chain->event_complete,
-					 chain->event_data);
-			sna_dri_frame_event_info_free(sna, chain->draw, chain);
-		} else
-			sna->dri.flip_pending = chain;
 	} else {
 		if (can_blit(sna, chain->draw, chain->front, chain->back)) {
 			DBG(("%s: emitting chained vsync'ed blit\n", __FUNCTION__));
-
 			chain->bo = sna_dri_copy_to_front(sna, chain->draw, NULL,
 							  get_private(chain->front)->bo,
 							  get_private(chain->back)->bo,
@@ -1526,6 +1591,14 @@ static void sna_dri_flip_event(struct sna *sna,
 	     flip->fe_tv_usec,
 	     flip->type));
 
+	if (flip->cache.bo == NULL) {
+		flip->cache = flip->scanout[1];
+		flip->scanout[1].bo = NULL;
+	}
+	if (flip->scanout[1].bo) {
+		kgem_bo_destroy(&sna->kgem, flip->scanout[1].bo);
+		flip->scanout[1].bo = NULL;
+	}
 	if (sna->dri.flip_pending == flip)
 		sna->dri.flip_pending = NULL;
 
@@ -1553,12 +1626,9 @@ static void sna_dri_flip_event(struct sna *sna,
 		if (sna->dri.flip_pending) {
 			sna_dri_frame_event_info_free(sna, flip->draw, flip);
 			chain_flip(sna);
-		} else if (!flip->next_front.name) {
-			/* Keep the pageflipping running for a couple of frames
-			 * so we keep the uncached scanouts alive.
-			 */
-			DBG(("%s: flip chain complete, off-delay=%d\n",
-			     __FUNCTION__, flip->off_delay));
+		} else if (!flip->mode) {
+			DBG(("%s: flip chain complete\n", __FUNCTION__));
+
 			if (flip->chain) {
 				sna_dri_remove_frame_event((WindowPtr)flip->draw,
 							   flip);
@@ -1570,31 +1640,8 @@ static void sna_dri_flip_event(struct sna *sna,
 				flip->draw = NULL;
 			}
 
-			if (flip->off_delay-- && flip->draw &&
-			    can_flip(sna, flip->draw, flip->front, flip->front) &&
-			    (flip->count = sna_page_flip(sna,
-							 get_private(flip->front)->bo,
-							 flip, flip->pipe))) {
-				assert(flip == sna_dri_window_get_chain((WindowPtr)flip->draw));
-				sna->dri.flip_pending = flip;
-			} else {
-				DBG(("%s: flip chain complete, off\n", __FUNCTION__));
-				sna_dri_frame_event_info_free(sna, flip->draw, flip);
-			}
-		} else if (flip->draw &&
-			   can_flip(sna, flip->draw, flip->front, flip->back) &&
-			   sna_dri_flip_continue(sna, flip)) {
-			DRI2SwapComplete(flip->client, flip->draw,
-					 0, 0, 0,
-					 DRI2_FLIP_COMPLETE,
-					 flip->client ? flip->event_complete : NULL,
-					 flip->event_data);
-			if (flip->count) {
-				flip->off_delay = FLIP_OFF_DELAY;
-				sna->dri.flip_pending = flip;
-			} else
-				sna_dri_frame_event_info_free(sna, flip->draw, flip);
-		} else {
+			sna_dri_frame_event_info_free(sna, flip->draw, flip);
+		} else if (!sna_dri_flip_continue(sna, flip)) {
 			DBG(("%s: no longer able to flip\n", __FUNCTION__));
 
 			if (flip->draw) {
@@ -1615,77 +1662,6 @@ static void sna_dri_flip_event(struct sna *sna,
 		}
 		break;
 
-#if USE_ASYNC_SWAP
-	case DRI2_ASYNC_FLIP:
-		DBG(("%s: async swap flip completed on pipe %d, pending? %d, new? %d\n",
-		     __FUNCTION__, flip->pipe,
-		     sna->dri.flip_pending != NULL,
-		     flip->front->name != flip->next_front.name));
-
-		if (sna->dri.flip_pending) {
-			chain_flip(sna);
-			goto finish_async_flip;
-		} else if (flip->front->name != flip->next_front.name) {
-			DBG(("%s: async flip continuing\n", __FUNCTION__));
-
-			flip->cache = flip->old_front;
-			flip->old_front = flip->next_front;
-
-			flip->count = sna_page_flip(sna,
-						    get_private(flip->front)->bo,
-						    flip, flip->pipe);
-			if (flip->count == 0) {
-				if (flip->chain)
-					goto chain_async_flip;
-				else
-					goto finish_async_flip;
-			}
-
-			flip->next_front.bo = get_private(flip->front)->bo;
-			flip->next_front.name = flip->front->name;
-			flip->off_delay = FLIP_OFF_DELAY;
-
-			sna->dri.flip_pending = flip;
-		} else {
-			if (flip->chain) {
-chain_async_flip:
-				sna_dri_remove_frame_event((WindowPtr)flip->draw,
-							   flip);
-				chain_swap(sna, flip->draw,
-					   flip->fe_frame,
-					   flip->fe_tv_sec,
-					   flip->fe_tv_usec,
-					   flip->chain);
-				flip->draw = NULL;
-			}
-
-			if (flip->draw &&
-			    can_flip(sna, flip->draw, flip->front, flip->back) &&
-			    flip->off_delay--) {
-				assert(flip == sna_dri_window_get_chain((WindowPtr)flip->draw));
-				DBG(("%s: queuing no-flip [delay=%d]\n",
-				     __FUNCTION__, flip->off_delay));
-				/* Just queue a no-op flip to trigger another event */
-				flip->count = sna_page_flip(sna,
-							    get_private(flip->front)->bo,
-							    flip, flip->pipe);
-				if (flip->count == 0)
-					goto finish_async_flip;
-
-				assert(flip->next_front.bo == get_private(flip->front)->bo);
-				assert(flip->next_front.name == flip->front->name);
-
-				sna->dri.flip_pending = flip;
-			} else {
-finish_async_flip:
-				DBG(("%s: async flip completed (drawable gone? %d)\n",
-				     __FUNCTION__, flip->draw == NULL));
-				sna_dri_frame_event_info_free(sna, flip->draw, flip);
-			}
-		}
-		break;
-#endif
-
 	default:
 		xf86DrvMsg(sna->scrn->scrnIndex, X_WARNING,
 			   "%s: unknown vblank event received\n", __func__);
@@ -1715,6 +1691,105 @@ sna_dri_page_flip_handler(struct sna *sna,
 	sna_dri_flip_event(sna, info);
 }
 
+static void
+sna_dri_immediate_xchg(struct sna *sna,
+		       DrawablePtr draw,
+		       struct sna_dri_frame_event *info,
+		       bool sync)
+{
+	drmVBlank vbl;
+
+	if (sna->flags & SNA_NO_WAIT)
+		sync = false;
+
+	DBG(("%s: emitting immediate exchange, throttling client, synced? %d\n",
+	     __FUNCTION__, sync));
+	VG_CLEAR(vbl);
+
+	if (sync) {
+		info->type = DRI2_XCHG_THROTTLE;
+		if (sna_dri_window_get_chain((WindowPtr)draw) == info) {
+			DBG(("%s: no pending xchg, starting chain\n",
+			     __FUNCTION__));
+
+			sna_dri_exchange_buffers(draw, info->front, info->back);
+			DRI2SwapComplete(info->client, draw, 0, 0, 0,
+					 DRI2_EXCHANGE_COMPLETE,
+					 info->event_complete,
+					 info->event_data);
+			vbl.request.type =
+				DRM_VBLANK_RELATIVE |
+				DRM_VBLANK_NEXTONMISS |
+				DRM_VBLANK_EVENT |
+				pipe_select(info->pipe);
+			vbl.request.sequence = 0;
+			vbl.request.signal = (unsigned long)info;
+			if (sna_wait_vblank(sna, &vbl))
+				sna_dri_frame_event_info_free(sna, draw, info);
+		}
+	} else {
+		sna_dri_exchange_buffers(draw, info->front, info->back);
+		DRI2SwapComplete(info->client, draw, 0, 0, 0,
+				 DRI2_EXCHANGE_COMPLETE,
+				 info->event_complete,
+				 info->event_data);
+		sna_dri_frame_event_info_free(sna, draw, info);
+	}
+}
+
+static void
+sna_dri_immediate_blit(struct sna *sna,
+		       DrawablePtr draw,
+		       struct sna_dri_frame_event *info,
+		       bool sync)
+{
+	if (sna->flags & SNA_NO_WAIT)
+		sync = false;
+
+	DBG(("%s: emitting immediate blit, throttling client, synced? %d\n",
+	     __FUNCTION__, sync));
+
+	if (sync) {
+		info->type = DRI2_SWAP_THROTTLE;
+		if (sna_dri_window_get_chain((WindowPtr)draw) == info) {
+			drmVBlank vbl;
+
+			DBG(("%s: no pending blit, starting chain\n",
+			     __FUNCTION__));
+
+			info->bo = sna_dri_copy_to_front(sna, draw, NULL,
+							 get_private(info->front)->bo,
+							 get_private(info->back)->bo,
+							 true);
+			DRI2SwapComplete(info->client, draw, 0, 0, 0,
+					 DRI2_BLIT_COMPLETE,
+					 info->event_complete,
+					 info->event_data);
+
+			VG_CLEAR(vbl);
+			vbl.request.type =
+				DRM_VBLANK_RELATIVE |
+				DRM_VBLANK_NEXTONMISS |
+				DRM_VBLANK_EVENT |
+				pipe_select(info->pipe);
+			vbl.request.sequence = 0;
+			vbl.request.signal = (unsigned long)info;
+			if (sna_wait_vblank(sna, &vbl))
+				sna_dri_frame_event_info_free(sna, draw, info);
+		}
+	} else {
+		info->bo = sna_dri_copy_to_front(sna, draw, NULL,
+						 get_private(info->front)->bo,
+						 get_private(info->back)->bo,
+						 false);
+		DRI2SwapComplete(info->client, draw, 0, 0, 0,
+				 DRI2_BLIT_COMPLETE,
+				 info->event_complete,
+				 info->event_data);
+		sna_dri_frame_event_info_free(sna, draw, info);
+	}
+}
+
 static CARD64
 get_current_msc_for_target(struct sna *sna, CARD64 target_msc, int pipe)
 {
@@ -1734,50 +1809,43 @@ get_current_msc_for_target(struct sna *sna, CARD64 target_msc, int pipe)
 }
 
 static bool
-sna_dri_schedule_flip(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
-		      DRI2BufferPtr back, CARD64 *target_msc, CARD64 divisor,
-		      CARD64 remainder, DRI2SwapEventPtr func, void *data)
+sna_dri_schedule_flip(ClientPtr client, DrawablePtr draw,
+		      DRI2BufferPtr front, DRI2BufferPtr back, int pipe,
+		      CARD64 *target_msc, CARD64 divisor, CARD64 remainder,
+		      DRI2SwapEventPtr func, void *data)
 {
 	struct sna *sna = to_sna_from_drawable(draw);
 	struct sna_dri_frame_event *info;
 	drmVBlank vbl;
-	int pipe;
 	CARD64 current_msc;
 
-	DBG(("%s(target_msc=%llu, divisor=%llu, remainder=%llu)\n",
-	     __FUNCTION__,
-	     (long long)*target_msc,
-	     (long long)divisor,
-	     (long long)remainder));
-
-	VG_CLEAR(vbl);
-
-	pipe = sna_dri_get_pipe(draw);
-	if (pipe == -1) {
-		/* XXX WARN_ON(sna->dri.flip_pending) ? */
-		if (sna->dri.flip_pending == NULL) {
-			sna_dri_exchange_buffers(draw, front, back);
-			DRI2SwapComplete(client, draw, 0, 0, 0,
-					DRI2_EXCHANGE_COMPLETE, func, data);
-			return true;
-		} else
-			return false;
-	}
-
 	current_msc = get_current_msc_for_target(sna, *target_msc, pipe);
 
 	DBG(("%s: target_msc=%u, current_msc=%u, divisor=%u\n", __FUNCTION__,
 	     (uint32_t)*target_msc, (uint32_t)current_msc, (uint32_t)divisor));
 
-	if (divisor == 0 && current_msc >= *target_msc) {
-		DBG(("%s: performing immediate swap on pipe %d, pending? %d\n",
-		     __FUNCTION__, pipe, sna->dri.flip_pending != NULL));
-
+	if (divisor == 0 && current_msc >= *target_msc - 1) {
 		info = sna->dri.flip_pending;
-		if (info && info->draw == draw && info->type == DRI2_FLIP_THROTTLE) {
-			DBG(("%s: chaining flip\n", __FUNCTION__));
-			info->next_front.name = 1;
-			return true;
+
+		DBG(("%s: performing immediate swap on pipe %d, pending? %d, mode: %d\n",
+		     __FUNCTION__, pipe, info != NULL, info ? info->mode : 0));
+
+		if (info && info->draw == draw) {
+			assert(info->type == DRI2_FLIP_THROTTLE);
+			assert(info->front == front);
+			assert(info->back == back);
+			if (current_msc >= *target_msc) {
+				DBG(("%s: executing xchg of pending flip\n",
+				     __FUNCTION__));
+				sna_dri_exchange_buffers(draw, front, back);
+				info->mode = 2;
+				goto new_back;
+			} else {
+				DBG(("%s: chaining flip\n", __FUNCTION__));
+				info->mode = 1;
+				current_msc++;
+				goto out;
+			}
 		}
 
 		info = calloc(1, sizeof(struct sna_dri_frame_event));
@@ -1794,6 +1862,9 @@ sna_dri_schedule_flip(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 		info->back = back;
 		info->pipe = pipe;
 
+		info->scanout[0].bo = ref(get_private(front)->bo);
+		info->scanout[0].name = info->front->name;
+
 		sna_dri_add_frame_event(draw, info);
 		sna_dri_reference_buffer(front);
 		sna_dri_reference_buffer(back);
@@ -1807,202 +1878,109 @@ sna_dri_schedule_flip(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 			     __FUNCTION__));
 			info->type = DRI2_FLIP;
 			sna->dri.flip_pending = info;
+			*target_msc = current_msc + 1;
 			return true;
 		}
 
 		if (!sna_dri_page_flip(sna, info)) {
 			sna_dri_frame_event_info_free(sna, draw, info);
 			return false;
-		} else if (info->type != DRI2_FLIP) {
-			get_private(info->back)->bo =
-				kgem_create_2d(&sna->kgem,
-					       draw->width,
-					       draw->height,
-					       draw->bitsPerPixel,
-					       get_private(info->front)->bo->tiling,
-					       CREATE_SCANOUT | CREATE_EXACT);
-			info->back->name = kgem_bo_flink(&sna->kgem,
-							 get_private(info->back)->bo);
-			info->off_delay = FLIP_OFF_DELAY;
-			sna->dri.flip_pending = info;
+		}
 
-			DRI2SwapComplete(info->client, draw, 0, 0, 0,
+		if (info->type != DRI2_FLIP) {
+			current_msc++;
+new_back:
+			sna_dri_flip_get_back(sna, info);
+			DRI2SwapComplete(client, draw, 0, 0, 0,
 					 DRI2_EXCHANGE_COMPLETE,
-					 info->event_complete,
-					 info->event_data);
-		} else {
-			info->back->name = info->old_front.name;
-			get_private(info->back)->bo = info->old_front.bo;
-			info->old_front.bo = NULL;
+					 func, data);
 		}
-	} else {
-		info = calloc(1, sizeof(struct sna_dri_frame_event));
-		if (info == NULL)
-			return false;
-
-		info->draw = draw;
-		info->client = client;
-		info->event_complete = func;
-		info->event_data = data;
-		info->front = front;
-		info->back = back;
-		info->pipe = pipe;
-		info->type = DRI2_FLIP;
-
-		sna_dri_add_frame_event(draw, info);
-		sna_dri_reference_buffer(front);
-		sna_dri_reference_buffer(back);
-
-		*target_msc &= 0xffffffff;
-		remainder &= 0xffffffff;
-
-		vbl.request.type =
-			DRM_VBLANK_ABSOLUTE |
-			DRM_VBLANK_EVENT |
-			pipe_select(pipe);
+out:
+		*target_msc = current_msc;
+		return true;
+	}
 
-		/*
-		 * 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 (current_msc < *target_msc) {
-			DBG(("%s: waiting for swap: current=%d, target=%d, divisor=%d\n",
-			     __FUNCTION__,
-			     (int)current_msc,
-			     (int)*target_msc,
-			     (int)divisor));
-			vbl.request.sequence = *target_msc;
-		} else {
-			DBG(("%s: missed target, queueing event for next: current=%d, target=%d, divisor=%d\n",
-			     __FUNCTION__,
-			     (int)current_msc,
-			     (int)*target_msc,
-			     (int)divisor));
-
-			if (divisor == 0)
-				divisor = 1;
-
-			vbl.request.sequence = current_msc - current_msc % divisor + remainder;
-
-			/*
-			 * If the calculated deadline vbl.request.sequence is
-			 * smaller than or equal to current_msc, it means
-			 * we've passed the last point when effective onset
-			 * frame seq could satisfy *seq % divisor == remainder,
-			 * so we need to wait for the next time this will
-			 * happen.
-			 *
-			 * This comparison takes the 1 frame swap delay
-			 * in pageflipping mode into account.
-			 */
-			if (vbl.request.sequence <= current_msc)
-				vbl.request.sequence += divisor;
+	info = calloc(1, sizeof(struct sna_dri_frame_event));
+	if (info == NULL)
+		return false;
 
-			/* Adjust returned value for 1 frame pageflip offset */
-			*target_msc = vbl.reply.sequence + 1;
-		}
+	info->draw = draw;
+	info->client = client;
+	info->event_complete = func;
+	info->event_data = data;
+	info->front = front;
+	info->back = back;
+	info->pipe = pipe;
+	info->type = DRI2_FLIP;
 
-		/* Account for 1 frame extra pageflip delay */
-		vbl.request.sequence -= 1;
-		vbl.request.signal = (unsigned long)info;
-		if (sna_wait_vblank(sna, &vbl)) {
-			sna_dri_frame_event_info_free(sna, draw, info);
-			return false;
-		}
-	}
+	info->scanout[0].bo = ref(get_private(front)->bo);
+	info->scanout[0].name = info->front->name;
 
-	return true;
-}
+	sna_dri_add_frame_event(draw, info);
+	sna_dri_reference_buffer(front);
+	sna_dri_reference_buffer(back);
 
-static void
-sna_dri_immediate_xchg(struct sna *sna,
-		       DrawablePtr draw,
-		       struct sna_dri_frame_event *info)
-{
-	drmVBlank vbl;
+	*target_msc &= 0xffffffff;
+	remainder &= 0xffffffff;
 
-	DBG(("%s: emitting immediate exchange, throttling client\n",
-	     __FUNCTION__));
 	VG_CLEAR(vbl);
 
-	if ((sna->flags & SNA_NO_WAIT) == 0) {
-		info->type = DRI2_XCHG_THROTTLE;
-		if (sna_dri_window_get_chain((WindowPtr)draw) == info) {
-			DBG(("%s: no pending xchg, starting chain\n",
-			     __FUNCTION__));
+	vbl.request.type =
+		DRM_VBLANK_ABSOLUTE |
+		DRM_VBLANK_EVENT |
+		pipe_select(pipe);
 
-			sna_dri_exchange_buffers(draw, info->front, info->back);
-			DRI2SwapComplete(info->client, draw, 0, 0, 0,
-					 DRI2_EXCHANGE_COMPLETE,
-					 info->event_complete,
-					 info->event_data);
-			vbl.request.type =
-				DRM_VBLANK_RELATIVE |
-				DRM_VBLANK_NEXTONMISS |
-				DRM_VBLANK_EVENT |
-				pipe_select(info->pipe);
-			vbl.request.sequence = 0;
-			vbl.request.signal = (unsigned long)info;
-			if (sna_wait_vblank(sna, &vbl))
-				sna_dri_frame_event_info_free(sna, draw, info);
-		}
+	/*
+	 * 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 (current_msc <= *target_msc - 1) {
+		DBG(("%s: waiting for swap: current=%d, target=%d, divisor=%d\n",
+		     __FUNCTION__,
+		     (int)current_msc,
+		     (int)*target_msc,
+		     (int)divisor));
+		vbl.request.sequence = *target_msc;
 	} else {
-		sna_dri_exchange_buffers(draw, info->front, info->back);
-		DRI2SwapComplete(info->client, draw, 0, 0, 0,
-				 DRI2_EXCHANGE_COMPLETE,
-				 info->event_complete,
-				 info->event_data);
-		sna_dri_frame_event_info_free(sna, draw, info);
-	}
-}
+		DBG(("%s: missed target, queueing event for next: current=%d, target=%d, divisor=%d\n",
+		     __FUNCTION__,
+		     (int)current_msc,
+		     (int)*target_msc,
+		     (int)divisor));
 
-static void
-sna_dri_immediate_blit(struct sna *sna,
-		       DrawablePtr draw,
-		       struct sna_dri_frame_event *info)
-{
-	DBG(("%s: emitting immediate blit, throttling client\n", __FUNCTION__));
+		if (divisor == 0)
+			divisor = 1;
 
-	if ((sna->flags & SNA_NO_WAIT) == 0) {
-		info->type = DRI2_SWAP_THROTTLE;
-		if (sna_dri_window_get_chain((WindowPtr)draw) == info) {
-			drmVBlank vbl;
+		vbl.request.sequence = current_msc - current_msc % divisor + remainder;
 
-			DBG(("%s: no pending blit, starting chain\n",
-			     __FUNCTION__));
+		/*
+		 * If the calculated deadline vbl.request.sequence is
+		 * smaller than or equal to current_msc, it means
+		 * we've passed the last point when effective onset
+		 * frame seq could satisfy *seq % divisor == remainder,
+		 * so we need to wait for the next time this will
+		 * happen.
+		 *
+		 * This comparison takes the 1 frame swap delay
+		 * in pageflipping mode into account.
+		 */
+		if (vbl.request.sequence <= current_msc)
+			vbl.request.sequence += divisor;
 
-			info->bo = sna_dri_copy_to_front(sna, draw, NULL,
-							 get_private(info->front)->bo,
-							 get_private(info->back)->bo,
-							 true);
-			DRI2SwapComplete(info->client, draw, 0, 0, 0,
-					 DRI2_BLIT_COMPLETE,
-					 info->event_complete,
-					 info->event_data);
+		/* Adjust returned value for 1 frame pageflip offset */
+		*target_msc = vbl.reply.sequence;
+	}
 
-			VG_CLEAR(vbl);
-			vbl.request.type =
-				DRM_VBLANK_RELATIVE |
-				DRM_VBLANK_NEXTONMISS |
-				DRM_VBLANK_EVENT |
-				pipe_select(info->pipe);
-			vbl.request.sequence = 0;
-			vbl.request.signal = (unsigned long)info;
-			if (sna_wait_vblank(sna, &vbl))
-				sna_dri_frame_event_info_free(sna, draw, info);
-		}
-	} else {
-		info->bo = sna_dri_copy_to_front(sna, draw, NULL,
-						 get_private(info->front)->bo,
-						 get_private(info->back)->bo,
-						 false);
-		DRI2SwapComplete(info->client, draw, 0, 0, 0,
-				 DRI2_BLIT_COMPLETE,
-				 info->event_complete,
-				 info->event_data);
+	/* Account for 1 frame extra pageflip delay */
+	vbl.request.sequence -= 1;
+	vbl.request.signal = (unsigned long)info;
+	if (sna_wait_vblank(sna, &vbl)) {
 		sna_dri_frame_event_info_free(sna, draw, info);
+		return false;
 	}
+
+	return true;
 }
 
 /*
@@ -2051,30 +2029,19 @@ sna_dri_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	divisor &= 0xffffffff;
 	remainder &= 0xffffffff;
 
-	if (can_flip(sna, draw, front, back)) {
-		DBG(("%s: try flip\n", __FUNCTION__));
-		if (sna_dri_schedule_flip(client, draw, front, back,
-					  target_msc, divisor, remainder,
-					  func, data))
-			return TRUE;
-	}
-
 	/* Drawable not displayed... just complete the swap */
 	pipe = sna_dri_get_pipe(draw);
 	if (pipe == -1) {
-		if (can_exchange(sna, draw, front, back)) {
-			DBG(("%s: unattached, exchange pixmaps\n", __FUNCTION__));
-			sna_dri_exchange_buffers(draw, front, back);
-
-			DRI2SwapComplete(client, draw, 0, 0, 0,
-					 DRI2_EXCHANGE_COMPLETE, func, data);
-			return TRUE;
-		}
-
 		DBG(("%s: off-screen, immediate update\n", __FUNCTION__));
 		goto blit_fallback;
 	}
 
+	if (can_flip(sna, draw, front, back) &&
+	    sna_dri_schedule_flip(client, draw, front, back, pipe,
+				  target_msc, divisor, remainder,
+				  func, data))
+		return TRUE;
+
 	VG_CLEAR(vbl);
 
 	info = calloc(1, sizeof(struct sna_dri_frame_event));
@@ -2099,16 +2066,19 @@ sna_dri_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	DBG(("%s: target_msc=%u, current_msc=%u, divisor=%u\n", __FUNCTION__,
 	     (uint32_t)*target_msc, (uint32_t)current_msc, (uint32_t)divisor));
 
-	if (divisor == 0 && current_msc >= *target_msc) {
+	if (divisor == 0 && current_msc >= *target_msc - 1) {
 		if (can_exchange(sna, draw, front, back)) {
-			sna_dri_immediate_xchg(sna, draw, info);
+			sna_dri_immediate_xchg(sna, draw, info,
+					       current_msc < *target_msc);
 		} else if (can_blit(sna, draw, front, back)) {
-			sna_dri_immediate_blit(sna, draw, info);
+			sna_dri_immediate_blit(sna, draw, info,
+					       current_msc < *target_msc);
 		} else {
 			DRI2SwapComplete(client, draw, 0, 0, 0,
 					 DRI2_BLIT_COMPLETE, func, data);
 			sna_dri_frame_event_info_free(sna, draw, info);
 		}
+		*target_msc = current_msc + 1;
 		return TRUE;
 	}
 
@@ -2168,13 +2138,13 @@ sna_dri_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	 */
 	if (vbl.request.sequence < current_msc)
 		vbl.request.sequence += divisor;
-	vbl.request.sequence -= 1;
+	*target_msc = vbl.reply.sequence;
 
+	vbl.request.sequence -= 1;
 	vbl.request.signal = (unsigned long)info;
 	if (sna_wait_vblank(sna, &vbl))
 		goto blit_fallback;
 
-	*target_msc = vbl.reply.sequence;
 	return TRUE;
 
 blit_fallback:
@@ -2204,19 +2174,20 @@ sna_dri_async_swap(ClientPtr client, DrawablePtr draw,
 		   DRI2SwapEventPtr func, void *data)
 {
 	struct sna *sna = to_sna_from_drawable(draw);
-	struct sna_dri_frame_event *info;
-	struct kgem_bo *bo;
-	int name;
+	CARD64 target_msc = 0;
+	int pipe;
 
 	DBG(("%s()\n", __FUNCTION__));
 
-	if (!can_flip(sna, draw, front, back)) {
-blit:
-		name = DRI2_BLIT_COMPLETE;
+	if (!can_flip(sna, draw, front, back) ||
+	    (pipe = sna_dri_get_pipe(draw)) < 0 ||
+	    !sna_dri_schedule_flip(client, draw, front, back, pipe,
+				   &target_msc, 0, 0, func, data)) {
+		pipe = DRI2_BLIT_COMPLETE;
 		if (can_exchange(sna, draw, front, back)) {
 			DBG(("%s: unable to flip, so xchg\n", __FUNCTION__));
 			sna_dri_exchange_buffers(draw, front, back);
-			name = DRI2_EXCHANGE_COMPLETE;
+			pipe = DRI2_EXCHANGE_COMPLETE;
 		} else if (can_blit(sna, draw, front, back)) {
 			DBG(("%s: unable to flip, so blit\n", __FUNCTION__));
 			sna_dri_copy_to_front(sna, draw, NULL,
@@ -2225,80 +2196,9 @@ blit:
 					      false);
 		}
 
-		DRI2SwapComplete(client, draw, 0, 0, 0, name, func, data);
-		return name == DRI2_EXCHANGE_COMPLETE;
+		DRI2SwapComplete(client, draw, 0, 0, 0, pipe, func, data);
+		return pipe == DRI2_EXCHANGE_COMPLETE;
 	}
-
-	bo = NULL;
-	name = 0;
-
-	info = sna->dri.flip_pending;
-	if (info == NULL) {
-		int pipe = sna_dri_get_pipe(draw);
-		if (pipe == -1)
-			goto blit;
-
-		DBG(("%s: no pending flip, so updating scanout\n",
-		     __FUNCTION__));
-
-		info = calloc(1, sizeof(struct sna_dri_frame_event));
-		if (!info)
-			goto blit;
-
-		info->client = client;
-		info->draw = draw;
-		info->type = DRI2_ASYNC_FLIP;
-		info->pipe = pipe;
-		info->front = front;
-		info->back = back;
-
-		sna_dri_add_frame_event(draw, info);
-		sna_dri_reference_buffer(front);
-		sna_dri_reference_buffer(back);
-
-		if (!sna_dri_page_flip(sna, info))
-			goto blit;
-
-		info->next_front.name = info->front->name;
-		info->next_front.bo = get_private(info->front)->bo;
-		info->off_delay = FLIP_OFF_DELAY;
-	} else if (info->type != DRI2_ASYNC_FLIP) {
-		/* A normal vsync'ed client is finishing, wait for it
-		 * to unpin the old framebuffer before taking over.
-		 */
-		goto blit;
-	} else {
-		DBG(("%s: pending flip, chaining next\n", __FUNCTION__));
-		if (info->next_front.name == info->front->name) {
-			name = info->cache.name;
-			bo = info->cache.bo;
-		} else {
-			name = info->front->name;
-			bo = get_private(info->front)->bo;
-		}
-		info->front->name = info->back->name;
-		get_private(info->front)->bo = get_private(info->back)->bo;
-	}
-
-	if (bo == NULL) {
-		DBG(("%s: creating new back buffer\n", __FUNCTION__));
-		bo = kgem_create_2d(&sna->kgem,
-				    draw->width,
-				    draw->height,
-				    draw->bitsPerPixel,
-				    get_private(info->front)->bo->tiling,
-				    CREATE_SCANOUT | CREATE_EXACT);
-		name = kgem_bo_flink(&sna->kgem, bo);
-	}
-	assert(bo->refcnt);
-	get_private(info->back)->bo = bo;
-	info->back->name = name;
-
-	set_bo(sna->front, get_private(info->front)->bo);
-	sna->dri.flip_pending = info;
-
-	DRI2SwapComplete(client, draw, 0, 0, 0,
-			 DRI2_EXCHANGE_COMPLETE, func, data);
 	return TRUE;
 }
 #endif
diff --git a/src/sna/sna_video.c b/src/sna/sna_video.c
index 8d111d5..05d76dd 100644
--- a/src/sna/sna_video.c
+++ b/src/sna/sna_video.c
@@ -104,8 +104,7 @@ sna_video_buffer(struct sna *sna,
 		if (video->tiled) {
 			video->buf = kgem_create_2d(&sna->kgem,
 						    frame->width, frame->height, 32,
-						    I915_TILING_X,
-						    CREATE_EXACT | CREATE_SCANOUT);
+						    I915_TILING_X, CREATE_EXACT);
 		} else {
 			video->buf = kgem_create_linear(&sna->kgem, frame->size,
 							CREATE_GTT_MAP);


More information about the xorg-commit mailing list