From 7a312bb1e27d579016e5b34161925732a574a286 Mon Sep 17 00:00:00 2001
From: Eric Anholt <eric@anholt.net>
Date: Fri, 16 Nov 2012 12:37:22 -0800
Subject: [PATCH] Add support for the new kernel async_flip interface.

I think with the server patch I sent, this should get us
better-than-copyregion performance through page-flipping.  Overall,
it's not a performance win on 800x600 openarena at 300fps, compared to
320 for copyregion.  My current theories:

1) pageflipping means that the backbuffer the client draws to is now
uncached.

2) Some other stalling is getting introduced.
---
 src/intel.h         |    1 +
 src/intel_display.c |    9 ++++++--
 src/intel_dri.c     |   59 ++++++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 64 insertions(+), 5 deletions(-)

diff --git a/src/intel.h b/src/intel.h
index 53ce33c..f09a34d 100644
--- a/src/intel.h
+++ b/src/intel.h
@@ -378,6 +378,7 @@ enum DRI2FrameEventType {
 	DRI2_SWAP,
 	DRI2_SWAP_CHAIN,
 	DRI2_FLIP,
+	DRI2_ASYNC_FLIP,
 	DRI2_WAITMSC,
 };
 
diff --git a/src/intel_display.c b/src/intel_display.c
index 2c93c2d..12a5a94 100644
--- a/src/intel_display.c
+++ b/src/intel_display.c
@@ -1552,7 +1552,7 @@ intel_do_pageflip(intel_screen_private *intel,
 	struct intel_mode *mode = crtc->mode;
 	unsigned int pitch = scrn->displayWidth * intel->cpp;
 	struct intel_pageflip *flip;
-	uint32_t new_fb_id;
+	uint32_t new_fb_id, flip_flags;
 	int i;
 
 	/*
@@ -1580,6 +1580,11 @@ intel_do_pageflip(intel_screen_private *intel,
 	mode->fe_tv_sec = 0;
 	mode->fe_tv_usec = 0;
 
+	if (flip_info->type == DRI2_ASYNC_FLIP)
+		flip_flags = DRM_MODE_PAGE_FLIP_ASYNC;
+	else
+		flip_flags = DRM_MODE_PAGE_FLIP_EVENT;
+
 	for (i = 0; i < config->num_crtc; i++) {
 		if (!intel_crtc_on(config->crtc[i]))
 			continue;
@@ -1605,7 +1610,7 @@ intel_do_pageflip(intel_screen_private *intel,
 		if (drmModePageFlip(mode->fd,
 				    crtc_id(crtc),
 				    new_fb_id,
-				    DRM_MODE_PAGE_FLIP_EVENT, flip)) {
+				    flip_flags, flip)) {
 			xf86DrvMsg(scrn->scrnIndex, X_WARNING,
 				   "flip queue failed: %s\n", strerror(errno));
 			free(flip);
diff --git a/src/intel_dri.c b/src/intel_dri.c
index 4c0827d..bfd4974 100644
--- a/src/intel_dri.c
+++ b/src/intel_dri.c
@@ -844,6 +844,8 @@ I830DRI2ScheduleFlip(struct intel_screen_private *intel,
 	drm_intel_bo *new_back, *old_back;
 	int tmp_name;
 
+	//ErrorF("non-async sched!\n");
+
 	if (!intel->use_triple_buffer) {
 		info->type = DRI2_SWAP;
 		if (!intel_do_pageflip(intel,
@@ -1163,6 +1165,7 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	DRI2FrameEventPtr swap_info = NULL;
 	enum DRI2FrameEventType swap_type = DRI2_SWAP;
 	CARD64 current_msc;
+	int delta_msc;
 
 	/* Drawable not displayed... just complete the swap */
 	if (pipe == -1)
@@ -1209,10 +1212,60 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 
 	current_msc = vbl.reply.sequence;
 
-	/* Flips need to be submitted one frame before */
+	delta_msc = (int)*target_msc - (int)current_msc;
+	/*ErrorF("ce %d div %lld tar %lld cur %lld delta %d\n",
+	       can_exchange(draw, front, back),
+	       (long long)divisor,
+	       (long long)*target_msc,
+	       (long long)current_msc,
+	       delta_msc);*/
 	if (can_exchange(draw, front, back)) {
-	    swap_type = DRI2_FLIP;
-	    flip = 1;
+		if (divisor == 0 && delta_msc <= 0) {
+			/* If they asked for syncing to a vblank that
+			 * already passed, and specified no divisor,
+			 * then assume their intent was "Sync to this
+			 * vblank if you can, and if I miss, then get
+			 * it on the screen as soon as possible", so
+			 * use the async flip path.  Multiple vendors
+			 * have asked for access to this kind of
+			 * behavior, and this seems like a reasonable
+			 * interface for it.
+			 *
+			 * Note that in this implementation, they only
+			 * get the as-soon-as-possible-even-by-tearing
+			 * behavior if the current_msc is already
+			 * passed when the swap is scheduled, not when
+			 * their rendering is complete and it's really
+			 * time to make the decision to vblank-sync or
+			 * not
+			 */
+			I830DRI2BufferPrivatePtr priv = swap_info->back->driverPrivate;
+
+			//ErrorF("try async\n");
+			swap_info->type = DRI2_ASYNC_FLIP;
+			if (intel_do_pageflip(intel,
+					      get_pixmap_bo(priv),
+					      swap_info, swap_info->pipe)) {
+				I830DRI2ExchangeBuffers(intel,
+							swap_info->front,
+							swap_info->back);
+				DRI2SwapComplete(swap_info->client, draw, 0, 0, 0,
+						 DRI2_FLIP_COMPLETE,
+						 swap_info->event_complete,
+						 swap_info->event_data);
+				return TRUE;
+			} else {
+				ErrorF("idpf refused!\n");
+			}
+		}
+
+		/* vblank-synced flips are stuffed into the
+		 * ringbuffer, so they need to be submitted any time
+		 * in the frame before the target.
+		 */
+		swap_type = DRI2_FLIP;
+		delta_msc--;
+		flip = 1;
 	}
 
 	swap_info->type = swap_type;
-- 
1.7.10.4

