xf86-video-intel: 22 commits - configure.ac man/intel.man src/intel_device.c src/intel_driver.h src/intel_options.c src/intel_options.h src/Makefile.am src/sna/gen8_render.c src/sna/kgem.c src/sna/kgem.h src/sna/Makefile.am src/sna/sna_accel.c src/sna/sna_display.c src/sna/sna_dri2.c src/sna/sna_dri3.c src/sna/sna_driver.c src/sna/sna.h src/sna/sna_io.c src/sna/sna_present.c src/uxa/intel_display.c src/uxa/intel_dri3.c src/uxa/intel_dri.c src/uxa/intel_driver.c src/uxa/intel.h src/uxa/intel_present.c src/uxa/intel_sync.c src/uxa/intel_uxa.c src/uxa/Makefile.am test/dri3.c test/dri3.h test/dri3-test.c test/.gitignore test/Makefile.am test/present-test.c tools/virtual.c

Chris Wilson ickle at kemper.freedesktop.org
Mon Jun 2 00:43:42 PDT 2014


 configure.ac            |   81 +++
 man/intel.man           |    5 
 src/Makefile.am         |    4 
 src/intel_device.c      |  101 +++-
 src/intel_driver.h      |    1 
 src/intel_options.c     |    1 
 src/intel_options.h     |    1 
 src/sna/Makefile.am     |   14 
 src/sna/gen8_render.c   |   20 
 src/sna/kgem.c          |   83 +++
 src/sna/kgem.h          |   20 
 src/sna/sna.h           |   56 ++
 src/sna/sna_accel.c     |   64 +-
 src/sna/sna_display.c   |  131 ++++-
 src/sna/sna_dri2.c      |  239 +++++-----
 src/sna/sna_dri3.c      |  383 ++++++++++++++++
 src/sna/sna_driver.c    |  124 ++++-
 src/sna/sna_io.c        |   13 
 src/sna/sna_present.c   |  472 ++++++++++++++++++++
 src/uxa/Makefile.am     |   13 
 src/uxa/intel.h         |   78 +++
 src/uxa/intel_display.c |  417 ++++++++++++++++--
 src/uxa/intel_dri.c     |  212 +++++----
 src/uxa/intel_dri3.c    |  140 ++++++
 src/uxa/intel_driver.c  |   89 ++-
 src/uxa/intel_present.c |  401 +++++++++++++++++
 src/uxa/intel_sync.c    |  111 ++++
 src/uxa/intel_uxa.c     |   67 ++
 test/.gitignore         |    2 
 test/Makefile.am        |   15 
 test/dri3-test.c        | 1100 ++++++++++++++++++++++++++++++++++++++++++++++++
 test/dri3.c             |  133 +++++
 test/dri3.h             |   50 ++
 test/present-test.c     |  726 +++++++++++++++++++++++++++++++
 tools/virtual.c         |  343 ++++++++++++--
 35 files changed, 5247 insertions(+), 463 deletions(-)

New commits:
commit ff36e1f7546e0ac4d6e831e5cf8d1317792ed7af
Author: Keith Packard <keithp at keithp.com>
Date:   Tue Nov 19 15:20:44 2013 -0800

    uxa: Add Present extension support
    
    Signed-off-by: Keith Packard <keithp at keithp.com>
    Reviewed-by: Eric Anholt <eric at anholt.net>
    
    Conflicts:
    	configure.ac
    	src/uxa/intel.h
    	src/uxa/intel_driver.c

diff --git a/src/uxa/Makefile.am b/src/uxa/Makefile.am
index 29a8329..0e10a2b 100644
--- a/src/uxa/Makefile.am
+++ b/src/uxa/Makefile.am
@@ -87,6 +87,12 @@ libuxa_la_SOURCES += \
 	$(NULL)
 endif
 
+if PRESENT
+libuxa_la_SOURCES += \
+	intel_present.c \
+	$(NULL)
+endif
+
 if XVMC
 AM_CFLAGS += -I$(top_srcdir)/xvmc
 libuxa_la_SOURCES += \
diff --git a/src/uxa/intel.h b/src/uxa/intel.h
index 6366db4..2ebad64 100644
--- a/src/uxa/intel.h
+++ b/src/uxa/intel.h
@@ -743,4 +743,20 @@ static inline Bool intel_sync_init(ScreenPtr screen) { return 0; }
 void intel_sync_close(ScreenPtr screen);
 #endif
 
+/*
+ * intel_present.c
+ */
+
+#if 0
+#define DebugPresent(x) ErrorF x
+#else
+#define DebugPresent(x)
+#endif
+
+#if HAVE_PRESENT
+Bool intel_present_screen_init(ScreenPtr screen);
+#else
+static inline Bool intel_present_screen_init(ScreenPtr screen) { return 0; }
+#endif
+
 #endif /* _I830_H_ */
diff --git a/src/uxa/intel_driver.c b/src/uxa/intel_driver.c
index 9e21742..1952dcc 100644
--- a/src/uxa/intel_driver.c
+++ b/src/uxa/intel_driver.c
@@ -877,6 +877,7 @@ I830ScreenInit(SCREEN_INIT_ARGS_DECL)
 	 */
 	intel->XvEnabled = TRUE;
 
+	if (!intel_init_initial_framebuffer(scrn))
 		return FALSE;
 
 	intel_batch_init(scrn);
@@ -936,6 +937,9 @@ I830ScreenInit(SCREEN_INIT_ARGS_DECL)
 		intel->dri3 = DRI_ACTIVE;
 #endif
 
+	if (xf86ReturnOptValBool(intel->Options, OPTION_PRESENT, TRUE))
+		intel_present_screen_init(screen);
+
 	xf86SetBackingStore(screen);
 	xf86SetSilkenMouse(screen);
 	miDCInitialize(screen, xf86GetPointerScreenFuncs());
diff --git a/src/uxa/intel_present.c b/src/uxa/intel_present.c
new file mode 100644
index 0000000..18fcbf8
--- /dev/null
+++ b/src/uxa/intel_present.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright © 2014 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <time.h>
+#include <errno.h>
+
+#include "xorg-server.h"
+#include "xf86.h"
+#include "xf86_OSproc.h"
+
+#include "xf86Pci.h"
+#include "xf86drm.h"
+
+#include "windowstr.h"
+#include "shadow.h"
+#include "fb.h"
+
+#include "intel.h"
+#include "i830_reg.h"
+
+#include "i915_drm.h"
+
+#include "present.h"
+
+#include "intel_glamor.h"
+#include "uxa.h"
+
+struct intel_present_vblank_event {
+	uint64_t        event_id;
+};
+
+static uint32_t pipe_select(int pipe)
+{
+	if (pipe > 1)
+		return pipe << DRM_VBLANK_HIGH_CRTC_SHIFT;
+	else if (pipe > 0)
+		return DRM_VBLANK_SECONDARY;
+	else
+		return 0;
+}
+
+static RRCrtcPtr
+intel_present_get_crtc(WindowPtr window)
+{
+	ScreenPtr screen = window->drawable.pScreen;
+	ScrnInfoPtr pScrn = xf86ScreenToScrn(screen);
+	BoxRec box, crtcbox;
+	xf86CrtcPtr crtc;
+	RRCrtcPtr randr_crtc = NULL;
+
+	box.x1 = window->drawable.x;
+	box.y1 = window->drawable.y;
+	box.x2 = box.x1 + window->drawable.width;
+	box.y2 = box.y1 + window->drawable.height;
+
+	crtc = intel_covering_crtc(pScrn, &box, NULL, &crtcbox);
+
+	/* Make sure the CRTC is valid and this is the real front buffer */
+	if (crtc != NULL && !crtc->rotatedData)
+		randr_crtc = crtc->randr_crtc;
+
+	return randr_crtc;
+}
+
+static int
+intel_present_crtc_pipe(ScreenPtr screen, RRCrtcPtr randr_crtc)
+{
+	xf86CrtcPtr crtc;
+
+	if (randr_crtc == NULL)
+		return 0;
+
+	crtc = randr_crtc->devPrivate;
+	return intel_crtc_to_pipe(crtc);
+}
+
+static int
+intel_present_get_ust_msc(RRCrtcPtr crtc, CARD64 *ust, CARD64 *msc)
+{
+	xf86CrtcPtr             xf86_crtc = crtc->devPrivate;
+	ScreenPtr               screen = crtc->pScreen;
+	ScrnInfoPtr             scrn = xf86ScreenToScrn(screen);
+
+	return intel_get_crtc_msc_ust(scrn, xf86_crtc, msc, ust);
+}
+
+/*
+ * Flush the DRM event queue when full; this
+ * makes space for new requests
+ */
+static Bool
+intel_present_flush_drm_events(ScreenPtr screen)
+{
+	ScrnInfoPtr             scrn = xf86ScreenToScrn(screen);
+	intel_screen_private    *intel = intel_get_screen_private(scrn);
+
+	return intel_mode_read_drm_events(intel) >= 0;
+}
+
+/*
+ * Called when the queued vblank event has occurred
+ */
+static void
+intel_present_vblank_handler(ScrnInfoPtr scrn, xf86CrtcPtr crtc, uint64_t msc, uint64_t usec, void *data)
+{
+	struct intel_present_vblank_event       *event = data;
+
+	present_event_notify(event->event_id, usec, msc);
+	free(event);
+}
+
+/*
+ * Called when the queued vblank is aborted
+ */
+static void
+intel_present_vblank_abort(ScrnInfoPtr scrn, xf86CrtcPtr crtc, void *data)
+{
+	struct intel_present_vblank_event       *event = data;
+
+	free(event);
+}
+
+/*
+ * Queue an event to report back to the Present extension when the specified
+ * MSC has past
+ */
+static int
+intel_present_queue_vblank(RRCrtcPtr                    crtc,
+                           uint64_t                     event_id,
+                           uint64_t                     msc)
+{
+	xf86CrtcPtr                             xf86_crtc = crtc->devPrivate;
+	ScreenPtr                               screen = crtc->pScreen;
+	ScrnInfoPtr                             scrn = xf86ScreenToScrn(screen);
+	intel_screen_private                    *intel = intel_get_screen_private(scrn);
+	int                                     pipe = intel_present_crtc_pipe(screen, crtc);
+	struct intel_present_vblank_event       *event;
+	drmVBlank                               vbl;
+	int                                     ret;
+	uint32_t                                seq;
+
+	event = calloc(sizeof(struct intel_present_vblank_event), 1);
+	if (!event)
+		return BadAlloc;
+	event->event_id = event_id;
+	seq = intel_drm_queue_alloc(scrn, xf86_crtc, event,
+				    intel_present_vblank_handler,
+				    intel_present_vblank_abort);
+	if (!seq) {
+		free(event);
+		return BadAlloc;
+	}
+
+	vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT | pipe_select(pipe);
+	vbl.request.sequence = intel_crtc_msc_to_sequence(scrn, xf86_crtc, msc);
+	vbl.request.signal = seq;
+	for (;;) {
+		ret = drmWaitVBlank(intel->drmSubFD, &vbl);
+		if (!ret)
+			break;
+		if (errno != EBUSY || !intel_present_flush_drm_events(screen))
+			return BadAlloc;
+	}
+	DebugPresent(("\t\tiq %lld seq %u msc %u (hw msc %u)\n", event_id, seq, low_msc, vbl.request.sequence));
+	return Success;
+}
+
+static Bool
+intel_present_event_match(void *data, void *match_data)
+{
+	struct intel_present_vblank_event       *event = data;
+	uint64_t                                *match = match_data;
+
+	return *match == event->event_id;
+}
+
+/*
+ * Remove a pending vblank event from the DRM queue so that it is not reported
+ * to the extension
+ */
+static void
+intel_present_abort_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
+{
+	ScreenPtr       screen = crtc->pScreen;
+	ScrnInfoPtr     scrn = xf86ScreenToScrn(screen);
+
+	intel_drm_abort(scrn, intel_present_event_match, &event_id);
+}
+
+/*
+ * Flush our batch buffer when requested by the Present extension.
+ */
+static void
+intel_present_flush(WindowPtr window)
+{
+	ScreenPtr                               screen = window->drawable.pScreen;
+	ScrnInfoPtr                             scrn = xf86ScreenToScrn(screen);
+	intel_screen_private                    *intel = intel_get_screen_private(scrn);
+
+	if (intel->flush_rendering)
+		intel->flush_rendering(intel);
+}
+
+/*
+ * Test to see if page flipping is possible on the target crtc
+ */
+static Bool
+intel_present_check_flip(RRCrtcPtr              crtc,
+                         WindowPtr              window,
+                         PixmapPtr              pixmap,
+                         Bool                   sync_flip)
+{
+	ScreenPtr               screen = window->drawable.pScreen;
+	ScrnInfoPtr             scrn = xf86ScreenToScrn(screen);
+	intel_screen_private    *intel = intel_get_screen_private(scrn);
+
+	if (!scrn->vtSema)
+		return FALSE;
+
+	if (intel->shadow_present)
+		return FALSE;
+
+	if (!intel->use_pageflipping)
+		return FALSE;
+
+	if (crtc && !intel_crtc_on(crtc->devPrivate))
+		return FALSE;
+
+	return TRUE;
+}
+
+/*
+ * Once the flip has been completed on all pipes, notify the
+ * extension code telling it when that happened
+ */
+static void
+intel_present_flip_event(uint64_t msc, uint64_t ust, void *pageflip_data)
+{
+	struct intel_present_vblank_event *event = pageflip_data;
+
+	present_event_notify(event->event_id, ust, msc);
+	free(event);
+}
+
+/*
+ * The flip has been aborted, free the structure
+ */
+static void
+intel_present_flip_abort(void *pageflip_data)
+{
+	struct intel_present_vblank_event *event = pageflip_data;
+
+	free(event);
+}
+
+/*
+ * Queue a flip on 'crtc' to 'pixmap' at 'target_msc'. If 'sync_flip' is true,
+ * then wait for vblank. Otherwise, flip immediately
+ */
+static Bool
+intel_present_flip(RRCrtcPtr                    crtc,
+                   uint64_t                     event_id,
+                   uint64_t                     target_msc,
+                   PixmapPtr                    pixmap,
+                   Bool                         sync_flip)
+{
+	ScreenPtr                               screen = crtc->pScreen;
+	ScrnInfoPtr                             scrn = xf86ScreenToScrn(screen);
+	intel_screen_private                    *intel = intel_get_screen_private(scrn);
+	struct intel_present_vblank_event       *event;
+	int                                     pipe = intel_present_crtc_pipe(screen, crtc);
+	dri_bo                                  *bo;
+	Bool                                    ret;
+
+	if (!intel_present_check_flip(crtc, screen->root, pixmap, sync_flip))
+		return FALSE;
+
+	bo = intel_get_pixmap_bo(pixmap);
+	if (!bo)
+		return FALSE;
+
+	event = calloc(1, sizeof(struct intel_present_vblank_event));
+	if (!event)
+		return FALSE;
+
+	event->event_id = event_id;
+
+	ret = intel_do_pageflip(intel, bo, pipe, !sync_flip,
+				event,
+				intel_present_flip_event,
+				intel_present_flip_abort);
+	if (!ret)
+		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+			   "present flip failed\n");
+	return ret;
+}
+
+/*
+ * Queue a flip back to the normal frame buffer
+ */
+static void
+intel_present_unflip(ScreenPtr screen, uint64_t event_id)
+{
+	ScrnInfoPtr                             scrn = xf86ScreenToScrn(screen);
+	intel_screen_private                    *intel = intel_get_screen_private(scrn);
+	struct intel_present_vblank_event       *event;
+	PixmapPtr                               pixmap = screen->GetScreenPixmap(screen);
+	dri_bo                                  *bo;
+	Bool                                    ret;
+
+	if (!intel_present_check_flip(NULL, screen->root, pixmap, true))
+		return;
+
+	bo = intel_get_pixmap_bo(pixmap);
+	if (!bo)
+		return;
+
+	event = calloc(1, sizeof(struct intel_present_vblank_event));
+	if (!event)
+		return;
+
+	event->event_id = event_id;
+
+	ret = intel_do_pageflip(intel, bo, -1, FALSE, event, intel_present_flip_event, intel_present_flip_abort);
+	if (!ret) {
+		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+			   "present unflip failed\n");
+	}
+}
+
+static present_screen_info_rec intel_present_screen_info = {
+	.version = PRESENT_SCREEN_INFO_VERSION,
+
+	.get_crtc = intel_present_get_crtc,
+	.get_ust_msc = intel_present_get_ust_msc,
+	.queue_vblank = intel_present_queue_vblank,
+	.abort_vblank = intel_present_abort_vblank,
+	.flush = intel_present_flush,
+
+	.capabilities = PresentCapabilityNone,
+	.check_flip = intel_present_check_flip,
+	.flip = intel_present_flip,
+	.unflip = intel_present_unflip,
+};
+
+static Bool
+intel_present_has_async_flip(ScreenPtr screen)
+{
+#ifdef DRM_CAP_ASYNC_PAGE_FLIP
+	ScrnInfoPtr             scrn = xf86ScreenToScrn(screen);
+	intel_screen_private    *intel = intel_get_screen_private(scrn);
+	int                     ret;
+	uint64_t                value;
+
+	ret = drmGetCap(intel->drmSubFD, DRM_CAP_ASYNC_PAGE_FLIP, &value);
+	if (ret == 0)
+		return value == 1;
+#endif
+	return FALSE;
+}
+
+Bool
+intel_present_screen_init(ScreenPtr screen)
+{
+	if (intel_present_has_async_flip(screen))
+		intel_present_screen_info.capabilities |= PresentCapabilityAsync;
+
+	return present_screen_init(screen, &intel_present_screen_info);
+}
commit 14ff08c2827d36f7f08256dc57b84aaf80d17ab4
Author: Keith Packard <keithp at keithp.com>
Date:   Tue Nov 19 23:20:38 2013 -0800

    uxa: Restructure DRM event handling.
    
    This refactors the drm interrupt handling logic quite a bit, both to
    allow for either DRI2 or Present handlers, but also to eliminate
    passing pointers through the kernel. Passing pointers left the kernel
    holding the only reference to some internal X server data structures.
    
    After a server reset, the X server would end up using stale pointers
    stored in those structures. Using simple integers makes it possible to
    empty the queue of pending interrupt data and then ignore the stale
    kernel data.
    
    Signed-off-by: Keith Packard <keithp at keithp.com>
    Reviewed-by: Eric Anholt <eric at anholt.net>

diff --git a/src/uxa/intel.h b/src/uxa/intel.h
index 1d641f2..6366db4 100644
--- a/src/uxa/intel.h
+++ b/src/uxa/intel.h
@@ -407,6 +407,20 @@ extern void intel_mode_disable_unused_functions(ScrnInfoPtr scrn);
 extern void intel_mode_remove_fb(intel_screen_private *intel);
 extern void intel_mode_close(intel_screen_private *intel);
 extern void intel_mode_fini(intel_screen_private *intel);
+extern int intel_mode_read_drm_events(intel_screen_private *intel);
+
+typedef void (*intel_drm_handler_proc)(ScrnInfoPtr scrn,
+                                       xf86CrtcPtr crtc,
+                                       uint64_t seq,
+                                       uint64_t usec,
+                                       void *data);
+
+typedef void (*intel_drm_abort_proc)(ScrnInfoPtr scrn,
+                                     xf86CrtcPtr crtc,
+                                     void *data);
+
+extern uint32_t intel_drm_queue_alloc(ScrnInfoPtr scrn, xf86CrtcPtr crtc, void *data, intel_drm_handler_proc handler, intel_drm_abort_proc abort);
+extern void intel_drm_abort(ScrnInfoPtr scrn, Bool (*match)(void *data, void *match_data), void *match_data);
 
 extern int intel_get_pipe_from_crtc_id(drm_intel_bufmgr *bufmgr, xf86CrtcPtr crtc);
 extern int intel_crtc_id(xf86CrtcPtr crtc);
@@ -434,6 +448,12 @@ typedef void (*DRI2SwapEventPtr)(ClientPtr client, void *data, int type,
 				 CARD64 ust, CARD64 msc, CARD64 sbc);
 #endif
 
+typedef void (*intel_pageflip_handler_proc) (uint64_t frame,
+                                             uint64_t usec,
+                                             void *data);
+
+typedef void (*intel_pageflip_abort_proc) (void *data);
+
 typedef struct _DRI2FrameEvent {
 	struct intel_screen_private *intel;
 
@@ -456,7 +476,11 @@ typedef struct _DRI2FrameEvent {
 
 extern Bool intel_do_pageflip(intel_screen_private *intel,
 			      dri_bo *new_front,
-			      DRI2FrameEventPtr flip_info, int ref_crtc_hw_id);
+			      int ref_crtc_hw_id,
+			      Bool async,
+			      void *pageflip_data,
+			      intel_pageflip_handler_proc pageflip_handler,
+			      intel_pageflip_abort_proc pageflip_abort);
 
 static inline intel_screen_private *
 intel_get_screen_private(ScrnInfoPtr scrn)
diff --git a/src/uxa/intel_display.c b/src/uxa/intel_display.c
index dac1a82..4a41d4d 100644
--- a/src/uxa/intel_display.c
+++ b/src/uxa/intel_display.c
@@ -61,6 +61,22 @@
 
 #define KNOWN_MODE_FLAGS ((1<<14)-1)
 
+struct intel_drm_queue {
+        struct list list;
+        xf86CrtcPtr crtc;
+        uint32_t seq;
+        void *data;
+        ScrnInfoPtr scrn;
+        intel_drm_handler_proc handler;
+        intel_drm_abort_proc abort;
+};
+
+static void
+intel_drm_abort_scrn(ScrnInfoPtr scrn);
+
+static uint32_t intel_drm_seq;
+static struct list intel_drm_queue;
+
 struct intel_mode {
 	int fd;
 	uint32_t fb_id;
@@ -68,14 +84,17 @@ struct intel_mode {
 	int cpp;
 
 	drmEventContext event_context;
-	DRI2FrameEventPtr flip_info;
 	int old_fb_id;
 	int flip_count;
 	uint64_t fe_msc;
-        uint64_t fe_usec;
+	uint64_t fe_usec;
 
 	struct list outputs;
 	struct list crtcs;
+
+	void *pageflip_data;
+	intel_pageflip_handler_proc pageflip_handler;
+	intel_pageflip_abort_proc pageflip_abort;
 };
 
 struct intel_pageflip {
@@ -361,6 +380,7 @@ intel_crtc_apply(xf86CrtcPtr crtc)
 
 	if (scrn->pScreen)
 		xf86_reload_cursors(scrn->pScreen);
+        intel_drm_abort_scrn(scrn);
 
 done:
 	free(output_ids);
@@ -1459,10 +1479,27 @@ fail:
 	return FALSE;
 }
 
+static void
+intel_pageflip_handler(ScrnInfoPtr scrn, xf86CrtcPtr crtc,
+                        uint64_t frame, uint64_t usec, void *data);
+
+static void
+intel_pageflip_abort(ScrnInfoPtr scrn, xf86CrtcPtr crtc, void *data);
+
+static void
+intel_pageflip_complete(struct intel_mode *mode);
+
+static void
+intel_drm_abort_seq (ScrnInfoPtr scrn, uint32_t seq);
+
 Bool
 intel_do_pageflip(intel_screen_private *intel,
 		  dri_bo *new_front,
-		  DRI2FrameEventPtr flip_info, int ref_crtc_hw_id)
+		  int ref_crtc_hw_id,
+		  Bool async,
+		  void *pageflip_data,
+		  intel_pageflip_handler_proc pageflip_handler,
+		  intel_pageflip_abort_proc pageflip_abort)
 {
 	ScrnInfoPtr scrn = intel->scrn;
 	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
@@ -1471,6 +1508,8 @@ intel_do_pageflip(intel_screen_private *intel,
 	unsigned int pitch = scrn->displayWidth * intel->cpp;
 	struct intel_pageflip *flip;
 	uint32_t new_fb_id;
+	uint32_t flags;
+	uint32_t seq;
 	int i;
 
 	/*
@@ -1485,6 +1524,10 @@ intel_do_pageflip(intel_screen_private *intel,
 	intel_glamor_flush(intel);
 	intel_batch_submit(scrn);
 
+	mode->pageflip_data = pageflip_data;
+	mode->pageflip_handler = pageflip_handler;
+	mode->pageflip_abort = pageflip_abort;
+
 	/*
 	 * Queue flips on all enabled CRTCs
 	 * Note that if/when we get per-CRTC buffers, we'll have to update this.
@@ -1497,13 +1540,13 @@ intel_do_pageflip(intel_screen_private *intel,
 	mode->fe_msc = 0;
 	mode->fe_usec = 0;
 
+	flags = DRM_MODE_PAGE_FLIP_EVENT;
+	if (async)
+		flags |= DRM_MODE_PAGE_FLIP_ASYNC;
 	for (i = 0; i < config->num_crtc; i++) {
 		if (!intel_crtc_on(config->crtc[i]))
 			continue;
 
-		mode->flip_info = flip_info;
-		mode->flip_count++;
-
 		crtc = config->crtc[i]->driver_private;
 
 		flip = calloc(1, sizeof(struct intel_pageflip));
@@ -1519,19 +1562,38 @@ intel_do_pageflip(intel_screen_private *intel,
 		flip->dispatch_me = (intel_crtc_to_pipe(crtc->crtc) == ref_crtc_hw_id);
 		flip->mode = mode;
 
+		seq = intel_drm_queue_alloc(scrn, config->crtc[i], flip, intel_pageflip_handler, intel_pageflip_abort);
+		if (!seq) {
+			free(flip);
+			goto error_undo;
+		}
+
+again:
 		if (drmModePageFlip(mode->fd,
 				    crtc_id(crtc),
 				    new_fb_id,
-				    DRM_MODE_PAGE_FLIP_EVENT, flip)) {
+				    flags, (void *)(uintptr_t)seq)) {
+			if (intel_mode_read_drm_events(intel)) {
+				xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+					   "flip queue retry\n");
+				goto again;
+			}
 			xf86DrvMsg(scrn->scrnIndex, X_WARNING,
 				   "flip queue failed: %s\n", strerror(errno));
+			if (seq)
+				intel_drm_abort_seq(scrn, seq);
 			free(flip);
 			goto error_undo;
 		}
+		mode->flip_count++;
 	}
 
 	mode->old_fb_id = mode->fb_id;
 	mode->fb_id = new_fb_id;
+
+	if (!mode->flip_count)
+		intel_pageflip_complete(mode);
+
 	return TRUE;
 
 error_undo:
@@ -1544,6 +1606,8 @@ error_undo:
 error_out:
 	xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Page flip failed: %s\n",
 		   strerror(errno));
+
+	mode->flip_count = 0;
 	return FALSE;
 }
 
@@ -1551,6 +1615,98 @@ static const xf86CrtcConfigFuncsRec intel_xf86crtc_config_funcs = {
 	intel_xf86crtc_resize
 };
 
+/*
+ * Enqueue a potential drm response; when the associated response
+ * appears, we've got data to pass to the handler from here
+ */
+uint32_t
+intel_drm_queue_alloc(ScrnInfoPtr scrn,
+		      xf86CrtcPtr crtc,
+		      void *data,
+		      intel_drm_handler_proc handler,
+		      intel_drm_abort_proc abort)
+{
+	struct intel_drm_queue  *q;
+
+	q = calloc(1, sizeof(struct intel_drm_queue));
+	if (!q)
+		return 0;
+
+	if (!intel_drm_seq)
+		++intel_drm_seq;
+	q->seq = intel_drm_seq++;
+	q->scrn = scrn;
+	q->crtc = crtc;
+	q->data = data;
+	q->handler = handler;
+	q->abort = abort;
+
+	list_add(&q->list, &intel_drm_queue);
+
+	return q->seq;
+}
+
+/*
+ * Abort one queued DRM entry, removing it
+ * from the list, calling the abort function and
+ * freeing the memory
+ */
+static void
+intel_drm_abort_one(struct intel_drm_queue *q)
+{
+	list_del(&q->list);
+	q->abort(q->scrn, q->crtc, q->data);
+	free(q);
+}
+
+/*
+ * Externally usable abort function that uses a callback to match a single queued
+ * entry to abort
+ */
+void
+intel_drm_abort(ScrnInfoPtr scrn, Bool (*match)(void *data, void *match_data), void *match_data)
+{
+	struct intel_drm_queue *q;
+
+	list_for_each_entry(q, &intel_drm_queue, list) {
+		if (match(q->data, match_data)) {
+			intel_drm_abort_one(q);
+			break;
+		}
+	}
+}
+
+/*
+ * Abort by drm queue sequence number
+ */
+static void
+intel_drm_abort_seq(ScrnInfoPtr scrn, uint32_t seq)
+{
+	struct intel_drm_queue *q;
+
+	list_for_each_entry(q, &intel_drm_queue, list) {
+		if (q->seq == seq) {
+			intel_drm_abort_one(q);
+			break;
+		}
+	}
+}
+
+/*
+ * Abort all queued entries on a specific scrn, used
+ * when resetting the X server
+ */
+static void
+intel_drm_abort_scrn(ScrnInfoPtr scrn)
+{
+	struct intel_drm_queue *q, *tmp;
+
+	list_for_each_entry_safe(q, tmp, &intel_drm_queue, list) {
+		if (q->scrn == scrn)
+			intel_drm_abort_one(q);
+	}
+}
+
 static uint32_t pipe_select(int pipe)
 {
 	if (pipe > 1)
@@ -1652,44 +1808,110 @@ intel_crtc_msc_to_sequence(ScrnInfoPtr scrn, xf86CrtcPtr crtc, uint64_t expect)
         return (uint32_t) (expect - intel_crtc->vblank_offset);
 }
 
+/*
+ * General DRM kernel handler. Looks for the matching sequence number in the
+ * drm event queue and calls the handler for it.
+ */
 static void
-intel_vblank_handler(int fd, unsigned int frame, unsigned int tv_sec,
-		       unsigned int tv_usec, void *event)
-{
-	I830DRI2FrameEventHandler(frame, tv_sec, tv_usec, event);
+intel_drm_handler(int fd, uint32_t frame, uint32_t sec, uint32_t usec, void *user_ptr)
+{
+	uint32_t user_data = (intptr_t)user_ptr;
+	struct intel_drm_queue *q;
+
+	list_for_each_entry(q, &intel_drm_queue, list) {
+		if (q->seq == user_data) {
+			list_del(&q->list);
+			q->handler(q->scrn, q->crtc,
+				   intel_sequence_to_crtc_msc(q->crtc, frame),
+				   (uint64_t)sec * 1000000 + usec, q->data);
+			free(q);
+			break;
+		}
+	}
 }
 
+
+/*
+ * Notify the page flip caller that the flip is
+ * complete
+ */
 static void
-intel_page_flip_handler(int fd, unsigned int frame, unsigned int tv_sec,
-			  unsigned int tv_usec, void *event_data)
+intel_pageflip_complete(struct intel_mode *mode)
+{
+	/* Release framebuffer */
+	drmModeRmFB(mode->fd, mode->old_fb_id);
+
+	if (!mode->pageflip_handler)
+		return;
+
+	mode->pageflip_handler(mode->fe_msc, mode->fe_usec,
+			       mode->pageflip_data);
+}
+
+/*
+ * One pageflip event has completed. Update the saved msc/ust values
+ * as needed, then check to see if the whole set of events are
+ * complete and notify the application at that point
+ */
+static struct intel_mode *
+intel_handle_pageflip(struct intel_pageflip *flip, uint64_t msc, uint64_t usec)
 {
-	struct intel_pageflip *flip = event_data;
 	struct intel_mode *mode = flip->mode;
 
-	/* Is this the event whose info shall be delivered to higher level? */
 	if (flip->dispatch_me) {
 		/* Yes: Cache msc, ust for later delivery. */
-		mode->fe_msc = frame;
-		mode->fe_usec = (uint64_t) tv_sec * 1000000 + tv_usec;
+		mode->fe_msc = msc;
+		mode->fe_usec = usec;
 	}
 	free(flip);
 
 	/* Last crtc completed flip? */
 	mode->flip_count--;
 	if (mode->flip_count > 0)
+		return NULL;
+
+	return mode;
+}
+
+/*
+ * Called from the DRM event queue when a single flip has completed
+ */
+static void
+intel_pageflip_handler(ScrnInfoPtr scrn, xf86CrtcPtr crtc,
+		       uint64_t msc, uint64_t usec, void *data)
+{
+	struct intel_pageflip   *flip = data;
+	struct intel_mode       *mode = intel_handle_pageflip(flip, msc, usec);
+
+	if (!mode)
+		return;
+	intel_pageflip_complete(mode);
+}
+
+/*
+ * Called from the DRM queue abort code when a flip has been aborted
+ */
+static void
+intel_pageflip_abort(ScrnInfoPtr scrn, xf86CrtcPtr crtc, void *data)
+{
+	struct intel_pageflip   *flip = data;
+	struct intel_mode       *mode = intel_handle_pageflip(flip, 0, 0);
+
+	if (!mode)
 		return;
 
 	/* Release framebuffer */
 	drmModeRmFB(mode->fd, mode->old_fb_id);
 
-	if (mode->flip_info == NULL)
+	if (!mode->pageflip_abort)
 		return;
 
-	/* Deliver cached msc, ust from reference crtc to flip event handler */
-	I830DRI2FlipEventHandler((uint32_t) mode->fe_msc, mode->fe_usec / 1000000,
-				 mode->fe_usec % 1000000, mode->flip_info);
+	mode->pageflip_abort(mode->pageflip_data);
 }
 
+/*
+ * Check for pending DRM events and process them.
+ */
 static void
 drm_wakeup_handler(pointer data, int err, pointer p)
 {
@@ -1705,6 +1927,26 @@ drm_wakeup_handler(pointer data, int err, pointer p)
 		drmHandleEvent(mode->fd, &mode->event_context);
 }
 
+/*
+ * If there are any available, read drm_events
+ */
+int
+intel_mode_read_drm_events(struct intel_screen_private *intel)
+{
+	struct intel_mode *mode = intel->modes;
+	struct pollfd p = { .fd = mode->fd, .events = POLLIN };
+	int r;
+
+	do {
+		r = poll(&p, 1, 0);
+	} while (r == -1 && (errno == EINTR || errno == EAGAIN));
+
+	if (r <= 0)
+		return 0;
+
+	return drmHandleEvent(mode->fd, &mode->event_context);
+}
+
 static drmModeEncoderPtr
 intel_get_kencoder(struct intel_mode *mode, int num)
 {
@@ -1804,8 +2046,12 @@ Bool intel_mode_pre_init(ScrnInfoPtr scrn, int fd, int cpp)
 	xf86InitialConfiguration(scrn, TRUE);
 
 	mode->event_context.version = DRM_EVENT_CONTEXT_VERSION;
-	mode->event_context.vblank_handler = intel_vblank_handler;
-	mode->event_context.page_flip_handler = intel_page_flip_handler;
+	mode->event_context.vblank_handler = intel_drm_handler;
+	mode->event_context.page_flip_handler = intel_drm_handler;
+
+	/* XXX assumes only one intel screen */
+	list_init(&intel_drm_queue);
+	intel_drm_seq = 0;
 
 	has_flipping = 0;
 	gp.param = I915_PARAM_HAS_PAGEFLIPPING;
@@ -1848,14 +2094,6 @@ intel_mode_remove_fb(intel_screen_private *intel)
 	}
 }
 
-static Bool has_pending_events(int fd)
-{
-	struct pollfd pfd;
-	pfd.fd = fd;
-	pfd.events = POLLIN;
-	return poll(&pfd, 1, 0) == 1;
-}
-
 void
 intel_mode_close(intel_screen_private *intel)
 {
@@ -1864,8 +2102,7 @@ intel_mode_close(intel_screen_private *intel)
 	if (mode == NULL)
 		return;
 
-	while (has_pending_events(mode->fd))
-		drmHandleEvent(mode->fd, &mode->event_context);
+        intel_drm_abort_scrn(intel->scrn);
 
 	RemoveBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA,
 				     drm_wakeup_handler, mode);
@@ -1944,7 +2181,8 @@ Bool intel_crtc_on(xf86CrtcPtr crtc)
 		return FALSE;
 
 	ret = (drm_crtc->mode_valid &&
-	       intel_crtc->mode->fb_id == drm_crtc->buffer_id);
+	       (intel_crtc->mode->fb_id == drm_crtc->buffer_id ||
+		intel_crtc->mode->old_fb_id == drm_crtc->buffer_id));
 	free(drm_crtc);
 
 	return ret;
diff --git a/src/uxa/intel_dri.c b/src/uxa/intel_dri.c
index 29b1942..ef66aa5 100644
--- a/src/uxa/intel_dri.c
+++ b/src/uxa/intel_dri.c
@@ -593,7 +593,7 @@ I830DRI2DrawableCrtc(DrawablePtr pDraw)
 
 	/* Make sure the CRTC is valid and this is the real front buffer */
 	if (crtc != NULL && !crtc->rotatedData)
-		return crtc;
+                return crtc;
 
 	return NULL;
 }
@@ -726,15 +726,15 @@ i830_dri2_add_frame_event(DRI2FrameEventPtr info)
 }
 
 static void
-i830_dri2_del_frame_event(DrawablePtr drawable, DRI2FrameEventPtr info)
+i830_dri2_del_frame_event(DRI2FrameEventPtr info)
 {
 	list_del(&info->client_resource);
 	list_del(&info->drawable_resource);
 
 	if (info->front)
-		I830DRI2DestroyBuffer(drawable, info->front);
+		I830DRI2DestroyBuffer(NULL, info->front);
 	if (info->back)
-		I830DRI2DestroyBuffer(drawable, info->back);
+		I830DRI2DestroyBuffer(NULL, info->back);
 
 	free(info);
 }
@@ -832,6 +832,24 @@ static drm_intel_bo *get_pixmap_bo(I830DRI2BufferPrivatePtr priv)
 	return bo;
 }
 
+static void
+I830DRI2FlipComplete(uint64_t frame, uint64_t usec, void *pageflip_data)
+{
+        DRI2FrameEventPtr info = pageflip_data;
+
+        I830DRI2FlipEventHandler((uint32_t) frame, usec / 1000000,
+                                 usec % 1000000,
+                                 info);
+}
+
+static void
+I830DRI2FlipAbort(void *pageflip_data)
+{
+        DRI2FrameEventPtr info = pageflip_data;
+
+        i830_dri2_del_frame_event(info);
+}
+
 /*
  * Our internal swap routine takes care of actually exchanging, blitting, or
  * flipping buffers as necessary.
@@ -849,7 +867,9 @@ I830DRI2ScheduleFlip(struct intel_screen_private *intel,
 		info->type = DRI2_SWAP;
 		if (!intel_do_pageflip(intel,
 				       get_pixmap_bo(priv),
-				       info, info->pipe))
+				       info->pipe, FALSE, info,
+                                       I830DRI2FlipComplete,
+                                       I830DRI2FlipAbort))
 			return FALSE;
 
 		I830DRI2ExchangeBuffers(intel, info->front, info->back);
@@ -904,7 +924,7 @@ I830DRI2ScheduleFlip(struct intel_screen_private *intel,
 	}
 
 	old_back = get_pixmap_bo(priv);
-	if (!intel_do_pageflip(intel, old_back, info, info->pipe)) {
+	if (!intel_do_pageflip(intel, old_back, info->pipe, FALSE, info, I830DRI2FlipComplete, I830DRI2FlipAbort)) {
 		intel->back_buffer = new_back;
 		return FALSE;
 	}
@@ -1003,7 +1023,7 @@ void I830DRI2FrameEventHandler(unsigned int frame, unsigned int tv_sec,
 		status = dixLookupDrawable(&drawable, swap_info->drawable_id, serverClient,
 					   M_ANY, DixWriteAccess);
 	if (status != Success) {
-		i830_dri2_del_frame_event(NULL, swap_info);
+		i830_dri2_del_frame_event(swap_info);
 		return;
 	}
 
@@ -1037,7 +1057,7 @@ void I830DRI2FrameEventHandler(unsigned int frame, unsigned int tv_sec,
 		break;
 	}
 
-	i830_dri2_del_frame_event(drawable, swap_info);
+	i830_dri2_del_frame_event(swap_info);
 }
 
 void I830DRI2FlipEventHandler(unsigned int frame, unsigned int tv_sec,
@@ -1098,7 +1118,7 @@ void I830DRI2FlipEventHandler(unsigned int frame, unsigned int tv_sec,
 						   serverClient,
 						   M_ANY, DixWriteAccess);
 			if (chain_drawable == NULL) {
-				i830_dri2_del_frame_event(chain_drawable, chain);
+				i830_dri2_del_frame_event(chain);
 			} else if (!can_exchange(chain_drawable, chain->front, chain->back) ||
 				   !I830DRI2ScheduleFlip(intel, chain_drawable, chain)) {
 				I830DRI2FallbackBlitSwap(chain_drawable,
@@ -1109,7 +1129,7 @@ void I830DRI2FlipEventHandler(unsigned int frame, unsigned int tv_sec,
 						 DRI2_BLIT_COMPLETE,
 						 chain->client ? chain->event_complete : NULL,
 						 chain->event_data);
-				i830_dri2_del_frame_event(chain_drawable, chain);
+				i830_dri2_del_frame_event(chain);
 			}
 		}
 		break;
@@ -1121,7 +1141,7 @@ void I830DRI2FlipEventHandler(unsigned int frame, unsigned int tv_sec,
 		break;
 	}
 
-	i830_dri2_del_frame_event(drawable, flip_info);
+	i830_dri2_del_frame_event(flip_info);
 }
 
 static uint32_t pipe_select(int pipe)
@@ -1134,6 +1154,28 @@ static uint32_t pipe_select(int pipe)
 		return 0;
 }
 
+static void
+intel_dri2_vblank_handler(ScrnInfoPtr scrn,
+                          xf86CrtcPtr crtc,
+                          uint64_t msc,
+                          uint64_t usec,
+                          void *data)
+{
+        DRI2FrameEventPtr swap_info = data;
+
+        I830DRI2FrameEventHandler((uint32_t) msc, usec / 1000000, usec % 1000000, swap_info);
+}
+
+static void
+intel_dri2_vblank_abort(ScrnInfoPtr scrn,
+                        xf86CrtcPtr crtc,
+                        void *data)
+{
+        DRI2FrameEventPtr swap_info = data;
+
+        i830_dri2_del_frame_event(swap_info);
+}
+
 /*
  * ScheduleSwap is responsible for requesting a DRM vblank event for the
  * appropriate frame.
@@ -1171,6 +1213,7 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	enum DRI2FrameEventType swap_type = DRI2_SWAP;
 	uint64_t current_msc, current_ust;
         uint64_t request_msc;
+        uint32_t seq;
 
 	/* Drawable not displayed... just complete the swap */
 	if (pipe == -1)
@@ -1249,8 +1292,13 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 		if (current_msc >= *target_msc)
 			*target_msc = current_msc;
 
+                seq = intel_drm_queue_alloc(scrn, crtc, swap_info, intel_dri2_vblank_handler, intel_dri2_vblank_abort);
+                if (!seq)
+                        goto blit_fallback;
+
 		vbl.request.sequence = intel_crtc_msc_to_sequence(scrn, crtc, *target_msc);
-		vbl.request.signal = (unsigned long)swap_info;
+		vbl.request.signal = seq;
+
 		ret = drmWaitVBlank(intel->drmSubFD, &vbl);
 		if (ret) {
 			xf86DrvMsg(scrn->scrnIndex, X_WARNING,
@@ -1292,12 +1340,14 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	if (request_msc <= current_msc)
 		request_msc += divisor;
 
-        vbl.request.sequence = intel_crtc_msc_to_sequence(scrn, crtc, request_msc);
+        seq = intel_drm_queue_alloc(scrn, crtc, swap_info, intel_dri2_vblank_handler, intel_dri2_vblank_abort);
+        if (!seq)
+                goto blit_fallback;
 
 	/* Account for 1 frame extra pageflip delay if flip > 0 */
-	vbl.request.sequence -= flip;
+        vbl.request.sequence = intel_crtc_msc_to_sequence(scrn, crtc, request_msc) - flip;
+	vbl.request.signal = seq;
 
-	vbl.request.signal = (unsigned long)swap_info;
 	ret = drmWaitVBlank(intel->drmSubFD, &vbl);
 	if (ret) {
 		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
@@ -1316,7 +1366,7 @@ blit_fallback:
 	I830DRI2FallbackBlitSwap(draw, front, back);
 	DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data);
 	if (swap_info)
-	    i830_dri2_del_frame_event(draw, swap_info);
+	    i830_dri2_del_frame_event(swap_info);
 	*target_msc = 0; /* offscreen, so zero out target vblank count */
 	return TRUE;
 }
@@ -1386,6 +1436,7 @@ I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
         xf86CrtcPtr crtc = I830DRI2DrawableCrtc(draw);
         int pipe = crtc ? intel_crtc_to_pipe(crtc) : -1;
 	CARD64 current_msc, current_ust, request_msc;
+        uint32_t seq;
 
 	/* Drawable not visible, return immediately */
 	if (pipe == -1)
@@ -1423,12 +1474,17 @@ I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 		 * sending us MSC targets from the past by forcibly updating
 		 * their count on this call.
 		 */
+                seq = intel_drm_queue_alloc(scrn, crtc, wait_info, intel_dri2_vblank_handler, intel_dri2_vblank_abort);
+                if (!seq)
+                        goto out_free;
+
 		if (current_msc >= target_msc)
 			target_msc = current_msc;
 		vbl.request.type =
 			DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT | pipe_select(pipe);
 		vbl.request.sequence = intel_crtc_msc_to_sequence(scrn, crtc, target_msc);
-		vbl.request.signal = (unsigned long)wait_info;
+		vbl.request.signal = seq;
+
 		ret = drmWaitVBlank(intel->drmSubFD, &vbl);
 		if (ret) {
 			static int limit = 5;
@@ -1465,9 +1521,13 @@ I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 	if ((current_msc % divisor) >= remainder)
                 request_msc += divisor;
 
+        seq = intel_drm_queue_alloc(scrn, crtc, wait_info, intel_dri2_vblank_handler, intel_dri2_vblank_abort);
+        if (!seq)
+                goto out_free;
+
 	vbl.request.sequence = intel_crtc_msc_to_sequence(scrn, crtc, request_msc);
+	vbl.request.signal = seq;
 
-	vbl.request.signal = (unsigned long)wait_info;
 	ret = drmWaitVBlank(intel->drmSubFD, &vbl);
 	if (ret) {
 		static int limit = 5;
@@ -1487,7 +1547,7 @@ I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 	return TRUE;
 
 out_free:
-	i830_dri2_del_frame_event(draw, wait_info);
+	i830_dri2_del_frame_event(wait_info);
 out_complete:
 	DRI2WaitMSCComplete(client, draw, target_msc, 0, 0);
 	return TRUE;
commit 7f28b1cfa20eb4fc11c1a9c1e0c0fac7847be72e
Author: Keith Packard <keithp at keithp.com>
Date:   Tue Nov 19 22:58:33 2013 -0800

    uxa: Support 64-bit MSC values. Handle kernel vageries about MSC reporting
    
    The kernel sometimes reports bogus MSC values, especially when
    suspending and resuming the machine. Deal with this by tracking an
    offset to ensure that the MSC seen by applications increases
    monotonically, and at a reasonable pace.
    
    Also, provide a full 64 bits of MSC value by noticing wrapping and
    tracking the high 32-bits of MSC separately.
    
    Signed-off-by: Keith Packard <keithp at keithp.com>
    Reviewed-by: Eric Anholt <eric at anholt.net>
    
    Conflicts:
    	src/uxa/intel_dri.c

diff --git a/src/uxa/intel.h b/src/uxa/intel.h
index 287ffe4..1d641f2 100644
--- a/src/uxa/intel.h
+++ b/src/uxa/intel.h
@@ -413,6 +413,15 @@ extern int intel_crtc_id(xf86CrtcPtr crtc);
 extern int intel_output_dpms_status(xf86OutputPtr output);
 extern void intel_copy_fb(ScrnInfoPtr scrn);
 
+int
+intel_get_crtc_msc_ust(ScrnInfoPtr scrn, xf86CrtcPtr crtc, uint64_t *msc, uint64_t *ust);
+
+uint32_t
+intel_crtc_msc_to_sequence(ScrnInfoPtr scrn, xf86CrtcPtr crtc, uint64_t expect);
+
+uint64_t
+intel_sequence_to_crtc_msc(xf86CrtcPtr crtc, uint32_t sequence);
+
 enum DRI2FrameEventType {
 	DRI2_SWAP,
 	DRI2_SWAP_CHAIN,
diff --git a/src/uxa/intel_display.c b/src/uxa/intel_display.c
index 46a009e..dac1a82 100644
--- a/src/uxa/intel_display.c
+++ b/src/uxa/intel_display.c
@@ -71,9 +71,8 @@ struct intel_mode {
 	DRI2FrameEventPtr flip_info;
 	int old_fb_id;
 	int flip_count;
-	unsigned int fe_frame;
-	unsigned int fe_tv_sec;
-	unsigned int fe_tv_usec;
+	uint64_t fe_msc;
+        uint64_t fe_usec;
 
 	struct list outputs;
 	struct list crtcs;
@@ -97,6 +96,9 @@ struct intel_crtc {
 	struct list link;
 	PixmapPtr scanout_pixmap;
 	uint32_t scanout_fb_id;
+	int32_t vblank_offset;
+	uint32_t msc_prev;
+	uint64_t msc_high;
 };
 
 struct intel_property {
@@ -1492,9 +1494,8 @@ intel_do_pageflip(intel_screen_private *intel,
 	 * Also, flips queued on disabled or incorrectly configured displays
 	 * may never complete; this is a configuration error.
 	 */
-	mode->fe_frame = 0;
-	mode->fe_tv_sec = 0;
-	mode->fe_tv_usec = 0;
+	mode->fe_msc = 0;
+	mode->fe_usec = 0;
 
 	for (i = 0; i < config->num_crtc; i++) {
 		if (!intel_crtc_on(config->crtc[i]))
@@ -1550,6 +1551,107 @@ static const xf86CrtcConfigFuncsRec intel_xf86crtc_config_funcs = {
 	intel_xf86crtc_resize
 };
 
+static uint32_t pipe_select(int pipe)
+{
+	if (pipe > 1)
+		return pipe << DRM_VBLANK_HIGH_CRTC_SHIFT;
+	else if (pipe > 0)
+		return DRM_VBLANK_SECONDARY;
+	else
+		return 0;
+}
+
+/*
+ * Get the current msc/ust value from the kernel
+ */
+static int
+intel_get_msc_ust(ScrnInfoPtr scrn, xf86CrtcPtr crtc, uint32_t *msc, uint64_t *ust)
+{
+	intel_screen_private *intel = intel_get_screen_private(scrn);
+	drmVBlank vbl;
+
+	/* Get current count */
+	vbl.request.type = DRM_VBLANK_RELATIVE | pipe_select(intel_crtc_to_pipe(crtc));
+	vbl.request.sequence = 0;
+	vbl.request.signal = 0;
+	if (drmWaitVBlank(intel->drmSubFD, &vbl)) {
+		*msc = 0;
+		*ust = 0;
+		return BadMatch;
+	} else {
+		*msc = vbl.reply.sequence;
+		*ust = (CARD64) vbl.reply.tval_sec * 1000000 + vbl.reply.tval_usec;
+		return Success;
+	}
+}
+
+/*
+ * Convert a 32-bit kernel MSC sequence number to a 64-bit local sequence
+ * number, adding in the vblank_offset and high 32 bits, and dealing
+ * with 64-bit wrapping
+ */
+uint64_t
+intel_sequence_to_crtc_msc(xf86CrtcPtr crtc, uint32_t sequence)
+{
+	struct intel_crtc *intel_crtc = crtc->driver_private;
+
+        sequence += intel_crtc->vblank_offset;
+        if ((int32_t) (sequence - intel_crtc->msc_prev) < -0x40000000)
+                intel_crtc->msc_high += 0x100000000L;
+        intel_crtc->msc_prev = sequence;
+        return intel_crtc->msc_high + sequence;
+}
+
+/*
+ * Get the current 64-bit adjust MSC and UST value
+ */
+int
+intel_get_crtc_msc_ust(ScrnInfoPtr scrn, xf86CrtcPtr crtc, uint64_t *msc, uint64_t *ust)
+{
+        uint32_t sequence;
+        int ret;
+
+        ret = intel_get_msc_ust(scrn, crtc, &sequence, ust);
+	if (ret)
+		return ret;
+
+        *msc = intel_sequence_to_crtc_msc(crtc, sequence);
+        return 0;
+}
+
+/*
+ * Convert a 64-bit adjusted MSC value into a 32-bit kernel sequence number,
+ * removing the high 32 bits and subtracting out the vblank_offset term.
+ *
+ * This also updates the vblank_offset when it notices that the value should
+ * change.
+ */
+
+#define MAX_VBLANK_OFFSET       1000
+
+uint32_t
+intel_crtc_msc_to_sequence(ScrnInfoPtr scrn, xf86CrtcPtr crtc, uint64_t expect)
+{
+	struct intel_crtc *intel_crtc = crtc->driver_private;
+        uint64_t msc, ust;
+
+	if (intel_get_crtc_msc_ust(scrn, crtc, &msc, &ust) == 0) {
+		int64_t diff = expect - msc;
+
+		/* We're way off here, assume that the kernel has lost its mind
+		 * and smack the vblank back to something sensible
+		 */
+		if (diff < -MAX_VBLANK_OFFSET || diff > MAX_VBLANK_OFFSET) {
+			intel_crtc->vblank_offset += (int32_t) diff;
+			if (intel_crtc->vblank_offset > -MAX_VBLANK_OFFSET &&
+			    intel_crtc->vblank_offset < MAX_VBLANK_OFFSET)
+				intel_crtc->vblank_offset = 0;
+		}
+	}
+
+        return (uint32_t) (expect - intel_crtc->vblank_offset);
+}
+
 static void
 intel_vblank_handler(int fd, unsigned int frame, unsigned int tv_sec,
 		       unsigned int tv_usec, void *event)
@@ -1567,9 +1669,8 @@ intel_page_flip_handler(int fd, unsigned int frame, unsigned int tv_sec,
 	/* Is this the event whose info shall be delivered to higher level? */
 	if (flip->dispatch_me) {
 		/* Yes: Cache msc, ust for later delivery. */
-		mode->fe_frame = frame;
-		mode->fe_tv_sec = tv_sec;
-		mode->fe_tv_usec = tv_usec;
+		mode->fe_msc = frame;
+		mode->fe_usec = (uint64_t) tv_sec * 1000000 + tv_usec;
 	}
 	free(flip);
 
@@ -1585,8 +1686,8 @@ intel_page_flip_handler(int fd, unsigned int frame, unsigned int tv_sec,
 		return;
 
 	/* Deliver cached msc, ust from reference crtc to flip event handler */
-	I830DRI2FlipEventHandler(mode->fe_frame, mode->fe_tv_sec,
-				 mode->fe_tv_usec, mode->flip_info);
+	I830DRI2FlipEventHandler((uint32_t) mode->fe_msc, mode->fe_usec / 1000000,
+				 mode->fe_usec % 1000000, mode->flip_info);
 }
 
 static void
diff --git a/src/uxa/intel_dri.c b/src/uxa/intel_dri.c
index d10673e..29b1942 100644
--- a/src/uxa/intel_dri.c
+++ b/src/uxa/intel_dri.c
@@ -575,14 +575,13 @@ static void I830DRI2ReferenceBuffer(DRI2Buffer2Ptr buffer)
 	}
 }
 
-static int
-I830DRI2DrawablePipe(DrawablePtr pDraw)
+static xf86CrtcPtr
+I830DRI2DrawableCrtc(DrawablePtr pDraw)
 {
 	ScreenPtr pScreen = pDraw->pScreen;
 	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
 	BoxRec box, crtcbox;
 	xf86CrtcPtr crtc = NULL;
-	int pipe = -1;
 
 	box.x1 = pDraw->x;
 	box.y1 = pDraw->y;
@@ -594,9 +593,9 @@ I830DRI2DrawablePipe(DrawablePtr pDraw)
 
 	/* Make sure the CRTC is valid and this is the real front buffer */
 	if (crtc != NULL && !crtc->rotatedData)
-		pipe = intel_crtc_to_pipe(crtc);
+		return crtc;
 
-	return pipe;
+	return NULL;
 }
 
 static RESTYPE	frame_event_client_type, frame_event_drawable_type;
@@ -954,7 +953,7 @@ can_exchange(DrawablePtr drawable, DRI2BufferPtr front, DRI2BufferPtr back)
 	if (!pScrn->vtSema)
 		return FALSE;
 
-	if (I830DRI2DrawablePipe(drawable) < 0)
+	if (I830DRI2DrawableCrtc(drawable) == NULL)
 		return FALSE;
 
 	if (!DRI2CanFlip(drawable))
@@ -1164,21 +1163,19 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
 	intel_screen_private *intel = intel_get_screen_private(scrn);
 	drmVBlank vbl;
-	int ret, pipe = I830DRI2DrawablePipe(draw), flip = 0;
+	int ret;
+        xf86CrtcPtr crtc = I830DRI2DrawableCrtc(draw);
+        int pipe = crtc ? intel_crtc_to_pipe(crtc) : -1;
+        int flip = 0;
 	DRI2FrameEventPtr swap_info = NULL;
 	enum DRI2FrameEventType swap_type = DRI2_SWAP;
-	CARD64 current_msc;
+	uint64_t current_msc, current_ust;
+        uint64_t request_msc;
 
 	/* Drawable not displayed... just complete the swap */
 	if (pipe == -1)
 	    goto blit_fallback;
 
-	/* Truncate to match kernel interfaces; means occasional overflow
-	 * misses, but that's generally not a big deal */
-	*target_msc &= 0xffffffff;
-	divisor &= 0xffffffff;
-	remainder &= 0xffffffff;
-
 	swap_info = calloc(1, sizeof(DRI2FrameEventRec));
 	if (!swap_info)
 	    goto blit_fallback;
@@ -1201,18 +1198,9 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	I830DRI2ReferenceBuffer(front);
 	I830DRI2ReferenceBuffer(back);
 
-	/* Get current count */
-	vbl.request.type = DRM_VBLANK_RELATIVE | pipe_select(pipe);
-	vbl.request.sequence = 0;
-	ret = drmWaitVBlank(intel->drmSubFD, &vbl);
-	if (ret) {
-		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
-			   "first get vblank counter failed: %s\n",
-			   strerror(errno));
-		goto blit_fallback;
-	}
-
-	current_msc = vbl.reply.sequence;
+        ret = intel_get_crtc_msc_ust(scrn, crtc, &current_msc, &current_ust);
+	if (ret)
+	    goto blit_fallback;
 
 	/* Flips need to be submitted one frame before */
 	if (can_exchange(draw, front, back)) {
@@ -1261,7 +1249,7 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 		if (current_msc >= *target_msc)
 			*target_msc = current_msc;
 
-		vbl.request.sequence = *target_msc;
+		vbl.request.sequence = intel_crtc_msc_to_sequence(scrn, crtc, *target_msc);
 		vbl.request.signal = (unsigned long)swap_info;
 		ret = drmWaitVBlank(intel->drmSubFD, &vbl);
 		if (ret) {
@@ -1271,7 +1259,7 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 			goto blit_fallback;
 		}
 
-		*target_msc = vbl.reply.sequence + flip;
+                *target_msc = intel_sequence_to_crtc_msc(crtc, vbl.reply.sequence + flip);
 		swap_info->frame = *target_msc;
 
 		return TRUE;
@@ -1287,8 +1275,8 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	if (flip == 0)
 		vbl.request.type |= DRM_VBLANK_NEXTONMISS;
 
-	vbl.request.sequence = current_msc - (current_msc % divisor) +
-		remainder;
+        request_msc = current_msc - (current_msc % divisor) +
+                remainder;
 
 	/*
 	 * If the calculated deadline vbl.request.sequence is smaller than
@@ -1301,8 +1289,10 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	 * into account, as well as a potential DRM_VBLANK_NEXTONMISS delay
 	 * if we are blitting/exchanging instead of flipping.
 	 */
-	if (vbl.request.sequence <= current_msc)
-		vbl.request.sequence += divisor;
+	if (request_msc <= current_msc)
+		request_msc += divisor;
+
+        vbl.request.sequence = intel_crtc_msc_to_sequence(scrn, crtc, request_msc);
 
 	/* Account for 1 frame extra pageflip delay if flip > 0 */
 	vbl.request.sequence -= flip;
@@ -1317,7 +1307,7 @@ I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front,
 	}
 
 	/* Adjust returned value for 1 fame pageflip offset of flip > 0 */
-	*target_msc = vbl.reply.sequence + flip;
+	*target_msc = intel_sequence_to_crtc_msc(crtc, vbl.reply.sequence + flip);
 	swap_info->frame = *target_msc;
 
 	return TRUE;
@@ -1350,22 +1340,18 @@ I830DRI2GetMSC(DrawablePtr draw, CARD64 *ust, CARD64 *msc)
 {
 	ScreenPtr screen = draw->pScreen;
 	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
-	intel_screen_private *intel = intel_get_screen_private(scrn);
-	drmVBlank vbl;
-	int ret, pipe = I830DRI2DrawablePipe(draw);
+	int ret;
+        xf86CrtcPtr crtc = I830DRI2DrawableCrtc(draw);
 
 	/* Drawable not displayed, make up a *monotonic* value */
-	if (pipe == -1) {
+	if (crtc == NULL) {
 fail:
 		*ust = gettime_us();
 		*msc = 0;
 		return TRUE;
 	}
 
-	vbl.request.type = DRM_VBLANK_RELATIVE | pipe_select(pipe);
-	vbl.request.sequence = 0;
-
-	ret = drmWaitVBlank(intel->drmSubFD, &vbl);
+        ret = intel_get_crtc_msc_ust(scrn, crtc, msc, ust);
 	if (ret) {
 		static int limit = 5;
 		if (limit) {
@@ -1378,9 +1364,6 @@ fail:
 		goto fail;
 	}
 
-	*ust = ((CARD64)vbl.reply.tval_sec * 1000000) + vbl.reply.tval_usec;
-	*msc = vbl.reply.sequence;
-
 	return TRUE;
 }
 
@@ -1399,14 +1382,10 @@ I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 	intel_screen_private *intel = intel_get_screen_private(scrn);
 	DRI2FrameEventPtr wait_info;
 	drmVBlank vbl;
-	int ret, pipe = I830DRI2DrawablePipe(draw);
-	CARD64 current_msc;
-
-	/* Truncate to match kernel interfaces; means occasional overflow
-	 * misses, but that's generally not a big deal */
-	target_msc &= 0xffffffff;
-	divisor &= 0xffffffff;
-	remainder &= 0xffffffff;
+	int ret;
+        xf86CrtcPtr crtc = I830DRI2DrawableCrtc(draw);
+        int pipe = crtc ? intel_crtc_to_pipe(crtc) : -1;
+	CARD64 current_msc, current_ust, request_msc;
 
 	/* Drawable not visible, return immediately */
 	if (pipe == -1)
@@ -1428,22 +1407,9 @@ I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 	}
 
 	/* Get current count */
-	vbl.request.type = DRM_VBLANK_RELATIVE | pipe_select(pipe);
-	vbl.request.sequence = 0;
-	ret = drmWaitVBlank(intel->drmSubFD, &vbl);
-	if (ret) {
-		static int limit = 5;
-		if (limit) {
-			xf86DrvMsg(scrn->scrnIndex, X_WARNING,
-				   "%s:%d get vblank counter failed: %s\n",
-				   __FUNCTION__, __LINE__,
-				   strerror(errno));
-			limit--;
-		}
-		goto out_free;
-	}
-
-	current_msc = vbl.reply.sequence;
+        ret = intel_get_crtc_msc_ust(scrn, crtc, &current_msc, &current_ust);
+	if (ret)
+	    goto out_free;
 
 	/*
 	 * If divisor is zero, or current_msc is smaller than target_msc,
@@ -1461,7 +1427,7 @@ I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 			target_msc = current_msc;
 		vbl.request.type =
 			DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT | pipe_select(pipe);
-		vbl.request.sequence = target_msc;
+		vbl.request.sequence = intel_crtc_msc_to_sequence(scrn, crtc, target_msc);
 		vbl.request.signal = (unsigned long)wait_info;
 		ret = drmWaitVBlank(intel->drmSubFD, &vbl);
 		if (ret) {
@@ -1476,7 +1442,7 @@ I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 			goto out_free;
 		}
 
-		wait_info->frame = vbl.reply.sequence;
+		wait_info->frame = intel_sequence_to_crtc_msc(crtc, vbl.reply.sequence);
 		DRI2BlockClient(client, draw);
 		return TRUE;
 	}
@@ -1488,9 +1454,8 @@ I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 	vbl.request.type =
 		DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT | pipe_select(pipe);
 
-	vbl.request.sequence = current_msc - (current_msc % divisor) +
-	    remainder;
-
+        request_msc = current_msc - (current_msc % divisor) +
+                remainder;
 	/*
 	 * If calculated remainder is larger than requested remainder,
 	 * it means we've passed the last point where
@@ -1498,7 +1463,9 @@ I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 	 * that will happen.
 	 */
 	if ((current_msc % divisor) >= remainder)
-	    vbl.request.sequence += divisor;
+                request_msc += divisor;
+
+	vbl.request.sequence = intel_crtc_msc_to_sequence(scrn, crtc, request_msc);
 
 	vbl.request.signal = (unsigned long)wait_info;
 	ret = drmWaitVBlank(intel->drmSubFD, &vbl);
@@ -1514,7 +1481,7 @@ I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc,
 		goto out_free;
 	}
 
-	wait_info->frame = vbl.reply.sequence;
+	wait_info->frame = intel_sequence_to_crtc_msc(crtc, vbl.reply.sequence);
 	DRI2BlockClient(client, draw);
 
 	return TRUE;
commit dd6db82680b05cde4a47116b7096c054f3837e20
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri May 9 20:26:19 2014 +0100

    uxa: Add DRI3 and miSyncShm support
    
    Based on a patch by Keith Packard.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/uxa/Makefile.am b/src/uxa/Makefile.am
index 4fa1b8c..29a8329 100644
--- a/src/uxa/Makefile.am
+++ b/src/uxa/Makefile.am
@@ -80,6 +80,13 @@ libuxa_la_LIBADD += \
 	$(NULL)
 endif
 
+if DRI3
+libuxa_la_SOURCES += \
+	intel_dri3.c \
+	intel_sync.c \
+	$(NULL)
+endif
+
 if XVMC
 AM_CFLAGS += -I$(top_srcdir)/xvmc
 libuxa_la_SOURCES += \
diff --git a/src/uxa/intel.h b/src/uxa/intel.h
index a4cae13..287ffe4 100644
--- a/src/uxa/intel.h
+++ b/src/uxa/intel.h
@@ -78,6 +78,10 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include <libudev.h>
 #endif
 
+#if HAVE_DRI3
+#include "misync.h"
+#endif
+
 /* remain compatible to xorg-server 1.6 */
 #ifndef MONITOR_EDID_COMPLETE_RAWDATA
 #define MONITOR_EDID_COMPLETE_RAWDATA EDID_COMPLETE_RAWDATA
@@ -162,7 +166,7 @@ enum last_3d {
 enum dri_type {
 	DRI_DISABLED,
 	DRI_NONE,
-	DRI_DRI2
+	DRI_ACTIVE
 };
 
 typedef struct intel_screen_private {
@@ -324,9 +328,8 @@ typedef struct intel_screen_private {
 	/* 965 render acceleration state */
 	struct gen4_render_state *gen4_render_state;
 
-	enum dri_type directRenderingType;	/* DRI enabled this generation. */
-
-	Bool directRenderingOpen;
+	/* DRI enabled this generation. */
+	enum dri_type dri2, dri3;
 	int drmSubFD;
 	char *deviceName;
 
@@ -356,6 +359,11 @@ typedef struct intel_screen_private {
 	pointer uevent_handler;
 #endif
 	Bool has_prime_vmap_flush;
+
+#if HAVE_DRI3
+	SyncScreenFuncsRec save_sync_screen_funcs;
+#endif
+	void (*flush_rendering)(struct intel_screen_private *intel);
 } intel_screen_private;
 
 #define INTEL_INFO(intel) ((intel)->info)
@@ -485,6 +493,9 @@ void I830DRI2FrameEventHandler(unsigned int frame, unsigned int tv_sec,
 void I830DRI2FlipEventHandler(unsigned int frame, unsigned int tv_sec,
 			      unsigned int tv_usec, DRI2FrameEventPtr flip_info);
 
+/* intel_dri3.c */
+Bool intel_dri3_screen_init(ScreenPtr screen);
+
 extern Bool intel_crtc_on(xf86CrtcPtr crtc);
 int intel_crtc_to_pipe(xf86CrtcPtr crtc);
 
@@ -691,4 +702,12 @@ static inline Bool intel_pixmap_is_offscreen(PixmapPtr pixmap)
 	return priv && priv->offscreen;
 }
 
+#if HAVE_DRI3
+Bool intel_sync_init(ScreenPtr screen);
+void intel_sync_close(ScreenPtr screen);
+#else
+static inline Bool intel_sync_init(ScreenPtr screen) { return 0; }
+void intel_sync_close(ScreenPtr screen);
+#endif
+
 #endif /* _I830_H_ */
diff --git a/src/uxa/intel_dri.c b/src/uxa/intel_dri.c
index 01209b9..d10673e 100644
--- a/src/uxa/intel_dri.c
+++ b/src/uxa/intel_dri.c
@@ -1650,6 +1650,5 @@ void I830DRI2CloseScreen(ScreenPtr screen)
 	intel_screen_private *intel = intel_get_screen_private(scrn);
 
 	DRI2CloseScreen(screen);
-	intel->directRenderingType = DRI_NONE;
 	drmFree(intel->deviceName);
 }
diff --git a/src/uxa/intel_dri3.c b/src/uxa/intel_dri3.c
new file mode 100644
index 0000000..dc8d762
--- /dev/null
+++ b/src/uxa/intel_dri3.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright © 2013-2014 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "xorg-server.h"
+#include "xf86.h"
+#include "fb.h"
+
+#include "intel.h"
+#include "dri3.h"
+
+static int
+intel_dri3_open(ScreenPtr screen,
+                RRProviderPtr provider,
+                int *out)
+{
+	int fd;
+
+	fd = intel_get_client_fd(xf86ScreenToScrn(screen));
+	if (fd < 0)
+		return -fd;
+
+	*out = fd;
+	return Success;
+}
+
+static PixmapPtr intel_dri3_pixmap_from_fd(ScreenPtr screen,
+					   int fd,
+					   CARD16 width,
+					   CARD16 height,
+					   CARD16 stride,
+					   CARD8 depth,
+					   CARD8 bpp)
+{
+	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+	intel_screen_private *intel = intel_get_screen_private(scrn);
+	struct intel_pixmap *priv;
+	PixmapPtr pixmap;
+	dri_bo *bo;
+
+	if (depth < 8)
+		return NULL;
+
+	switch (bpp) {
+	case 8:
+	case 16:
+	case 32:
+		break;
+	default:
+		return NULL;
+	}
+
+	pixmap = fbCreatePixmap(screen, 0, 0, depth, 0);
+	if (!pixmap)
+		return NULL;
+
+	if (!screen->ModifyPixmapHeader(pixmap, width, height, 0, 0, stride, NULL))
+		goto free_pixmap;
+
+	bo = drm_intel_bo_gem_create_from_prime(intel->bufmgr,
+						fd, (uint32_t)height * stride);
+	if (bo == NULL)
+		goto free_pixmap;
+
+	intel_set_pixmap_bo(pixmap, bo);
+	dri_bo_unreference(bo);
+
+	priv = intel_get_pixmap_private(pixmap);
+	if (priv == NULL)
+		goto free_pixmap;
+
+	priv->pinned |= PIN_DRI3;
+
+	return pixmap;
+
+free_pixmap:
+	fbDestroyPixmap(pixmap);
+	return NULL;
+}
+
+static int intel_dri3_fd_from_pixmap(ScreenPtr screen,
+				     PixmapPtr pixmap,
+				     CARD16 *stride,
+				     CARD32 *size)
+{
+	struct intel_pixmap *priv;
+	int fd;
+
+	priv = intel_get_pixmap_private(pixmap);
+	if (!priv)
+		return -1;
+
+	if (priv->stride > UINT16_MAX)
+		return -1;
+
+	if (drm_intel_bo_gem_export_to_prime(priv->bo, &fd) < 0)
+		return -1;
+
+	priv->pinned |= PIN_DRI3;
+
+	*stride = priv->stride;
+	*size = priv->bo->size;
+	return fd;
+}
+
+static dri3_screen_info_rec intel_dri3_screen_info = {
+        .version = DRI3_SCREEN_INFO_VERSION,
+
+        .open = intel_dri3_open,
+        .pixmap_from_fd = intel_dri3_pixmap_from_fd,
+        .fd_from_pixmap = intel_dri3_fd_from_pixmap
+};
+
+Bool
+intel_dri3_screen_init(ScreenPtr screen)
+{
+        return dri3_screen_init(screen, &intel_dri3_screen_info);
+}
diff --git a/src/uxa/intel_driver.c b/src/uxa/intel_driver.c
index 654038c..9e21742 100644
--- a/src/uxa/intel_driver.c
+++ b/src/uxa/intel_driver.c
@@ -232,15 +232,15 @@ static void intel_check_dri_option(ScrnInfoPtr scrn)
 {
 	intel_screen_private *intel = intel_get_screen_private(scrn);
 
-	intel->directRenderingType = DRI_NONE;
+	intel->dri2 = intel->dri3 = DRI_NONE;
 	if (!intel_option_cast_string_to_bool(intel, OPTION_DRI, TRUE))
-		intel->directRenderingType = DRI_DISABLED;
+		intel->dri2 = intel->dri3 = DRI_DISABLED;
 
 	if (scrn->depth != 16 && scrn->depth != 24 && scrn->depth != 30) {
 		xf86DrvMsg(scrn->scrnIndex, X_CONFIG,
 			   "DRI is disabled because it "
 			   "runs only at depths 16, 24, and 30.\n");
-		intel->directRenderingType = DRI_DISABLED;
+		intel->dri2 = intel->dri3 = DRI_DISABLED;
 	}
 }
 
@@ -599,9 +599,15 @@ static Bool I830PreInit(ScrnInfoPtr scrn, int flags)
 		return FALSE;
 	}
 
-	/* Load the dri2 module if requested. */
-	if (intel->directRenderingType != DRI_DISABLED)
-		xf86LoadSubModule(scrn, "dri2");
+	/* Load the dri modules if requested. */
+#if HAVE_DRI2
+	if (intel->dri2 != DRI_DISABLED && !xf86LoadSubModule(scrn, "dri2"))
+		intel->dri2 = DRI_DISABLED;
+#endif
+#if HAVE_DRI3
+	if (intel->dri3 != DRI_DISABLED && !xf86LoadSubModule(scrn, "dri3"))
+		intel->dri3 = DRI_DISABLED;
+#endif
 
 	return TRUE;
 }
@@ -871,13 +877,6 @@ I830ScreenInit(SCREEN_INIT_ARGS_DECL)
 	 */
 	intel->XvEnabled = TRUE;
 
-#ifdef DRI2
-	if (intel->directRenderingType == DRI_NONE
-	    && I830DRI2ScreenInit(screen))
-		intel->directRenderingType = DRI_DRI2;
-#endif
-
-	if (!intel_init_initial_framebuffer(scrn))
 		return FALSE;
 
 	intel_batch_init(scrn);
@@ -893,6 +892,7 @@ I830ScreenInit(SCREEN_INIT_ARGS_DECL)
 	if (!miSetPixmapDepths())
 		return FALSE;
 
+	/* Must be first, before anything else installs screen callbacks. */
 	if (!fbScreenInit(screen, NULL,
 			  scrn->virtualX, scrn->virtualY,
 			  scrn->xDpi, scrn->yDpi,
@@ -924,6 +924,18 @@ I830ScreenInit(SCREEN_INIT_ARGS_DECL)
 		return FALSE;
 	}
 
+#if HAVE_DRI2
+	if (intel->dri2 == DRI_NONE && I830DRI2ScreenInit(screen))
+		intel->dri2 = DRI_ACTIVE;
+#endif
+
+#if HAVE_DRI3
+	if (!intel_sync_init(screen))
+		intel->dri3 = DRI_DISABLED;
+	if (intel->dri3 == DRI_NONE && intel_dri3_screen_init(screen))
+		intel->dri3 = DRI_ACTIVE;
+#endif
+
 	xf86SetBackingStore(screen);
 	xf86SetSilkenMouse(screen);
 	miDCInitialize(screen, xf86GetPointerScreenFuncs());
@@ -977,7 +989,7 @@ I830ScreenInit(SCREEN_INIT_ARGS_DECL)
 #ifdef INTEL_XVMC
 	if (INTEL_INFO(intel)->gen >= 040)
 		intel->XvMCEnabled = TRUE;
-	from = ((intel->directRenderingType == DRI_DRI2) &&
+	from = (intel->dri2 == DRI_ACTIVE &&
 		xf86GetOptValBool(intel->Options, OPTION_XVMC,
 				  &intel->XvMCEnabled) ? X_CONFIG : X_DEFAULT);
 	xf86DrvMsg(scrn->scrnIndex, from, "Intel XvMC decoder %sabled\n",
@@ -987,25 +999,44 @@ I830ScreenInit(SCREEN_INIT_ARGS_DECL)
 	if (intel->XvEnabled)
 		I830InitVideo(screen);
 
-#if defined(DRI2)
-	switch (intel->directRenderingType) {
-	case DRI_DRI2:
-		intel->directRenderingOpen = TRUE;
+#if HAVE_DRI2
+	switch (intel->dri2) {
+	case DRI_ACTIVE:
+		xf86DrvMsg(scrn->scrnIndex, X_INFO,
+			   "DRI2: Enabled\n");
+		break;
+	case DRI_DISABLED:
+		xf86DrvMsg(scrn->scrnIndex, X_INFO,
+			   "DRI2: Disabled\n");
+		break;
+	case DRI_NONE:
+		xf86DrvMsg(scrn->scrnIndex, X_INFO,
+			   "DRI2: Failed\n");
+		break;
+	}
+#else
+	xf86DrvMsg(scrn->scrnIndex, X_INFO,
+		   "DRI2: Not available\n");
+#endif
+
+#if HAVE_DRI3
+	switch (intel->dri3) {
+	case DRI_ACTIVE:
 		xf86DrvMsg(scrn->scrnIndex, X_INFO,
-			   "direct rendering: DRI2 Enabled\n");
+			   "DRI3: Enabled\n");
 		break;
 	case DRI_DISABLED:
 		xf86DrvMsg(scrn->scrnIndex, X_INFO,
-			   "direct rendering: Disabled\n");
+			   "DRI3: Disabled\n");
 		break;
 	case DRI_NONE:
 		xf86DrvMsg(scrn->scrnIndex, X_INFO,
-			   "direct rendering: Failed\n");
+			   "DRI3: Failed\n");
 		break;
 	}
 #else
 	xf86DrvMsg(scrn->scrnIndex, X_INFO,
-		   "direct rendering: Not available\n");
+		   "DRI3: Not available\n");
 #endif
 
 	if (serverGeneration == 1)
@@ -1142,12 +1173,18 @@ static Bool I830CloseScreen(CLOSE_SCREEN_ARGS_DECL)
 	screen->CloseScreen = intel->CloseScreen;
 	(*screen->CloseScreen) (CLOSE_SCREEN_ARGS);
 
-	if (intel->directRenderingOpen
-	    && intel->directRenderingType == DRI_DRI2) {
-		intel->directRenderingOpen = FALSE;
+	if (intel->dri2 == DRI_ACTIVE) {
 		I830DRI2CloseScreen(screen);
+		intel->dri2 = DRI_NONE;
 	}
 
+	if (intel->dri3 == DRI_ACTIVE) {
+		/* nothing to do here? */
+		intel->dri3 = DRI_NONE;
+	}
+
+	intel_sync_close(screen);
+
 	xf86GARTCloseScreen(scrn->scrnIndex);
 
 	scrn->vtSema = FALSE;
diff --git a/src/uxa/intel_sync.c b/src/uxa/intel_sync.c
new file mode 100644
index 0000000..cd73a1b
--- /dev/null
+++ b/src/uxa/intel_sync.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright © 2013-2014 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include "intel.h"
+#include "misyncshm.h"
+#include "misyncstr.h"
+
+/*
+ * This whole file exists to wrap a sync fence trigger operation
+ * so that we can flush the batch buffer to provide serialization
+ * between the server and the shm fence client
+ */
+
+static DevPrivateKeyRec intel_sync_fence_private_key;
+
+typedef struct _intel_sync_fence_private {
+        SyncFenceSetTriggeredFunc set_triggered;
+} intel_sync_fence_private;
+
+#define SYNC_FENCE_PRIV(pFence)                                         \
+        (intel_sync_fence_private *) dixLookupPrivate(&pFence->devPrivates, &intel_sync_fence_private_key)
+
+static void
+intel_sync_fence_set_triggered (SyncFence *fence)
+{
+	ScreenPtr screen = fence->pScreen;
+	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+	intel_screen_private *intel = intel_get_screen_private(scrn);
+	intel_sync_fence_private *private = SYNC_FENCE_PRIV(fence);
+
+	/* Flush pending rendering operations */
+	if (intel->flush_rendering)
+		intel->flush_rendering(intel);
+
+	fence->funcs.SetTriggered = private->set_triggered;
+	fence->funcs.SetTriggered(fence);
+	private->set_triggered = fence->funcs.SetTriggered;
+	fence->funcs.SetTriggered = intel_sync_fence_set_triggered;
+}
+
+static void
+intel_sync_create_fence(ScreenPtr screen,
+                        SyncFence *fence,
+                        Bool initially_triggered)
+{
+	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+	intel_screen_private *intel = intel_get_screen_private(scrn);
+	SyncScreenFuncsPtr screen_funcs = miSyncGetScreenFuncs(screen);
+	intel_sync_fence_private *private = SYNC_FENCE_PRIV(fence);
+
+	screen_funcs->CreateFence = intel->save_sync_screen_funcs.CreateFence;
+	screen_funcs->CreateFence(screen, fence, initially_triggered);
+	intel->save_sync_screen_funcs.CreateFence = screen_funcs->CreateFence;
+	screen_funcs->CreateFence = intel_sync_create_fence;
+
+	private->set_triggered = fence->funcs.SetTriggered;
+	fence->funcs.SetTriggered = intel_sync_fence_set_triggered;
+}
+
+Bool
+intel_sync_init(ScreenPtr screen)
+{
+	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+	intel_screen_private *intel = intel_get_screen_private(scrn);
+	SyncScreenFuncsPtr screen_funcs;
+
+	if (!miSyncShmScreenInit(screen))
+		return FALSE;
+
+	if (!dixPrivateKeyRegistered(&intel_sync_fence_private_key)) {
+		if (!dixRegisterPrivateKey(&intel_sync_fence_private_key,
+					   PRIVATE_SYNC_FENCE,
+					   sizeof (intel_sync_fence_private)))
+			return FALSE;
+	}
+
+	screen_funcs = miSyncGetScreenFuncs(screen);
+	intel->save_sync_screen_funcs.CreateFence = screen_funcs->CreateFence;
+	screen_funcs->CreateFence = intel_sync_create_fence;
+	return TRUE;
+}
+
+void
+intel_sync_close(ScreenPtr screen)
+{
+        ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+        intel_screen_private *intel = intel_get_screen_private(scrn);
+        SyncScreenFuncsPtr screen_funcs = miSyncGetScreenFuncs(screen);
+
+        if (screen_funcs)
+                screen_funcs->CreateFence = intel->save_sync_screen_funcs.CreateFence;
+}
diff --git a/src/uxa/intel_uxa.c b/src/uxa/intel_uxa.c
index e90b148..3bc1d23 100644
--- a/src/uxa/intel_uxa.c
+++ b/src/uxa/intel_uxa.c
@@ -633,18 +633,38 @@ dri_bo *intel_get_pixmap_bo(PixmapPtr pixmap)
 	return intel->bo;
 }
 
+static unsigned intel_get_tile_width(intel_screen_private *intel, int tiling, int pitch)
+{
+	unsigned long tile_width;
+
+	if (tiling == I915_TILING_NONE)
+		return 4;
+
+	tile_width = (tiling == I915_TILING_Y) ? 128 : 512;
+	if (INTEL_INFO(intel)->gen >= 040)
+		return tile_width;
+
+	while (tile_width < pitch)
+		tile_width <<= 1;
+
+	return tile_width;
+}
+
 void intel_set_pixmap_bo(PixmapPtr pixmap, dri_bo * bo)
 {
+	ScrnInfoPtr scrn = xf86ScreenToScrn(pixmap->drawable.pScreen);
+	intel_screen_private *intel = intel_get_screen_private(scrn);
 	struct intel_pixmap *priv;
 
 	priv = intel_get_pixmap_private(pixmap);
 	if (priv == NULL && bo == NULL)
-	    return;
+		return;
 
 	if (priv != NULL) {
 		if (priv->bo == bo)
 			return;
 
+free_priv:
 		dri_bo_unreference(priv->bo);
 		list_del(&priv->batch);
 
@@ -653,9 +673,9 @@ void intel_set_pixmap_bo(PixmapPtr pixmap, dri_bo * bo)
 	}
 
 	if (bo != NULL) {
-		uint32_t tiling;
-		uint32_t swizzle_mode;
-		int ret;
+		uint32_t tiling, swizzle_mode;
+		unsigned tile_width;
+		int size, stride;
 
 		priv = calloc(1, sizeof (struct intel_pixmap));
 		if (priv == NULL)
@@ -667,15 +687,45 @@ void intel_set_pixmap_bo(PixmapPtr pixmap, dri_bo * bo)
 		priv->bo = bo;
 		priv->stride = intel_pixmap_pitch(pixmap);
 
-		ret = drm_intel_bo_get_tiling(bo, &tiling, &swizzle_mode);
-		if (ret != 0) {
-			FatalError("Couldn't get tiling on bo %p: %s\n",
-				   bo, strerror(-ret));
+		if (drm_intel_bo_get_tiling(bo, &tiling, &swizzle_mode)) {
+			bo = NULL;
+			goto free_priv;
 		}
 
 		priv->tiling = tiling;
 		priv->busy = -1;
 		priv->offscreen = 1;
+
+		stride = (pixmap->drawable.width * pixmap->drawable.bitsPerPixel + 7) / 8;
+		tile_width = intel_get_tile_width(intel, tiling, stride);
+		stride = ALIGN(stride, tile_width);
+
+		if (priv->stride < stride ||
+		    priv->stride & (tile_width - 1) ||
+		    priv->stride >= KB(32)) {
+			bo = NULL;
+			goto free_priv;
+		}
+
+		if (tiling != I915_TILING_NONE) {
+			int height;
+
+			if (IS_GEN2(intel))
+				height = 16;
+			else if (tiling == I915_TILING_X)
+				height = 8;
+			else
+				height = 32;
+
+			height = ALIGN(pixmap->drawable.height, 2*height);
+			size = intel_get_fence_size(intel, priv->stride * height);
+		} else
+			size = priv->stride * pixmap->drawable.height;
+
+		if (bo->size < size || bo->size > intel->max_bo_size) {
+			bo = NULL;
+			goto free_priv;
+		}
 	}
 
   BAIL:
@@ -1422,5 +1472,6 @@ Bool intel_uxa_init(ScreenPtr screen)
 	uxa_set_fallback_debug(screen, intel->fallback_debug);
 	uxa_set_force_fallback(screen, intel->force_fallback);
 
+	intel->flush_rendering = intel_flush_rendering;
 	return TRUE;
 }
commit 975b9798be77b30cbed485583d0ccb48318708f7
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed May 21 08:58:24 2014 +0100

    sna: Add support for Present
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/Makefile.am b/src/sna/Makefile.am
index aa3f6a5..a299ea2 100644
--- a/src/sna/Makefile.am
+++ b/src/sna/Makefile.am
@@ -120,6 +120,12 @@ libsna_la_SOURCES += sna_dri3.c
 libsna_la_LIBADD += $(DRI3_LIBS)
 endif
 
+if PRESENT
+AM_CFLAGS += $(PRESENT_CFLAGS)
+libsna_la_SOURCES += sna_present.c
+libsna_la_LIBADD += $(PRESENT_LIBS)
+endif
+
 if XVMC
 libsna_la_SOURCES += \
 	sna_video_hwmc.h \
diff --git a/src/sna/sna.h b/src/sna/sna.h
index 5114927..b9a40db 100644
--- a/src/sna/sna.h
+++ b/src/sna/sna.h
@@ -340,6 +340,13 @@ struct sna {
 #endif
 	} dri3;
 
+	struct sna_present {
+		bool available;
+		bool open;
+#if HAVE_PRESENT
+#endif
+	} present;
+
 	struct sna_xv {
 		XvAdaptorPtr adaptors;
 		int num_adaptors;
@@ -538,6 +545,22 @@ static inline bool sna_dri3_open(struct sna *sna, ScreenPtr pScreen) { return fa
 static inline void sna_dri3_close(struct sna *sna, ScreenPtr pScreen) { }
 #endif
 
+#if HAVE_PRESENT
+bool sna_present_open(struct sna *sna, ScreenPtr pScreen);
+void sna_present_update(struct sna *sna);
+void sna_present_close(struct sna *sna, ScreenPtr pScreen);
+void sna_present_flip_handler(struct sna *sna,
+			      struct drm_event_vblank *event);
+void sna_present_vblank_handler(struct sna *sna,
+				struct drm_event_vblank *event);
+#else
+static inline bool sna_present_open(struct sna *sna, ScreenPtr pScreen) { return false; }
+static inline void sna_present_update(struct sna *sna) { }
+static inline void sna_present_close(struct sna *sna, ScreenPtr pScreen) { }
+static inline void sna_present_flip_handler(struct sna *sna, struct drm_event_vblank *event) { }
+static inline void sna_present_vblank_handler(struct sna *sna, struct drm_event_vblank *event) { }
+#endif
+
 extern bool sna_crtc_set_sprite_rotation(xf86CrtcPtr crtc, uint32_t rotation);
 extern int sna_crtc_to_pipe(xf86CrtcPtr crtc);
 extern uint32_t sna_crtc_to_sprite(xf86CrtcPtr crtc);
diff --git a/src/sna/sna_display.c b/src/sna/sna_display.c
index 2b7e473..62dce47 100644
--- a/src/sna/sna_display.c
+++ b/src/sna/sna_display.c
@@ -4602,6 +4602,8 @@ sna_crtc_config_notify(ScreenPtr screen)
 
 	sna_mode_update(sna);
 	sna_cursors_reload(sna);
+
+	sna_present_update(sna);
 }
 
 #if HAS_PIXMAP_SHARING
@@ -5778,11 +5780,17 @@ void sna_mode_wakeup(struct sna *sna)
 		struct drm_event *e = (struct drm_event *)&buffer[i];
 		switch (e->type) {
 		case DRM_EVENT_VBLANK:
-			sna_dri2_vblank_handler(sna, (struct drm_event_vblank *)e);
+			if (((uintptr_t)((struct drm_event_vblank *)e)->user_data) & 2)
+				sna_present_vblank_handler(sna, (struct drm_event_vblank *)e);
+			else
+				sna_dri2_vblank_handler(sna, (struct drm_event_vblank *)e);
 			break;
 		case DRM_EVENT_FLIP_COMPLETE:
 			if (((struct drm_event_vblank *)e)->user_data) {
-				sna_dri2_page_flip_handler(sna, (struct drm_event_vblank *)e);
+				if (((uintptr_t)((struct drm_event_vblank *)e)->user_data) & 2)
+					sna_present_flip_handler(sna, (struct drm_event_vblank *)e);
+				else
+					sna_dri2_page_flip_handler(sna, (struct drm_event_vblank *)e);
 			} else {
 				if (!--sna->mode.shadow_flip)
 					sna_mode_redisplay(sna);
diff --git a/src/sna/sna_driver.c b/src/sna/sna_driver.c
index 97f27e2..7a58a86 100644
--- a/src/sna/sna_driver.c
+++ b/src/sna/sna_driver.c
@@ -646,6 +646,13 @@ static Bool sna_pre_init(ScrnInfoPtr scrn, int flags)
 
 	setup_dri(sna);
 
+	sna->present.available = false;
+	if (xf86ReturnOptValBool(sna->Options, OPTION_PRESENT, TRUE)) {
+#if HAVE_PRESENT
+		sna->present.available = !!xf86LoadSubModule(scrn, "present");
+#endif
+	}
+
 	sna_acpi_init(sna);
 
 	return TRUE;
@@ -867,6 +874,11 @@ static Bool sna_early_close_screen(CLOSE_SCREEN_ARGS_DECL)
 	sna_uevent_fini(scrn);
 	sna_mode_close(sna);
 
+	if (sna->present.open) {
+		sna_present_close(sna, screen);
+		sna->present.open = false;
+	}
+
 	if (sna->dri3.open) {
 		sna_dri3_close(sna, screen);
 		sna->dri3.open = false;
@@ -1098,6 +1110,12 @@ sna_screen_init(SCREEN_INIT_ARGS_DECL)
 	sna_video_init(sna, screen);
 	sna_dri_init(sna, screen);
 
+	if (sna->present.available)
+		sna->present.open = sna_present_open(sna, screen);
+	if (sna->present.open)
+		xf86DrvMsg(sna->scrn->scrnIndex, X_INFO,
+			   "hardware support for Present enabled\n");
+
 	if (serverGeneration == 1)
 		xf86ShowUnusedOptions(scrn->scrnIndex, scrn->options);
 
diff --git a/src/sna/sna_present.c b/src/sna/sna_present.c
new file mode 100644
index 0000000..cacef78
--- /dev/null
+++ b/src/sna/sna_present.c
@@ -0,0 +1,472 @@
+/*
+ * Copyright © 2014 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <xf86drm.h>
+
+#include "sna.h"
+
+#include <xf86.h>
+#include <present.h>
+
+static present_screen_info_rec present_info;
+
+struct sna_present_event {
+	uint64_t event_id;
+	unsigned int count;
+	xf86CrtcPtr crtc;
+	uint64_t msc;
+	uint64_t ust;
+};
+
+static inline struct sna_present_event *
+to_present_event(uintptr_t  data)
+{
+	return (struct sna_present_event *)(data & ~3);
+}
+
+#define MARK_PRESENT(x) ((void *)((uintptr_t)(x) | 2))
+
+static int pipe_from_crtc(RRCrtcPtr crtc)
+{
+	return crtc ? sna_crtc_to_pipe(crtc->devPrivate) : -1;
+}
+
+static uint32_t pipe_select(int pipe)
+{
+	if (pipe > 1)
+		return pipe << DRM_VBLANK_HIGH_CRTC_SHIFT;
+	else if (pipe > 0)
+		return DRM_VBLANK_SECONDARY;
+	else
+		return 0;
+}
+
+static inline int sna_wait_vblank(struct sna *sna, union drm_wait_vblank *vbl, int pipe)
+{
+	DBG(("%s(pipe=%d, waiting until seq=%u%s)\n",
+	     __FUNCTION__, pipe, vbl->request.sequence,
+	     vbl->request.type & DRM_VBLANK_RELATIVE ? " [relative]" : ""));
+	vbl->request.type |= pipe_select(pipe);
+	return drmIoctl(sna->kgem.fd, DRM_IOCTL_WAIT_VBLANK, vbl);
+}
+
+static RRCrtcPtr
+sna_present_get_crtc(WindowPtr window)
+{
+	struct sna *sna = to_sna_from_drawable(&window->drawable);
+	BoxRec box;
+	xf86CrtcPtr crtc;
+
+	DBG(("%s\n", __FUNCTION__));
+
+	box.x1 = window->drawable.x;
+	box.y1 = window->drawable.y;
+	box.x2 = box.x1 + window->drawable.width;
+	box.y2 = box.y1 + window->drawable.height;
+
+	crtc = sna_covering_crtc(sna, &box, NULL);
+	if (crtc)
+		return crtc->randr_crtc;
+
+	return NULL;
+}
+
+static int
+sna_present_get_ust_msc(RRCrtcPtr crtc, CARD64 *ust, CARD64 *msc)
+{
+	struct sna *sna = to_sna_from_screen(crtc->pScreen);
+	int pipe = pipe_from_crtc(crtc);
+	union drm_wait_vblank vbl;
+
+	DBG(("%s(pipe=%d)\n", __FUNCTION__, pipe));
+
+	VG_CLEAR(vbl);
+	vbl.request.type = DRM_VBLANK_RELATIVE;
+	vbl.request.sequence = 0;
+	if (sna_wait_vblank(sna, &vbl, pipe) == 0) {
+		*ust = ust64(vbl.reply.tval_sec, vbl.reply.tval_usec);
+		*msc = sna_crtc_record_vblank(crtc->devPrivate, &vbl);
+	} else {
+		const struct ust_msc *swap = sna_crtc_last_swap(crtc->devPrivate);
+		*ust = ust64(swap->tv_sec, swap->tv_usec);
+		*msc = swap->msc;
+	}
+
+	DBG(("%s: pipe=%d, tv=%d.%06d msc=%lld\n", __FUNCTION__, pipe,
+	     (int)(*ust / 1000000), (int)(*ust % 1000000),
+	     (long long)*msc));
+
+	return Success;
+}
+
+void
+sna_present_vblank_handler(struct sna *sna, struct drm_event_vblank *event)
+{
+	struct sna_present_event *info = to_present_event(event->user_data);
+
+	DBG(("%s: pipe=%d event=%lld, tv=%d.%06d msc=%d\n", __FUNCTION__,
+	     sna_crtc_to_pipe(info->crtc), (long long)info->event_id,
+	     event->tv_sec, event->tv_usec, event->sequence));
+	present_event_notify(info->event_id,
+			     ust64(event->tv_sec, event->tv_usec),
+			     sna_crtc_record_event(info->crtc, event));
+	free(info);
+}
+
+static int
+sna_present_queue_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
+{
+	struct sna *sna = to_sna_from_screen(crtc->pScreen);
+	struct sna_present_event *event;
+	union drm_wait_vblank vbl;
+
+	DBG(("%s(pipe=%d, event=%lld, msc=%lld)\n",
+	     __FUNCTION__, pipe_from_crtc(crtc),
+	     (long long)event_id, (long long)msc));
+
+	event = malloc(sizeof(struct sna_present_event));
+	if (event == NULL)
+		return BadAlloc;
+
+	event->event_id = event_id;
+	event->crtc = crtc->devPrivate;
+
+	VG_CLEAR(vbl);
+	vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
+	vbl.request.sequence = msc;
+	vbl.request.signal = (uintptr_t)MARK_PRESENT(event);
+	if (sna_wait_vblank(sna, &vbl, sna_crtc_to_pipe(event->crtc))) {
+		DBG(("%s: vblank enqueue failed\n", __FUNCTION__));
+		free(event);
+		return BadMatch;
+	}
+
+	return Success;
+}
+
+static void
+sna_present_abort_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
+{
+	DBG(("%s(pipe=%d, event=%lld, msc=%lld)\n",
+	     __FUNCTION__, pipe_from_crtc(crtc),
+	     (long long)event_id, (long long)msc));
+}
+
+static void
+sna_present_flush(WindowPtr window)
+{
+	PixmapPtr pixmap = get_window_pixmap(window);
+	struct sna_pixmap *priv;
+
+	DBG(("%s(pixmap=%ld)\n", __FUNCTION__, pixmap->drawable.serialNumber));
+
+	priv = sna_pixmap_move_to_gpu(pixmap, MOVE_READ | MOVE_ASYNC_HINT | __MOVE_FORCE);
+	if (priv && priv->gpu_bo)
+		kgem_scanout_flush(&to_sna_from_pixmap(pixmap)->kgem, priv->gpu_bo);
+}
+
+static bool
+check_flip__crtc(struct sna *sna,
+		 RRCrtcPtr crtc)
+{
+	if (!sna->scrn->vtSema) {
+		DBG(("%s: not master\n", __FUNCTION__));
+		return false;
+	}
+
+	if (!sna_crtc_is_on(crtc->devPrivate)) {
+		DBG(("%s: CRTC off\n", __FUNCTION__));
+		return false;
+	}
+
+	if (sna->mode.shadow_active) {
+		DBG(("%s: shadow buffer active\n", __FUNCTION__));
+		return false;
+	}
+
+	return true;
+}
+
+static Bool
+sna_present_check_flip(RRCrtcPtr crtc,
+		       WindowPtr window,
+		       PixmapPtr pixmap,
+		       Bool sync_flip)
+{
+	struct sna *sna = to_sna_from_pixmap(pixmap);
+	struct sna_pixmap *flip;
+
+	DBG(("%s(pipe=%d, pixmap=%ld, sync_flip=%d)\n",
+	     __FUNCTION__,
+	     pipe_from_crtc(crtc),
+	     pixmap->drawable.serialNumber,
+	     sync_flip));
+
+	if (sna->flags & SNA_NO_FLIP) {
+		DBG(("%s: flips not suported\n", __FUNCTION__));
+		return FALSE;
+	}
+
+	if (sync_flip) {
+		if ((sna->flags & SNA_HAS_FLIP) == 0) {
+			DBG(("%s: async flips not suported\n", __FUNCTION__));
+			return FALSE;
+		}
+	} else {
+		if ((sna->flags & SNA_HAS_ASYNC_FLIP) == 0) {
+			DBG(("%s: async flips not suported\n", __FUNCTION__));
+			return FALSE;
+		}
+	}
+
+	if (!check_flip__crtc(sna, crtc)) {
+		DBG(("%s: flip invalid for CRTC\n", __FUNCTION__));
+		return FALSE;
+	}
+
+	flip = sna_pixmap(pixmap);
+	if (flip == NULL) {
+		DBG(("%s: unattached pixmap\n", __FUNCTION__));
+		return FALSE;
+	}
+
+	if (flip->cpu_bo && IS_STATIC_PTR(flip->ptr)) {
+		DBG(("%s: SHM pixmap\n", __FUNCTION__));
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static uint64_t gettime_ust64(void)
+{
+	struct timespec tv;
+
+	if (clock_gettime(CLOCK_MONOTONIC, &tv))
+		return 0;
+
+	return ust64(tv.tv_sec, tv.tv_nsec / 1000);
+}
+
+static Bool
+page_flip__async(RRCrtcPtr crtc,
+		 uint64_t event_id,
+		 uint64_t target_msc,
+		 struct kgem_bo *bo)
+{
+	DBG(("%s(pipe=%d, event=%lld, handle=%d)\n",
+	     __FUNCTION__,
+	     pipe_from_crtc(crtc),
+	     (long long)event_id,
+	     bo->handle));
+
+	if (!sna_page_flip(to_sna_from_screen(crtc->pScreen), bo, NULL, -1)) {
+		DBG(("%s: async pageflip failed\n", __FUNCTION__));
+		present_info.capabilities &= ~PresentCapabilityAsync;
+		return FALSE;
+	}
+
+	present_event_notify(event_id, gettime_ust64(), target_msc);
+	return TRUE;
+}
+
+void
+sna_present_flip_handler(struct sna *sna, struct drm_event_vblank *event)
+{
+	struct sna_present_event *info = to_present_event(event->user_data);
+
+	DBG(("%s(count=%d, ref-pipe?=%d)\n", __FUNCTION__,
+	     info->count, event->user_data & 1));
+
+	if (event->user_data & 1) {
+		info->msc = sna_crtc_record_event(info->crtc, event);
+		info->ust = ust64(event->tv_sec, event->tv_usec);
+	}
+
+	if (--info->count)
+		return;
+
+	DBG(("%s: pipe=%d, tv=%d.%06d msc %lld, complete\n", __FUNCTION__,
+	     info->crtc ? sna_crtc_to_pipe(info->crtc) : -1,
+	     info->crtc ? (int)(info->ust / 1000000) : event->tv_sec,
+	     info->crtc ? (int)(info->ust % 1000000) : event->tv_usec,
+	     info->crtc ? (long long)info->msc : (long long)event->sequence));
+	present_event_notify(info->event_id, info->ust, info->msc);
+	free(info);
+}
+
+static Bool
+page_flip(ScreenPtr screen,
+	  RRCrtcPtr crtc,
+	  uint64_t event_id,
+	  struct kgem_bo *bo)
+{
+	struct sna *sna = to_sna_from_screen(screen);
+	struct sna_present_event *event;
+
+	DBG(("%s(pipe=%d, event=%lld, handle=%d)\n",
+	     __FUNCTION__,
+	     pipe_from_crtc(crtc),
+	     (long long)event_id,
+	     bo->handle));
+
+	event = malloc(sizeof(struct sna_present_event));
+	if (event == NULL)
+		return FALSE;
+
+	event->event_id = event_id;
+	event->crtc = crtc ? crtc->devPrivate : NULL;
+	event->count = sna_page_flip(sna, bo,
+				     MARK_PRESENT(event),
+				     crtc ? sna_crtc_to_pipe(event->crtc) : -1);
+	if (event->count == 0) {
+		DBG(("%s: pageflip failed\n", __FUNCTION__));
+		free(event);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static struct kgem_bo *
+get_flip_bo(PixmapPtr pixmap)
+{
+	struct sna_pixmap *priv;
+
+	DBG(("%s(pixmap=%ld)\n", __FUNCTION__, pixmap->drawable.serialNumber));
+
+	priv = sna_pixmap_move_to_gpu(pixmap, MOVE_READ | __MOVE_FORCE);
+	if (priv == NULL) {
+		DBG(("%s: cannot force pixmap to the GPU\n", __FUNCTION__));
+		return NULL;
+	}
+
+	if (priv->gpu_bo->tiling == I915_TILING_Y &&
+	    !sna_pixmap_change_tiling(pixmap, I915_TILING_X)) {
+		DBG(("%s: bad tiling, cannot convert\n", __FUNCTION__));
+		return NULL;
+	}
+
+	priv->pinned |= PIN_SCANOUT;
+	return priv->gpu_bo;
+}
+
+static Bool
+sna_present_flip(RRCrtcPtr crtc,
+		 uint64_t event_id,
+		 uint64_t target_msc,
+		 PixmapPtr pixmap,
+		 Bool sync_flip)
+{
+	struct kgem_bo *bo;
+
+	DBG(("%s(pipe=%d, event=%lld, msc=%lld, pixmap=%ld, sync?=%d)\n",
+	     __FUNCTION__,
+	     pipe_from_crtc(crtc),
+	     (long long)event_id,
+	     (long long)target_msc,
+	     pixmap->drawable.serialNumber, sync_flip));
+
+	if (!check_flip__crtc(to_sna_from_pixmap(pixmap), crtc)) {
+		DBG(("%s: flip invalid for CRTC\n", __FUNCTION__));
+		return FALSE;
+	}
+
+	bo = get_flip_bo(pixmap);
+	if (bo == NULL) {
+		DBG(("%s: flip invalid bo\n", __FUNCTION__));
+		return FALSE;
+	}
+
+	if (sync_flip)
+		return page_flip(crtc->pScreen, crtc, event_id, bo);
+	else
+		return page_flip__async(crtc, event_id, target_msc, bo);
+}
+
+static void
+sna_present_unflip(ScreenPtr screen, uint64_t event_id)
+{
+	struct kgem_bo *bo;
+
+	DBG(("%s(event=%lld)\n", __FUNCTION__, (long long)event_id));
+	bo = get_flip_bo(screen->GetScreenPixmap(screen));
+	if (bo == NULL || !page_flip(screen, NULL, event_id, bo)) {
+		struct sna *sna = to_sna_from_screen(screen);
+		const struct ust_msc *swap;
+		DBG(("%s: failed, trying to restore original mode\n", __FUNCTION__));
+		xf86SetDesiredModes(sna->scrn);
+		swap = sna_crtc_last_swap(sna_mode_first_crtc(sna));
+		present_event_notify(event_id,
+				     ust64(swap->tv_sec, swap->tv_usec),
+				     swap->msc);
+	}
+}
+
+static present_screen_info_rec present_info = {
+	.version = PRESENT_SCREEN_INFO_VERSION,
+
+	.get_crtc = sna_present_get_crtc,
+	.get_ust_msc = sna_present_get_ust_msc,
+	.queue_vblank = sna_present_queue_vblank,
+	.abort_vblank = sna_present_abort_vblank,
+	.flush = sna_present_flush,
+
+	.capabilities = PresentCapabilityNone,
+	.check_flip = sna_present_check_flip,
+	.flip = sna_present_flip,
+	.unflip = sna_present_unflip,
+};
+
+bool sna_present_open(struct sna *sna, ScreenPtr screen)
+{
+	if (sna->mode.num_real_crtc == 0)
+		return false;
+
+	sna_present_update(sna);
+
+	return present_screen_init(screen, &present_info);
+}
+
+void sna_present_update(struct sna *sna)
+{
+	if (sna->flags & SNA_HAS_ASYNC_FLIP)
+		present_info.capabilities |= PresentCapabilityAsync;
+	else
+		present_info.capabilities &= ~PresentCapabilityAsync;
+
+	DBG(("%s: has_async_flip? %d\n", __FUNCTION__,
+	     !!(present_info.capabilities & PresentCapabilityAsync)));
+}
+
+void sna_present_close(struct sna *sna, ScreenPtr screen)
+{
+	DBG(("%s()\n", __FUNCTION__));
+}
diff --git a/test/dri3.c b/test/dri3.c
index b5eb4ba..45f3285 100644
--- a/test/dri3.c
+++ b/test/dri3.c
@@ -43,7 +43,7 @@ Pixmap dri3_create_pixmap(Display *dpy,
 		xcb_dri3_pixmap_from_buffer(c, pixmap, draw, size, width, height, stride, depth, bpp, fd);
 		return pixmap;
 	}
-	return NullPixmap;
+	return 0;
 }
 
 int dri3_create_fd(Display *dpy,
commit d8eb87f84f88ad2df42c6fed1d93df76589a14e3
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu May 8 16:25:32 2014 +0100

    sna: Add support for DRI3
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/Makefile.am b/src/sna/Makefile.am
index a334982..aa3f6a5 100644
--- a/src/sna/Makefile.am
+++ b/src/sna/Makefile.am
@@ -109,11 +109,17 @@ libsna_la_SOURCES = \
 	$(NULL)
 
 if DRI2
-AM_CFLAGS += @DRI2_CFLAGS@
+AM_CFLAGS += $(DRI2_CFLAGS)
 libsna_la_SOURCES += sna_dri2.c
 libsna_la_LIBADD += $(DRI2_LIBS) @CLOCK_GETTIME_LIBS@
 endif
 
+if DRI3
+AM_CFLAGS += $(DRI3_CFLAGS)
+libsna_la_SOURCES += sna_dri3.c
+libsna_la_LIBADD += $(DRI3_LIBS)
+endif
+
 if XVMC
 libsna_la_SOURCES += \
 	sna_video_hwmc.h \
diff --git a/src/sna/kgem.c b/src/sna/kgem.c
index 17b5b1a..3eb63d8 100644
--- a/src/sna/kgem.c
+++ b/src/sna/kgem.c
@@ -1619,6 +1619,9 @@ static uint32_t kgem_surface_size(struct kgem *kgem,
 
 	*pitch = ALIGN(width * bpp / 8, tile_width);
 	height = ALIGN(height, tile_height);
+	DBG(("%s: tile_width=%d, tile_height=%d => aligned pitch=%d, height=%d\n",
+	     __FUNCTION__, tile_width, tile_height, *pitch, height));
+
 	if (kgem->gen >= 040)
 		return PAGE_ALIGN(*pitch * height);
 
@@ -1649,6 +1652,47 @@ static uint32_t kgem_surface_size(struct kgem *kgem,
 	return tile_width;
 }
 
+bool kgem_check_surface_size(struct kgem *kgem,
+			     uint32_t width,
+			     uint32_t height,
+			     uint32_t bpp,
+			     uint32_t tiling,
+			     uint32_t pitch,
+			     uint32_t size)
+{
+	uint32_t min_size, min_pitch;
+	int tile_width, tile_height, tile_size;
+
+	DBG(("%s(width=%d, height=%d, bpp=%d, tiling=%d, pitch=%d, size=%d)\n",
+	     __FUNCTION__, width, height, bpp, tiling, pitch, size));
+
+	if (pitch & 3)
+		return false;
+
+	min_size = kgem_surface_size(kgem, kgem->has_relaxed_fencing, 0,
+				     width, height, bpp, tiling,
+				     &min_pitch);
+
+	DBG(("%s: min_pitch=%d, min_size=%d\n", __FUNCTION__, min_pitch, min_size));
+
+	if (size < min_size)
+		return false;
+
+	if (pitch < min_pitch)
+		return false;
+
+	kgem_get_tile_size(kgem, tiling, min_pitch,
+			   &tile_width, &tile_height, &tile_size);
+
+	DBG(("%s: tile_width=%d, tile_size=%d\n", __FUNCTION__, tile_width, tile_size));
+	if (pitch & (tile_width - 1))
+		return false;
+	if (size & (tile_size - 1))
+		return false;
+
+	return true;
+}
+
 static uint32_t kgem_aligned_height(struct kgem *kgem,
 				    uint32_t height, uint32_t tiling)
 {
@@ -3848,8 +3892,16 @@ struct kgem_bo *kgem_create_for_prime(struct kgem *kgem, int name, uint32_t size
 
 	/* Query actual size, overriding specified if available */
 	seek = lseek(args.fd, 0, SEEK_END);
-	if (seek != -1)
+	DBG(("%s: estimated size=%ld, actual=%lld\n",
+	     __FUNCTION__, (long)size, (long long)seek));
+	if (seek != -1) {
+		if (size > seek) {
+			DBG(("%s(name=%d) estimated required size [%d] is larger than actual [%ld]\n", __FUNCTION__, name, size, (long)seek));
+			gem_close(kgem->fd, args.handle);
+			return NULL;
+		}
 		size = seek;
+	}
 
 	DBG(("%s: new handle=%d, tiling=%d\n", __FUNCTION__,
 	     args.handle, tiling.tiling_mode));
diff --git a/src/sna/kgem.h b/src/sna/kgem.h
index a9fb939..6e61909 100644
--- a/src/sna/kgem.h
+++ b/src/sna/kgem.h
@@ -268,6 +268,14 @@ unsigned kgem_can_create_2d(struct kgem *kgem, int width, int height, int depth)
 #define KGEM_CAN_CREATE_LARGE	0x4
 #define KGEM_CAN_CREATE_GTT	0x8
 
+bool kgem_check_surface_size(struct kgem *kgem,
+			     uint32_t width,
+			     uint32_t height,
+			     uint32_t bpp,
+			     uint32_t tiling,
+			     uint32_t pitch,
+			     uint32_t size);
+
 struct kgem_bo *
 kgem_replace_bo(struct kgem *kgem,
 		struct kgem_bo *src,
diff --git a/src/sna/sna.h b/src/sna/sna.h
index d40ccf4..5114927 100644
--- a/src/sna/sna.h
+++ b/src/sna/sna.h
@@ -61,10 +61,14 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include <drm.h>
 #include <i915_drm.h>
 
-#ifdef HAVE_DRI2_H
+#if HAVE_DRI2
 #include <dri2.h>
 #endif
 
+#if HAVE_DRI3
+#include <misync.h>
+#endif
+
 #if HAVE_UDEV
 #include <libudev.h>
 #endif
@@ -161,6 +165,9 @@ struct sna_pixmap {
 	uint8_t cpu :1;
 };
 
+#define IS_STATIC_PTR(ptr) ((uintptr_t)(ptr) & 1)
+#define MAKE_STATIC_PTR(ptr) ((void*)((uintptr_t)(ptr) | 1))
+
 struct sna_glyph {
 	PicturePtr atlas;
 	struct sna_coordinate coordinate;
@@ -324,6 +331,15 @@ struct sna {
 #endif
 	} dri2;
 
+	struct sna_dri3 {
+		bool available;
+		bool open;
+#if HAVE_DRI3
+		SyncScreenCreateFenceFunc create_fence;
+		struct list pixmaps;
+#endif
+	} dri3;
+
 	struct sna_xv {
 		XvAdaptorPtr adaptors;
 		int num_adaptors;
@@ -496,7 +512,7 @@ static inline uint64_t ust64(int tv_sec, int tv_usec)
 	return (uint64_t)tv_sec * 1000000 + tv_usec;
 }
 
-#if HAVE_DRI2_H
+#if HAVE_DRI2
 bool sna_dri2_open(struct sna *sna, ScreenPtr pScreen);
 void sna_dri2_page_flip_handler(struct sna *sna, struct drm_event_vblank *event);
 void sna_dri2_vblank_handler(struct sna *sna, struct drm_event_vblank *event);
@@ -514,6 +530,14 @@ static inline void sna_dri2_reset_scanout(struct sna *sna) { }
 static inline void sna_dri2_close(struct sna *sna, ScreenPtr pScreen) { }
 #endif
 
+#if HAVE_DRI3
+bool sna_dri3_open(struct sna *sna, ScreenPtr pScreen);
+void sna_dri3_close(struct sna *sna, ScreenPtr pScreen);
+#else
+static inline bool sna_dri3_open(struct sna *sna, ScreenPtr pScreen) { return false; }
+static inline void sna_dri3_close(struct sna *sna, ScreenPtr pScreen) { }
+#endif
+
 extern bool sna_crtc_set_sprite_rotation(xf86CrtcPtr crtc, uint32_t rotation);
 extern int sna_crtc_to_pipe(xf86CrtcPtr crtc);
 extern uint32_t sna_crtc_to_sprite(xf86CrtcPtr crtc);
@@ -569,7 +593,7 @@ get_drawable_dy(DrawablePtr drawable)
 	return 0;
 }
 
-bool sna_pixmap_attach_to_bo(PixmapPtr pixmap, struct kgem_bo *bo);
+struct sna_pixmap *sna_pixmap_attach_to_bo(PixmapPtr pixmap, struct kgem_bo *bo);
 static inline bool sna_pixmap_is_scanout(struct sna *sna, PixmapPtr pixmap)
 {
 	return (pixmap == sna->front &&
@@ -929,6 +953,7 @@ void sna_accel_create(struct sna *sna);
 void sna_accel_block_handler(struct sna *sna, struct timeval **tv);
 void sna_accel_wakeup_handler(struct sna *sna);
 void sna_accel_watch_flush(struct sna *sna, int enable);
+void sna_accel_flush(struct sna *sna);
 void sna_accel_close(struct sna *sna);
 void sna_accel_free(struct sna *sna);
 
diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index 869427d..a02c5eb 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -108,9 +108,6 @@
 #define NO_TILE_8x8 0
 #define NO_STIPPLE_8x8 0
 
-#define IS_STATIC_PTR(ptr) ((uintptr_t)(ptr) & 1)
-#define MAKE_STATIC_PTR(ptr) ((void*)((uintptr_t)(ptr) | 1))
-
 #define IS_COW_OWNER(ptr) ((uintptr_t)(ptr) & 1)
 #define MAKE_COW_OWNER(ptr) ((void*)((uintptr_t)(ptr) | 1))
 #define COW(ptr) (void *)((uintptr_t)(ptr) & ~1)
@@ -562,8 +559,8 @@ static void __sna_pixmap_free_cpu(struct sna *sna, struct sna_pixmap *priv)
 		sna->debug_memory.cpu_bo_allocs--;
 		sna->debug_memory.cpu_bo_bytes -= kgem_bo_size(priv->cpu_bo);
 #endif
-		if (!priv->cpu_bo->reusable) {
-			assert(priv->cpu_bo->flush == true);
+		if (priv->cpu_bo->flush) {
+			assert(!priv->cpu_bo->reusable);
 			kgem_bo_sync__cpu(&sna->kgem, priv->cpu_bo);
 			sna_accel_watch_flush(sna, -1);
 		}
@@ -712,6 +709,7 @@ static struct sna_pixmap *
 _sna_pixmap_init(struct sna_pixmap *priv, PixmapPtr pixmap)
 {
 	list_init(&priv->flush_list);
+	list_init(&priv->cow_list);
 	priv->source_count = SOURCE_BIAS;
 	priv->pixmap = pixmap;
 
@@ -747,26 +745,37 @@ static struct sna_pixmap *sna_pixmap_attach(PixmapPtr pixmap)
 	return _sna_pixmap_init(priv, pixmap);
 }
 
-bool sna_pixmap_attach_to_bo(PixmapPtr pixmap, struct kgem_bo *bo)
+struct sna_pixmap *sna_pixmap_attach_to_bo(PixmapPtr pixmap, struct kgem_bo *bo)
 {
 	struct sna_pixmap *priv;
 
 	assert(bo);
+	assert(bo->proxy == NULL);
+	assert(bo->unique_id);
 
 	priv = sna_pixmap_attach(pixmap);
 	if (!priv)
-		return false;
+		return NULL;
+
+	DBG(("%s: attaching %s handle=%d to pixmap=%ld\n",
+	     __FUNCTION__, bo->snoop ? "CPU" : "GPU", bo->handle, pixmap->drawable.serialNumber));
 
 	assert(!priv->mapped);
 	assert(!priv->move_to_gpu);
 
-	priv->gpu_bo = kgem_bo_reference(bo);
-	assert(priv->gpu_bo->proxy == NULL);
-	sna_damage_all(&priv->gpu_damage,
-		       pixmap->drawable.width,
-		       pixmap->drawable.height);
+	if (bo->snoop) {
+		priv->cpu_bo = bo;
+		sna_damage_all(&priv->cpu_damage,
+				pixmap->drawable.width,
+				pixmap->drawable.height);
+	} else {
+		priv->gpu_bo = bo;
+		sna_damage_all(&priv->gpu_damage,
+				pixmap->drawable.width,
+				pixmap->drawable.height);
+	}
 
-	return true;
+	return priv;
 }
 
 static int bits_per_pixel(int depth)
@@ -1402,6 +1411,7 @@ static void __sna_free_pixmap(struct sna *sna,
 			      PixmapPtr pixmap,
 			      struct sna_pixmap *priv)
 {
+	DBG(("%s(pixmap=%ld)\n", __FUNCTION__, pixmap->drawable.serialNumber));
 	list_del(&priv->flush_list);
 
 	assert(priv->gpu_damage == NULL);
@@ -1455,11 +1465,11 @@ static Bool sna_destroy_pixmap(PixmapPtr pixmap)
 		DBG(("%s: pixmap=%ld discarding cow, refcnt=%d\n",
 		     __FUNCTION__, pixmap->drawable.serialNumber, cow->refcnt));
 		assert(cow->refcnt);
-		list_del(&priv->cow_list);
 		if (!--cow->refcnt)
 			free(cow);
 		priv->cow = NULL;
 	}
+	list_del(&priv->cow_list);
 
 	if (priv->move_to_gpu)
 		(void)priv->move_to_gpu(sna, priv, 0);
@@ -1472,6 +1482,8 @@ static Bool sna_destroy_pixmap(PixmapPtr pixmap)
 	}
 
 	if (priv->shm && kgem_bo_is_busy(priv->cpu_bo)) {
+		DBG(("%s: deferring release of active SHM pixmap=%ld\n",
+		     __FUNCTION__, pixmap->drawable.serialNumber));
 		sna_add_flush_pixmap(sna, priv, priv->cpu_bo);
 		kgem_bo_submit(&sna->kgem, priv->cpu_bo); /* XXX ShmDetach */
 	} else
@@ -1813,7 +1825,8 @@ sna_pixmap_undo_cow(struct sna *sna, struct sna_pixmap *priv, unsigned flags)
 	assert(priv->gpu_bo == cow->bo);
 	assert(cow->refcnt);
 
-	list_del(&priv->cow_list);
+	if (!IS_COW_OWNER(priv->cow))
+		list_del(&priv->cow_list);
 
 	if (!--cow->refcnt) {
 		DBG(("%s: freeing cow\n", __FUNCTION__));
@@ -1960,7 +1973,6 @@ sna_pixmap_make_cow(struct sna *sna,
 		     cow->bo->handle));
 
 		src_priv->cow = MAKE_COW_OWNER(cow);
-		list_init(&src_priv->cow_list);
 	}
 
 	if (cow == COW(dst_priv->cow)) {
@@ -16509,11 +16521,8 @@ static Bool sna_change_window_attributes(WindowPtr win, unsigned long mask)
 	return ret;
 }
 
-static void
-sna_accel_flush_callback(CallbackListPtr *list,
-			 pointer user_data, pointer call_data)
+void sna_accel_flush(struct sna *sna)
 {
-	struct sna *sna = user_data;
 	struct sna_pixmap *priv;
 
 	/* XXX we should be able to reduce the frequency of flushes further
@@ -16559,6 +16568,13 @@ sna_accel_flush_callback(CallbackListPtr *list,
 		kgem_submit(&sna->kgem);
 }
 
+static void
+sna_accel_flush_callback(CallbackListPtr *list,
+			 pointer user_data, pointer call_data)
+{
+	sna_accel_flush(user_data);
+}
+
 static struct sna_pixmap *sna_accel_scanout(struct sna *sna)
 {
 	struct sna_pixmap *priv;
@@ -16673,7 +16689,7 @@ static void timer_enable(struct sna *sna, int whom, int interval)
 	DBG(("%s (time=%ld), starting timer %d\n", __FUNCTION__, (long)TIME, whom));
 }
 
-static bool sna_accel_do_flush(struct sna *sna)
+static bool sna_scanout_do_flush(struct sna *sna)
 {
 	struct sna_pixmap *priv;
 	int interval;
@@ -16862,7 +16878,7 @@ skip:
 #endif
 }
 
-static void sna_accel_flush(struct sna *sna)
+static void sna_scanout_flush(struct sna *sna)
 {
 	struct sna_pixmap *priv = sna_accel_scanout(sna);
 	bool busy;
@@ -17355,8 +17371,8 @@ void sna_accel_block_handler(struct sna *sna, struct timeval **tv)
 	}
 
 restart:
-	if (sna_accel_do_flush(sna))
-		sna_accel_flush(sna);
+	if (sna_scanout_do_flush(sna))
+		sna_scanout_flush(sna);
 	assert(sna_accel_scanout(sna) == NULL ||
 	       sna_accel_scanout(sna)->gpu_bo->exec == NULL ||
 	       sna->timer_active & (1<<(FLUSH_TIMER)));
diff --git a/src/sna/sna_display.c b/src/sna/sna_display.c
index 6803409..2b7e473 100644
--- a/src/sna/sna_display.c
+++ b/src/sna/sna_display.c
@@ -5356,8 +5356,10 @@ sna_crtc_redisplay__composite(xf86CrtcPtr crtc, RegionPtr region, struct kgem_bo
 					bo->pitch, NULL))
 		goto free_pixmap;
 
-	if (!sna_pixmap_attach_to_bo(pixmap, bo))
+	if (!sna_pixmap_attach_to_bo(pixmap, kgem_bo_reference(bo))) {
+		kgem_bo_destroy(&sna->kgem, bo);
 		goto free_pixmap;
+	}
 
 	src = CreatePicture(None, &sna->front->drawable, format,
 			    0, NULL, serverClient, &error);
diff --git a/src/sna/sna_dri2.c b/src/sna/sna_dri2.c
index 2784014..317f598 100644
--- a/src/sna/sna_dri2.c
+++ b/src/sna/sna_dri2.c
@@ -309,8 +309,8 @@ static struct kgem_bo *sna_pixmap_set_dri(struct sna *sna,
 	     __FUNCTION__, pixmap->drawable.serialNumber));
 
 	priv = sna_pixmap(pixmap);
-	if (priv != NULL && priv->shm) {
-		DBG(("%s: SHM Pixmap, BadAlloc\n", __FUNCTION__));
+	if (priv != NULL && IS_STATIC_PTR(priv->ptr) && priv->cpu_bo) {
+		DBG(("%s: SHM or unattached Pixmap, BadAlloc\n", __FUNCTION__));
 		return NULL;
 	}
 
@@ -669,7 +669,7 @@ static void set_bo(PixmapPtr pixmap, struct kgem_bo *bo)
 	assert(bo->proxy == NULL);
 	assert(bo->flush);
 	assert(priv->pinned & PIN_DRI2);
-	assert((priv->pinned & PIN_PRIME) == 0);
+	assert((priv->pinned & (PIN_PRIME | PIN_DRI3)) == 0);
 	assert(priv->flush);
 
 	if (priv->cow && priv->gpu_bo != bo)
@@ -2601,32 +2601,36 @@ namecmp(const char *s1, const char *s2)
 	return c1 - c2;
 }
 
-static bool is_bool(const char *str)
+static bool is_level(const char **str)
 {
-	if (str == NULL)
-		return true;
+	const char *s = *str;
+	char *end;
+	unsigned val;
 
-	if (*str == '\0')
+	if (s == NULL || *s == '\0')
 		return true;
 
-	if (namecmp(str, "1") == 0)
-		return true;
-	if (namecmp(str, "on") == 0)
+	if (namecmp(s, "on") == 0)
 		return true;
-	if (namecmp(str, "true") == 0)
+	if (namecmp(s, "true") == 0)
 		return true;
-	if (namecmp(str, "yes") == 0)
+	if (namecmp(s, "yes") == 0)
 		return true;
 
-	if (namecmp(str, "0") == 0)
+	if (namecmp(s, "0") == 0)
 		return true;
-	if (namecmp(str, "off") == 0)
+	if (namecmp(s, "off") == 0)
 		return true;
-	if (namecmp(str, "false") == 0)
+	if (namecmp(s, "false") == 0)
 		return true;
-	if (namecmp(str, "no") == 0)
+	if (namecmp(s, "no") == 0)
 		return true;
 
+	val = strtoul(s, &end, 0);
+	if (val && *end == '\0')
+		return true;
+	if (val && *end == ':')
+		*str = end + 1;
 	return false;
 }
 
@@ -2634,7 +2638,7 @@ static const char *dri_driver_name(struct sna *sna)
 {
 	const char *s = xf86GetOptValString(sna->Options, OPTION_DRI);
 
-	if (is_bool(s)) {
+	if (is_level(&s)) {
 		if (sna->kgem.gen < 030)
 			return has_i830_dri() ? "i830" : "i915";
 		else if (sna->kgem.gen < 040)
diff --git a/src/sna/sna_dri3.c b/src/sna/sna_dri3.c
new file mode 100644
index 0000000..0e58e79
--- /dev/null
+++ b/src/sna/sna_dri3.c
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 2014 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <xf86drm.h>
+
+#include "sna.h"
+
+#include <xf86.h>
+#include <dri3.h>
+#include <misyncshm.h>
+#include <misyncstr.h>
+
+static DevPrivateKeyRec sna_sync_fence_private_key;
+struct sna_sync_fence {
+	SyncFenceSetTriggeredFunc set_triggered;
+};
+
+static inline struct sna_sync_fence *sna_sync_fence(SyncFence *fence)
+{
+	return dixLookupPrivate(&fence->devPrivates, &sna_sync_fence_private_key);
+}
+
+static void sna_sync_flush(struct sna *sna, struct sna_pixmap *priv)
+{
+	struct kgem_bo *bo = NULL;
+
+	DBG(("%s(pixmap=%ld)\n", __FUNCTION__, priv->pixmap->drawable.serialNumber));
+	assert(priv);
+
+	if (priv->pinned & PIN_DRI3) {
+		assert(priv->gpu_bo);
+		assert(priv->pinned & PIN_DRI3);
+		DBG(("%s: flushing prime GPU bo, handle=%ld\n", __FUNCTION__, priv->gpu_bo->handle));
+		if (sna_pixmap_move_to_gpu(priv->pixmap, MOVE_READ | MOVE_WRITE | MOVE_ASYNC_HINT | __MOVE_FORCE)) {
+			sna_damage_all(&priv->gpu_damage,
+				       priv->pixmap->drawable.width,
+				       priv->pixmap->drawable.height);
+			bo = priv->gpu_bo;
+		}
+	} else {
+		assert(priv->cpu_bo);
+		assert(IS_STATIC_PTR(priv->ptr));
+		DBG(("%s: flushing prime CPU bo, handle=%ld\n", __FUNCTION__, priv->cpu_bo->handle));
+		if (sna_pixmap_move_to_cpu(priv->pixmap, MOVE_READ | MOVE_WRITE | MOVE_ASYNC_HINT))
+			bo = priv->cpu_bo;
+	}
+
+	if (bo != NULL) {
+		kgem_bo_submit(&sna->kgem, bo);
+		kgem_bo_unclean(&sna->kgem, bo);
+	}
+}
+
+static void
+sna_sync_fence_set_triggered(SyncFence *fence)
+{
+	struct sna *sna = to_sna_from_screen(fence->pScreen);
+	struct sna_sync_fence *sna_fence = sna_sync_fence(fence);
+	DrawablePtr draw = NULL;
+
+	DBG(("%s()\n", __FUNCTION__));
+
+#if 0
+	draw = miSyncShmFenceGetDrawable(fence);
+#endif
+	if (draw) {
+		DBG(("%s: associated pixmap=%ld\n", __FUNCTION__, get_drawable_pixmap(draw)->drawable.serialNumber));
+		sna_sync_flush(sna, sna_pixmap(get_drawable_pixmap(draw)));
+	} else { /* SyncFence are currently per-screen, sigh */
+		struct sna_pixmap *priv;
+
+		DBG(("%s: flushing all DRI3 pixmaps\n", __FUNCTION__));
+		list_for_each_entry(priv, &sna->dri3.pixmaps, cow_list)
+			sna_sync_flush(sna, priv);
+
+		sna_accel_flush(sna);
+	}
+
+	DBG(("%s: complete, chaining up\n", __FUNCTION__));
+	fence->funcs.SetTriggered = sna_fence->set_triggered;
+	sna_fence->set_triggered(fence);
+	sna_fence->set_triggered = fence->funcs.SetTriggered;
+	fence->funcs.SetTriggered = sna_sync_fence_set_triggered;
+}
+
+static void
+sna_sync_create_fence(ScreenPtr screen, SyncFence *fence, Bool initially_triggered)
+{
+	struct sna *sna = to_sna_from_screen(screen);
+	SyncScreenFuncsPtr funcs = miSyncGetScreenFuncs(screen);
+
+	DBG(("%s()\n", __FUNCTION__));
+
+	funcs->CreateFence = sna->dri3.create_fence;
+	sna->dri3.create_fence(screen, fence, initially_triggered);
+	sna->dri3.create_fence = funcs->CreateFence;
+	funcs->CreateFence = sna_sync_create_fence;
+
+	sna_sync_fence(fence)->set_triggered = fence->funcs.SetTriggered;
+	fence->funcs.SetTriggered = sna_sync_fence_set_triggered;
+}
+
+static bool
+sna_sync_open(struct sna *sna, ScreenPtr screen)
+{
+	SyncScreenFuncsPtr funcs;
+
+	DBG(("%s()\n", __FUNCTION__));
+
+	if (!miSyncShmScreenInit(screen))
+		return false;
+
+	if (!dixPrivateKeyRegistered(&sna_sync_fence_private_key)) {
+		if (!dixRegisterPrivateKey(&sna_sync_fence_private_key,
+					   PRIVATE_SYNC_FENCE,
+					   sizeof(struct sna_sync_fence)))
+			return false;
+	}
+
+	funcs = miSyncGetScreenFuncs(screen);
+	sna->dri3.create_fence = funcs->CreateFence;
+	funcs->CreateFence = sna_sync_create_fence;
+
+	return true;
+}
+
+static int sna_dri3_open_device(ScreenPtr screen,
+				RRProviderPtr provider,
+				int *out)
+{
+	int fd;
+
+	DBG(("%s()\n", __FUNCTION__));
+	fd = intel_get_client_fd(xf86ScreenToScrn(screen));
+	if (fd < 0)
+		return -fd;
+
+	*out = fd;
+	return Success;
+}
+
+static PixmapPtr sna_dri3_pixmap_from_fd(ScreenPtr screen,
+					 int fd,
+					 CARD16 width,
+					 CARD16 height,
+					 CARD16 stride,
+					 CARD8 depth,
+					 CARD8 bpp)
+{
+	struct sna *sna = to_sna_from_screen(screen);
+	PixmapPtr pixmap;
+	struct sna_pixmap *priv;
+	struct kgem_bo *bo;
+
+	DBG(("%s(fd=%d, width=%d, height=%d, stride=%d, depth=%d, bpp=%d)\n",
+	     __FUNCTION__, fd, width, height, stride, depth, bpp));
+	if (width > INT16_MAX || height > INT16_MAX)
+		return NULL;
+
+	if ((uint32_t)width * bpp > (uint32_t)stride * 8)
+		return NULL;
+
+	if (depth < 8)
+		return NULL;
+
+	switch (bpp) {
+	case 8:
+	case 16:
+	case 32:
+		break;
+	default:
+		return NULL;
+	}
+
+	bo = kgem_create_for_prime(&sna->kgem, fd, (uint32_t)stride * height);
+	if (bo == NULL)
+		return NULL;
+
+	/* Check for a duplicate */
+	list_for_each_entry(priv, &sna->dri3.pixmaps, cow_list) {
+		int other_stride = 0;
+		if (bo->snoop) {
+			assert(priv->cpu_bo);
+			assert(IS_STATIC_PTR(priv->ptr));
+			if (bo->handle == priv->cpu_bo->handle)
+				other_stride = priv->cpu_bo->pitch;
+		} else  {
+			assert(priv->gpu_bo);
+			assert(priv->pinned & PIN_DRI3);
+			if (bo->handle == priv->gpu_bo->handle)
+				other_stride = priv->gpu_bo->pitch;
+		}
+		if (other_stride) {
+			pixmap = priv->pixmap;
+			DBG(("%s: imported fd matches existing DRI3 pixmap=%ld\n", __FUNCTION__, pixmap->drawable.serialNumber));
+			bo->handle = 0; /* fudge to prevent gem_close */
+			kgem_bo_destroy(&sna->kgem, bo);
+			if (width != pixmap->drawable.width ||
+			    height != pixmap->drawable.height ||
+			    depth != pixmap->drawable.depth ||
+			    bpp != pixmap->drawable.bitsPerPixel ||
+			    stride != other_stride) {
+				DBG(("%s: imported fd mismatches existing DRI3 pixmap (width=%d, height=%d, depth=%d, bpp=%d, stride=%d)\n", __FUNCTION__,
+				     pixmap->drawable.width,
+				     pixmap->drawable.height,
+				     pixmap->drawable.depth,
+				     pixmap->drawable.bitsPerPixel,
+				     other_stride));
+				return NULL;
+			}
+			sna_sync_flush(sna, priv);
+			pixmap->refcnt++;
+			return pixmap;
+		}
+	}
+
+	if (!kgem_check_surface_size(&sna->kgem,
+				     width, height, bpp,
+				     bo->tiling, stride, kgem_bo_size(bo))) {
+		DBG(("%s: client supplied pitch=%d, size=%d too small for %dx%d surface\n",
+		     __FUNCTION__, stride, kgem_bo_size(bo), width, height));
+		goto free_bo;
+	}
+
+	pixmap = sna_pixmap_create_unattached(screen, 0, 0, depth);
+	if (pixmap == NullPixmap)
+		goto free_bo;
+
+	if (!screen->ModifyPixmapHeader(pixmap, width, height,
+					depth, bpp, stride, NULL))
+		goto free_pixmap;
+
+	priv = sna_pixmap_attach_to_bo(pixmap, bo);
+	if (priv == NULL)
+		goto free_pixmap;
+
+	bo->pitch = stride;
+	priv->stride = stride;
+
+	if (bo->snoop) {
+		assert(priv->cpu_bo == bo);
+		pixmap->devPrivate.ptr = kgem_bo_map__cpu(&sna->kgem, priv->cpu_bo);
+		if (pixmap->devPrivate.ptr == NULL)
+			goto free_pixmap;
+
+		pixmap->devKind = stride;
+		priv->ptr = MAKE_STATIC_PTR(pixmap->devPrivate.ptr);
+	} else {
+		assert(priv->gpu_bo == bo);
+		priv->pinned |= PIN_DRI3;
+	}
+	list_add(&priv->cow_list, &sna->dri3.pixmaps);
+
+	return pixmap;
+
+free_pixmap:
+	screen->DestroyPixmap(pixmap);
+free_bo:
+	kgem_bo_destroy(&sna->kgem, bo);
+	return NULL;
+}
+
+static int sna_dri3_fd_from_pixmap(ScreenPtr screen,
+				   PixmapPtr pixmap,
+				   CARD16 *stride,
+				   CARD32 *size)
+{
+	struct sna *sna = to_sna_from_screen(screen);
+	struct sna_pixmap *priv;
+	struct kgem_bo *bo = NULL;
+	int fd;
+
+	DBG(("%s(pixmap=%ld, width=%d, height=%d)\n", __FUNCTION__,
+	     pixmap->drawable.serialNumber, pixmap->drawable.width, pixmap->drawable.height));
+	if (pixmap == sna->front && sna->flags & SNA_TEAR_FREE) {
+		DBG(("%s: DRI3 protocol cannot support TearFree frontbuffers\n", __FUNCTION__));
+		return -1;
+	}
+
+	priv = sna_pixmap(pixmap);
+	if (priv && IS_STATIC_PTR(priv->ptr) && priv->cpu_bo) {
+		if (sna_pixmap_move_to_cpu(pixmap, MOVE_READ | MOVE_WRITE | MOVE_ASYNC_HINT))
+			bo = priv->cpu_bo;
+	} else {
+		priv = sna_pixmap_move_to_gpu(pixmap, MOVE_READ | MOVE_WRITE | MOVE_ASYNC_HINT | __MOVE_FORCE | __MOVE_DRI);
+		if (priv != NULL) {
+			sna_damage_all(&priv->gpu_damage,
+					pixmap->drawable.width,
+					pixmap->drawable.height);
+			bo = priv->gpu_bo;
+		}
+	}
+	if (bo == NULL) {
+		DBG(("%s: pixmap not supported by GPU\n", __FUNCTION__));
+		return -1;
+	}
+	assert(priv != NULL);
+
+	if (bo->pitch > UINT16_MAX) {
+		DBG(("%s: pixmap pitch (%d) too large for DRI3 protocol\n",
+		     __FUNCTION__, bo->pitch));
+		return -1;
+	}
+
+	fd = kgem_bo_export_to_prime(&sna->kgem, bo);
+	if (fd == -1) {
+		DBG(("%s: exporting handle=%d to fd failed\n", __FUNCTION__, bo->handle));
+		return -1;
+	}
+
+	if (bo == priv->gpu_bo && (priv->pinned & PIN_DRI3) == 0) {
+		list_add(&priv->cow_list, &sna->dri3.pixmaps);
+		priv->pinned |= PIN_DRI3;
+	}
+
+	*stride = (priv->pinned & PIN_DRI3) ? priv->gpu_bo->pitch : priv->cpu_bo->pitch;
+	*size = kgem_bo_size((priv->pinned & PIN_DRI3) ? priv->gpu_bo : priv->cpu_bo);
+	DBG(("%s: exporting %s pixmap=%ld, handle=%d, stride=%d, size=%d\n",
+	     __FUNCTION__,
+	     (priv->pinned & PIN_DRI3) ? "GPU" : "CPU", pixmap->drawable.serialNumber,
+	     (priv->pinned & PIN_DRI3) ? priv->gpu_bo->handle : priv->cpu_bo->handle,
+	     *stride, *size));
+	return fd;
+}
+
+static dri3_screen_info_rec sna_dri3_info = {
+	.version = DRI3_SCREEN_INFO_VERSION,
+
+	.open = sna_dri3_open_device,
+	.pixmap_from_fd = sna_dri3_pixmap_from_fd,
+	.fd_from_pixmap = sna_dri3_fd_from_pixmap,
+};
+
+bool sna_dri3_open(struct sna *sna, ScreenPtr screen)
+{
+	DBG(("%s()\n", __FUNCTION__));
+
+	if (!sna_sync_open(sna, screen))
+		return false;
+
+	list_init(&sna->dri3.pixmaps);
+	return dri3_screen_init(screen, &sna_dri3_info);
+}
+
+void sna_dri3_close(struct sna *sna, ScreenPtr screen)
+{
+	SyncScreenFuncsPtr funcs;
+
+	DBG(("%s()\n", __FUNCTION__));
+
+	funcs = miSyncGetScreenFuncs(screen);
+	if (funcs)
+		funcs->CreateFence = sna->dri3.create_fence;
+}
diff --git a/src/sna/sna_driver.c b/src/sna/sna_driver.c
index 9d079cb..97f27e2 100644
--- a/src/sna/sna_driver.c
+++ b/src/sna/sna_driver.c
@@ -379,6 +379,37 @@ static Bool sna_option_cast_to_bool(struct sna *sna, int id, Bool val)
 	return val;
 }
 
+static unsigned sna_option_cast_to_unsigned(struct sna *sna, int id, unsigned val)
+{
+	const char *str = xf86GetOptValString(sna->Options, id);
+	unsigned v;
+
+	if (str == NULL || *str == '\0')
+		return val;
+
+	if (namecmp(str, "on") == 0)
+		return val;
+	if (namecmp(str, "true") == 0)
+		return val;
+	if (namecmp(str, "yes") == 0)
+		return val;
+
+	if (namecmp(str, "0") == 0)
+		return 0;
+	if (namecmp(str, "off") == 0)
+		return 0;
+	if (namecmp(str, "false") == 0)
+		return 0;
+	if (namecmp(str, "no") == 0)
+		return 0;
+
+	v = atoi(str);
+	if (v)
+		return v;
+
+	return val;
+}
+
 static Bool fb_supports_depth(int fd, int depth)
 {
 	struct drm_i915_gem_create create;
@@ -407,6 +438,24 @@ static Bool fb_supports_depth(int fd, int depth)
 	return ret;
 }
 
+static void setup_dri(struct sna *sna)
+{
+	unsigned level;
+
+	sna->dri2.available = false;
+	sna->dri3.available = false;
+
+	level = sna_option_cast_to_unsigned(sna, OPTION_DRI, ~0);
+#if HAVE_DRI3
+	if (level >= 3)
+		sna->dri3.available = !!xf86LoadSubModule(sna->scrn, "dri3");
+#endif
+#if HAVE_DRI2
+	if (level >= 2)
+		sna->dri2.available = !!xf86LoadSubModule(sna->scrn, "dri2");
+#endif
+}
+
 /**
  * This is called before ScreenInit to do any require probing of screen
  * configuration.
@@ -595,9 +644,7 @@ static Bool sna_pre_init(ScrnInfoPtr scrn, int flags)
 	xf86SetGamma(scrn, zeros);
 	xf86SetDpi(scrn, 0, 0);
 
-	sna->dri2.available = false;
-	if (sna_option_cast_to_bool(sna, OPTION_DRI, TRUE))
-		sna->dri2.available = !!xf86LoadSubModule(scrn, "dri2");
+	setup_dri(sna);
 
 	sna_acpi_init(sna);
 
@@ -820,6 +867,11 @@ static Bool sna_early_close_screen(CLOSE_SCREEN_ARGS_DECL)
 	sna_uevent_fini(scrn);
 	sna_mode_close(sna);
 
+	if (sna->dri3.open) {
+		sna_dri3_close(sna, screen);
+		sna->dri3.open = false;
+	}
+
 	if (sna->dri2.open) {
 		sna_dri2_close(sna, screen);
 		sna->dri2.open = false;
@@ -901,6 +953,25 @@ sna_register_all_privates(void)
 	return TRUE;
 }
 
+static void sna_dri_init(struct sna *sna, ScreenPtr screen)
+{
+	char str[128] = "";
+
+	if (sna->dri2.available)
+		sna->dri2.open = sna_dri2_open(sna, screen);
+	if (sna->dri2.open)
+		strcat(str, "DRI2 ");
+
+	if (sna->dri3.available)
+		sna->dri3.open = sna_dri3_open(sna, screen);
+	if (sna->dri3.open)
+		strcat(str, "DRI3 ");
+
+	if (*str)
+		xf86DrvMsg(sna->scrn->scrnIndex, X_INFO,
+			   "direct rendering: %senabled\n", str);
+}
+
 static size_t
 agp_aperture_size(struct pci_device *dev, int gen)
 {
@@ -1025,11 +1096,7 @@ sna_screen_init(SCREEN_INIT_ARGS_DECL)
 	xf86DPMSInit(screen, xf86DPMSSet, 0);
 
 	sna_video_init(sna, screen);
-	if (sna->dri2.available)
-		sna->dri2.open = sna_dri2_open(sna, screen);
-	if (sna->dri2.open)
-		xf86DrvMsg(scrn->scrnIndex, X_INFO,
-			   "direct rendering: DRI2 Enabled\n");
+	sna_dri_init(sna, screen);
 
 	if (serverGeneration == 1)
 		xf86ShowUnusedOptions(scrn->scrnIndex, scrn->options);
commit 0fbe8995d51b4643f1cf06c07da8b4b5ac5ae7c3
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu May 22 11:17:35 2014 +0100

    sna: Relax tiling height restrictions
    
    Only force the even-tile-row alignment if we have an old GPU with an old
    kernel that doesn't perform conservative alignment for us (as required).
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/kgem.c b/src/sna/kgem.c
index 1a52557..17b5b1a 100644
--- a/src/sna/kgem.c
+++ b/src/sna/kgem.c
@@ -1584,16 +1584,16 @@ static uint32_t kgem_surface_size(struct kgem *kgem,
 		if (tiling) {
 			if (kgem->gen < 030) {
 				tile_width = 128;
-				tile_height = 32;
+				tile_height = 16;
 			} else {
 				tile_width = 512;
-				tile_height = 16;
+				tile_height = 8;
 			}
 		} else {
 			tile_width = 2 * bpp >> 3;
 			tile_width = ALIGN(tile_width,
 					   kgem_pitch_alignment(kgem, flags));
-			tile_height = 2;
+			tile_height = 1;
 		}
 	} else switch (tiling) {
 	default:
@@ -1601,19 +1601,21 @@ static uint32_t kgem_surface_size(struct kgem *kgem,
 		tile_width = 2 * bpp >> 3;
 		tile_width = ALIGN(tile_width,
 				   kgem_pitch_alignment(kgem, flags));
-		tile_height = 2;
+		tile_height = 1;
 		break;
 
-		/* XXX align to an even tile row */
 	case I915_TILING_X:
 		tile_width = 512;
-		tile_height = 16;
+		tile_height = 8;
 		break;
 	case I915_TILING_Y:
 		tile_width = 128;
-		tile_height = 64;
+		tile_height = 32;
 		break;
 	}
+	/* XXX align to an even tile row */
+	if (!kgem->has_relaxed_fencing)
+		tile_height *= 2;
 
 	*pitch = ALIGN(width * bpp / 8, tile_width);
 	height = ALIGN(height, tile_height);
@@ -1653,7 +1655,7 @@ static uint32_t kgem_aligned_height(struct kgem *kgem,
 	uint32_t tile_height;
 
 	if (kgem->gen <= 030) {
-		tile_height = tiling ? kgem->gen < 030 ? 32 : 16 : 1;
+		tile_height = tiling ? kgem->gen < 030 ? 16 : 8 : 1;
 	} else switch (tiling) {
 		/* XXX align to an even tile row */
 	default:
@@ -1661,13 +1663,17 @@ static uint32_t kgem_aligned_height(struct kgem *kgem,
 		tile_height = 1;
 		break;
 	case I915_TILING_X:
-		tile_height = 16;
+		tile_height = 8;
 		break;
 	case I915_TILING_Y:
-		tile_height = 64;
+		tile_height = 32;
 		break;
 	}
 
+	/* XXX align to an even tile row */
+	if (!kgem->has_relaxed_fencing)
+		tile_height *= 2;
+
 	return ALIGN(height, tile_height);
 }
 
commit 67b37332bd45dd4cea297107bfdc9df21984fdcd
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue May 20 15:37:13 2014 +0100

    intel-virtual-output: Add DRI3 xfer path
    
    Just as proof-of-principle.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/configure.ac b/configure.ac
index 8b0bd30..353f942 100644
--- a/configure.ac
+++ b/configure.ac
@@ -255,6 +255,11 @@ fi
 if test "x$tools" != "xno"; then
 	ivo_requires="xinerama xrandr xdamage xfixes xcursor xtst xrender xext x11 pixman-1"
 	PKG_CHECK_MODULES(IVO, [$ivo_requires], [ivo="yes"], [ivo="no"])
+	PKG_CHECK_MODULES(IVO_DRI3, [xcb-dri3 xcb-sync x11-xcb xshmfence x11], [ivo_dri3="yes"], [ivo_dri3="no"])
+	if test "x$ivo_dri3" = "xyes"; then
+		IVO_CFLAGS="$IVO_CFLAGS $IVO_DRI3_CFLAGS -DDRI3"
+		IVO_LIBS="$IVO_LIBS $IVO_DRI3_LIBS"
+	fi
 	AC_CHECK_HEADER([sys/timerfd.h], [], [ivo="no"])
 	if test "x$ivo" = "xno"; then
 		if test "x$tools" = "xyes"; then
diff --git a/tools/virtual.c b/tools/virtual.c
index 1d490c5..a5dccb4 100644
--- a/tools/virtual.c
+++ b/tools/virtual.c
@@ -85,6 +85,7 @@ struct display {
 	int xfixes_event, xfixes_error;
 	int rr_event, rr_error, rr_active;
 	int xinerama_event, xinerama_error, xinerama_active;
+	int dri3_active;
 	Window root;
 	Visual *visual;
 	Damage damage;
@@ -156,6 +157,11 @@ struct clone {
 	int width, height, depth;
 	struct { int x1, x2, y1, y2; } damaged;
 	int rr_update;
+
+	struct dri3_fence {
+		XID xid;
+		void *addr;
+	} dri3;
 };
 
 struct context {
@@ -305,6 +311,143 @@ can_use_shm(Display *dpy,
 	return has_shm;
 }
 
+#ifdef DRI3
+#include <X11/Xlib-xcb.h>
+#include <X11/xshmfence.h>
+#include <xcb/xcb.h>
+#include <xcb/dri3.h>
+#include <xcb/sync.h>
+static Pixmap dri3_create_pixmap(Display *dpy,
+				 Drawable draw,
+				 int width, int height, int depth,
+				 int fd, int bpp, int stride, int size)
+{
+	xcb_connection_t *c = XGetXCBConnection(dpy);
+	xcb_pixmap_t pixmap = xcb_generate_id(c);
+	xcb_dri3_pixmap_from_buffer(c, pixmap, draw, size, width, height, stride, depth, bpp, fd);
+	return pixmap;
+}
+
+static int dri3_create_fd(Display *dpy,
+			  Pixmap pixmap,
+			  int *stride)
+{
+	xcb_connection_t *c = XGetXCBConnection(dpy);
+	xcb_dri3_buffer_from_pixmap_cookie_t cookie;
+	xcb_dri3_buffer_from_pixmap_reply_t *reply;
+
+	cookie = xcb_dri3_buffer_from_pixmap(c, pixmap);
+	reply = xcb_dri3_buffer_from_pixmap_reply(c, cookie, NULL);
+	if (!reply)
+		return -1;
+
+	if (reply->nfd != 1)
+		return -1;
+
+	*stride = reply->stride;
+	return xcb_dri3_buffer_from_pixmap_reply_fds(c, reply)[0];
+}
+
+static int dri3_query_version(Display *dpy, int *major, int *minor)
+{
+	xcb_connection_t *c = XGetXCBConnection(dpy);
+	xcb_dri3_query_version_reply_t *reply;
+
+	*major = *minor = -1;
+
+	reply = xcb_dri3_query_version_reply(c,
+					     xcb_dri3_query_version(c,
+								    XCB_DRI3_MAJOR_VERSION,
+								    XCB_DRI3_MINOR_VERSION),
+					     NULL);
+	if (reply == NULL)
+		return -1;
+
+	*major = reply->major_version;
+	*minor = reply->minor_version;
+	free(reply);
+
+	return 0;
+}
+
+static int dri3_exists(Display *dpy)
+{
+	int major, minor;
+
+	if (dri3_query_version(dpy, &major, &minor) < 0)
+		return 0;
+
+	return major >= 0;
+}
+
+static void dri3_create_fence(Display *dpy, Drawable d, struct dri3_fence *fence)
+{
+	xcb_connection_t *c = XGetXCBConnection(dpy);
+	struct dri3_fence f;
+	int fd;
+
+	fd = xshmfence_alloc_shm();
+	if (fd < 0)
+		return;
+
+	f.addr = xshmfence_map_shm(fd);
+	if (f.addr == NULL) {
+		close(fd);
+		return;
+	}
+
+	f.xid = xcb_generate_id(c);
+	xcb_dri3_fence_from_fd(c, d, f.xid, 0, fd);
+
+	*fence = f;
+}
+
+static void dri3_fence_flush(Display *dpy, struct dri3_fence *fence)
+{
+	xcb_sync_trigger_fence(XGetXCBConnection(dpy), fence->xid);
+}
+
+static void dri3_fence_free(Display *dpy, struct dri3_fence *fence)
+{
+	xshmfence_unmap_shm(fence->addr);
+	xcb_sync_destroy_fence(XGetXCBConnection(dpy), fence->xid);
+}
+
+#else
+
+static int dri3_exists(Display *dpy)
+{
+	return 0;
+}
+
+static void dri3_create_fence(Display *dpy, Drawable d, struct dri3_fence *fence)
+{
+}
+
+static void dri3_fence_flush(Display *dpy, struct dri3_fence *fence)
+{
+}
+
+static void dri3_fence_free(Display *dpy, struct dri3_fence *fence)
+{
+}
+
+static Pixmap dri3_create_pixmap(Display *dpy,
+				 Drawable draw,
+				 int width, int height, int depth,
+				 int fd, int bpp, int stride, int size)
+{
+	return None;
+}
+
+static int dri3_create_fd(Display *dpy,
+			  Pixmap pixmap,
+			  int *stride)
+{
+	return -1;
+}
+#endif
+
 static int timerfd(int hz)
 {
 	struct itimerspec it;
@@ -868,13 +1011,11 @@ static int mode_width(const XRRModeInfo *mode, Rotation rotation)
 
 static void output_init_xfer(struct clone *clone, struct output *output)
 {
-	if (output->use_shm_pixmap) {
+	if (output->pixmap == None && output->use_shm_pixmap) {
 		DBG(("%s-%s: creating shm pixmap\n", DisplayString(output->dpy), output->name));
 		XSync(output->dpy, False);
 		_x_error_occurred = 0;
 
-		if (output->pixmap)
-			XFreePixmap(output->dpy, output->pixmap);
 		output->pixmap = XShmCreatePixmap(output->dpy, output->window,
 						  clone->shm.shmaddr, &output->shm,
 						  clone->width, clone->height, clone->depth);
@@ -913,35 +1054,34 @@ static void output_init_xfer(struct clone *clone, struct output *output)
 	}
 }
 
+static int bpp_for_depth(int depth)
+{
+	switch (depth) {
+	case 1: return 1;
+	case 8: return 8;
+	case 15: return 16;
+	case 16: return 16;
+	case 24: return 24;
+	case 32: return 32;
+	default: return 0;
+	}
+}
+
 static int clone_init_xfer(struct clone *clone)
 {
 	int width, height;
 
-	if (clone->src.mode.id == 0) {
-		if (clone->width == 0 && clone->height == 0)
-			return 0;
-
+	if (clone->dst.mode.id == 0) {
 		clone->width = 0;
 		clone->height = 0;
-
-		if (clone->src.use_shm)
-			XShmDetach(clone->src.dpy, &clone->src.shm);
-		if (clone->dst.use_shm)
-			XShmDetach(clone->dst.dpy, &clone->dst.shm);
-
-		if (clone->shm.shmaddr) {
-			shmdt(clone->shm.shmaddr);
-			clone->shm.shmaddr = 0;
-		}
-
-		clone->damaged.x2 = clone->damaged.y2 = INT_MIN;
-		clone->damaged.x1 = clone->damaged.y1 = INT_MAX;
-		return 0;
+	} else if (clone->dri3.xid) {
+		width = clone->dst.display->width;
+		height = clone->dst.display->height;
+	} else {
+		width = mode_width(&clone->src.mode, clone->src.rotation);
+		height = mode_height(&clone->src.mode, clone->src.rotation);
 	}
 
-	width = mode_width(&clone->src.mode, clone->src.rotation);
-	height = mode_height(&clone->src.mode, clone->src.rotation);
-
 	if (width == clone->width && height == clone->height)
 		return 0;
 
@@ -949,40 +1089,93 @@ static int clone_init_xfer(struct clone *clone)
 	     DisplayString(clone->dst.dpy), clone->dst.name,
 	     width, height));
 
-	clone->width = width;
-	clone->height = height;
+	if (clone->src.use_shm)
+		XShmDetach(clone->src.dpy, &clone->src.shm);
+	if (clone->dst.use_shm)
+		XShmDetach(clone->dst.dpy, &clone->dst.shm);
 
-	if (clone->shm.shmaddr)
+	if (clone->shm.shmaddr) {
 		shmdt(clone->shm.shmaddr);
+		clone->shm.shmaddr = NULL;
+	}
 
-	clone->shm.shmid = shmget(IPC_PRIVATE,
-				  height * stride_for_depth(width, clone->depth),
-				  IPC_CREAT | 0666);
-	if (clone->shm.shmid == -1)
-		return errno;
+	if (clone->src.pixmap) {
+		XFreePixmap(clone->src.dpy, clone->src.pixmap);
+		clone->src.pixmap = 0;
+	}
 
-	clone->shm.shmaddr = shmat(clone->shm.shmid, 0, 0);
-	if (clone->shm.shmaddr == (char *) -1) {
-		shmctl(clone->shm.shmid, IPC_RMID, NULL);
-		return ENOMEM;
+	if (clone->dst.pixmap) {
+		XFreePixmap(clone->dst.dpy, clone->dst.pixmap);
+		clone->dst.pixmap = 0;
+	}
+
+	if ((width | height) == 0) {
+		clone->damaged.x2 = clone->damaged.y2 = INT_MIN;
+		clone->damaged.x1 = clone->damaged.y1 = INT_MAX;
+		return 0;
 	}
 
-	init_image(clone);
+	if (clone->dri3.xid) {
+		int fd, stride;
+		Pixmap src;
+
+		_x_error_occurred = 0;
+
+		fd = dri3_create_fd(clone->dst.dpy, clone->dst.window, &stride);
+		src = dri3_create_pixmap(clone->src.dpy, clone->src.window,
+					 width, height, clone->depth,
+					 fd, bpp_for_depth(clone->depth),
+					 stride, lseek(fd, 0, SEEK_END));
 
-	if (clone->src.use_shm) {
-		clone->src.shm = clone->shm;
-		clone->src.shm.readOnly = False;
-		XShmAttach(clone->src.dpy, &clone->src.shm);
 		XSync(clone->src.dpy, False);
-	}
-	if (clone->dst.use_shm) {
-		clone->dst.shm = clone->shm;
-		clone->dst.shm.readOnly = !clone->dst.use_shm_pixmap;
-		XShmAttach(clone->dst.dpy, &clone->dst.shm);
-		XSync(clone->dst.dpy, False);
+		if (!_x_error_occurred) {
+			clone->src.pixmap = src;
+			clone->width = width;
+			clone->height = height;
+		} else {
+			XFreePixmap(clone->src.dpy, src);
+			close(fd);
+			dri3_fence_free(clone->src.dpy, &clone->dri3);
+			clone->dri3.xid = 0;
+		}
 	}
 
-	shmctl(clone->shm.shmid, IPC_RMID, NULL);
+	width = mode_width(&clone->src.mode, clone->src.rotation);
+	height = mode_height(&clone->src.mode, clone->src.rotation);
+
+	if (!clone->dri3.xid) {
+		clone->shm.shmid = shmget(IPC_PRIVATE,
+					  height * stride_for_depth(width, clone->depth),
+					  IPC_CREAT | 0666);
+		if (clone->shm.shmid == -1)
+			return errno;
+
+		clone->shm.shmaddr = shmat(clone->shm.shmid, 0, 0);
+		if (clone->shm.shmaddr == (char *) -1) {
+			shmctl(clone->shm.shmid, IPC_RMID, NULL);
+			return ENOMEM;
+		}
+
+		init_image(clone);
+
+		if (clone->src.use_shm) {
+			clone->src.shm = clone->shm;
+			clone->src.shm.readOnly = False;
+			XShmAttach(clone->src.dpy, &clone->src.shm);
+			XSync(clone->src.dpy, False);
+		}
+		if (clone->dst.use_shm) {
+			clone->dst.shm = clone->shm;
+			clone->dst.shm.readOnly = !clone->dst.use_shm_pixmap;
+			XShmAttach(clone->dst.dpy, &clone->dst.shm);
+			XSync(clone->dst.dpy, False);
+		}
+
+		shmctl(clone->shm.shmid, IPC_RMID, NULL);
+
+		clone->width = width;
+		clone->height = height;
+	}
 
 	output_init_xfer(clone, &clone->src);
 	output_init_xfer(clone, &clone->dst);
@@ -1095,8 +1288,6 @@ static int context_update(struct context *ctx)
 		DBG(("%s-%s output changed? %d\n",
 		     DisplayString(ctx->clones[n].dst.display->dpy), ctx->clones[n].dst.name, changed));
 
-		if (changed)
-			clone_init_xfer(&ctx->clones[n]);
 		context_changed |= changed;
 	}
 	XRRFreeScreenResources(res);
@@ -1359,6 +1550,8 @@ ungrab:
 	for (n = 0; n < ctx->nclone; n++) {
 		struct clone *clone = &ctx->clones[n];
 
+		clone_init_xfer(clone);
+
 		if (clone->dst.rr_crtc == 0)
 			continue;
 
@@ -1647,8 +1840,6 @@ static void put_dst(struct clone *c, const XRectangle *clip)
 			  clip->width, clip->height);
 		c->dst.serial = 0;
 	}
-
-	display_mark_flush(c->dst.display);
 }
 
 static int clone_paint(struct clone *c)
@@ -1703,15 +1894,37 @@ static int clone_paint(struct clone *c)
 		c->damaged.y2 = c->src.y + c->height;
 	}
 
-	clip.x = c->damaged.x1;
-	clip.y = c->damaged.y1;
-	clip.width  = c->damaged.x2 - c->damaged.x1;
-	clip.height = c->damaged.y2 - c->damaged.y1;
-	get_src(c, &clip);
+	if (c->dri3.xid) {
+		if (c->src.use_render) {
+			XRenderComposite(c->src.dpy, PictOpSrc,
+					 c->src.win_picture, 0, c->src.pix_picture,
+					 c->damaged.x1, c->damaged.y1,
+					 0, 0,
+					 c->damaged.x1 + c->dst.x - c->src.x,
+					 c->damaged.y1 + c->dst.y - c->src.y,
+					 c->damaged.x2 - c->damaged.x1,
+					 c->damaged.y2 - c->damaged.y1);
+		} else {
+			XCopyArea(c->src.dpy, c->src.window, c->src.pixmap, c->src.gc,
+				  c->damaged.x1, c->damaged.y1,
+				  c->damaged.x2 - c->damaged.x1,
+				  c->damaged.y2 - c->damaged.y1,
+				  c->damaged.x1 + c->dst.x - c->src.x,
+				  c->damaged.y1 + c->dst.y - c->src.y);
+		}
+		dri3_fence_flush(c->src.dpy, &c->dri3);
+	} else {
+		clip.x = c->damaged.x1;
+		clip.y = c->damaged.y1;
+		clip.width  = c->damaged.x2 - c->damaged.x1;
+		clip.height = c->damaged.y2 - c->damaged.y1;
+		get_src(c, &clip);
 
-	clip.x += c->dst.x - c->src.x;
-	clip.y += c->dst.y - c->src.y;
-	put_dst(c, &clip);
+		clip.x += c->dst.x - c->src.x;
+		clip.y += c->dst.y - c->src.y;
+		put_dst(c, &clip);
+	}
+	display_mark_flush(c->dst.display);
 
 done:
 	c->damaged.x2 = c->damaged.y2 = INT_MIN;
@@ -2010,6 +2223,11 @@ static int clone_init_depth(struct clone *clone)
 	     clone->src.use_render != NULL,
 	     clone->dst.use_render != NULL));
 
+	if (!clone->dst.use_render &&
+	    clone->src.display->dri3_active &&
+	    clone->dst.display->dri3_active)
+		dri3_create_fence(clone->src.dpy, clone->src.window, &clone->dri3);
+
 	return 0;
 }
 
@@ -2073,6 +2291,11 @@ static int add_display(struct context *ctx, Display *dpy)
 	     display->xinerama_event,
 	     display->xinerama_error));
 
+	display->dri3_active = dri3_exists(dpy);
+	DBG(("%s: dri3_active?=%d\n",
+	     DisplayString(dpy),
+	     display->dri3_active));
+
 	/* first display (source) is slightly special */
 	if (!first_display) {
 		display->invisible_cursor = display_load_invisible_cursor(display);
commit 9cf6cd9726ed5ba73bb8c38c06f7b5c78706309b
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed May 21 12:52:18 2014 +0100

    Add rudimentary tests for Present
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/configure.ac b/configure.ac
index e89e5a3..8b0bd30 100644
--- a/configure.ac
+++ b/configure.ac
@@ -239,7 +239,7 @@ if test "x$shm" = "xyes"; then
 		  AC_MSG_RESULT(assuming no))
 fi
 
-PKG_CHECK_MODULES(X11_DRI3, [xcb-dri3 xcb-sync x11-xcb xshmfence x11 xrender xext libdrm], [x11_dri3="yes"], [x11_dri3="no"])
+PKG_CHECK_MODULES(X11_DRI3, [xcb-dri3 xcb-sync xcb-present x11-xcb xshmfence x11 xrender xext libdrm], [x11_dri3="yes"], [x11_dri3="no"])
 AM_CONDITIONAL(X11_DRI3, test "x$x11_dri3" = "xyes" -a "x$shm" = "xyes")
 
 AC_ARG_ENABLE(tools,
diff --git a/test/.gitignore b/test/.gitignore
index 8e6b1bc..717ef07 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -23,3 +23,4 @@ dri2-race
 dri2-swap
 dri2-test
 dri3-test
+present-test
diff --git a/test/Makefile.am b/test/Makefile.am
index 7a8916c..e33a6b9 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -33,6 +33,7 @@ endif
 if X11_DRI3
 stress_TESTS += \
 	dri3-test \
+	present-test \
 	$(NULL)
 endif
 check_PROGRAMS = $(stress_TESTS)
diff --git a/test/dri3.c b/test/dri3.c
index 9950331..b5eb4ba 100644
--- a/test/dri3.c
+++ b/test/dri3.c
@@ -38,9 +38,12 @@ Pixmap dri3_create_pixmap(Display *dpy,
 			  int fd, int bpp, int stride, int size)
 {
 	xcb_connection_t *c = XGetXCBConnection(dpy);
-	xcb_pixmap_t pixmap = xcb_generate_id(c);
-	xcb_dri3_pixmap_from_buffer(c, pixmap, draw, size, width, height, stride, depth, bpp, fd);
-	return pixmap;
+	if (fd >= 0) {
+		xcb_pixmap_t pixmap = xcb_generate_id(c);
+		xcb_dri3_pixmap_from_buffer(c, pixmap, draw, size, width, height, stride, depth, bpp, fd);
+		return pixmap;
+	}
+	return NullPixmap;
 }
 
 int dri3_create_fd(Display *dpy,
diff --git a/test/present-test.c b/test/present-test.c
new file mode 100644
index 0000000..6b562eb
--- /dev/null
+++ b/test/present-test.c
@@ -0,0 +1,726 @@
+/*
+ * Copyright (c) 2014 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <X11/Xlib.h>
+#include <X11/Xlib-xcb.h>
+#include <X11/xshmfence.h>
+#include <X11/Xutil.h>
+#include <X11/Xlibint.h>
+#include <X11/extensions/randr.h>
+#include <X11/extensions/Xrandr.h>
+#include <X11/extensions/Xrender.h>
+#include <X11/extensions/XShm.h>
+#if HAVE_X11_EXTENSIONS_SHMPROTO_H
+#include <X11/extensions/shmproto.h>
+#elif HAVE_X11_EXTENSIONS_SHMSTR_H
+#include <X11/extensions/shmstr.h>
+#else
+#error Failed to find the right header for X11 MIT-SHM protocol definitions
+#endif
+#include <xcb/xcb.h>
+#include <xcb/present.h>
+#include <xf86drm.h>
+#include <i915_drm.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <signal.h>
+
+#include <sys/mman.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <pciaccess.h>
+
+#include "dri3.h"
+
+#define ALIGN(x, y) (((x) + (y) - 1) & -(y))
+#define PAGE_ALIGN(x) ALIGN(x, 4096)
+
+#define GTT I915_GEM_DOMAIN_GTT
+#define CPU I915_GEM_DOMAIN_CPU
+
+static int _x_error_occurred;
+static uint32_t stamp;
+
+static int
+_check_error_handler(Display     *display,
+		     XErrorEvent *event)
+{
+	printf("X11 error from display %s, serial=%ld, error=%d, req=%d.%d\n",
+	       DisplayString(display),
+	       event->serial,
+	       event->error_code,
+	       event->request_code,
+	       event->minor_code);
+	_x_error_occurred++;
+	return False; /* ignored */
+}
+
+static int is_i915_device(int fd)
+{
+	drm_version_t version;
+	char name[5] = "";
+
+	memset(&version, 0, sizeof(version));
+	version.name_len = 4;
+	version.name = name;
+
+	if (drmIoctl(fd, DRM_IOCTL_VERSION, &version))
+		return 0;
+
+	return strcmp("i915", name) == 0;
+}
+
+static int is_intel(int fd)
+{
+	struct drm_i915_getparam gp;
+	int ret;
+
+	/* Confirm that this is a i915.ko device with GEM/KMS enabled */
+	ret = is_i915_device(fd);
+	if (ret) {
+		gp.param = I915_PARAM_HAS_GEM;
+		gp.value = &ret;
+		if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp))
+			ret = 0;
+	}
+	return ret;
+}
+
+static void *setup_msc(Display *dpy,  Window win)
+{
+	xcb_connection_t *c = XGetXCBConnection(dpy);
+	xcb_void_cookie_t cookie;
+	uint32_t id = xcb_generate_id(c);
+	xcb_generic_error_t *error;
+	void *q;
+
+	cookie = xcb_present_select_input_checked(c, id, win, XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY);
+	q = xcb_register_for_special_xge(c, &xcb_present_id, id, &stamp);
+
+	error = xcb_request_check(c, cookie);
+	assert(error == NULL);
+
+	return q;
+}
+
+static uint64_t check_msc(Display *dpy, Window win, void *q, uint64_t last_msc)
+{
+	xcb_connection_t *c = XGetXCBConnection(dpy);
+	uint64_t msc = 0;
+
+	xcb_present_notify_msc(c, win, 0, 0, 0, 0);
+	xcb_flush(c);
+
+	do {
+		xcb_present_complete_notify_event_t *ce;
+		xcb_generic_event_t *ev;
+
+		ev = xcb_wait_for_special_event(c, q);
+		if (ev == NULL)
+			break;
+
+		ce = (xcb_present_complete_notify_event_t *)ev;
+		if (ce->kind != XCB_PRESENT_COMPLETE_KIND_PIXMAP)
+			msc = ce->msc;
+		free(ev);
+	} while (msc == 0);
+
+	if (msc < last_msc) {
+		printf("Invalid MSC: was %llu, now %llu\n",
+		       (long long)last_msc, (long long)msc);
+	}
+
+	return msc;
+}
+
+static void teardown_msc(Display *dpy, void *q)
+{
+	xcb_unregister_for_special_event(XGetXCBConnection(dpy), q);
+}
+static int test_whole(Display *dpy)
+{
+	Pixmap pixmap;
+	struct dri3_fence fence;
+	Window root;
+	unsigned int width, height;
+	unsigned border, depth;
+	int x, y, ret = 1;
+
+	XGetGeometry(dpy, DefaultRootWindow(dpy),
+		     &root, &x, &y, &width, &height, &border, &depth);
+
+	if (dri3_create_fence(dpy, root, &fence))
+		return 0;
+
+	printf("Testing whole screen flip: %dx%d\n", width, height);
+	_x_error_occurred = 0;
+
+	xshmfence_reset(fence.addr);
+
+	pixmap = XCreatePixmap(dpy, root, width, height, depth);
+	xcb_present_pixmap(XGetXCBConnection(dpy),
+			   root, pixmap,
+			   0, /* sbc */
+			   0, /* valid */
+			   0, /* update */
+			   0, /* x_off */
+			   0, /* y_off */
+			   None,
+			   None, /* wait fence */
+			   fence.xid,
+			   XCB_PRESENT_OPTION_NONE,
+			   0, /* target msc */
+			   0, /* divisor */
+			   0, /* remainder */
+			   0, NULL);
+	XFreePixmap(dpy, pixmap);
+
+	pixmap = XCreatePixmap(dpy, root, width, height, depth);
+	xcb_present_pixmap(XGetXCBConnection(dpy),
+			   root, pixmap,
+			   0, /* sbc */
+			   0, /* valid */
+			   0, /* update */
+			   0, /* x_off */
+			   0, /* y_off */
+			   None,
+			   None, /* wait fence */
+			   None, /* sync fence */
+			   XCB_PRESENT_OPTION_NONE,
+			   0, /* target msc */
+			   0, /* divisor */
+			   0, /* remainder */
+			   0, NULL);
+	XFreePixmap(dpy, pixmap);
+	XFlush(dpy);
+
+	ret = !!xshmfence_await(fence.addr);
+	dri3_fence_free(dpy, &fence);
+
+	XSync(dpy, True);
+	ret += !!_x_error_occurred;
+
+	return ret;
+}
+
+static inline XRRScreenResources *_XRRGetScreenResourcesCurrent(Display *dpy, Window window)
+{
+	XRRScreenResources *res;
+
+	res = XRRGetScreenResourcesCurrent(dpy, window);
+	if (res == NULL)
+		res = XRRGetScreenResources(dpy, window);
+
+	return res;
+}
+
+static XRRModeInfo *lookup_mode(XRRScreenResources *res, int id)
+{
+	int i;
+
+	for (i = 0; i < res->nmode; i++) {
+		if (res->modes[i].id == id)
+			return &res->modes[i];
+	}
+
+	return NULL;
+}
+
+static int for_each_crtc(Display *dpy,
+			  int (*func)(Display *dpy,
+				      RRCrtc crtc,
+				      int width, int height,
+				      void *closure),
+			  void *closure)
+{
+	XRRScreenResources *res;
+	XRRCrtcInfo **original_crtc;
+	int i, j, err = 0;
+
+	if (!XRRQueryVersion(dpy, &i, &j))
+		return -1;
+
+	res = _XRRGetScreenResourcesCurrent(dpy, DefaultRootWindow(dpy));
+	if (res == NULL)
+		return -1;
+
+	original_crtc = malloc(sizeof(XRRCrtcInfo *)*res->ncrtc);
+	for (i = 0; i < res->ncrtc; i++)
+		original_crtc[i] = XRRGetCrtcInfo(dpy, res, res->crtcs[i]);
+
+	printf("noutput=%d, ncrtc=%d\n", res->noutput, res->ncrtc);
+
+	for (i = 0; i < res->noutput; i++) {
+		XRROutputInfo *output;
+		XRRModeInfo *mode;
+
+		output = XRRGetOutputInfo(dpy, res, res->outputs[i]);
+		if (output == NULL)
+			continue;
+
+		mode = NULL;
+		if (res->nmode)
+			mode = lookup_mode(res, output->modes[0]);
+
+		for (j = 0; mode && j < output->ncrtc; j++) {
+			printf("[%d, %d] -- OUTPUT:%ld, CRTC:%ld\n",
+			       i, j, (long)res->outputs[i], (long)output->crtcs[j]);
+			XRRSetCrtcConfig(dpy, res, output->crtcs[j], CurrentTime,
+					 0, 0, output->modes[0], RR_Rotate_0, &res->outputs[i], 1);
+			XSync(dpy, True);
+
+			err += func(dpy, output->crtcs[j], mode->width, mode->height, closure);
+
+			XRRSetCrtcConfig(dpy, res, output->crtcs[j], CurrentTime,
+					 0, 0, None, RR_Rotate_0, NULL, 0);
+			XSync(dpy, True);
+		}
+
+		XRRFreeOutputInfo(output);
+	}
+
+	for (i = 0; i < res->ncrtc; i++)
+		XRRSetCrtcConfig(dpy, res, res->crtcs[i], CurrentTime,
+				 original_crtc[i]->x,
+				 original_crtc[i]->y,
+				 original_crtc[i]->mode,
+				 original_crtc[i]->rotation,
+				 original_crtc[i]->outputs,
+				 original_crtc[i]->noutput);
+
+	free(original_crtc);
+	XRRFreeScreenResources(res);
+
+	return j;
+}
+
+struct test_crtc {
+	Window win;
+	int depth;
+	unsigned flags;
+
+	struct dri3_fence fence;
+	void *queue;
+	uint64_t msc;
+};
+#define SYNC 0x1
+
+static int __test_crtc(Display *dpy, RRCrtc crtc,
+		       int width, int height,
+		       void *closure)
+{
+	struct test_crtc *test = closure;
+	Pixmap pixmap;
+	int err = 0;
+
+	test->msc = check_msc(dpy, test->win, test->queue, test->msc);
+
+	if (test->flags & SYNC)
+		xshmfence_reset(test->fence.addr);
+
+	pixmap = XCreatePixmap(dpy, test->win, width, height, test->depth);
+	xcb_present_pixmap(XGetXCBConnection(dpy),
+			   test->win, pixmap,
+			   0, /* sbc */
+			   0, /* valid */
+			   0, /* update */
+			   0, /* x_off */
+			   0, /* y_off */
+			   crtc,
+			   None, /* wait fence */
+			   test->flags & SYNC ? test->fence.xid : None,
+			   XCB_PRESENT_OPTION_NONE,
+			   0, /* target msc */
+			   1, /* divisor */
+			   0, /* remainder */
+			   0, NULL);
+	XFreePixmap(dpy, pixmap);
+
+	if (test->flags & SYNC) {
+		pixmap = XCreatePixmap(dpy, test->win, width, height, test->depth);
+		xcb_present_pixmap(XGetXCBConnection(dpy),
+				   test->win, pixmap,
+				   1, /* sbc */
+				   0, /* valid */
+				   0, /* update */
+				   0, /* x_off */
+				   0, /* y_off */
+				   crtc,
+				   None, /* wait fence */
+				   None, /* sync fence */
+				   XCB_PRESENT_OPTION_NONE,
+				   1, /* target msc */
+				   1, /* divisor */
+				   0, /* remainder */
+				   0, NULL);
+		XFreePixmap(dpy, pixmap);
+		XFlush(dpy);
+		err += !!xshmfence_await(test->fence.addr);
+	}
+
+	test->msc = check_msc(dpy, test->win, test->queue, test->msc);
+	return err;
+}
+
+static int test_crtc(Display *dpy, void *queue, uint64_t last_msc)
+{
+	struct test_crtc test;
+	int err = 0;
+
+	XSync(dpy, True);
+	_x_error_occurred = 0;
+
+	test.win = DefaultRootWindow(dpy);
+	test.depth = DefaultDepth(dpy, DefaultScreen(dpy));
+	if (dri3_create_fence(dpy, test.win, &test.fence))
+		return -1;
+	test.queue = queue;
+	test.msc = last_msc;
+
+	printf("Testing each crtc, without waiting for each flip\n");
+	test.flags = 0;
+	err += for_each_crtc(dpy, __test_crtc, &test);
+
+	printf("Testing each crtc, waiting for flips to complete\n");
+	test.flags = SYNC;
+	err += for_each_crtc(dpy, __test_crtc, &test);
+
+	test.msc = check_msc(dpy, test.win, test.queue, test.msc);
+	dri3_fence_free(dpy, &test.fence);
+
+	XSync(dpy, True);
+	err += !!_x_error_occurred;
+
+	if (err)
+		printf("%s: failures=%d\n", __func__, err);
+
+	return err;
+}
+
+static int
+can_use_shm(Display *dpy)
+{
+	int major, minor, has_pixmap;
+
+	if (!XShmQueryExtension(dpy))
+		return 0;
+
+	XShmQueryVersion(dpy, &major, &minor, &has_pixmap);
+	return has_pixmap;
+}
+
+static int test_shm(Display *dpy)
+{
+	Window win = DefaultRootWindow(dpy);
+	XShmSegmentInfo shm;
+	Pixmap pixmap;
+	Window root;
+	unsigned int width, height;
+	unsigned border, depth;
+	int x, y, ret = 1;
+
+	if (!can_use_shm(dpy))
+		return 0;
+
+	_x_error_occurred = 0;
+
+	XGetGeometry(dpy, win, &root, &x, &y,
+		     &width, &height, &border, &depth);
+
+	printf("Using %dx%d SHM\n", width, height);
+
+	shm.shmid = shmget(IPC_PRIVATE, height * 4*width, IPC_CREAT | 0666);
+	if (shm.shmid == -1)
+		return 0;
+
+	shm.shmaddr = shmat(shm.shmid, 0, 0);
+	if (shm.shmaddr == (char *) -1)
+		goto rmid;
+
+	shm.readOnly = False;
+	XShmAttach(dpy, &shm);
+
+	pixmap = XShmCreatePixmap(dpy, DefaultRootWindow(dpy),
+				  shm.shmaddr, &shm, width, height, 24);
+	if (_x_error_occurred)
+		goto detach;
+
+	xcb_present_pixmap(XGetXCBConnection(dpy),
+			   win, pixmap,
+			   0, /* sbc */
+			   0, /* valid */
+			   0, /* update */
+			   0, /* x_off */
+			   0, /* y_off */
+			   None,
+			   None, /* wait fence */
+			   None,
+			   XCB_PRESENT_OPTION_NONE,
+			   0, /* target msc */
+			   0, /* divisor */
+			   0, /* remainder */
+			   0, NULL);
+	XFreePixmap(dpy, pixmap);
+
+	XSync(dpy, True);
+	if (_x_error_occurred)
+		goto detach;
+
+	ret = 0;
+detach:
+	XShmDetach(dpy, &shm);
+	shmdt(shm.shmaddr);
+	XSync(dpy, False);
+rmid:
+	shmctl(shm.shmid, IPC_RMID, NULL);
+	return ret;
+}
+
+static uint32_t gem_create(int fd, int size)
+{
+	struct drm_i915_gem_create create;
+
+	create.handle = 0;
+	create.size = size;
+	(void)drmIoctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create);
+
+	return create.handle;
+}
+
+struct local_i915_gem_caching {
+	uint32_t handle;
+	uint32_t caching;
+};
+
+#define LOCAL_I915_GEM_SET_CACHING	0x2f
+#define LOCAL_IOCTL_I915_GEM_SET_CACHING DRM_IOW(DRM_COMMAND_BASE + LOCAL_I915_GEM_SET_CACHING, struct local_i915_gem_caching)
+
+static int gem_set_caching(int fd, uint32_t handle, int caching)
+{
+	struct local_i915_gem_caching arg;
+
+	arg.handle = handle;
+	arg.caching = caching;
+
+	return drmIoctl(fd, LOCAL_IOCTL_I915_GEM_SET_CACHING, &arg) == 0;
+}
+
+static int gem_export(int fd, uint32_t handle)
+{
+	struct drm_prime_handle args;
+
+	args.handle = handle;
+	args.flags = O_CLOEXEC;
+
+	if (drmIoctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args))
+		return -1;
+
+	return args.fd;
+}
+
+static void gem_close(int fd, uint32_t handle)
+{
+	struct drm_gem_close close;
+
+	close.handle = handle;
+	(void)drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &close);
+}
+
+static int test_dri3(Display *dpy)
+{
+	Window win = DefaultRootWindow(dpy);
+	Pixmap pixmap;
+	Window root;
+	unsigned int width, height;
+	unsigned border, depth;
+	unsigned stride, size;
+	int x, y, ret = 1;
+	int device, handle;
+	int bpp;
+
+	device = dri3_open(dpy);
+	if (device < 0)
+		return 0;
+
+	if (!is_intel(device))
+		return 0;
+
+	printf("Opened Intel DRI3 device\n");
+
+	XGetGeometry(dpy, win, &root, &x, &y,
+		     &width, &height, &border, &depth);
+
+	switch (depth) {
+	case 8: bpp = 8; break;
+	case 15: case 16: bpp = 16; break;
+	case 24: case 32: bpp = 32; break;
+	default: return 0;
+	}
+
+	stride = width * bpp/8;
+	size = PAGE_ALIGN(stride * height);
+	printf("Creating DRI3 %dx%d (source stride=%d, size=%d) for GTT\n",
+	       width, height, stride, size);
+
+	pixmap = 0;
+	handle = gem_create(device, size);
+	if (handle) {
+		pixmap = dri3_create_pixmap(dpy, root,
+					     width, height, depth,
+					     gem_export(device, handle), bpp, stride, size);
+		gem_close(device, handle);
+	}
+	if (pixmap == 0)
+		goto fail;
+
+	xcb_present_pixmap(XGetXCBConnection(dpy),
+			   win, pixmap,
+			   0, /* sbc */
+			   0, /* valid */
+			   0, /* update */
+			   0, /* x_off */
+			   0, /* y_off */
+			   None,
+			   None, /* wait fence */
+			   None,
+			   XCB_PRESENT_OPTION_NONE,
+			   0, /* target msc */
+			   0, /* divisor */
+			   0, /* remainder */
+			   0, NULL);
+	XFreePixmap(dpy, pixmap);
+
+	XSync(dpy, True);
+	if (_x_error_occurred)
+		goto fail;
+
+	printf("Creating DRI3 %dx%d (source stride=%d, size=%d) for CPU\n",
+	       width, height, stride, size);
+
+	pixmap = 0;
+	handle = gem_create(device, size);
+	if (handle) {
+		gem_set_caching(device, handle, CPU);
+		handle = dri3_create_pixmap(dpy, root,
+					     width, height, depth,
+					     gem_export(device, handle), bpp, stride, size);
+		gem_close(device, handle);
+	}
+	if (pixmap == 0)
+		goto fail;
+
+	xcb_present_pixmap(XGetXCBConnection(dpy),
+			   win, pixmap,
+			   0, /* sbc */
+			   0, /* valid */
+			   0, /* update */
+			   0, /* x_off */
+			   0, /* y_off */
+			   None,
+			   None, /* wait fence */
+			   None,
+			   XCB_PRESENT_OPTION_NONE,
+			   0, /* target msc */
+			   0, /* divisor */
+			   0, /* remainder */
+			   0, NULL);
+	XFreePixmap(dpy, pixmap);
+
+	XSync(dpy, True);
+	if (_x_error_occurred)
+		goto fail;
+
+	ret = 0;
+fail:
+	close(device);
+	return ret;
+}
+
+static int has_present(Display *dpy)
+{
+	xcb_connection_t *c = XGetXCBConnection(dpy);
+	xcb_present_query_version_reply_t *reply;
+	xcb_generic_error_t *error = NULL;
+
+	reply = xcb_present_query_version_reply(c,
+						xcb_present_query_version(c,
+									  XCB_PRESENT_MAJOR_VERSION,
+									  XCB_PRESENT_MINOR_VERSION),
+						&error);
+
+	free(reply);
+	free(error);
+
+	return reply != NULL;
+}
+
+int main(void)
+{
+	Display *dpy;
+	Window root;
+	int error = 0;
+	uint64_t last_msc;
+	void *queue;
+
+	dpy = XOpenDisplay(NULL);
+	if (dpy == NULL)
+		return 77;
+
+	if (!has_present(dpy))
+		return 77;
+
+	root = DefaultRootWindow(dpy);
+
+	signal(SIGALRM, SIG_IGN);
+	XSetErrorHandler(_check_error_handler);
+
+	queue = setup_msc(dpy, root);
+	last_msc = check_msc(dpy, root, queue, 0);
+
+	error += test_whole(dpy);
+	last_msc = check_msc(dpy, root, queue, last_msc);
+
+	error += test_crtc(dpy, queue, last_msc);
+	last_msc = check_msc(dpy, root, queue, last_msc);
+
+	error += test_shm(dpy);
+	last_msc = check_msc(dpy, root, queue, last_msc);
+
+	error += test_dri3(dpy);
+	last_msc = check_msc(dpy, root, queue, last_msc);
+
+	teardown_msc(dpy, queue);
+
+	return !!error;
+}
commit 6ab6734369fbd902a23109f4c3626df9d529891c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri May 9 11:08:15 2014 +0100

    Add rudimentary tests for DRI3
    
    This is a simple little test to create a pixmap from a local bo, copy it
    to a normal pixmap, then read it back by importing it into anther local
    bo. It tests the fundamental mechanisms of opening a DRI3 render device,
    importing into pixmaps, exporting into /buffers and a read-barrier.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/configure.ac b/configure.ac
index ec237c4..e89e5a3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -239,6 +239,9 @@ if test "x$shm" = "xyes"; then
 		  AC_MSG_RESULT(assuming no))
 fi
 
+PKG_CHECK_MODULES(X11_DRI3, [xcb-dri3 xcb-sync x11-xcb xshmfence x11 xrender xext libdrm], [x11_dri3="yes"], [x11_dri3="no"])
+AM_CONDITIONAL(X11_DRI3, test "x$x11_dri3" = "xyes" -a "x$shm" = "xyes")
+
 AC_ARG_ENABLE(tools,
               AS_HELP_STRING([--disable-tools],
 			     [Enable building and installing the miscellaneous tools [default=auto]]),
diff --git a/test/.gitignore b/test/.gitignore
index 979d896..8e6b1bc 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -22,3 +22,4 @@ vsync.avi
 dri2-race
 dri2-swap
 dri2-test
+dri3-test
diff --git a/test/Makefile.am b/test/Makefile.am
index df022e2..7a8916c 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -30,6 +30,11 @@ stress_TESTS += \
 	$(NULL)
 endif
 
+if X11_DRI3
+stress_TESTS += \
+	dri3-test \
+	$(NULL)
+endif
 check_PROGRAMS = $(stress_TESTS)
 
 noinst_PROGRAMS = lowlevel-blt-bench
@@ -53,6 +58,15 @@ libtest_la_SOURCES += \
 	$(NULL)
 endif
 
+if X11_DRI3
+libtest_la_SOURCES += \
+	dri3.c \
+	dri3.h \
+	$(NULL)
+AM_CFLAGS += $(X11_DRI3_CFLAGS)
+LDADD += $(X11_DRI3_LIBS)
+endif
+
 vsync.avi: mkvsync.sh
 	./mkvsync.sh $@
 
diff --git a/test/dri3-test.c b/test/dri3-test.c
new file mode 100644
index 0000000..3fe011a
--- /dev/null
+++ b/test/dri3-test.c
@@ -0,0 +1,1100 @@
+/*
+ * Copyright (c) 2014 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xlibint.h>
+#include <X11/extensions/Xrender.h>
+#include <X11/extensions/XShm.h>
+#if HAVE_X11_EXTENSIONS_SHMPROTO_H
+#include <X11/extensions/shmproto.h>
+#elif HAVE_X11_EXTENSIONS_SHMSTR_H
+#include <X11/extensions/shmstr.h>
+#else
+#error Failed to find the right header for X11 MIT-SHM protocol definitions
+#endif
+#include <xf86drm.h>
+#include <i915_drm.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <sys/mman.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <pciaccess.h>
+
+#include "dri3.h"
+#include "../src/i915_pciids.h"
+
+#define ALIGN(x, y) (((x) + (y) - 1) & -(y))
+#define PAGE_ALIGN(x) ALIGN(x, 4096)
+
+#define GTT I915_GEM_DOMAIN_GTT
+#define CPU I915_GEM_DOMAIN_CPU
+
+static int _x_error_occurred;
+
+static const struct pci_id_match ids[] = {
+	INTEL_I830_IDS(020),
+	INTEL_I845G_IDS(021),
+	INTEL_I85X_IDS(022),
+	INTEL_I865G_IDS(023),
+
+	INTEL_I915G_IDS(030),
+	INTEL_I915GM_IDS(030),
+	INTEL_I945G_IDS(031),
+	INTEL_I945GM_IDS(031),
+
+	INTEL_G33_IDS(033),
+	INTEL_PINEVIEW_IDS(033),
+
+	INTEL_I965G_IDS(040),
+	INTEL_I965GM_IDS(040),
+
+	INTEL_G45_IDS(045),
+	INTEL_GM45_IDS(045),
+
+	INTEL_IRONLAKE_D_IDS(050),
+	INTEL_IRONLAKE_M_IDS(050),
+
+	INTEL_SNB_D_IDS(060),
+	INTEL_SNB_M_IDS(060),
+
+	INTEL_IVB_D_IDS(070),
+	INTEL_IVB_M_IDS(070),
+
+	INTEL_HSW_D_IDS(075),
+	INTEL_HSW_M_IDS(075),
+
+	INTEL_VLV_D_IDS(071),
+	INTEL_VLV_M_IDS(071),
+
+	INTEL_BDW_D_IDS(0100),
+	INTEL_BDW_M_IDS(0100),
+};
+
+static int i915_gen(int device)
+{
+	struct drm_i915_getparam gp;
+	int devid = 0;
+	int n;
+
+	gp.param = I915_PARAM_CHIPSET_ID;
+	gp.value = &devid;
+
+	if (drmIoctl(device, DRM_IOCTL_I915_GETPARAM, &gp))
+		return 0;
+
+	for (n = 0; n < sizeof(ids)/sizeof(ids[0]); n++) {
+		if (devid == ids[n].device_id)
+			return ids[n].match_data;
+	}
+
+	return 0;
+}
+
+static int is_i915_device(int fd)
+{
+	drm_version_t version;
+	char name[5] = "";
+
+	memset(&version, 0, sizeof(version));
+	version.name_len = 4;
+	version.name = name;
+
+	if (drmIoctl(fd, DRM_IOCTL_VERSION, &version))
+		return 0;
+
+	return strcmp("i915", name) == 0;
+}
+
+static int is_intel(int fd)
+{
+	struct drm_i915_getparam gp;
+	int ret;
+
+	/* Confirm that this is a i915.ko device with GEM/KMS enabled */
+	ret = is_i915_device(fd);
+	if (ret) {
+		gp.param = I915_PARAM_HAS_GEM;
+		gp.value = &ret;
+		if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp))
+			ret = 0;
+	}
+	return ret;
+}
+
+static uint32_t gem_create(int fd, int size)
+{
+	struct drm_i915_gem_create create;
+
+	create.handle = 0;
+	create.size = size;
+	(void)drmIoctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create);
+
+	return create.handle;
+}
+
+struct local_i915_gem_caching {
+	uint32_t handle;
+	uint32_t caching;
+};
+
+#define LOCAL_I915_GEM_SET_CACHING	0x2f
+#define LOCAL_IOCTL_I915_GEM_SET_CACHING DRM_IOW(DRM_COMMAND_BASE + LOCAL_I915_GEM_SET_CACHING, struct local_i915_gem_caching)
+
+static int gem_set_caching(int fd, uint32_t handle, int caching)
+{
+	struct local_i915_gem_caching arg;
+
+	arg.handle = handle;
+	arg.caching = caching;
+
+	return drmIoctl(fd, LOCAL_IOCTL_I915_GEM_SET_CACHING, &arg) == 0;
+}
+
+static int gem_export(int fd, uint32_t handle)
+{
+	struct drm_prime_handle args;
+
+	args.handle = handle;
+	args.flags = O_CLOEXEC;
+
+	if (drmIoctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args))
+		return -1;
+
+	return args.fd;
+}
+
+static uint32_t gem_import(int fd, int name)
+{
+	struct drm_prime_handle args;
+
+	args.fd = name;
+	args.flags = 0;
+	if (drmIoctl(fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &args))
+		return 0;
+
+	return args.handle;
+}
+
+static int gem_write(int fd, uint32_t handle, int offset, void *data, int len)
+{
+	struct drm_i915_gem_pwrite gem_pwrite;
+
+	gem_pwrite.handle = handle;
+	gem_pwrite.offset = offset;
+	gem_pwrite.size = len;
+	gem_pwrite.data_ptr = (uintptr_t)data;
+	return drmIoctl(fd, DRM_IOCTL_I915_GEM_PWRITE, &gem_pwrite);
+}
+
+static void *gem_mmap(int fd, uint32_t handle, int size, unsigned prot, int domain)
+{
+	struct drm_i915_gem_set_domain set_domain;
+	void *ptr;
+
+	if (domain == CPU) {
+		struct drm_i915_gem_mmap mmap_arg;
+
+		mmap_arg.handle = handle;
+		mmap_arg.offset = 0;
+		mmap_arg.size = size;
+		if (drmIoctl(fd, DRM_IOCTL_I915_GEM_MMAP, &mmap_arg))
+			return NULL;
+
+		ptr = (void *)(uintptr_t)mmap_arg.addr_ptr;
+	} else {
+		struct drm_i915_gem_mmap_gtt mmap_arg;
+
+		mmap_arg.handle = handle;
+		if (drmIoctl(fd, DRM_IOCTL_I915_GEM_MMAP_GTT, &mmap_arg))
+			return NULL;
+
+		ptr = mmap(0, size, prot, MAP_SHARED, fd, mmap_arg.offset);
+		if (ptr == MAP_FAILED)
+			return NULL;
+	}
+
+	set_domain.handle = handle;
+	set_domain.read_domains = domain;
+	set_domain.write_domain = prot & PROT_WRITE ? set_domain.read_domains : 0;
+	if (drmIoctl(fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain)) {
+		munmap(ptr, size);
+		return NULL;
+	}
+
+	return ptr;
+}
+
+static void gem_sync(int fd, uint32_t handle, int read)
+{
+	struct drm_i915_gem_set_domain set_domain;
+
+	set_domain.handle = handle;
+	set_domain.read_domains = read;
+	set_domain.write_domain = 0;
+	drmIoctl(fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain);
+}
+
+static int gem_get_tiling(int fd, uint32_t handle)
+{
+	struct drm_i915_gem_get_tiling tiling;
+
+	tiling.handle = handle;
+	tiling.tiling_mode = -1;
+	(void)drmIoctl(fd, DRM_IOCTL_I915_GEM_GET_TILING, &tiling);
+	return tiling.tiling_mode;
+}
+
+static void gem_close(int fd, uint32_t handle)
+{
+	struct drm_gem_close close;
+
+	close.handle = handle;
+	(void)drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &close);
+}
+
+static void gem_fill(int fd, uint32_t handle, uint32_t pixel, uint32_t size, int domain)
+{
+	uint32_t *ptr, s;
+
+	ptr = gem_mmap(fd, handle, size, PROT_READ | PROT_WRITE, domain);
+	if (ptr == NULL)
+		return;
+
+	for (s = 0; s < size; s += 4)
+		ptr[s/4] = pixel;
+	munmap(ptr, size);
+}
+
+static int check_pixmap(Display *dpy, Pixmap pix,
+			int x, int y, uint32_t expected, int bpp)
+{
+	XImage *image;
+	int w = 32 / bpp;
+
+	image = XGetImage(dpy, pix, x - (x % w), y, w, 1, AllPlanes, ZPixmap);
+	if (image == NULL)
+		return 0;
+
+	if (*(uint32_t *)image->data != expected) {
+		printf("pixmap[%d, %d]:%d = %08x\n", x, y, bpp, *(uint32_t *)image->data);
+		return 0;
+	}
+	XDestroyImage(image);
+
+	return 1;
+}
+
+static int check_pixel(int fd, uint32_t handle, uint32_t stride, uint32_t size,
+		       int x, int y, uint32_t expected, int bpp, int domain)
+{
+	uint32_t *ptr;
+	int w = 32 / bpp;
+
+	assert((stride & 3) == 0);
+
+	ptr = gem_mmap(fd, handle, size, PROT_READ, domain);
+	if (ptr == NULL)
+		return 0;
+
+	if (ptr[(y*stride + x - (x % w))/4] != expected) {
+		printf("pixel[%d, %d]:%d = %08x\n", x, y, bpp, ptr[(y * stride + x)/4]);
+		return 0;
+	}
+	munmap(ptr, size);
+
+	return 1;
+}
+
+static GC get_gc(Display *dpy, Drawable d, int depth)
+{
+	static GC gc[33];
+	if (gc[depth] == NULL) {
+		XGCValues gcv;
+
+		gcv.graphics_exposures = False;
+		gc[depth] = XCreateGC(dpy, d, GCGraphicsExposures, &gcv);
+	}
+	return gc[depth];
+}
+
+static int
+can_use_shm(Display *dpy)
+{
+	int major, minor, has_pixmap;
+
+	if (!XShmQueryExtension(dpy))
+		return 0;
+
+	XShmQueryVersion(dpy, &major, &minor, &has_pixmap);
+	return has_pixmap;
+}
+
+static int gpu_fill(int device, int handle, int width, int height, int pitch, int bpp, int tiling, uint32_t pixel)
+{
+	struct drm_i915_gem_execbuffer2 execbuf;
+	struct drm_i915_gem_relocation_entry gem_reloc[2];
+	struct drm_i915_gem_exec_object2 gem_exec[2];
+	uint32_t batch[10];
+	int gen = i915_gen(device);
+	int len = 0;
+	int ret;
+
+	if (gen == 0)
+		return -ENODEV;
+
+	batch[0] = 2 << 29 | 0x50 << 22;
+	batch[0] |= (gen >= 0100 ? 5 : 4);
+	batch[1] = pitch;
+	if (gen >= 040 && tiling) {
+		batch[0] |= 1 << 11;
+		batch[1] >>= 2;
+	}
+
+	batch[1] |= 0xf0 << 16;
+	switch (bpp) {
+	default: assert(0);
+	case 32: batch[0] |= 1 << 21 | 1 << 20;
+		 batch[1] |= 1 << 25; /* RGB8888 */
+	case 16: batch[1] |= 1 << 24; /* RGB565 */
+	case 8: break;
+	}
+
+	batch[2] = 0;
+	batch[3] = height << 16 | width;
+	batch[4] = 0;
+	len = 5;
+	if (gen >= 0100)
+		batch[len++] = 0;
+	batch[len++] = pixel;
+	batch[len++] = 0xA << 23;
+	if (len & 1)
+		len++;
+
+	gem_reloc[0].offset = 4 * sizeof(uint32_t);
+	gem_reloc[0].delta = 0;
+	gem_reloc[0].target_handle = handle;
+	gem_reloc[0].read_domains = I915_GEM_DOMAIN_RENDER;
+	gem_reloc[0].write_domain = I915_GEM_DOMAIN_RENDER;
+	gem_reloc[0].presumed_offset = 0;
+
+	memset(gem_exec, 0, sizeof(gem_exec));
+	gem_exec[0].handle = handle;
+	gem_exec[1].handle = gem_create(device, 4096);
+	gem_exec[1].relocation_count = 1;
+	gem_exec[1].relocs_ptr = (uintptr_t)gem_reloc;
+
+	memset(&execbuf, 0, sizeof(execbuf));
+	execbuf.buffers_ptr = (uintptr_t)gem_exec;
+	execbuf.buffer_count = 2;
+	execbuf.batch_len = len * sizeof(uint32_t);
+	execbuf.flags = gen >= 060 ? I915_EXEC_BLT : 0;
+
+	ret = gem_write(device, gem_exec[1].handle, 0, batch, execbuf.batch_len);
+	if (ret == 0)
+		ret = drmIoctl(device, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf);
+	if (ret < 0)
+		ret = -errno;
+
+	gem_close(device, gem_exec[1].handle);
+	return ret;
+}
+
+static int test_shm(Display *dpy, int device,
+		    int width, int height)
+{
+	const int x_loc[] = {0, width/2, width-1};
+	const int y_loc[] = {0, height/2, height-1};
+	uint32_t pixel = 0xffff00ff;
+	XShmSegmentInfo shm;
+	Pixmap pixmap;
+	uint32_t handle = 0;
+	uint32_t *ptr;
+	int stride, fd;
+	int x, y;
+
+	if (!can_use_shm(dpy))
+		return 0;
+
+	printf("Creating %dx%d SHM pixmap\n", width, height);
+	_x_error_occurred = 0;
+
+	shm.shmid = shmget(IPC_PRIVATE, height * 4*width, IPC_CREAT | 0666);
+	if (shm.shmid == -1)
+		return 0;
+
+	shm.shmaddr = shmat(shm.shmid, 0, 0);
+	if (shm.shmaddr == (char *) -1) {
+		shmctl(shm.shmid, IPC_RMID, NULL);
+		return 0;
+	}
+
+	shm.readOnly = False;
+	XShmAttach(dpy, &shm);
+
+	pixmap = XShmCreatePixmap(dpy, DefaultRootWindow(dpy),
+				  shm.shmaddr, &shm, width, height, 24);
+	XSync(dpy, False);
+	shmctl(shm.shmid, IPC_RMID, NULL);
+
+	if (_x_error_occurred) {
+		XShmDetach(dpy, &shm);
+		shmdt(shm.shmaddr);
+		return 0;
+	}
+
+	printf("Testing write of %dx%d SHM pixmap via DRI3 fd\n", width, height);
+
+	fd = dri3_create_fd(dpy, pixmap, &stride);
+	if (fd < 0)
+		goto fail;
+
+	handle = gem_import(device, fd);
+	close(fd);
+	if (handle == 0)
+		goto fail;
+
+	if (gpu_fill(device, handle, width, height, stride, 32, I915_TILING_NONE, pixel))
+		goto fail;
+
+	gem_sync(device, handle, CPU);
+	ptr = (uint32_t *)shm.shmaddr;
+	for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+		for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+			if (ptr[y_loc[y]*width + x_loc[x]] != pixel) {
+				printf("pixel[%d, %d]:%d = %08x\n", x, y, 32, ptr[y_loc[y] * width + x_loc[x]]);
+				goto fail;
+			}
+
+	for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+		for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+			if (!check_pixmap(dpy, pixmap,
+					  x_loc[x], y_loc[y],
+					  pixel, 32))
+				goto fail;
+
+	if (_x_error_occurred)
+		goto fail;
+
+out:
+	gem_close(device, handle);
+	XFreePixmap(dpy, pixmap);
+	XShmDetach(dpy, &shm);
+	shmdt(shm.shmaddr);
+	return fd != -1;
+
+fail:
+	printf("%s failed at (%dx%d)\n",
+	       __func__, width, height);
+	fd = -1;
+	goto out;
+}
+
+static int test_read_after_write(Display *dpy, int device,
+				 int width, int height, int depth,
+				 int domain)
+{
+	const uint32_t pixel = 0xffff00ff;
+	const int x_loc[] = {0, width/2, width-1};
+	const int y_loc[] = {0, height/2, height-1};
+	Window root = RootWindow(dpy, DefaultScreen(dpy));
+	uint32_t src, dst;
+	int src_fd, dst_fd;
+	int src_stride, src_size;
+	int dst_stride, dst_size;
+	Pixmap src_pix, dst_pix;
+	struct dri3_fence fence;
+	int x, y, bpp;
+
+	_x_error_occurred = 0;
+
+	switch (depth) {
+	case 8: bpp = 8; break;
+	case 16: bpp = 16; break;
+	case 24: bpp = 32; break;
+	case 32: bpp = 32; break;
+	default: return 0;
+	}
+
+	src_stride = width * bpp/8;
+	src_size = PAGE_ALIGN(src_stride * height);
+	printf("Creating %dx%d (source stride=%d, size=%d, domain=%d)\n",
+	       width, height, src_stride, src_size, domain);
+
+	src = gem_create(device, src_size);
+	if (!src)
+		goto fail;
+
+	if (domain == CPU)
+		gem_set_caching(device, src, 1);
+
+	gem_fill(device, src, pixel, src_size, domain);
+
+	src_fd = gem_export(device, src);
+	if (src_fd < 0)
+		goto fail;
+
+	src_pix = dri3_create_pixmap(dpy, root,
+				     width, height, depth,
+				     src_fd, bpp, src_stride, src_size);
+
+	for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+		for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+			if (!check_pixmap(dpy, src_pix,
+					  x_loc[x], y_loc[y],
+					  pixel, bpp))
+				goto fail;
+	close(src_fd);
+
+	dst_pix = XCreatePixmap(dpy, root, width, height, depth);
+	if (dri3_create_fence(dpy, dst_pix, &fence))
+		goto fail;
+
+	dst_fd = dri3_create_fd(dpy, dst_pix, &dst_stride);
+	if (dst_fd < 0)
+		goto fail;
+	dst_size = lseek(dst_fd, 0, SEEK_END);
+	printf("Comparing %dx%d (destination stride=%d, size=%d)\n",
+	       width, height, dst_stride, dst_size);
+	dst = gem_import(device, dst_fd);
+	if (dst == 0)
+		goto fail;
+	close(dst_fd);
+
+	XCopyArea(dpy, src_pix, dst_pix,
+		  get_gc(dpy, dst_pix, depth),
+		  0, 0, width, height, 0, 0);
+	dri3_fence_sync(dpy, &fence);
+	dri3_fence_free(dpy, &fence);
+
+	for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+		for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+			if (!check_pixel(device, dst, dst_stride, dst_size,
+					 x_loc[x], y_loc[y],
+					 pixel, bpp, GTT))
+				goto fail;
+
+	XFreePixmap(dpy, dst_pix);
+	XFreePixmap(dpy, src_pix);
+
+	gem_close(device, src);
+	gem_close(device, dst);
+
+	if (_x_error_occurred)
+		goto fail;
+
+	return 0;
+
+fail:
+	printf("%s failed at (%dx%d), depth=%d, domain=%d\n",
+	       __func__, width, height, depth, domain);
+	return 1;
+}
+
+static XRenderPictFormat *format_for_depth(Display *dpy, int depth)
+{
+	switch (depth) {
+	case 8: return XRenderFindStandardFormat(dpy, PictStandardA8);
+	case 24: return XRenderFindStandardFormat(dpy, PictStandardRGB24);
+	case 32: return XRenderFindStandardFormat(dpy, PictStandardARGB32);
+	default: assert(0); return NULL;
+	}
+}
+
+static int test_read(Display *dpy, int device,
+		     int width, int height,
+		     int domain)
+{
+	const uint32_t pixel = 0xffff00ff;
+	const XRenderColor color = { 0xffff, 0x0000, 0xffff, 0xffff };
+	const int x_loc[] = {0, width/2, width-1};
+	const int y_loc[] = {0, height/2, height-1};
+	Window root = RootWindow(dpy, DefaultScreen(dpy));
+	uint32_t dst;
+	int dst_stride, dst_size, dst_fd;
+	Pixmap src_pix, dst_pix;
+	Picture src_pic;
+	struct dri3_fence fence;
+	int depth = 32, bpp = 32;
+	int x, y;
+
+	_x_error_occurred = 0;
+
+	dst_stride = width * bpp/8;
+	dst_size = PAGE_ALIGN(dst_stride * height);
+	printf("Creating %dx%d (destination stride=%d, size=%d, domain=%d)\n",
+	       width, height, dst_stride, dst_size, domain);
+
+	dst = gem_create(device, dst_size);
+	if (!dst)
+		goto fail;
+
+	if (domain == CPU)
+		gem_set_caching(device, dst, 1);
+
+	gem_fill(device, dst, ~pixel, dst_size, domain);
+
+	dst_fd = gem_export(device, dst);
+	if (dst_fd < 0)
+		goto fail;
+
+	dst_pix = dri3_create_pixmap(dpy, root,
+				     width, height, depth,
+				     dst_fd, bpp, dst_stride, dst_size);
+	XSync(dpy, True);
+	if (_x_error_occurred)
+		goto fail;
+	if (dri3_create_fence(dpy, dst_pix, &fence))
+		goto fail;
+
+	src_pix = XCreatePixmap(dpy, root, width, height, depth);
+	src_pic = XRenderCreatePicture(dpy, src_pix, format_for_depth(dpy, depth), 0, NULL);
+	XRenderFillRectangle(dpy, PictOpSrc, src_pic, &color, 0, 0, width, height);
+	XCopyArea(dpy, src_pix, dst_pix,
+		  get_gc(dpy, dst_pix, depth),
+		  0, 0, width, height, 0, 0);
+	dri3_fence_sync(dpy, &fence);
+	dri3_fence_free(dpy, &fence);
+
+	for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+		for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+			if (!check_pixel(device, dst, dst_stride, dst_size,
+					 x_loc[x], y_loc[y],
+					 pixel, bpp, domain))
+				goto fail;
+
+	XFreePixmap(dpy, dst_pix);
+	XRenderFreePicture(dpy, src_pic);
+	XFreePixmap(dpy, src_pix);
+
+	gem_close(device, dst);
+
+	if (_x_error_occurred)
+		goto fail;
+
+	return 0;
+
+fail:
+	printf("%s failed at (%dx%d), depth=%d, domain=%d\n",
+	       __func__, width, height, depth, domain);
+	return 1;
+}
+
+static int test_dup_pixmap(Display *dpy, int device)
+{
+	const uint32_t pixel = 0xffff00ff;
+	const XRenderColor color = { 0xffff, 0x0000, 0xffff, 0xffff };
+	const XRenderColor inverse = { 0, 0xffff, 0, 0 };
+	int width = 400, height = 400;
+	const int x_loc[] = {0, width/2, width-1};
+	const int y_loc[] = {0, height/2, height-1};
+	Window root = RootWindow(dpy, DefaultScreen(dpy));
+	uint32_t handle;
+	int stride, size, fd;
+	Pixmap src_pix, dst_pix;
+	Picture src_pic, dst_pic;
+	struct dri3_fence fence;
+	int depth = 32, bpp = 32;
+	int x, y;
+
+	_x_error_occurred = 0;
+
+	printf("%s: Creating %dx%d pixmap\n", __func__, width, height);
+	src_pix = XCreatePixmap(dpy, root, width, height, depth);
+	src_pic = XRenderCreatePicture(dpy, src_pix, format_for_depth(dpy, depth), 0, NULL);
+	fd = dri3_create_fd(dpy, src_pix, &stride);
+	if (fd < 0)
+		goto fail;
+
+	size = lseek(fd, 0, SEEK_END);
+	handle = gem_import(device, fd);
+
+	printf("%s: Creating duplicate from pixmap exported fd\n", __func__);
+	dst_pix = dri3_create_pixmap(dpy, root,
+				     width, height, depth,
+				     fd, bpp, stride, size);
+	dst_pic = XRenderCreatePicture(dpy, dst_pix, format_for_depth(dpy, depth), 0, NULL);
+	XSync(dpy, True);
+	if (_x_error_occurred)
+		goto fail;
+
+	printf("%s: Filling src with %08x, reading dst\n", __func__, pixel);
+	XRenderFillRectangle(dpy, PictOpSrc, src_pic, &color, 0, 0, width, height);
+	for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+		for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+			if (!check_pixmap(dpy, dst_pix,
+					  x_loc[x], y_loc[y],
+					  pixel, 32))
+				goto fail;
+
+	printf("%s: Filling dst with %08x, reading src\n", __func__, ~pixel);
+	XRenderFillRectangle(dpy, PictOpSrc, dst_pic, &inverse, 0, 0, width, height);
+	for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+		for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+			if (!check_pixmap(dpy, dst_pix,
+					  x_loc[x], y_loc[y],
+					  ~pixel, 32))
+				goto fail;
+
+	if (dri3_create_fence(dpy, src_pix, &fence))
+		goto fail;
+
+	printf("%s: Filling src with %08x, reading fd\n", __func__, pixel);
+	XRenderFillRectangle(dpy, PictOpSrc, src_pic, &color, 0, 0, width, height);
+	dri3_fence_sync(dpy, &fence);
+	dri3_fence_free(dpy, &fence);
+	for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+		for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+			if (!check_pixel(device, handle, stride, size,
+					 x_loc[x], y_loc[y],
+					 pixel, bpp, GTT))
+				goto fail;
+
+	printf("%s: Filling fd with %08x, reading src\n", __func__, ~pixel);
+	if (gpu_fill(device, handle, width, height, stride, 32, gem_get_tiling(device, handle), ~pixel))
+		goto fail;
+	for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+		for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+			if (!check_pixmap(dpy, src_pix,
+					  x_loc[x], y_loc[y],
+					  ~pixel, 32))
+				goto fail;
+
+	if (dri3_create_fence(dpy, dst_pix, &fence))
+		goto fail;
+
+	printf("%s: Filling dst with %08x, reading fd\n", __func__, pixel);
+	XRenderFillRectangle(dpy, PictOpSrc, dst_pic, &color, 0, 0, width, height);
+	dri3_fence_sync(dpy, &fence);
+	dri3_fence_free(dpy, &fence);
+	for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+		for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+			if (!check_pixel(device, handle, stride, size,
+					 x_loc[x], y_loc[y],
+					 pixel, bpp, GTT))
+				goto fail;
+
+	printf("%s: Filling fd with %08x, reading dst\n", __func__, ~pixel);
+	if (gpu_fill(device, handle, width, height, stride, 32, gem_get_tiling(device, handle), ~pixel))
+		goto fail;
+	for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+		for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+			if (!check_pixmap(dpy, dst_pix,
+					  x_loc[x], y_loc[y],
+					  ~pixel, 32))
+				goto fail;
+
+	XRenderFreePicture(dpy, src_pic);
+	XFreePixmap(dpy, src_pix);
+
+	if (dri3_create_fence(dpy, dst_pix, &fence))
+		goto fail;
+
+	printf("%s: Closed original src, filling dst with %08x, reading fd\n", __func__, pixel);
+	XRenderFillRectangle(dpy, PictOpSrc, dst_pic, &color, 0, 0, width, height);
+	dri3_fence_sync(dpy, &fence);
+	dri3_fence_free(dpy, &fence);
+	for (x = 0; x < sizeof(x_loc)/sizeof(x_loc[0]); x++)
+		for (y = 0; y < sizeof(y_loc)/sizeof(y_loc[0]); y++)
+			if (!check_pixel(device, handle, stride, size,
+					 x_loc[x], y_loc[y],
+					 pixel, bpp, GTT))
+				goto fail;
+
+	XRenderFreePicture(dpy, dst_pic);
+	XFreePixmap(dpy, dst_pix);
+
+	gem_close(device, handle);
+
+	if (_x_error_occurred)
+		goto fail;
+
+	return 0;
+
+fail:
+	printf("%s failed at (%dx%d), depth=%d\n",
+	       __func__, width, height, depth);
+	return 1;
+}
+
+static int test_bad_size(Display *dpy, int device)
+{
+	Window root = RootWindow(dpy, DefaultScreen(dpy));
+	uint32_t src;
+	int src_fd;
+	Pixmap src_pix;
+	int line = -1;
+
+	_x_error_occurred = 0;
+
+	src = gem_create(device, 4096);
+	if (!src)
+		goto fail;
+
+	src_fd = gem_export(device, src);
+	if (src_fd < 0)
+		goto fail;
+
+	src_pix = dri3_create_pixmap(dpy, root,
+				     16, 16, 32,
+				     dup(src_fd), 32, 16*4, 4096);
+	line = __LINE__;
+	XSync(dpy, True);
+	if (_x_error_occurred)
+		goto fail;
+	XFreePixmap(dpy, src_pix);
+	_x_error_occurred = 0;
+
+	src_pix = dri3_create_pixmap(dpy, root,
+				     32, 32, 32,
+				     dup(src_fd), 32, 32*4, 4096);
+	line = __LINE__;
+	XSync(dpy, True);
+	if (_x_error_occurred)
+		goto fail;
+	XFreePixmap(dpy, src_pix);
+	_x_error_occurred = 0;
+
+	src_pix = dri3_create_pixmap(dpy, root,
+				     64, 64, 32,
+				     dup(src_fd), 32, 64*4, 4096);
+	line = __LINE__;
+	XSync(dpy, True);
+	if (!_x_error_occurred)
+		goto fail;
+	_x_error_occurred = 0;
+
+	src_pix = dri3_create_pixmap(dpy, root,
+				     64, 64, 32,
+				     dup(src_fd), 32, 64*4, 64*64*4);
+	line = __LINE__;
+	XSync(dpy, True);
+	if (!_x_error_occurred)
+		goto fail;
+	_x_error_occurred = 0;
+
+	src_pix = dri3_create_pixmap(dpy, root,
+				     INT16_MAX, INT16_MAX, 8,
+				     dup(src_fd), 8, INT16_MAX, UINT32_MAX);
+	line = __LINE__;
+	XSync(dpy, True);
+	if (!_x_error_occurred)
+		goto fail;
+	_x_error_occurred = 0;
+
+	close(src_fd);
+	gem_close(device, src);
+
+	return 0;
+
+fail:
+	printf("%s failed at line %d\n", __func__, line);
+	return 1;
+}
+
+static int test_bad_pitch(Display *dpy, int device)
+{
+	Window root = RootWindow(dpy, DefaultScreen(dpy));
+	uint32_t src;
+	int src_fd;
+	Pixmap src_pix;
+	int line = -1;
+
+	_x_error_occurred = 0;
+
+	src = gem_create(device, 4096);
+	if (!src)
+		goto fail;
+
+	src_fd = gem_export(device, src);
+	if (src_fd < 0)
+		goto fail;
+
+	src_pix = dri3_create_pixmap(dpy, root,
+				     16, 16, 32,
+				     dup(src_fd), 32, 16*4, 4096);
+	line = __LINE__;
+	XSync(dpy, True);
+	if (_x_error_occurred)
+		goto fail;
+	XFreePixmap(dpy, src_pix);
+	_x_error_occurred = 0;
+
+	src_pix = dri3_create_pixmap(dpy, root,
+				     256, 2, 32,
+				     dup(src_fd), 32, 256*4, 4096);
+	line = __LINE__;
+	XSync(dpy, True);
+	if (_x_error_occurred)
+		goto fail;
+	XFreePixmap(dpy, src_pix);
+	_x_error_occurred = 0;
+
+	src_pix = dri3_create_pixmap(dpy, root,
+				     256, 2, 32,
+				     dup(src_fd), 32, 256, 4096);
+	line = __LINE__;
+	XSync(dpy, True);
+	if (!_x_error_occurred)
+		goto fail;
+	_x_error_occurred = 0;
+
+	src_pix = dri3_create_pixmap(dpy, root,
+				     256, 2, 32,
+				     dup(src_fd), 32, 16384, 4096);
+	line = __LINE__;
+	XSync(dpy, True);
+	if (!_x_error_occurred)
+		goto fail;
+	_x_error_occurred = 0;
+
+	src_pix = dri3_create_pixmap(dpy, root,
+				     256, 2, 32,
+				     dup(src_fd), 32, 1023, 4096);
+	line = __LINE__;
+	XSync(dpy, True);
+	if (!_x_error_occurred)
+		goto fail;
+	_x_error_occurred = 0;
+
+	src_pix = dri3_create_pixmap(dpy, root,
+				     256, 2, 32,
+				     dup(src_fd), 32, 1025, 4096);
+	line = __LINE__;
+	XSync(dpy, True);
+	if (!_x_error_occurred)
+		goto fail;
+	_x_error_occurred = 0;
+
+	close(src_fd);
+	gem_close(device, src);
+
+	return 0;
+
+fail:
+	printf("%s failed at line %d\n", __func__, line);
+	return 1;
+}
+
+static int
+_check_error_handler(Display     *display,
+		     XErrorEvent *event)
+{
+	printf("X11 error from display %s, serial=%ld, error=%d, req=%d.%d\n",
+	       DisplayString(display),
+	       event->serial,
+	       event->error_code,
+	       event->request_code,
+	       event->minor_code);
+	_x_error_occurred++;
+	return False; /* ignored */
+}
+
+int main(void)
+{
+	Display *dpy;
+	int device;
+	int error = 0;
+
+	dpy = XOpenDisplay(NULL);
+	if (dpy == NULL)
+		return 77;
+
+	if (DefaultDepth(dpy, DefaultScreen(dpy)) != 24)
+		return 77;
+
+	XSetErrorHandler(_check_error_handler);
+
+	device = dri3_open(dpy);
+	if (device < 0)
+		return 127;
+
+	if (!is_intel(device))
+		return 77;
+
+	printf("Opened Intel DRI3 device\n");
+
+	error += test_bad_size(dpy, device);
+	error += test_bad_pitch(dpy, device);
+
+	error += test_shm(dpy, device, 400, 300);
+	error += test_shm(dpy, device, 300, 400);
+
+	error += test_read(dpy, device, 400, 200, GTT);
+	error += test_read(dpy, device, 4000, 20, GTT);
+	error += test_read(dpy, device, 16000, 10, GTT);
+	error += test_read(dpy, device, 30000, 10, GTT);
+
+	error += test_read(dpy, device, 200, 400, GTT);
+	error += test_read(dpy, device, 20, 4000, GTT);
+	error += test_read(dpy, device, 16, 16000, GTT);
+	error += test_read(dpy, device, 16, 30000, GTT);
+
+	error += test_read(dpy, device, 400, 200, CPU);
+	error += test_read(dpy, device, 4000, 20, CPU);
+	error += test_read(dpy, device, 16000, 10, CPU);
+	error += test_read(dpy, device, 30000, 10, CPU);
+
+	error += test_read(dpy, device, 200, 400, CPU);
+	error += test_read(dpy, device, 20, 4000, CPU);
+	error += test_read(dpy, device, 16, 16000, CPU);
+	error += test_read(dpy, device, 16, 30000, CPU);
+
+	error += test_read_after_write(dpy, device, 400, 200, 24, GTT);
+	error += test_read_after_write(dpy, device, 4000, 20, 24, GTT);
+	error += test_read_after_write(dpy, device, 16000, 10, 24, GTT);
+	error += test_read_after_write(dpy, device, 30000, 10, 24, GTT);
+	error += test_read_after_write(dpy, device, 30000, 10, 8, GTT);
+
+	error += test_read_after_write(dpy, device, 200, 400, 24, GTT);
+	error += test_read_after_write(dpy, device, 20, 4000, 24, GTT);
+	error += test_read_after_write(dpy, device, 16, 16000, 24, GTT);
+	error += test_read_after_write(dpy, device, 16, 30000, 24, GTT);
+
+	error += test_read_after_write(dpy, device, 400, 200, 24, CPU);
+	error += test_read_after_write(dpy, device, 4000, 20, 24, CPU);
+	error += test_read_after_write(dpy, device, 16000, 10, 24, CPU);
+	error += test_read_after_write(dpy, device, 30000, 10, 24, CPU);
+	error += test_read_after_write(dpy, device, 30000, 10, 8, CPU);
+
+	error += test_read_after_write(dpy, device, 200, 400, 24, CPU);
+	error += test_read_after_write(dpy, device, 20, 4000, 24, CPU);
+	error += test_read_after_write(dpy, device, 16, 16000, 24, CPU);
+	error += test_read_after_write(dpy, device, 16, 30000, 24, CPU);
+
+	error += test_dup_pixmap(dpy, device);
+
+	return !!error;
+}
diff --git a/test/dri3.c b/test/dri3.c
new file mode 100644
index 0000000..9950331
--- /dev/null
+++ b/test/dri3.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2014 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#include <X11/Xlib.h>
+#include <X11/Xlib-xcb.h>
+#include <X11/xshmfence.h>
+#include <xcb/xcb.h>
+#include <xcb/dri3.h>
+#include <xcb/sync.h>
+#include <unistd.h>
+
+#include "dri3.h"
+
+Pixmap dri3_create_pixmap(Display *dpy,
+			  Drawable draw,
+			  int width, int height, int depth,
+			  int fd, int bpp, int stride, int size)
+{
+	xcb_connection_t *c = XGetXCBConnection(dpy);
+	xcb_pixmap_t pixmap = xcb_generate_id(c);
+	xcb_dri3_pixmap_from_buffer(c, pixmap, draw, size, width, height, stride, depth, bpp, fd);
+	return pixmap;
+}
+
+int dri3_create_fd(Display *dpy,
+		   Pixmap pixmap,
+		   int *stride)
+{
+	xcb_connection_t *c = XGetXCBConnection(dpy);
+	xcb_dri3_buffer_from_pixmap_cookie_t cookie;
+	xcb_dri3_buffer_from_pixmap_reply_t *reply;
+
+	cookie = xcb_dri3_buffer_from_pixmap(c, pixmap);
+	reply = xcb_dri3_buffer_from_pixmap_reply(c, cookie, NULL);
+	if (!reply)
+		return -1;
+
+	if (reply->nfd != 1)
+		return -1;
+
+	*stride = reply->stride;
+	return xcb_dri3_buffer_from_pixmap_reply_fds(c, reply)[0];
+}
+
+int dri3_create_fence(Display *dpy, Pixmap pixmap, struct dri3_fence *fence)
+{
+	xcb_connection_t *c = XGetXCBConnection(dpy);
+	struct dri3_fence f;
+	int fd;
+
+	fd = xshmfence_alloc_shm();
+	if (fd < 0)
+		return -1;
+
+	f.addr = xshmfence_map_shm(fd);
+	if (f.addr == NULL) {
+		close(fd);
+		return -1;
+	}
+
+	f.xid = xcb_generate_id(c);
+	xcb_dri3_fence_from_fd(c, pixmap, f.xid, 0, fd);
+
+	*fence = f;
+	return 0;
+}
+
+void dri3_fence_sync(Display *dpy, struct dri3_fence *fence)
+{
+	xcb_connection_t *c = XGetXCBConnection(dpy);
+
+	xshmfence_reset(fence->addr);
+
+	xcb_sync_trigger_fence(c, fence->xid);
+	xcb_flush(c);
+
+	xshmfence_await(fence->addr);
+}
+
+void dri3_fence_free(Display *dpy, struct dri3_fence *fence)
+{
+	xcb_connection_t *c = XGetXCBConnection(dpy);
+
+	xshmfence_unmap_shm(fence->addr);
+	xcb_sync_destroy_fence(c, fence->xid);
+}
+
+int dri3_open__full(Display *dpy, Window root, unsigned provider)
+{
+	xcb_connection_t *c = XGetXCBConnection(dpy);
+	xcb_dri3_open_cookie_t cookie;
+	xcb_dri3_open_reply_t *reply;
+
+	cookie = xcb_dri3_open(c, root, provider);
+	reply = xcb_dri3_open_reply(c, cookie, NULL);
+
+	if (!reply)
+		return -1;
+
+	if (reply->nfd != 1)
+		return -1;
+
+	return xcb_dri3_open_reply_fds(c, reply)[0];
+}
+
+int dri3_open(Display *dpy)
+{
+	return dri3_open__full(dpy, RootWindow(dpy, DefaultScreen(dpy)), None);
+}
diff --git a/test/dri3.h b/test/dri3.h
new file mode 100644
index 0000000..55d2943
--- /dev/null
+++ b/test/dri3.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#ifndef DRI3_H
+#define DRI3_H
+
+#include <X11/X.h>
+
+int dri3_open(Display *dpy);
+int dri3_open__full(Display *dpy, Window root, unsigned provider);
+
+Pixmap dri3_create_pixmap(Display *dpy,
+			  Drawable draw,
+			  int width, int height, int depth,
+			  int fd, int bpp, int stride, int size);
+int dri3_create_fd(Display *dpy,
+		   Pixmap pixmap,
+		   int *stride);
+
+struct dri3_fence {
+	XID xid;
+	void *addr;
+};
+
+int dri3_create_fence(Display *dpy, Pixmap pixmap, struct dri3_fence *out);
+void dri3_fence_sync(Display *dpy, struct dri3_fence *fence);
+void dri3_fence_free(Display *dpy, struct dri3_fence *fence);
+
+#endif /* DRI3_H */
commit d6240d197be1e752c0de26fbf84fc8fa8d55383c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed May 14 07:55:03 2014 +0100

    intel: Clear structs for valgrind
    
    When probing the module, clear structs passed into unknown ioctls to
    keep valgrind quiet.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/Makefile.am b/src/Makefile.am
index da75125..aa8b47e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -34,6 +34,10 @@ AM_CFLAGS = \
 	@NOWARNFLAGS@ \
 	$(NULL)
 
+if VALGRIND
+AM_CFLAGS += $(VALGRIND_CFLAGS)
+endif
+
 intel_drv_la_LTLIBRARIES = intel_drv.la
 intel_drv_la_LDFLAGS = -module -avoid-version
 intel_drv_ladir = $(moduledir)/drivers
diff --git a/src/intel_device.c b/src/intel_device.c
index 7140beb..c1c6d84 100644
--- a/src/intel_device.c
+++ b/src/intel_device.c
@@ -37,8 +37,6 @@
 #include <stdlib.h>
 #include <errno.h>
 
-#include <sys/ioctl.h>
-
 #include <pciaccess.h>
 
 #include <xorg-server.h>
@@ -52,6 +50,16 @@
 #include <xf86platformBus.h>
 #endif
 
+#ifdef HAVE_VALGRIND
+#include <valgrind.h>
+#include <memcheck.h>
+#define VG(x) x
+#else
+#define VG(x)
+#endif
+
+#define VG_CLEAR(s) VG(memset(&s, 0, sizeof(s)))
+
 #include "intel_driver.h"
 #include "fd.h"
 
@@ -70,11 +78,11 @@ static int __intel_get_device_id(int fd)
 	struct drm_i915_getparam gp;
 	int devid = 0;
 
-	memset(&gp, 0, sizeof(gp));
+	VG_CLEAR(gp);
 	gp.param = I915_PARAM_CHIPSET_ID;
 	gp.value = &devid;
 
-	if (ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp, sizeof(gp)))
+	if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp))
 		return 0;
 
 	return devid;
@@ -126,6 +134,7 @@ static int is_i915_gem(int fd)
 	if (ret) {
 		struct drm_i915_getparam gp;
 
+		VG_CLEAR(gp);
 		gp.param = I915_PARAM_HAS_GEM;
 		gp.value = &ret;
 
commit d27c948a4382c4024ce5e76f1ea82103fc8e9d66
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri May 9 13:58:37 2014 +0100

    intel: Add common routines and configure probing for DRI3
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/configure.ac b/configure.ac
index f9f3836..ec237c4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -276,7 +276,9 @@ AC_ARG_WITH(xorg-module-dir,
 
 AC_ARG_ENABLE(dri,
 	      AS_HELP_STRING([--disable-dri],
-			     [Disable DRI support [[default=auto]]]))
+			     [Disable DRI support [[default=auto]]]),
+	      [DRI=$enableval],
+	      [DRI=auto])
 
 AC_ARG_ENABLE(xvmc, AS_HELP_STRING([--disable-xvmc],
                                   [Disable XvMC support [[default=yes]]]),
@@ -445,13 +447,13 @@ fi
 
 
 DRI1=no
-if test "x$enable_dri" != "xno" -a "x$UMS" = "xyes"; then
-	PKG_CHECK_MODULES(DRI1, [xf86driproto], [DRI1="yes"], [DRI1="no"])
+if test "x$DRI" != "xno" -a "x$UMS" = "xyes"; then
+	PKG_CHECK_MODULES(DRI1, [xf86driproto], [DRI1=$DRI], [DRI1=no])
         save_CFLAGS="$CFLAGS"
         save_CPPFLAGS="$CPPFLAGS"
         CFLAGS="$CFLAGS $XORG_CFLAGS $DRI1_CFLAGS $DRM_CFLAGS"
         CPPFLAGS="$CPPFLAGS $XORG_CFLAGS $DRI1_CFLAGS $DRM_CFLAGS"
-        AC_CHECK_HEADERS([dri.h sarea.h dristruct.h], [DRI1="yes"], [DRI1="no"],
+        AC_CHECK_HEADERS([dri.h sarea.h dristruct.h], [], [DRI1=no],
                 [/* for dri.h */
                  #include <xf86str.h>
                  /* for dristruct.h */
@@ -463,7 +465,7 @@ if test "x$enable_dri" != "xno" -a "x$UMS" = "xyes"; then
                  # include <sarea.h>
                  #endif
                 ])
-        CFLAGS="$save_CFLAGS $DEBUGFLAGS"
+        CFLAGS="$save_CFLAGS"
         CPPFLAGS="$save_CPPFLAGS"
 fi
 
@@ -478,14 +480,15 @@ else
         DRI1_CFLAGS=""
         DRI1_LIBS=""
 
-        if test "x$enable_dri" = "xyes" -a "x$UMS" = "xyes"; then
+        if test "x$DRI" = "xyes" -a "x$UMS" = "xyes"; then
                 AC_MSG_ERROR([DRI1 requested but prerequisites not found])
         fi
 fi
 
 DRI2=no
-if test "x$enable_dri" != "xno"; then
-	PKG_CHECK_MODULES(DRI2, [dri2proto >= 2.6], DRI2="yes", DRI2="no")
+DRI3=no
+if test "x$DRI" != "xno"; then
+	PKG_CHECK_MODULES(DRI2, [dri2proto >= 2.6], [DRI2=$DRI], [DRI2=no])
 	dridriverdir=`$PKG_CONFIG --variable=dridriverdir dri`
 	if test "x$dridriverdir" = "x"; then
 		dridriverdir="$libdir/dri"
@@ -494,21 +497,38 @@ if test "x$enable_dri" != "xno"; then
 	if test "x$DRI2" != "xno"; then
 		save_CFLAGS=$CFLAGS
 		CFLAGS="$XORG_CFLAGS $DRM_CFLAGS $DRI1_CFLAGS $DRI2_CFLAGS"
-		AC_CHECK_HEADERS([dri2.h], DRI2="yes", DRI2="no", [
+		AC_CHECK_HEADERS([dri2.h], [], [DRI2=no], [
 #include <dixstruct.h>
 #include <drm.h>
 ])
 		CFLAGS=$save_CFLAGS
 	fi
+
+	XORG_DRIVER_CHECK_EXT(DRI3, dri3proto)
+	if test "x$_EXT_CHECK" != "xno"; then
+		DRI3=$DRI
+	fi
+	if test "x$DRI3" != "xno"; then
+		save_CFLAGS=$CFLAGS
+		CFLAGS="$XORG_CFLAGS $DRI3_CFLAGS"
+		AC_CHECK_DECL(DRI3, [], [DRI3=no], [#include <xorg-server.h>])
+		AC_CHECK_HEADERS([misyncstr.h misyncshm.h], [], [DRI3=no], [
+#include <xorg-server.h>
+#include <xf86str.h>
+#include <misync.h>
+])
+		CFLAGS=$save_CFLAGS
+	fi
 fi
+
 AC_MSG_CHECKING([whether to include DRI2 support])
-AM_CONDITIONAL(DRI2, test "x$DRI2" = "xyes")
+AM_CONDITIONAL(DRI2, test "x$DRI2" != "xno")
 AC_MSG_RESULT([$DRI2])
 if test "x$DRI2" != "xno"; then
         AC_DEFINE(HAVE_DRI2,1,[Enable DRI2 driver support])
 	dri_msg="$dri_msg DRI2"
 else
-	if test "x$enable_dri" = "xyes" -a "x$KMS" = "xyes"; then
+	if test "x$DRI" = "xyes" -a "x$KMS" = "xyes"; then
 		AC_MSG_ERROR([DRI2 requested but prerequisites not found])
 	fi
 
@@ -516,8 +536,39 @@ else
 	UXA=no
 fi
 
+AC_MSG_CHECKING([whether to include DRI3 support])
+AM_CONDITIONAL(DRI3, test "x$DRI3" != "xno")
+AC_MSG_RESULT([$DRI3])
+if test "x$DRI3" != "xno"; then
+        AC_DEFINE(HAVE_DRI3,1,[Enable DRI3 driver support])
+	dri_msg="$dri_msg DRI3"
+else
+	if test "x$DRI" = "xyes" -a "x$KMS" = "xyes"; then
+		AC_MSG_ERROR([DRI3 requested but prerequisites not found])
+	fi
+fi
+
 AC_CHECK_HEADERS([X11/extensions/dpmsconst.h])
 
+has_present="no"
+XORG_DRIVER_CHECK_EXT(PRESENT, presentproto)
+if test "x$_EXT_CHECK" != "xno"; then
+	save_CFLAGS=$CFLAGS
+	CFLAGS=$XORG_CFLAGS
+	AC_CHECK_HEADERS([present.h], [has_present="yes"], [], [
+#include <xorg-server.h>
+#include <xf86str.h>
+])
+	CFLAGS=$save_CFLAGS
+fi
+AC_MSG_CHECKING([whether to include PRESENT support])
+AM_CONDITIONAL(PRESENT, test "x$has_present" != "xno")
+AC_MSG_RESULT([$has_present])
+if test "x$has_present" != "xno"; then
+        AC_DEFINE(HAVE_PRESENT,1,[Enable PRESENT driver support])
+	dri_msg="$dri_msg Present"
+fi
+
 AC_MSG_CHECKING([whether to include UXA support])
 AC_MSG_RESULT([$UXA])
 AM_CONDITIONAL(UXA, test "x$UXA" != "xno")
diff --git a/man/intel.man b/man/intel.man
index e1361bf..27206e3 100644
--- a/man/intel.man
+++ b/man/intel.man
@@ -121,6 +121,11 @@ Disable or enable acceleration.
 .IP
 Default: acceleration is enabled.
 .TP
+.BI "Option \*qPresent\*q \*q" boolean \*q
+Enable use of hardware counters and flow control for the Present extension.
+.IP
+Default: Enabled
+.TP
 .BI "Option \*qAccelMethod\*q \*q" string \*q
 Select acceleration method.
 There are a couple of backends available for accelerating the DDX. \*qUXA\*q (Unified
diff --git a/src/intel_device.c b/src/intel_device.c
index 4337228..7140beb 100644
--- a/src/intel_device.c
+++ b/src/intel_device.c
@@ -104,7 +104,7 @@ static inline void intel_set_device(ScrnInfoPtr scrn, struct intel_device *dev)
 	xf86GetEntityPrivate(scrn->entityList[0], intel_device_key)->ptr = dev;
 }
 
-static Bool is_i915_device(int fd)
+static int is_i915_device(int fd)
 {
 	drm_version_t version;
 	char name[5] = "";
@@ -114,30 +114,40 @@ static Bool is_i915_device(int fd)
 	version.name = name;
 
 	if (drmIoctl(fd, DRM_IOCTL_VERSION, &version))
-		return FALSE;
+		return 0;
 
 	return strcmp("i915", name) == 0;
 }
 
-static int __intel_check_device(int fd)
+static int is_i915_gem(int fd)
 {
-	int ret;
+	int ret = is_i915_device(fd);
 
-	/* Confirm that this is a i915.ko device with GEM/KMS enabled */
-	ret = is_i915_device(fd);
 	if (ret) {
 		struct drm_i915_getparam gp;
+
 		gp.param = I915_PARAM_HAS_GEM;
 		gp.value = &ret;
+
 		if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp))
-			ret = FALSE;
+			ret = 0;
 	}
+
+	return ret;
+}
+
+static int __intel_check_device(int fd)
+{
+	int ret;
+
+	/* Confirm that this is a i915.ko device with GEM/KMS enabled */
+	ret = is_i915_gem(fd);
 	if (ret && !hosted()) {
 		struct drm_mode_card_res res;
 
 		memset(&res, 0, sizeof(res));
 		if (drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
-			ret = FALSE;
+			ret = 0;
 	}
 
 	return ret;
@@ -203,20 +213,25 @@ static char *find_master_node(int fd)
 	return drmGetDeviceNameFromFd(fd);
 }
 
+static int is_render_node(int fd, struct stat *st)
+{
+	if (fstat(fd, st))
+		return 0;
+
+	if (!S_ISCHR(st->st_mode))
+		return 0;
+
+	return st->st_rdev & 0x80;
+}
+
 static char *find_render_node(int fd)
 {
 #if defined(USE_RENDERNODE)
 	struct stat master, render;
 	char buf[128];
 
-	if (fstat(fd, &master))
-		return NULL;
-
-	if (!S_ISCHR(master.st_mode))
-		return NULL;
-
 	/* Are we a render-node ourselves? */
-	if (master.st_rdev & 0x80)
+	if (is_render_node(fd, &master))
 		return NULL;
 
 	sprintf(buf, "/dev/dri/renderD%d", (int)((master.st_rdev | 0x80) & 0xbf));
@@ -400,6 +415,45 @@ const char *intel_get_client_name(ScrnInfoPtr scrn)
 	return dev->render_node;
 }
 
+static int authorise(struct intel_device *dev, int fd)
+{
+	struct stat st;
+	drm_magic_t magic;
+
+	assert(is_i915_gem(fd));
+
+	if (is_render_node(fd, &st)) /* restricted authority, do not elevate */
+		return 1;
+
+	return drmGetMagic(fd, &magic) == 0 && drmAuthMagic(dev->fd, magic) == 0;
+}
+
+int intel_get_client_fd(ScrnInfoPtr scrn)
+{
+	struct intel_device *dev;
+	int fd = -1;
+
+	dev = intel_device(scrn);
+	assert(dev);
+	assert(dev->fd != -1);
+	assert(dev->render_node);
+
+#ifdef O_CLOEXEC
+	fd = open(dev->render_node, O_RDWR | O_CLOEXEC);
+#endif
+	if (fd < 0)
+		fd = fd_set_cloexec(open(dev->render_node, O_RDWR));
+	if (fd < 0)
+		return -BadAlloc;
+
+	if (!authorise(dev, fd)) {
+		close(fd);
+		return -BadMatch;
+	}
+
+	return fd;
+}
+
 int intel_get_device_id(ScrnInfoPtr scrn)
 {
 	struct intel_device *dev = intel_device(scrn);
diff --git a/src/intel_driver.h b/src/intel_driver.h
index 182a30e..3dc640c 100644
--- a/src/intel_driver.h
+++ b/src/intel_driver.h
@@ -127,6 +127,7 @@ int intel_open_device(int entity_num,
 		      struct xf86_platform_device *dev);
 int intel_get_device(ScrnInfoPtr scrn);
 const char *intel_get_client_name(ScrnInfoPtr scrn);
+int intel_get_client_fd(ScrnInfoPtr scrn);
 int intel_get_device_id(ScrnInfoPtr scrn);
 int intel_get_master(ScrnInfoPtr scrn);
 int intel_put_master(ScrnInfoPtr scrn);
diff --git a/src/intel_options.c b/src/intel_options.c
index 51d5462..ff8541a 100644
--- a/src/intel_options.c
+++ b/src/intel_options.c
@@ -9,6 +9,7 @@ const OptionInfoRec intel_options[] = {
 	{OPTION_ACCEL_METHOD,	"AccelMethod",	OPTV_STRING,	{0},	0},
 	{OPTION_BACKLIGHT,	"Backlight",	OPTV_STRING,	{0},	0},
 	{OPTION_DRI,		"DRI",		OPTV_STRING,	{0},	0},
+	{OPTION_PRESENT,	"Present",	OPTV_BOOLEAN,	{0},	1},
 	{OPTION_COLOR_KEY,	"ColorKey",	OPTV_INTEGER,	{0},	0},
 	{OPTION_VIDEO_KEY,	"VideoKey",	OPTV_INTEGER,	{0},	0},
 	{OPTION_TILING_2D,	"Tiling",	OPTV_BOOLEAN,	{0},	1},
diff --git a/src/intel_options.h b/src/intel_options.h
index 6873a6d..7e2cbd9 100644
--- a/src/intel_options.h
+++ b/src/intel_options.h
@@ -16,6 +16,7 @@ enum intel_options {
 	OPTION_ACCEL_METHOD,
 	OPTION_BACKLIGHT,
 	OPTION_DRI,
+	OPTION_PRESENT,
 	OPTION_VIDEO_KEY,
 	OPTION_COLOR_KEY,
 	OPTION_TILING_2D,
commit 8a02886a24c8699374a39a9363e72bec0e7bbe30
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri May 30 21:09:34 2014 +0100

    sna/dri2: Use real async flips
    
    Presuming that we have both kernel support and Xorg support, of course.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_dri2.c b/src/sna/sna_dri2.c
index 4bfa8e1..2784014 100644
--- a/src/sna/sna_dri2.c
+++ b/src/sna/sna_dri2.c
@@ -91,6 +91,7 @@ enum frame_event_type {
 	FLIP,
 	FLIP_THROTTLE,
 	FLIP_COMPLETE,
+	FLIP_ASYNC,
 };
 
 struct dri_bo {
@@ -1251,7 +1252,7 @@ void sna_dri2_destroy_window(WindowPtr win)
 }
 
 static void
-update_scanout(struct sna *sna, struct sna_dri2_frame_event *info, struct kgem_bo *bo, int name)
+update_scanout(struct sna *sna, struct kgem_bo *bo, int name)
 {
 	assert(sna->dri2.scanout[1].bo == NULL);
 	assert(sna->dri2.scanout[0].bo->scanout);
@@ -1264,9 +1265,52 @@ update_scanout(struct sna *sna, struct sna_dri2_frame_event *info, struct kgem_b
 	DBG(("%s: pending scanout handle=%d, active scanout handle=%d\n",
 	     __FUNCTION__, sna->dri2.scanout[0].bo->handle, sna->dri2.scanout[1].bo->handle));
 	assert(sna->dri2.scanout[0].bo->handle != sna->dri2.scanout[1].bo->handle);
+}
 
-	assert(sna->dri2.flip_pending == NULL || sna->dri2.flip_pending == info);
-	sna->dri2.flip_pending = info;
+static void
+retire_scanout(struct sna *sna,
+	       struct sna_dri2_frame_event *flip)
+{
+	struct dri_bo *c = NULL;
+
+	if (sna->dri2.scanout[1].bo == NULL)
+		return;
+
+	DBG(("%s: retiring previous scanout handle=%d, name=%d, refcnt=%d (current scanout handle=%d)\n",
+	     __FUNCTION__,
+	     sna->dri2.scanout[1].bo->handle,
+	     sna->dri2.scanout[1].name,
+	     sna->dri2.scanout[1].bo->refcnt,
+	     sna->dri2.scanout[0].bo->handle));
+
+	if (flip &&
+	    sna->dri2.scanout[1].bo != sna->dri2.scanout[0].bo &&
+	    sna->dri2.scanout[1].bo->refcnt == 1) {
+		DBG(("%s: adding old scanout to flip cache\n", __FUNCTION__));
+		if (!list_is_empty(&flip->cache))
+			c = list_last_entry(&flip->cache, struct dri_bo, link);
+		if (c) {
+			if (c->bo == NULL)
+				_list_del(&c->link);
+			else
+				c = NULL;
+		}
+		if (c == NULL)
+			c = malloc(sizeof(*c));
+		if (c != NULL) {
+			c->bo = sna->dri2.scanout[1].bo;
+			c->name = sna->dri2.scanout[1].name;
+			list_add(&c->link, &flip->cache);
+		}
+	}
+
+	if (c == NULL) {
+		DBG(("%s: not caching old scanout handle=%d, still busy\n",
+		     __FUNCTION__, sna->dri2.scanout[1].bo->handle));
+		kgem_bo_destroy(&sna->kgem, sna->dri2.scanout[1].bo);
+	}
+
+	sna->dri2.scanout[1].bo = NULL;
 }
 
 static bool
@@ -1284,11 +1328,20 @@ sna_dri2_page_flip(struct sna *sna, struct sna_dri2_frame_event *info)
 	assert(sna->dri2.scanout[1].bo == NULL);
 	assert(bo->refcnt);
 
-	info->count = sna_page_flip(sna, bo, info, info->pipe);
+	if (info->type == FLIP_ASYNC)
+		info->count = sna_page_flip(sna, bo, NULL, -1);
+	else
+		info->count = sna_page_flip(sna, bo, info, info->pipe);
 	if (!info->count)
 		return false;
 
-	update_scanout(sna, info, bo, info->back->name);
+	update_scanout(sna, bo, info->back->name);
+
+	assert(sna->dri2.flip_pending == NULL || sna->dri2.flip_pending == info);
+	if (info->type == FLIP_ASYNC)
+		retire_scanout(sna, NULL);
+	else
+		sna->dri2.flip_pending = info;
 
 	DBG(("%s: marked handle=%d as scanout, swap front (handle=%d, name=%d) and back (handle=%d, name=%d)\n",
 	     __FUNCTION__, bo->handle,
@@ -1802,7 +1855,10 @@ sna_dri2_flip_continue(struct sna *sna, struct sna_dri2_frame_event *info)
 			if (!info->count)
 				return false;
 
-			update_scanout(sna, info, bo, info->front->name);
+			update_scanout(sna, bo, info->front->name);
+
+			assert(sna->dri2.flip_pending == NULL || sna->dri2.flip_pending == info);
+			sna->dri2.flip_pending = info;
 		}
 	} else {
 		info->type = -info->mode;
@@ -1884,45 +1940,7 @@ static void sna_dri2_flip_event(struct sna *sna,
 
 	assert(!sna->mode.shadow_flip);
 
-	if (sna->dri2.scanout[1].bo) {
-		struct dri_bo *c = NULL;
-
-		DBG(("%s: retiring previous scanout handle=%d, name=%d, refcnt=%d (current scanout handle=%d)\n",
-		     __FUNCTION__,
-		     sna->dri2.scanout[1].bo->handle,
-		     sna->dri2.scanout[1].name,
-		     sna->dri2.scanout[1].bo->refcnt,
-		     sna->dri2.scanout[0].bo->handle));
-
-		if (sna->dri2.scanout[1].bo != sna->dri2.scanout[0].bo &&
-		    sna->dri2.scanout[1].bo->refcnt == 1) {
-			DBG(("%s: adding old scanout to flip cache\n", __FUNCTION__));
-			if (!list_is_empty(&flip->cache))
-				c = list_last_entry(&flip->cache, struct dri_bo, link);
-			if (c) {
-				if (c->bo == NULL)
-					_list_del(&c->link);
-				else
-					c = NULL;
-			}
-			if (c == NULL)
-				c = malloc(sizeof(*c));
-			if (c != NULL) {
-				c->bo = sna->dri2.scanout[1].bo;
-				c->name = sna->dri2.scanout[1].name;
-				list_add(&c->link, &flip->cache);
-			}
-		}
-
-		if (c == NULL) {
-			DBG(("%s: not caching old scanout handle=%d, still busy\n",
-			     __FUNCTION__, sna->dri2.scanout[1].bo->handle));
-			kgem_bo_destroy(&sna->kgem, sna->dri2.scanout[1].bo);
-		}
-
-		sna->dri2.scanout[1].bo = NULL;
-	}
-
+	retire_scanout(sna, flip);
 	if (sna->dri2.flip_pending == flip)
 		sna->dri2.flip_pending = NULL;
 
@@ -2022,8 +2040,9 @@ static int use_triple_buffer(struct sna *sna, ClientPtr client, bool async)
 	}
 
 	if (async) {
-		DBG(("%s: running async, using FLIP_COMPLETE\n", __FUNCTION__));
-		return FLIP_COMPLETE;
+		DBG(("%s: running async, using %s\n", __FUNCTION__,
+		     sna->flags & SNA_HAS_ASYNC_FLIP ? "FLIP_ASYNC" : "FLIP_COMPLETE"));
+		return sna->flags & SNA_HAS_ASYNC_FLIP ? FLIP_ASYNC : FLIP_COMPLETE;
 	}
 
 #if XORG_CAN_TRIPLE_BUFFER
@@ -2154,18 +2173,21 @@ sna_dri2_schedule_flip(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc,
 		} else {
 			info->type = type = use_triple_buffer(sna, client, *target_msc == 0);
 			if (!sna_dri2_page_flip(sna, info)) {
+				DBG(("%s: flip failed, falling back\n", __FUNCTION__));
 				sna_dri2_frame_event_info_free(sna, draw, info);
 				return false;
 			}
 		}
 
 		swap_limit(draw, 1 + (type == FLIP_THROTTLE));
-		if (type == FLIP_COMPLETE) {
+		if (type >= FLIP_COMPLETE) {
 new_back:
 			if (!XORG_CAN_TRIPLE_BUFFER)
 				sna_dri2_get_back(sna, info);
 			DBG(("%s: fake triple buffering, unblocking client\n", __FUNCTION__));
 			frame_swap_complete(sna, info, DRI2_EXCHANGE_COMPLETE);
+			if (info->type == FLIP_ASYNC)
+				sna_dri2_frame_event_info_free(sna, draw, info);
 		}
 out:
 		DBG(("%s: target_msc=%llu\n", __FUNCTION__, current_msc + 1));
@@ -2624,7 +2646,6 @@ static const char *dri_driver_name(struct sna *sna)
 	return s;
 }
 
-
 bool sna_dri2_open(struct sna *sna, ScreenPtr screen)
 {
 	DRI2InfoRec info;
commit c17d704a5baa0c902994f035d3da39d77d31fef3
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri May 30 21:21:00 2014 +0100

    sna: Enable kernel support for asynchronous flips
    
    If a flip fails, attempt to restore the previous working configuration
    (using a modeset) then disable further flipping.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna.h b/src/sna/sna.h
index 7a1b693..d40ccf4 100644
--- a/src/sna/sna.h
+++ b/src/sna/sna.h
@@ -243,6 +243,8 @@ struct sna {
 #define SNA_PERFORMANCE		0x100
 #define SNA_POWERSAVE		0x200
 #define SNA_REMOVE_OUTPUTS	0x400
+#define SNA_HAS_FLIP		0x10000
+#define SNA_HAS_ASYNC_FLIP	0x20000
 #define SNA_REDISCOVER		0x40000000
 #define SNA_REPROBE		0x80000000
 
diff --git a/src/sna/sna_display.c b/src/sna/sna_display.c
index 2a88717..6803409 100644
--- a/src/sna/sna_display.c
+++ b/src/sna/sna_display.c
@@ -88,6 +88,8 @@ union compat_mode_get_connector{
 #define DEFAULT_DPI 96
 #endif
 
+#define DRM_MODE_PAGE_FLIP_ASYNC 0x02
+
 #if 0
 #define __DBG DBG
 #else
@@ -4096,6 +4098,9 @@ static int do_page_flip(struct sna *sna, struct kgem_bo *bo,
 	int count = 0;
 	int i;
 
+	if ((sna->flags & (data ? SNA_HAS_FLIP : SNA_HAS_ASYNC_FLIP)) == 0)
+		return 0;
+
 	/*
 	 * Queue flips on all enabled CRTCs
 	 * Note that if/when we get per-CRTC buffers, we'll have to update this.
@@ -4118,32 +4123,42 @@ static int do_page_flip(struct sna *sna, struct kgem_bo *bo,
 
 		arg.crtc_id = crtc->id;
 		arg.fb_id = get_fb(sna, bo, width, height);
-		if (arg.fb_id == 0)
-			goto disable;
+		if (arg.fb_id == 0) {
+			assert(count == 0);
+			return 0;
+		}
 
 		/* Only the reference crtc will finally deliver its page flip
 		 * completion event. All other crtc's events will be discarded.
 		 */
-		arg.user_data = (uintptr_t)data;
-		arg.user_data |= crtc->pipe == pipe;
-		arg.flags = DRM_MODE_PAGE_FLIP_EVENT;
+		if (data) {
+			arg.user_data = (uintptr_t)data;
+			arg.user_data |= crtc->pipe == pipe;
+			arg.flags = DRM_MODE_PAGE_FLIP_EVENT;
+		} else {
+			arg.user_data = 0;
+			arg.flags = DRM_MODE_PAGE_FLIP_ASYNC;
+		}
 		arg.reserved = 0;
 
 		DBG(("%s: crtc %d id=%d, pipe=%d, [ref? %d] --> fb %d\n",
 		     __FUNCTION__, i, crtc->id, crtc->pipe,
 		     crtc->pipe == pipe, arg.fb_id));
 		if (drmIoctl(sna->kgem.fd, DRM_IOCTL_MODE_PAGE_FLIP, &arg)) {
-			DBG(("%s: flip [fb=%d] on crtc %d id=%d failed - %d\n",
+			DBG(("%s: flip [fb=%d] on crtc %d id=%d pipe=%d failed - %d\n",
 			     __FUNCTION__, arg.fb_id, i, crtc->id, crtc->pipe, errno));
-disable:
-			if (count == 0)
-				return 0;
-
 			xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR,
-				   "%s: page flipping failed, disabling CRTC:%d (pipe=%d)\n",
-				   __FUNCTION__, crtc->id, crtc->pipe);
-			sna_crtc_disable(config->crtc[i]);
-			continue;
+				   "page flipping failed, on CRTC:%d (pipe=%d), disabling %s page flips\n",
+				   crtc->id, crtc->pipe, data ? "synchronous": "asynchronous");
+			if (count && !xf86SetDesiredModes(sna->scrn)) {
+				xf86DrvMsg(sna->scrn->scrnIndex, X_ERROR,
+					   "failed to restore display configuration\n");
+				for (; i < sna->mode.num_real_crtc; i++)
+					sna_crtc_disable(config->crtc[i]);
+			}
+
+			sna->flags &= ~(data ? SNA_HAS_FLIP : SNA_HAS_ASYNC_FLIP);
+			return 0;
 		}
 
 		if (crtc->bo != bo) {
@@ -4522,6 +4537,58 @@ sanitize_outputs(struct sna *sna)
 		config->output[i]->crtc = NULL;
 }
 
+static bool has_flip(struct sna *sna)
+{
+	drm_i915_getparam_t gp;
+	int v;
+
+	if (sna->flags & SNA_NO_FLIP)
+		return false;
+
+	v = 0;
+
+	VG_CLEAR(gp);
+	gp.param = I915_PARAM_HAS_PAGEFLIPPING;
+	gp.value = &v;
+
+	if (drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GETPARAM, &gp))
+		return false;
+
+	VG(VALGRIND_MAKE_MEM_DEFINED(&v, sizeof(v)));
+	return v > 0;
+}
+
+static bool has_flip__async(struct sna *sna)
+{
+#define LOCAL_IOCTL_GET_CAP	DRM_IOWR(0x0c, struct local_get_cap)
+#define DRM_CAP_ASYNC_PAGE_FLIP 0x7
+	struct local_get_cap {
+		uint64_t name;
+		uint64_t value;
+	} cap = { DRM_CAP_ASYNC_PAGE_FLIP };
+
+	if (sna->flags & SNA_NO_FLIP)
+		return false;
+
+	if (drmIoctl(sna->kgem.fd, LOCAL_IOCTL_GET_CAP, &cap) == 0)
+		return cap.value > 0;
+
+	return false;
+}
+
+static void
+probe_capabilities(struct sna *sna)
+{
+	sna->flags &= ~(SNA_HAS_FLIP | SNA_HAS_ASYNC_FLIP);
+	if (has_flip(sna))
+		sna->flags |= SNA_HAS_FLIP;
+	if (has_flip__async(sna))
+		sna->flags |= SNA_HAS_ASYNC_FLIP;
+	DBG(("%s: page flips? %s, async? %s\n", __FUNCTION__,
+	     sna->flags & SNA_HAS_FLIP ? "enabled" : "disabled",
+	     sna->flags & SNA_HAS_ASYNC_FLIP ? "enabled" : "disabled"));
+}
+
 static void
 sna_crtc_config_notify(ScreenPtr screen)
 {
@@ -4529,6 +4596,8 @@ sna_crtc_config_notify(ScreenPtr screen)
 
 	DBG(("%s\n", __FUNCTION__));
 
+	probe_capabilities(sna);
+
 	sna_dri2_reset_scanout(sna);
 
 	sna_mode_update(sna);
@@ -4552,6 +4621,8 @@ bool sna_mode_pre_init(ScrnInfoPtr scrn, struct sna *sna)
 		return true;
 	}
 
+	probe_capabilities(sna);
+
 	if (!xf86GetOptValInteger(sna->Options, OPTION_VIRTUAL, &num_fake))
 		num_fake = 1;
 
diff --git a/src/sna/sna_dri2.c b/src/sna/sna_dri2.c
index f6d2fea..4bfa8e1 100644
--- a/src/sna/sna_dri2.c
+++ b/src/sna/sna_dri2.c
@@ -1330,7 +1330,7 @@ can_flip(struct sna * sna,
 		return false;
 	}
 
-	if (sna->flags & SNA_NO_FLIP) {
+	if ((sna->flags & (SNA_HAS_FLIP | SNA_HAS_ASYNC_FLIP)) == 0) {
 		DBG(("%s: no, pageflips disabled\n", __FUNCTION__));
 		return false;
 	}
diff --git a/src/sna/sna_driver.c b/src/sna/sna_driver.c
index f609254..9d079cb 100644
--- a/src/sna/sna_driver.c
+++ b/src/sna/sna_driver.c
@@ -294,27 +294,6 @@ static bool has_vsync(struct sna *sna)
 	return true;
 }
 
-static bool has_pageflipping(struct sna *sna)
-{
-	drm_i915_getparam_t gp;
-	int v;
-
-	if (sna->flags & SNA_IS_HOSTED)
-		return false;
-
-	v = 0;
-
-	VG_CLEAR(gp);
-	gp.param = I915_PARAM_HAS_PAGEFLIPPING;
-	gp.value = &v;
-
-	if (drmIoctl(sna->kgem.fd, DRM_IOCTL_I915_GETPARAM, &gp))
-		return false;
-
-	VG(VALGRIND_MAKE_MEM_DEFINED(&v, sizeof(v)));
-	return v > 0;
-}
-
 static void sna_setup_capabilities(ScrnInfoPtr scrn, int fd)
 {
 #if HAS_PIXMAP_SHARING && defined(DRM_CAP_PRIME)
@@ -576,7 +555,7 @@ static Bool sna_pre_init(ScrnInfoPtr scrn, int flags)
 		sna->flags |= SNA_NO_VSYNC;
 	DBG(("%s: vsync? %s\n", __FUNCTION__, sna->flags & SNA_NO_VSYNC ? "disabled" : "enabled"));
 
-	if (!has_pageflipping(sna) ||
+	if (sna->flags & SNA_IS_HOSTED ||
 	    !xf86ReturnOptValBool(sna->Options, OPTION_PAGEFLIP, TRUE))
 		sna->flags |= SNA_NO_FLIP;
 	DBG(("%s: page flips? %s\n", __FUNCTION__, sna->flags & SNA_NO_FLIP ? "disabled" : "enabled"));
commit c95a4002d1d27937731c59e84254ccb3b1246ccd
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Jun 1 08:44:13 2014 +0100

    sna/dri2: Hide MSC reported as going backwards
    
    OML_sync_control mandates that MSC must be monotonic, so if the kernel
    reports that they go backwards, lie.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_display.c b/src/sna/sna_display.c
index 1761be6..2a88717 100644
--- a/src/sna/sna_display.c
+++ b/src/sna/sna_display.c
@@ -233,11 +233,17 @@ int sna_crtc_is_on(xf86CrtcPtr crtc)
 
 static inline uint64_t msc64(struct sna_crtc *sna_crtc, uint32_t seq)
 {
-	if ((int32_t)(seq - sna_crtc->last_seq) < -0x40000000) {
-		sna_crtc->wrap_seq++;
-		DBG(("%s: pipe=%d wrapped was %u, now %u, wraps=%u\n",
-		     __FUNCTION__, sna_crtc->pipe,
-		     sna_crtc->last_seq, seq, sna_crtc->wrap_seq));
+	if (seq < sna_crtc->last_seq) {
+		if (sna_crtc->last_seq - seq > 0x40000000) {
+			sna_crtc->wrap_seq++;
+			DBG(("%s: pipe=%d wrapped; was %u, now %u, wraps=%u\n",
+			     __FUNCTION__, sna_crtc->pipe,
+			     sna_crtc->last_seq, seq, sna_crtc->wrap_seq));
+		} else  {
+			ERR(("%s: pipe=%d msc went backwards; was %u, now %u\n",
+			     __FUNCTION__, sna_crtc->pipe, sna_crtc->last_seq, seq));
+			seq = sna_crtc->last_seq;
+		}
 	}
 	sna_crtc->last_seq = seq;
 	return (uint64_t)sna_crtc->wrap_seq << 32 | seq;
diff --git a/src/sna/sna_dri2.c b/src/sna/sna_dri2.c
index 117f879..f6d2fea 100644
--- a/src/sna/sna_dri2.c
+++ b/src/sna/sna_dri2.c
@@ -2410,37 +2410,29 @@ static int
 sna_dri2_get_msc(DrawablePtr draw, CARD64 *ust, CARD64 *msc)
 {
 	struct sna *sna = to_sna_from_drawable(draw);
-	union drm_wait_vblank vbl;
 	xf86CrtcPtr crtc = sna_dri2_get_crtc(draw);
+	const struct ust_msc *swap;
 
 	DBG(("%s(draw=%ld, pipe=%d)\n", __FUNCTION__, draw->id,
 	     crtc ? sna_crtc_to_pipe(crtc) : -1));
-	if (crtc == NULL) {
-		const struct ust_msc *swap;
 
-		crtc = sna_mode_first_crtc(sna);
-fail:
-		/* Drawable not displayed, make up a *monotonic* value */
-		swap = sna_crtc_last_swap(crtc);
-		*msc = draw_current_msc(draw, crtc, swap->msc);
-		*ust = ust64(swap->tv_sec, swap->tv_usec);
-		return TRUE;
-	}
+	if (crtc != NULL) {
+		union drm_wait_vblank vbl;
 
-	VG_CLEAR(vbl);
-	vbl.request.type = _DRM_VBLANK_RELATIVE;
-	vbl.request.sequence = 0;
-	if (sna_wait_vblank(sna, &vbl, sna_crtc_to_pipe(crtc)) == 0) {
-		*ust = ust64(vbl.reply.tval_sec, vbl.reply.tval_usec);
-		*msc = draw_current_msc(draw, crtc, sna_crtc_record_vblank(crtc, &vbl));
-		DBG(("%s: msc=%llu, ust=%llu\n", __FUNCTION__,
-		     (long long)*msc, (long long)*ust));
-	} else {
-		DBG(("%s: query failed on pipe %d, ret=%d\n",
-		     __FUNCTION__, sna_crtc_to_pipe(crtc), errno));
-		goto fail;
-	}
+		VG_CLEAR(vbl);
+		vbl.request.type = _DRM_VBLANK_RELATIVE;
+		vbl.request.sequence = 0;
+		if (sna_wait_vblank(sna, &vbl, sna_crtc_to_pipe(crtc)) == 0)
+			sna_crtc_record_vblank(crtc, &vbl);
+	} else
+		/* Drawable not displayed, make up a *monotonic* value */
+		crtc = sna_mode_first_crtc(sna);
 
+	swap = sna_crtc_last_swap(crtc);
+	*msc = draw_current_msc(draw, crtc, swap->msc);
+	*ust = ust64(swap->tv_sec, swap->tv_usec);
+	DBG(("%s: msc=%llu, ust=%llu\n", __FUNCTION__,
+	     (long long)*msc, (long long)*ust));
 	return TRUE;
 }
 
commit b1c07c20a371795a8dc4102f83fe542d62d8a4e6
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat May 31 21:58:13 2014 +0100

    sna/dri2: Always force the async blits to be immediate
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_dri2.c b/src/sna/sna_dri2.c
index 497bd5b..117f879 100644
--- a/src/sna/sna_dri2.c
+++ b/src/sna/sna_dri2.c
@@ -1735,7 +1735,7 @@ sna_dri2_immediate_blit(struct sna *sna,
 	     event));
 
 	info->type = SWAP_THROTTLE;
-	if (sna_dri2_window_get_chain((WindowPtr)draw) == info) {
+	if (!sync || sna_dri2_window_get_chain((WindowPtr)draw) == info) {
 		DBG(("%s: no pending blit, starting chain\n",
 		     __FUNCTION__));
 
commit 1b5f91e14226799ef4ec6461de93fa9193ef2ebc
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat May 31 21:41:45 2014 +0100

    sna/dri2: Decouple spent flip
    
    If the flip is already completed before we finish the previous flip, the
    drawable has been replaced already and we can unchain the flip queue.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_dri2.c b/src/sna/sna_dri2.c
index a832099..497bd5b 100644
--- a/src/sna/sna_dri2.c
+++ b/src/sna/sna_dri2.c
@@ -1786,11 +1786,24 @@ sna_dri2_flip_continue(struct sna *sna, struct sna_dri2_frame_event *info)
 		if (bo != sna_pixmap(sna->front)->gpu_bo)
 			return false;
 
-		info->count = sna_page_flip(sna, bo, info, info->pipe);
-		if (!info->count)
+		if (bo == sna->dri2.scanout[0].bo) {
+			DBG(("%s: flip chain already complete\n", __FUNCTION__));
+
+			if (info->draw) {
+				sna_dri2_remove_frame_event((WindowPtr)info->draw, info);
+				if (info->chain)
+					chain_swap(sna, info->draw, info->chain);
+			}
+
+			info->draw = NULL;
 			return false;
+		} else {
+			info->count = sna_page_flip(sna, bo, info, info->pipe);
+			if (!info->count)
+				return false;
 
-		update_scanout(sna, info, bo, info->front->name);
+			update_scanout(sna, info, bo, info->front->name);
+		}
 	} else {
 		info->type = -info->mode;
 
commit 0ee1940a4ffc1901534fa3bf15adb427527a8c20
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat May 31 13:21:04 2014 +0100

    sna/dri2: Mark queued flip MSC as the one after next
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_dri2.c b/src/sna/sna_dri2.c
index 72f34d6..a832099 100644
--- a/src/sna/sna_dri2.c
+++ b/src/sna/sna_dri2.c
@@ -1275,7 +1275,7 @@ sna_dri2_page_flip(struct sna *sna, struct sna_dri2_frame_event *info)
 	struct kgem_bo *bo = get_private(info->back)->bo;
 	struct dri_bo tmp;
 
-	DBG(("%s()\n", __FUNCTION__));
+	DBG(("%s(type=%d)\n", __FUNCTION__, info->type));
 
 	assert(sna_pixmap_get_buffer(sna->front) == info->front);
 	assert(get_drawable_pixmap(info->draw)->drawable.height * bo->pitch <= kgem_bo_size(bo));
@@ -1820,7 +1820,8 @@ static void chain_flip(struct sna *sna)
 	struct sna_dri2_frame_event *chain = sna->dri2.flip_pending;
 
 	assert(chain->type == FLIP);
-	DBG(("%s: chaining type=%d\n", __FUNCTION__, chain->type));
+	DBG(("%s: chaining type=%d, cancelled?=%d\n",
+	     __FUNCTION__, chain->type, chain->draw == NULL));
 
 	sna->dri2.flip_pending = NULL;
 	if (chain->draw == NULL) {
@@ -1830,8 +1831,7 @@ static void chain_flip(struct sna *sna)
 
 	assert(chain == sna_dri2_window_get_chain((WindowPtr)chain->draw));
 
-	if (chain->type == FLIP &&
-	    can_flip(sna, chain->draw, chain->front, chain->back, chain->crtc) &&
+	if (can_flip(sna, chain->draw, chain->front, chain->back, chain->crtc) &&
 	    sna_dri2_page_flip(sna, chain)) {
 		DBG(("%s: performing chained flip\n", __FUNCTION__));
 	} else {
@@ -1839,8 +1839,8 @@ static void chain_flip(struct sna *sna)
 		chain->bo = __sna_dri2_copy_region(sna, chain->draw, NULL,
 						  chain->back, chain->front,
 						  true);
-#if XORG_CAN_TRIPLE_BUFFER
-		{
+
+		if (XORG_CAN_TRIPLE_BUFFER) {
 			union drm_wait_vblank vbl;
 
 			VG_CLEAR(vbl);
@@ -1857,7 +1857,7 @@ static void chain_flip(struct sna *sna)
 				return;
 			}
 		}
-#endif
+
 		DBG(("%s: fake triple buffering (or vblank wait failed), unblocking client\n", __FUNCTION__));
 		frame_swap_complete(sna, chain, DRI2_BLIT_COMPLETE);
 		sna_dri2_frame_event_info_free(sna, chain->draw, chain);
@@ -2080,8 +2080,10 @@ sna_dri2_schedule_flip(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc,
 		int type;
 
 		info = sna->dri2.flip_pending;
-		DBG(("%s: performing immediate swap on pipe %d, pending? %d, mode: %d\n",
-		     __FUNCTION__, sna_crtc_to_pipe(crtc), info != NULL, info ? info->mode : 0));
+		DBG(("%s: performing immediate swap on pipe %d, pending? %d, mode: %d, continuation? %d\n",
+		     __FUNCTION__, sna_crtc_to_pipe(crtc),
+		     info != NULL, info ? info->mode : 0,
+		     info && info->draw == draw));
 
 		if (info && info->draw == draw) {
 			assert(info->type != FLIP);
@@ -2135,6 +2137,7 @@ sna_dri2_schedule_flip(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc,
 			     __FUNCTION__));
 			info->type = type = FLIP;
 			sna->dri2.flip_pending = info;
+			current_msc++;
 		} else {
 			info->type = type = use_triple_buffer(sna, client, *target_msc == 0);
 			if (!sna_dri2_page_flip(sna, info)) {
commit 2df4466adae7f299eac3f7fd3e5f33ee4b22e53f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri May 30 13:27:32 2014 +0100

    sna/gen8: Clear all URB push constant allocations
    
    A little paranoia to clear the unused portion of the URB and dedicate it
    to VS entries.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/gen8_render.c b/src/sna/gen8_render.c
index 5c22bbb..c66f7cc 100644
--- a/src/sna/gen8_render.c
+++ b/src/sna/gen8_render.c
@@ -410,16 +410,24 @@ static void
 gen8_emit_urb(struct sna *sna)
 {
 #if SIM
+	OUT_BATCH(GEN8_3DSTATE_PUSH_CONSTANT_ALLOC_VS | (2 - 2));
+	OUT_BATCH(0 << PUSH_CONSTANT_BUFFER_OFFSET_SHIFT |
+		  0 << PUSH_CONSTANT_BUFFER_SIZE_SHIFT);
+
+	OUT_BATCH(GEN8_3DSTATE_PUSH_CONSTANT_ALLOC_GS | (2 - 2));
+	OUT_BATCH(0 << PUSH_CONSTANT_BUFFER_OFFSET_SHIFT |
+		  0 << PUSH_CONSTANT_BUFFER_SIZE_SHIFT);
+
 	OUT_BATCH(GEN8_3DSTATE_PUSH_CONSTANT_ALLOC_PS | (2 - 2));
 	OUT_BATCH(0 << PUSH_CONSTANT_BUFFER_OFFSET_SHIFT |
-		  4 << PUSH_CONSTANT_BUFFER_SIZE_SHIFT); /* 8KiB */
+		  0 << PUSH_CONSTANT_BUFFER_SIZE_SHIFT);
 #endif
 
 	/* num of VS entries must be divisible by 8 if size < 9 */
 	OUT_BATCH(GEN8_3DSTATE_URB_VS | (2 - 2));
-	OUT_BATCH(960 << URB_ENTRY_NUMBER_SHIFT |
+	OUT_BATCH(1024 << URB_ENTRY_NUMBER_SHIFT |
 		  (2 - 1) << URB_ENTRY_SIZE_SHIFT |
-		  1 << URB_STARTING_ADDRESS_SHIFT);
+		  0 << URB_STARTING_ADDRESS_SHIFT);
 
 	OUT_BATCH(GEN8_3DSTATE_URB_HS | (2 - 2));
 	OUT_BATCH(0 << URB_ENTRY_SIZE_SHIFT |
commit 1618d6cfa63e31ebaedadf575dcbdf5b07451bde
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri May 30 12:14:40 2014 +0100

    sna/gen8: Set GT level
    
    We use the GT level to decide how favourable it is to use the GPU in
    various circumstances, now set it.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/gen8_render.c b/src/sna/gen8_render.c
index bda3700..5c22bbb 100644
--- a/src/sna/gen8_render.c
+++ b/src/sna/gen8_render.c
@@ -3823,6 +3823,12 @@ static bool gen8_render_setup(struct sna *sna)
 	struct sna_static_stream general;
 	struct gen8_sampler_state *ss;
 	int i, j, k, l, m;
+	uint32_t devid;
+
+	devid = intel_get_device_id(sna->scrn);
+	if (devid & 0xf)
+		state->gt = ((devid >> 4) & 0xf) + 1;
+	DBG(("%s: gt=%d\n", __FUNCTION__, state->gt));
 
 	sna_static_stream_init(&general);
 
commit bb49222a514b1d6041f3d9530a22f5701377118b
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jun 2 08:25:52 2014 +0100

    sna: Add DBG hints for using inplace CPU mmappings
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/kgem.h b/src/sna/kgem.h
index 12a99db..a9fb939 100644
--- a/src/sna/kgem.h
+++ b/src/sna/kgem.h
@@ -683,14 +683,22 @@ static inline bool kgem_bo_can_map__cpu(struct kgem *kgem,
 					struct kgem_bo *bo,
 					bool write)
 {
+	DBG(("%s: handle=%d\n", __FUNCTION__, bo->handle));
 	assert(bo->refcnt);
 
-	if (bo->purged || (bo->scanout && write))
+	if (bo->purged || (bo->scanout && write)) {
+		DBG(("%s: no, writing to scanout? %d, or is stolen [inaccessible via CPU]? %d\n",
+		     __FUNCTION__, bo->scanout && write, bo->purged));
 		return false;
+	}
 
-	if (kgem->has_llc)
+	if (kgem->has_llc) {
+		DBG(("%s: yes, has LLC and target is in LLC\n", __FUNCTION__));
 		return true;
+	}
 
+	DBG(("%s: non-LLC - CPU domain? %d, clean? %d\n",
+	     __FUNCTION__, bo->domain == DOMAIN_CPU, !write || bo->exec == NULL));
 	if (bo->domain != DOMAIN_CPU)
 		return false;
 
diff --git a/src/sna/sna_io.c b/src/sna/sna_io.c
index 2e9204f..c71435a 100644
--- a/src/sna/sna_io.c
+++ b/src/sna/sna_io.c
@@ -636,11 +636,16 @@ fallback:
 
 static bool upload_inplace__tiled(struct kgem *kgem, struct kgem_bo *bo)
 {
-	if (!kgem->memcpy_to_tiled_x)
-		return false;
-
-	if (bo->tiling != I915_TILING_X)
+	DBG(("%s: tiling=%d\n", __FUNCTION__, bo->tiling));
+	switch (bo->tiling) {
+	case I915_TILING_Y:
 		return false;
+	case I915_TILING_X:
+		if (!kgem->memcpy_to_tiled_x)
+			return false;
+	default:
+		break;
+	}
 
 	return kgem_bo_can_map__cpu(kgem, bo, true);
 }
commit 487df2e7b105b996c4d620ec2af6331ebc167f47
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jun 2 08:30:57 2014 +0100

    sna: Silence a DBG compiler warning
    
    kgem.c: In function '_kgem_submit':
    kgem.c:3243:12: warning: ignoring return value of 'write', declared with attribute warn_unused_result [-Wunused-result]
           write(fd, kgem->batch, batch_end*sizeof(uint32_t));
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/kgem.c b/src/sna/kgem.c
index 167169b..1a52557 100644
--- a/src/sna/kgem.c
+++ b/src/sna/kgem.c
@@ -3190,7 +3190,8 @@ void _kgem_submit(struct kgem *kgem)
 				if (DEBUG_SYNC) {
 					int fd = open("/tmp/batchbuffer", O_WRONLY | O_CREAT | O_APPEND, 0666);
 					if (fd != -1) {
-						write(fd, kgem->batch, batch_end*sizeof(uint32_t));
+						int ignored = write(fd, kgem->batch, batch_end*sizeof(uint32_t));
+						assert(ignored == batch_end*sizeof(uint32_t));
 						close(fd);
 					}
 


More information about the xorg-commit mailing list