xserver: Branch 'dri2-swapbuffers' - 2 commits

Jesse Barnes jbarnes at kemper.freedesktop.org
Fri Oct 2 16:51:57 PDT 2009


 glx/glxcmds.c             |    2 
 glx/glxdrawable.h         |    2 
 glx/glxdri.c              |    2 
 glx/glxdri2.c             |   16 +
 glx/swap_interval.c       |    5 
 hw/xfree86/dri2/dri2.c    |  398 ++++++++++++++++++++++++++++++++++++++++++----
 hw/xfree86/dri2/dri2.h    |   39 ++++
 hw/xfree86/dri2/dri2ext.c |  154 ++++++++++++++++-
 8 files changed, 571 insertions(+), 47 deletions(-)

New commits:
commit 0290b68d3ea9d8f50f850c67d41074429aa6dd7e
Author: Jesse Barnes <jbarnes at virtuousgeek.org>
Date:   Thu Sep 3 14:23:52 2009 -0700

    DRI2: initial MSC support
    
    Add support for MSC queries and waits along the same lines as asynchronous
    swapbuffers.

diff --git a/glx/glxcmds.c b/glx/glxcmds.c
index f5632d1..d5e33e1 100644
--- a/glx/glxcmds.c
+++ b/glx/glxcmds.c
@@ -1481,7 +1481,7 @@ int __glXDisp_SwapBuffers(__GLXclientState *cl, GLbyte *pc)
 	return error;
 
     if (pGlxDraw->type == DRAWABLE_WINDOW &&
-	(*pGlxDraw->swapBuffers)(pGlxDraw) == GL_FALSE)
+	(*pGlxDraw->swapBuffers)(cl->client, pGlxDraw) == GL_FALSE)
 	return __glXError(GLXBadDrawable);
 
     return Success;
diff --git a/glx/glxdrawable.h b/glx/glxdrawable.h
index 3f165ed..2a365c5 100644
--- a/glx/glxdrawable.h
+++ b/glx/glxdrawable.h
@@ -45,7 +45,7 @@ enum {
 
 struct __GLXdrawable {
     void (*destroy)(__GLXdrawable *private);
-    GLboolean (*swapBuffers)(__GLXdrawable *);
+    GLboolean (*swapBuffers)(ClientPtr client, __GLXdrawable *);
     void      (*copySubBuffer)(__GLXdrawable *drawable,
 			       int x, int y, int w, int h);
     void      (*waitX)(__GLXdrawable *);
diff --git a/glx/glxdri.c b/glx/glxdri.c
index c9d226b..8537609 100644
--- a/glx/glxdri.c
+++ b/glx/glxdri.c
@@ -245,7 +245,7 @@ __glXDRIdrawableDestroy(__GLXdrawable *drawable)
 }
 
 static GLboolean
-__glXDRIdrawableSwapBuffers(__GLXdrawable *basePrivate)
+__glXDRIdrawableSwapBuffers(ClientPtr client, __GLXdrawable *basePrivate)
 {
     __GLXDRIdrawable *private = (__GLXDRIdrawable *) basePrivate;
     __GLXDRIscreen *screen =
diff --git a/glx/glxdri2.c b/glx/glxdri2.c
index cdf6794..c7a797e 100644
--- a/glx/glxdri2.c
+++ b/glx/glxdri2.c
@@ -174,18 +174,15 @@ __glXDRIdrawableWaitGL(__GLXdrawable *drawable)
  * swap should happen, then perform the copy when we receive it.
  */
 static GLboolean
-__glXDRIdrawableSwapBuffers(__GLXdrawable *drawable)
+__glXDRIdrawableSwapBuffers(ClientPtr client, __GLXdrawable *drawable)
 {
     __GLXDRIdrawable *priv = (__GLXDRIdrawable *) drawable;
     __GLXDRIscreen *screen = priv->screen;
-    int interval = 1;
-
-    if (screen->swapControl)
-	interval = screen->swapControl->getSwapInterval(priv->driDrawable);
+    CARD64 unused;
 
     (*screen->flush->flushInvalidate)(priv->driDrawable);
 
-    if (DRI2SwapBuffers(drawable->pDraw, interval) != Success)
+    if (DRI2SwapBuffers(client, drawable->pDraw, 0, 0, 0, &unused) != Success)
 	return FALSE;
 
     return TRUE;
@@ -194,12 +191,10 @@ __glXDRIdrawableSwapBuffers(__GLXdrawable *drawable)
 static int
 __glXDRIdrawableSwapInterval(__GLXdrawable *drawable, int interval)
 {
-    __GLXDRIdrawable *draw = (__GLXDRIdrawable *) drawable;
-    __GLXDRIscreen *screen =
-	(__GLXDRIscreen *) glxGetScreen(drawable->pDraw->pScreen);
+    if (interval <= 0) /* || interval > BIGNUM? */
+	return GLX_BAD_VALUE;
 
-    if (screen->swapControl)
-	screen->swapControl->setSwapInterval(draw->driDrawable, interval);
+    DRI2SwapInterval(drawable->pDraw, interval);
 
     return 0;
 }
diff --git a/hw/xfree86/dri2/dri2.c b/hw/xfree86/dri2/dri2.c
index e462db5..0cbcc7e 100644
--- a/hw/xfree86/dri2/dri2.c
+++ b/hw/xfree86/dri2/dri2.c
@@ -34,6 +34,7 @@
 #include <xorg-config.h>
 #endif
 
+#include <errno.h>
 #include <xf86drm.h>
 #include "xf86Module.h"
 #include "scrnintstr.h"
@@ -59,17 +60,75 @@ typedef struct _DRI2Drawable {
     unsigned int	 swapsPending;
     unsigned int	 flipsPending;
     ClientPtr		 blockedClient;
+    int			 swap_interval;
+    CARD64		 swap_count;
+    CARD64		 last_swap_target; /* most recently queued swap target */
 } DRI2DrawableRec, *DRI2DrawablePtr;
 
 typedef struct _DRI2Screen *DRI2ScreenPtr;
-typedef struct _DRI2SwapData *DRI2SwapDataPtr;
+typedef struct _DRI2FrameEvent *DRI2FrameEventPtr;
 
-typedef struct _DRI2SwapData {
+#define container_of(ptr,type,mem) ((type *)((char *)(ptr) - offsetof(type, \
+								      mem)))
+
+struct list {
+    struct list *prev, *next;
+};
+
+static inline void list_init(struct list *l)
+{
+    l->prev = l;
+    l->next = l;
+}
+
+static inline void __list_add(struct list *l, struct list *prev,
+			      struct list *next)
+{
+    prev->next = l;
+    l->prev = prev;
+    l->next = next;
+    next->prev = l;
+}
+
+static inline void list_add(struct list *l, struct list *head)
+{
+    __list_add(l, head, head->next);
+}
+
+static inline void list_add_tail(struct list *l, struct list *head)
+{
+    __list_add(l, head->prev, head);
+}
+
+static inline void list_del(struct list *l)
+{
+    l->prev->next = l->next;
+    l->next->prev = l->prev;
+    list_init(l);
+}
+
+static inline Bool list_is_empty(struct list *l)
+{
+    return l->next == l;
+}
+
+#define list_foreach_safe(cur, tmp, head)			\
+    for (cur = (head)->next, tmp = cur->next; cur != (head);	\
+	 cur = tmp, tmp = cur->next)
+
+enum DRI2FrameEventType {
+    DRI2_SWAP,
+    DRI2_WAITMSC,
+};
+
+typedef struct _DRI2FrameEvent {
     DrawablePtr		 pDraw;
     ScreenPtr		 pScreen;
+    ClientPtr		 client;
+    enum DRI2FrameEventType type;
     int			 frame;
-    DRI2SwapDataPtr	 next;
-} DRI2SwapDataRec;
+    struct list		 link;
+} DRI2FrameEventRec;
 
 typedef struct _DRI2Screen {
     const char			*driverName;
@@ -77,12 +136,15 @@ typedef struct _DRI2Screen {
     int				 fd;
     unsigned int		 lastSequence;
     drmEventContext		 event_context;
-    DRI2SwapDataPtr		 swaps;	    /* Pending swap list */
+    struct list			 swaps;
 
     DRI2CreateBufferProcPtr	 CreateBuffer;
     DRI2DestroyBufferProcPtr	 DestroyBuffer;
     DRI2CopyRegionProcPtr	 CopyRegion;
+    DRI2SetupSwapProcPtr	 SetupSwap;
     DRI2SwapBuffersProcPtr	 SwapBuffers;
+    DRI2GetMSCProcPtr		 GetMSC;
+    DRI2SetupWaitMSCProcPtr	 SetupWaitMSC;
 
     HandleExposuresProcPtr       HandleExposures;
 } DRI2ScreenRec;
@@ -140,6 +202,9 @@ DRI2CreateDrawable(DrawablePtr pDraw)
     pPriv->swapsPending = 0;
     pPriv->flipsPending = 0;
     pPriv->blockedClient = NULL;
+    pPriv->swap_count = 0;
+    pPriv->swap_interval = 1;
+    pPriv->last_swap_target = 0;
 
     if (pDraw->type == DRAWABLE_WINDOW)
     {
@@ -382,46 +447,66 @@ DRI2FlipCheck(DrawablePtr pDraw)
     return TRUE;
 }
 
-static Bool DRI2AddSwap(DrawablePtr pDraw, int frame)
+static Bool DRI2AddFrameEvent(DrawablePtr pDraw, ClientPtr client,
+			      enum DRI2FrameEventType type, int frame)
 {
     DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
     DRI2DrawablePtr pPriv;
-    DRI2SwapDataPtr new;
+    DRI2FrameEventPtr new;
 
     pPriv = DRI2GetDrawable(pDraw);
     if (pPriv == NULL)
 	return FALSE;
 
-    new = xcalloc(1, sizeof(DRI2SwapDataRec));
+    new = xcalloc(1, sizeof(DRI2FrameEventRec));
     if (!new)
 	return FALSE;
 
     new->pScreen = pDraw->pScreen;
     new->pDraw = pDraw;
+    new->client = client;
     new->frame = frame;
-    new->next = ds->swaps;
-    ds->swaps = new;
+    new->type = type;
+
+    list_add_tail(&new->link, &ds->swaps);
 
     return TRUE;
 }
 
-static void DRI2RemoveSwap(DRI2SwapDataPtr swap)
+static void DRI2RemoveFrameEvent(DRI2FrameEventPtr event)
 {
-    ScreenPtr	    pScreen = swap->pScreen;
-    DRI2ScreenPtr   ds = DRI2GetScreen(pScreen);
-    DRI2SwapDataPtr cur = ds->swaps;
+    list_del(&event->link);
+    xfree(event);
+}
 
-    while (cur) {
-	if (cur == swap) {
-	    cur->next = swap->next;
-	    xfree(swap);
-	}
-	cur = cur->next;
-    }
+static void
+DRI2WaitMSCComplete(DRI2FrameEventPtr swap, unsigned int sequence,
+		    unsigned int tv_sec, unsigned int tv_usec)
+{
+    DrawablePtr	    pDraw = swap->pDraw;
+    DRI2DrawablePtr pPriv;
+
+    pPriv = DRI2GetDrawable(pDraw);
+    if (pPriv == NULL)
+	return;
+
+    ProcDRI2WaitMSCReply(swap->client, ((CARD64)tv_sec * 1000000) + tv_usec,
+			 sequence, pPriv->swap_count);
+
+    if (pPriv->blockedClient)
+	AttendClient(pPriv->blockedClient);
+
+    pPriv->blockedClient = NULL;
+}
+
+/* Wake up clients waiting for flip/swap completion */
+static void DRI2SwapComplete(DRI2DrawablePtr pPriv)
+{
+    pPriv->swap_count++;
 }
 
 static void
-DRI2SwapSubmit(DRI2SwapDataPtr swap)
+DRI2SwapSubmit(DRI2FrameEventPtr swap)
 {
     DrawablePtr	    pDraw = swap->pDraw;
     ScreenPtr	    pScreen = swap->pScreen;
@@ -471,8 +556,7 @@ DRI2SwapSubmit(DRI2SwapDataPtr swap)
     box.y2 = pPriv->height;
     REGION_INIT(pScreen, &region, &box, 0);
 
-    DRI2CopyRegion(pDraw, &region,
-		   DRI2BufferFrontLeft, DRI2BufferBackLeft);
+    DRI2CopyRegion(pDraw, &region, DRI2BufferFrontLeft, DRI2BufferBackLeft);
     pPriv->swapsPending--;
 
     DRI2SwapComplete(pPriv);
@@ -482,14 +566,39 @@ static void drm_vblank_handler(int fd, unsigned int frame, unsigned int tv_sec,
 			       unsigned int tv_usec, void *user_data)
 {
     DRI2ScreenPtr ds = user_data;
-    DRI2SwapDataPtr cur = ds->swaps;
+    DRI2DrawablePtr pPriv;
+    struct list *cur, *tmp;
+
+    if (list_is_empty(&ds->swaps)) {
+	ErrorF("tried to dequeue non-existent swap\n");
+	return;
+    }
 
-    while (cur) {
-	if (cur->frame == frame) {
-	    DRI2SwapSubmit(cur);
-	    DRI2RemoveSwap(cur);
+    list_foreach_safe(cur, tmp, &ds->swaps) {
+	DRI2FrameEventPtr swap = container_of(cur, DRI2FrameEventRec, link);
+
+	if (swap->frame != frame)
+	    continue;
+
+	pPriv = DRI2GetDrawable(swap->pDraw);
+	if (pPriv == NULL) {
+	    DRI2RemoveFrameEvent(swap);
+	    ErrorF("no dri2priv??\n");
+	    continue; /* FIXME: check priv refcounting */
+	}
+
+	switch (swap->type) {
+	case DRI2_SWAP:
+	    DRI2SwapSubmit(swap);
+	    break;
+	case DRI2_WAITMSC:
+	    DRI2WaitMSCComplete(swap, frame, tv_sec, tv_usec);
+	    break;
+	default:
+	    /* Unknown type */
+	    break;
 	}
-	cur = cur->next;
+	DRI2RemoveFrameEvent(swap);
     }
 }
 
@@ -504,17 +613,24 @@ drm_wakeup_handler(pointer data, int err, pointer p)
 }
 
 int
-DRI2SwapBuffers(DrawablePtr pDraw, int interval)
+DRI2SwapBuffers(ClientPtr client, DrawablePtr pDraw, CARD64 target_msc,
+		CARD64 divisor, CARD64 remainder, CARD64 *swap_target)
 {
     DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
     DRI2DrawablePtr pPriv;
-    drmVBlank       vbl;
+    CARD64	    event_frame;
+    int             ret;
 
     pPriv = DRI2GetDrawable(pDraw);
     if (pPriv == NULL)
 	return BadDrawable;
 
-    vbl.request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT;
+    /*
+     * Swap target for this swap is last swap target + swap interval since
+     * we have to account for the current swap count, interval, and the
+     * number of pending swaps.
+     */
+    *swap_target = pPriv->last_swap_target + pPriv->swap_interval;
 
     if (DRI2FlipCheck(pDraw)) {
 	/*
@@ -522,25 +638,28 @@ DRI2SwapBuffers(DrawablePtr pDraw, int interval)
 	 * frame - 1 to honor the swap interval.
 	 */
 	pPriv->flipsPending++;
-	if (interval > 1) {
-	    vbl.request.sequence = interval - 1;
+	if (pPriv->swap_interval > 1) {
+	    *swap_target = *swap_target - 1;
 	    /* fixme: prevent cliprect changes between now and the flip */
 	} else {
+	    /* FIXME: perform immediate page flip */
 	    DRI2SwapComplete(pPriv);
-	    return Success;
+	    return 0;
 	}
     } else {
 	pPriv->swapsPending++;
-	vbl.request.sequence = interval;
     }
 
-    /* fixme: get correct crtc for this drawable */
-    drmWaitVBlank(ds->fd, &vbl);
+    ret = (*ds->SetupSwap)(pDraw, *swap_target, divisor, remainder, ds,
+			   &event_frame);
+    if (!ret)
+	return BadDrawable;
 
-    /* Request an event for the requested frame */
-    if (!DRI2AddSwap(pDraw, vbl.reply.sequence))
+    if (!DRI2AddFrameEvent(pDraw, client, DRI2_SWAP, event_frame))
 	return BadValue;
 
+    pPriv->last_swap_target = *swap_target;
+
     return Success;
 }
 
@@ -564,15 +683,88 @@ DRI2WaitSwap(ClientPtr client, DrawablePtr pDrawable)
     return FALSE;
 }
 
-/* Wake up clients waiting for flip/swap completion */
-void DRI2SwapComplete(void *data)
+void
+DRI2SwapInterval(DrawablePtr pDrawable, int interval)
 {
-    DRI2DrawablePtr pPriv = data;
+    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDrawable);
 
-    if (pPriv->blockedClient)
-	AttendClient(pPriv->blockedClient);
+    /* fixme: check against arbitrary max? */
 
-    pPriv->blockedClient = NULL;
+    pPriv->swap_interval = interval;
+}
+
+
+
+int
+DRI2GetMSC(DrawablePtr pDraw, CARD64 *ust, CARD64 *msc, CARD64 *sbc)
+{
+    DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
+    DRI2DrawablePtr pPriv;
+    Bool ret;
+
+    pPriv = DRI2GetDrawable(pDraw);
+    if (pPriv == NULL)
+	return BadDrawable;
+
+    if (!ds->GetMSC)
+	FatalError("advertised MSC support w/o driver hook\n");
+
+    ret = (*ds->GetMSC)(pDraw, ust, msc);
+    if (!ret)
+	return BadDrawable;
+
+    *sbc = pPriv->swap_count;
+
+    return Success;
+}
+
+int
+DRI2WaitMSC(ClientPtr client, DrawablePtr pDraw, CARD64 target_msc,
+	    CARD64 divisor, CARD64 remainder)
+{
+    DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
+    DRI2DrawablePtr pPriv;
+    CARD64 event_frame;
+    Bool ret;
+
+    pPriv = DRI2GetDrawable(pDraw);
+    if (pPriv == NULL)
+	return BadDrawable;
+
+    ret = (*ds->SetupWaitMSC)(pDraw, target_msc, divisor, remainder, ds,
+			      &event_frame);
+    if (!ret) {
+	ErrorF("setupmsc failed: %d\n", ret);
+	return BadDrawable;
+    }
+
+    ret = DRI2AddFrameEvent(pDraw, client, DRI2_WAITMSC, event_frame);
+    if (!ret)
+	return BadDrawable;
+
+    /* DDX returned > 0, block the client until its wait completes */
+
+    if (pPriv->blockedClient == NULL) {
+	IgnoreClient(client);
+	pPriv->blockedClient = client;
+    }
+
+    return Success;
+}
+
+int
+DRI2WaitSBC(DrawablePtr pDraw, CARD64 target_sbc, CARD64 *ust, CARD64 *msc,
+	    CARD64 *sbc)
+{
+    DRI2DrawablePtr pPriv;
+
+    pPriv = DRI2GetDrawable(pDraw);
+    if (pPriv == NULL)
+	return BadDrawable;
+
+    /* fixme: put client to sleep until swap count hits target */
+
+    return Success;
 }
 
 void
@@ -668,12 +860,18 @@ DRI2ScreenInit(ScreenPtr pScreen, DRI2InfoPtr info)
     ds->DestroyBuffer  = info->DestroyBuffer;
     ds->CopyRegion     = info->CopyRegion;
 
-    if (info->version >= 4)
+    if (info->version >= 4) {
+	ds->SetupSwap = info->SetupSwap;
 	ds->SwapBuffers = info->SwapBuffers;
+	ds->SetupWaitMSC = info->SetupWaitMSC;
+	ds->GetMSC = info->GetMSC;
+    }
 
     ds->event_context.version = DRM_EVENT_CONTEXT_VERSION;
     ds->event_context.vblank_handler = drm_vblank_handler;
 
+    list_init(&ds->swaps);
+
     AddGeneralSocket(ds->fd);
     RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA,
 				   drm_wakeup_handler, ds);
diff --git a/hw/xfree86/dri2/dri2.h b/hw/xfree86/dri2/dri2.h
index e8f12d1..57f34bf 100644
--- a/hw/xfree86/dri2/dri2.h
+++ b/hw/xfree86/dri2/dri2.h
@@ -58,6 +58,12 @@ typedef void		(*DRI2CopyRegionProcPtr)(DrawablePtr pDraw,
 						 RegionPtr pRegion,
 						 DRI2BufferPtr pDestBuffer,
 						 DRI2BufferPtr pSrcBuffer);
+typedef int		(*DRI2SetupSwapProcPtr)(DrawablePtr pDraw,
+						CARD64 target_msc,
+						CARD64 divisor,
+						CARD64 remainder,
+						void *data,
+						CARD64 *event_frame);
 typedef Bool		(*DRI2SwapBuffersProcPtr)(ScreenPtr pScreen,
 						  DRI2BufferPtr pFrontBuffer,
 						  DRI2BufferPtr pBackBuffer,
@@ -71,6 +77,14 @@ typedef DRI2BufferPtr	(*DRI2CreateBufferProcPtr)(DrawablePtr pDraw,
 						   unsigned int format);
 typedef void		(*DRI2DestroyBufferProcPtr)(DrawablePtr pDraw,
 						    DRI2BufferPtr buffer);
+typedef int		(*DRI2GetMSCProcPtr)(DrawablePtr pDraw, CARD64 *ust,
+					     CARD64 *msc);
+typedef int		(*DRI2SetupWaitMSCProcPtr)(DrawablePtr pDraw,
+						   CARD64 target_msc,
+						   CARD64 divisor,
+						   CARD64 remainder,
+						   void *data,
+						   CARD64 *event_frame);
 
 /**
  * Version of the DRI2InfoRec structure defined in this header
@@ -86,8 +100,10 @@ typedef struct {
     DRI2CreateBufferProcPtr	CreateBuffer;
     DRI2DestroyBufferProcPtr	DestroyBuffer;
     DRI2CopyRegionProcPtr	CopyRegion;
+    DRI2SetupSwapProcPtr	SetupSwap;
     DRI2SwapBuffersProcPtr	SwapBuffers;
-
+    DRI2GetMSCProcPtr		GetMSC;
+    DRI2SetupWaitMSCProcPtr	SetupWaitMSC;
 }  DRI2InfoRec, *DRI2InfoPtr;
 
 extern _X_EXPORT Bool DRI2ScreenInit(ScreenPtr	pScreen,
@@ -141,8 +157,23 @@ extern _X_EXPORT DRI2BufferPtr *DRI2GetBuffersWithFormat(DrawablePtr pDraw,
 	int *width, int *height, unsigned int *attachments, int count,
 	int *out_count);
 
-extern _X_EXPORT int DRI2SwapBuffers(DrawablePtr pDrawable, int interval);
+extern _X_EXPORT void DRI2SwapInterval(DrawablePtr pDrawable, int interval);
+extern _X_EXPORT int DRI2SwapBuffers(ClientPtr client, DrawablePtr pDrawable,
+				     CARD64 target_msc, CARD64 divisor,
+				     CARD64 remainder, CARD64 *swap_target);
 extern _X_EXPORT Bool DRI2WaitSwap(ClientPtr client, DrawablePtr pDrawable);
-extern _X_EXPORT void DRI2SwapComplete(void *data);
+
+extern _X_EXPORT int DRI2GetMSC(DrawablePtr pDrawable, CARD64 *ust,
+				CARD64 *msc, CARD64 *sbc);
+extern _X_EXPORT int DRI2WaitMSC(ClientPtr client, DrawablePtr pDrawable,
+				 CARD64 target_msc, CARD64 divisor,
+				 CARD64 remainder);
+extern _X_EXPORT int ProcDRI2WaitMSCReply(ClientPtr client, CARD64 ust,
+					  CARD64 msc, CARD64 sbc);
+extern _X_EXPORT int DRI2WaitSBC(DrawablePtr pDraw, CARD64 target_sbc,
+				 CARD64 *ust, CARD64 *msc, CARD64 *sbc);
+#define DRI2_WAITMSC_ERROR -1
+#define DRI2_WAITMSC_DONE 0
+#define DRI2_WAITMSC_BLOCK 1
 
 #endif
diff --git a/hw/xfree86/dri2/dri2ext.c b/hw/xfree86/dri2/dri2ext.c
index d0fd341..012a7a2 100644
--- a/hw/xfree86/dri2/dri2ext.c
+++ b/hw/xfree86/dri2/dri2ext.c
@@ -253,9 +253,6 @@ ProcDRI2GetBuffers(ClientPtr client)
     if (!validDrawable(client, stuff->drawable, &pDrawable, &status))
 	return status;
 
-    if (DRI2WaitSwap(client, pDrawable))
-	return client->noClientException;
-
     attachments = (unsigned int *) &stuff[1];
     buffers = DRI2GetBuffers(pDrawable, &width, &height,
 			     attachments, stuff->count, &count);
@@ -279,9 +276,6 @@ ProcDRI2GetBuffersWithFormat(ClientPtr client)
     if (!validDrawable(client, stuff->drawable, &pDrawable, &status))
 	return status;
 
-    if (DRI2WaitSwap(client, pDrawable))
-	return client->noClientException;
-
     attachments = (unsigned int *) &stuff[1];
     buffers = DRI2GetBuffersWithFormat(pDrawable, &width, &height,
 				       attachments, stuff->count, &count);
@@ -327,11 +321,26 @@ ProcDRI2CopyRegion(ClientPtr client)
     return client->noClientException;
 }
 
+static void
+load_swap_reply(xDRI2SwapBuffersReply *rep, CARD64 sbc)
+{
+    rep->swap_hi = sbc >> 32;
+    rep->swap_lo = sbc & 0xffffffff;
+}
+
+static CARD64
+vals_to_card64(CARD32 lo, CARD32 hi)
+{
+    return (CARD64)hi << 32 | lo;
+}
+
 static int
 ProcDRI2SwapBuffers(ClientPtr client)
 {
     REQUEST(xDRI2SwapBuffersReq);
+    xDRI2SwapBuffersReply rep;
     DrawablePtr pDrawable;
+    CARD64 target_msc, divisor, remainder, swap_target;
     int status;
 
     REQUEST_SIZE_MATCH(xDRI2SwapBuffersReq);
@@ -339,7 +348,132 @@ ProcDRI2SwapBuffers(ClientPtr client)
     if (!validDrawable(client, stuff->drawable, &pDrawable, &status))
 	return status;
 
-    return DRI2SwapBuffers(pDrawable, 0); /* get swap interval... */
+    target_msc = vals_to_card64(stuff->target_msc_lo, stuff->target_msc_hi);
+    divisor = vals_to_card64(stuff->divisor_lo, stuff->divisor_hi);
+    remainder = vals_to_card64(stuff->remainder_lo, stuff->remainder_hi);
+
+    status = DRI2SwapBuffers(client, pDrawable, target_msc, divisor, remainder,
+			     &swap_target);
+    if (status != Success)
+	return BadDrawable;
+
+    rep.type = X_Reply;
+    rep.length = 0;
+    rep.sequenceNumber = client->sequence;
+    load_swap_reply(&rep, swap_target);
+
+    WriteToClient(client, sizeof(xDRI2SwapBuffersReply), &rep);
+
+    return client->noClientException;
+}
+
+static void
+load_msc_reply(xDRI2MSCReply *rep, CARD64 ust, CARD64 msc, CARD64 sbc)
+{
+    rep->ust_hi = ust >> 32;
+    rep->ust_lo = ust & 0xffffffff;
+    rep->msc_hi = msc >> 32;
+    rep->msc_lo = msc & 0xffffffff;
+    rep->sbc_hi = sbc >> 32;
+    rep->sbc_lo = sbc & 0xffffffff;
+}
+
+static int
+ProcDRI2GetMSC(ClientPtr client)
+{
+    REQUEST(xDRI2GetMSCReq);
+    xDRI2MSCReply rep;
+    DrawablePtr pDrawable;
+    CARD64 ust, msc, sbc;
+    int status;
+
+    REQUEST_SIZE_MATCH(xDRI2GetMSCReq);
+
+    if (!validDrawable(client, stuff->drawable, &pDrawable, &status))
+	return status;
+
+    status = DRI2GetMSC(pDrawable, &ust, &msc, &sbc);
+    if (status != Success)
+	return status;
+
+    rep.type = X_Reply;
+    rep.length = 0;
+    rep.sequenceNumber = client->sequence;
+    load_msc_reply(&rep, ust, msc, sbc);
+
+    WriteToClient(client, sizeof(xDRI2MSCReply), &rep);
+
+    return client->noClientException;
+}
+
+static int
+ProcDRI2WaitMSC(ClientPtr client)
+{
+    REQUEST(xDRI2WaitMSCReq);
+    DrawablePtr pDrawable;
+    CARD64 target, divisor, remainder;
+    int status;
+
+    /* FIXME: in restart case, client may be gone at this point */
+
+    REQUEST_SIZE_MATCH(xDRI2WaitMSCReq);
+
+    if (!validDrawable(client, stuff->drawable, &pDrawable, &status))
+	return status;
+
+    target = vals_to_card64(stuff->target_msc_lo, stuff->target_msc_hi);
+    divisor = vals_to_card64(stuff->divisor_lo, stuff->divisor_hi);
+    remainder = vals_to_card64(stuff->remainder_lo, stuff->remainder_hi);
+
+    status = DRI2WaitMSC(client, pDrawable, target, divisor, remainder);
+    if (status != Success)
+	return status;
+
+    return client->noClientException;
+}
+
+int
+ProcDRI2WaitMSCReply(ClientPtr client, CARD64 ust, CARD64 msc, CARD64 sbc)
+{
+    xDRI2MSCReply rep;
+
+    rep.type = X_Reply;
+    rep.length = 0;
+    rep.sequenceNumber = client->sequence;
+    load_msc_reply(&rep, ust, msc, sbc);
+
+    WriteToClient(client, sizeof(xDRI2MSCReply), &rep);
+
+    return client->noClientException;
+}
+
+static int
+ProcDRI2WaitSBC(ClientPtr client)
+{
+    REQUEST(xDRI2WaitSBCReq);
+    xDRI2MSCReply rep;
+    DrawablePtr pDrawable;
+    CARD64 target, ust, msc, sbc;
+    int status;
+
+    REQUEST_SIZE_MATCH(xDRI2WaitSBCReq);
+
+    if (!validDrawable(client, stuff->drawable, &pDrawable, &status))
+	return status;
+
+    target = vals_to_card64(stuff->target_sbc_lo, stuff->target_sbc_hi);
+    status = DRI2WaitSBC(pDrawable, target, &ust, &msc, &sbc);
+    if (status != Success)
+	return status;
+
+    rep.type = X_Reply;
+    rep.length = 0;
+    rep.sequenceNumber = client->sequence;
+    load_msc_reply(&rep, ust, msc, sbc);
+
+    WriteToClient(client, sizeof(xDRI2MSCReply), &rep);
+
+    return client->noClientException;
 }
 
 static int
@@ -372,6 +506,12 @@ ProcDRI2Dispatch (ClientPtr client)
 	return ProcDRI2GetBuffersWithFormat(client);
     case X_DRI2SwapBuffers:
 	return ProcDRI2SwapBuffers(client);
+    case X_DRI2GetMSC:
+	return ProcDRI2GetMSC(client);
+    case X_DRI2WaitMSC:
+	return ProcDRI2WaitMSC(client);
+    case X_DRI2WaitSBC:
+	return ProcDRI2WaitSBC(client);
     default:
 	return BadRequest;
     }
commit 9419b678f41dcd5658e83ae644a7a3923030ca9f
Author: Jesse Barnes <jbarnes at virtuousgeek.org>
Date:   Wed Sep 2 13:59:56 2009 -0700

    DRI2: add support for scheduled swaps & flips for swap interval support
    
    Add support for scheduling swaps and flips in the future and tie that into a
    swap interval handling infrastructure.
    
    At swapbuffers time, queue a blit or flip and request a kernel event when the
    specified frame number is reached.  Once that event is received, perform the
    blit or flip and unblock the client if necessary.

diff --git a/glx/glxdri2.c b/glx/glxdri2.c
index 4b89c31..cdf6794 100644
--- a/glx/glxdri2.c
+++ b/glx/glxdri2.c
@@ -167,15 +167,25 @@ __glXDRIdrawableWaitGL(__GLXdrawable *drawable)
 		   DRI2BufferFrontLeft, DRI2BufferFakeFrontLeft);
 }
 
+/*
+ * Copy or flip back to front, honoring the swap interval if possible.
+ *
+ * If the kernel supports it, we request an event for the frame when the
+ * swap should happen, then perform the copy when we receive it.
+ */
 static GLboolean
 __glXDRIdrawableSwapBuffers(__GLXdrawable *drawable)
 {
     __GLXDRIdrawable *priv = (__GLXDRIdrawable *) drawable;
     __GLXDRIscreen *screen = priv->screen;
+    int interval = 1;
+
+    if (screen->swapControl)
+	interval = screen->swapControl->getSwapInterval(priv->driDrawable);
 
     (*screen->flush->flushInvalidate)(priv->driDrawable);
 
-    if (DRI2SwapBuffers(drawable->pDraw) != Success)
+    if (DRI2SwapBuffers(drawable->pDraw, interval) != Success)
 	return FALSE;
 
     return TRUE;
@@ -184,6 +194,13 @@ __glXDRIdrawableSwapBuffers(__GLXdrawable *drawable)
 static int
 __glXDRIdrawableSwapInterval(__GLXdrawable *drawable, int interval)
 {
+    __GLXDRIdrawable *draw = (__GLXDRIdrawable *) drawable;
+    __GLXDRIscreen *screen =
+	(__GLXDRIscreen *) glxGetScreen(drawable->pDraw->pScreen);
+
+    if (screen->swapControl)
+	screen->swapControl->setSwapInterval(draw->driDrawable, interval);
+
     return 0;
 }
 
diff --git a/glx/swap_interval.c b/glx/swap_interval.c
index 3a52420..728944b 100644
--- a/glx/swap_interval.c
+++ b/glx/swap_interval.c
@@ -68,7 +68,7 @@ int DoSwapInterval(__GLXclientState *cl, GLbyte *pc, int do_swap)
 
     if (cx->drawPriv == NULL) {
 	client->errorValue = tag;
-	return __glXError(GLXBadDrawable);
+	return BadValue;
     }
     
     pc += __GLX_VENDPRIV_HDR_SIZE;
@@ -76,6 +76,9 @@ int DoSwapInterval(__GLXclientState *cl, GLbyte *pc, int do_swap)
       ? bswap_32(*(int *)(pc + 0))
       :          *(int *)(pc + 0);
 
+    if (interval <= 0)
+	return BadValue;
+
     (void) (*cx->pGlxScreen->swapInterval)(cx->drawPriv, interval);
     return Success;
 }
diff --git a/hw/xfree86/dri2/dri2.c b/hw/xfree86/dri2/dri2.c
index 8f5e0c3..e462db5 100644
--- a/hw/xfree86/dri2/dri2.c
+++ b/hw/xfree86/dri2/dri2.c
@@ -56,15 +56,28 @@ typedef struct _DRI2Drawable {
     int			 height;
     DRI2BufferPtr	*buffers;
     int			 bufferCount;
-    unsigned int	 swapPending;
+    unsigned int	 swapsPending;
+    unsigned int	 flipsPending;
     ClientPtr		 blockedClient;
 } DRI2DrawableRec, *DRI2DrawablePtr;
 
+typedef struct _DRI2Screen *DRI2ScreenPtr;
+typedef struct _DRI2SwapData *DRI2SwapDataPtr;
+
+typedef struct _DRI2SwapData {
+    DrawablePtr		 pDraw;
+    ScreenPtr		 pScreen;
+    int			 frame;
+    DRI2SwapDataPtr	 next;
+} DRI2SwapDataRec;
+
 typedef struct _DRI2Screen {
     const char			*driverName;
     const char			*deviceName;
     int				 fd;
     unsigned int		 lastSequence;
+    drmEventContext		 event_context;
+    DRI2SwapDataPtr		 swaps;	    /* Pending swap list */
 
     DRI2CreateBufferProcPtr	 CreateBuffer;
     DRI2DestroyBufferProcPtr	 DestroyBuffer;
@@ -72,7 +85,7 @@ typedef struct _DRI2Screen {
     DRI2SwapBuffersProcPtr	 SwapBuffers;
 
     HandleExposuresProcPtr       HandleExposures;
-} DRI2ScreenRec, *DRI2ScreenPtr;
+} DRI2ScreenRec;
 
 static DRI2ScreenPtr
 DRI2GetScreen(ScreenPtr pScreen)
@@ -86,6 +99,9 @@ DRI2GetDrawable(DrawablePtr pDraw)
     WindowPtr		  pWin;
     PixmapPtr		  pPixmap;
 
+    if (!pDraw)
+	return NULL;
+
     if (pDraw->type == DRAWABLE_WINDOW)
     {
 	pWin = (WindowPtr) pDraw;
@@ -121,7 +137,8 @@ DRI2CreateDrawable(DrawablePtr pDraw)
     pPriv->height = pDraw->height;
     pPriv->buffers = NULL;
     pPriv->bufferCount = 0;
-    pPriv->swapPending = FALSE;
+    pPriv->swapsPending = 0;
+    pPriv->flipsPending = 0;
     pPriv->blockedClient = NULL;
 
     if (pDraw->type == DRAWABLE_WINDOW)
@@ -365,19 +382,64 @@ DRI2FlipCheck(DrawablePtr pDraw)
     return TRUE;
 }
 
-int
-DRI2SwapBuffers(DrawablePtr pDraw)
+static Bool DRI2AddSwap(DrawablePtr pDraw, int frame)
 {
     DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
     DRI2DrawablePtr pPriv;
+    DRI2SwapDataPtr new;
+
+    pPriv = DRI2GetDrawable(pDraw);
+    if (pPriv == NULL)
+	return FALSE;
+
+    new = xcalloc(1, sizeof(DRI2SwapDataRec));
+    if (!new)
+	return FALSE;
+
+    new->pScreen = pDraw->pScreen;
+    new->pDraw = pDraw;
+    new->frame = frame;
+    new->next = ds->swaps;
+    ds->swaps = new;
+
+    return TRUE;
+}
+
+static void DRI2RemoveSwap(DRI2SwapDataPtr swap)
+{
+    ScreenPtr	    pScreen = swap->pScreen;
+    DRI2ScreenPtr   ds = DRI2GetScreen(pScreen);
+    DRI2SwapDataPtr cur = ds->swaps;
+
+    while (cur) {
+	if (cur == swap) {
+	    cur->next = swap->next;
+	    xfree(swap);
+	}
+	cur = cur->next;
+    }
+}
+
+static void
+DRI2SwapSubmit(DRI2SwapDataPtr swap)
+{
+    DrawablePtr	    pDraw = swap->pDraw;
+    ScreenPtr	    pScreen = swap->pScreen;
+    DRI2ScreenPtr   ds = DRI2GetScreen(pScreen);
+    DRI2DrawablePtr pPriv;
     DRI2BufferPtr   pDestBuffer, pSrcBuffer;
-    int		    i;
     BoxRec	    box;
     RegionRec	    region;
+    int             ret, i;
 
     pPriv = DRI2GetDrawable(pDraw);
     if (pPriv == NULL)
-	return BadDrawable;
+	return;
+
+    if (pPriv->refCount == 0) {
+	xfree(pPriv);
+	return;
+    }
 
     pDestBuffer = NULL;
     pSrcBuffer = NULL;
@@ -389,23 +451,97 @@ DRI2SwapBuffers(DrawablePtr pDraw)
 	    pSrcBuffer = pPriv->buffers[i];
     }
     if (pSrcBuffer == NULL || pDestBuffer == NULL)
-	return BadValue;
+	return;
 
-    if (DRI2FlipCheck(pDraw) &&
-	(*ds->SwapBuffers)(pDraw, pDestBuffer, pSrcBuffer, pPriv))
-    {
-	pPriv->swapPending = TRUE;
-	return Success;
+    /* Ask the driver for a flip */
+    if (pPriv->flipsPending) {
+	pPriv->flipsPending--;
+	ret = (*ds->SwapBuffers)(pScreen, pDestBuffer, pSrcBuffer, pPriv);
+	if (ret == TRUE)
+	    return;
+
+	pPriv->swapsPending++;
+	/* Schedule a copy for the next frame here? */
     }
 
+    /* Swaps need a copy when we get the vblank event */
     box.x1 = 0;
     box.y1 = 0;
-    box.x2 = pDraw->width;
-    box.y2 = pDraw->height;
-    REGION_INIT(drawable->pDraw->pScreen, &region, &box, 0);
-    
-    return DRI2CopyRegion(pDraw, &region,
-			  DRI2BufferFrontLeft, DRI2BufferBackLeft);
+    box.x2 = pPriv->width;
+    box.y2 = pPriv->height;
+    REGION_INIT(pScreen, &region, &box, 0);
+
+    DRI2CopyRegion(pDraw, &region,
+		   DRI2BufferFrontLeft, DRI2BufferBackLeft);
+    pPriv->swapsPending--;
+
+    DRI2SwapComplete(pPriv);
+}
+
+static void drm_vblank_handler(int fd, unsigned int frame, unsigned int tv_sec,
+			       unsigned int tv_usec, void *user_data)
+{
+    DRI2ScreenPtr ds = user_data;
+    DRI2SwapDataPtr cur = ds->swaps;
+
+    while (cur) {
+	if (cur->frame == frame) {
+	    DRI2SwapSubmit(cur);
+	    DRI2RemoveSwap(cur);
+	}
+	cur = cur->next;
+    }
+}
+
+static void
+drm_wakeup_handler(pointer data, int err, pointer p)
+{
+    DRI2ScreenPtr ds = data;
+    fd_set *read_mask = p;
+
+    if (err >= 0 && FD_ISSET(ds->fd, read_mask))
+	drmHandleEvent(ds->fd, &ds->event_context);
+}
+
+int
+DRI2SwapBuffers(DrawablePtr pDraw, int interval)
+{
+    DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
+    DRI2DrawablePtr pPriv;
+    drmVBlank       vbl;
+
+    pPriv = DRI2GetDrawable(pDraw);
+    if (pPriv == NULL)
+	return BadDrawable;
+
+    vbl.request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT;
+
+    if (DRI2FlipCheck(pDraw)) {
+	/*
+	 * Flips schedule for the next vblank, so we need to schedule them at
+	 * frame - 1 to honor the swap interval.
+	 */
+	pPriv->flipsPending++;
+	if (interval > 1) {
+	    vbl.request.sequence = interval - 1;
+	    /* fixme: prevent cliprect changes between now and the flip */
+	} else {
+	    DRI2SwapComplete(pPriv);
+	    return Success;
+	}
+    } else {
+	pPriv->swapsPending++;
+	vbl.request.sequence = interval;
+    }
+
+    /* fixme: get correct crtc for this drawable */
+    drmWaitVBlank(ds->fd, &vbl);
+
+    /* Request an event for the requested frame */
+    if (!DRI2AddSwap(pDraw, vbl.reply.sequence))
+	return BadValue;
+
+    return Success;
 }
 
 Bool
@@ -416,7 +552,8 @@ DRI2WaitSwap(ClientPtr client, DrawablePtr pDrawable)
     /* If we're currently waiting for a swap on this drawable, reset
      * the request and suspend the client.  We only support one
      * blocked client per drawable. */
-    if (pPriv->swapPending && pPriv->blockedClient == NULL) {
+    if ((pPriv->swapsPending || pPriv->flipsPending) &&
+	pPriv->blockedClient == NULL) {
 	ResetCurrentRequest(client);
 	client->sequence--;
 	IgnoreClient(client);
@@ -427,19 +564,15 @@ DRI2WaitSwap(ClientPtr client, DrawablePtr pDrawable)
     return FALSE;
 }
 
-void
-DRI2SwapComplete(void *data)
+/* Wake up clients waiting for flip/swap completion */
+void DRI2SwapComplete(void *data)
 {
     DRI2DrawablePtr pPriv = data;
 
     if (pPriv->blockedClient)
 	AttendClient(pPriv->blockedClient);
 
-    pPriv->swapPending = FALSE;
     pPriv->blockedClient = NULL;
-
-    if (pPriv->refCount == 0)
-	xfree(pPriv);
 }
 
 void
@@ -470,7 +603,7 @@ DRI2DestroyDrawable(DrawablePtr pDraw)
     /* If the window is destroyed while we have a swap pending, don't
      * actually free the priv yet.  We'll need it in the DRI2SwapComplete()
      * callback and we'll free it there once we're done. */
-    if (!pPriv->swapPending)
+    if (!pPriv->swapsPending && !pPriv->flipsPending)
 	xfree(pPriv);
 
     if (pDraw->type == DRAWABLE_WINDOW)
@@ -538,6 +671,13 @@ DRI2ScreenInit(ScreenPtr pScreen, DRI2InfoPtr info)
     if (info->version >= 4)
 	ds->SwapBuffers = info->SwapBuffers;
 
+    ds->event_context.version = DRM_EVENT_CONTEXT_VERSION;
+    ds->event_context.vblank_handler = drm_vblank_handler;
+
+    AddGeneralSocket(ds->fd);
+    RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA,
+				   drm_wakeup_handler, ds);
+
     dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, ds);
 
     xf86DrvMsg(pScreen->myNum, X_INFO, "[DRI2] Setup complete\n");
diff --git a/hw/xfree86/dri2/dri2.h b/hw/xfree86/dri2/dri2.h
index 42bdb09..e8f12d1 100644
--- a/hw/xfree86/dri2/dri2.h
+++ b/hw/xfree86/dri2/dri2.h
@@ -58,7 +58,7 @@ typedef void		(*DRI2CopyRegionProcPtr)(DrawablePtr pDraw,
 						 RegionPtr pRegion,
 						 DRI2BufferPtr pDestBuffer,
 						 DRI2BufferPtr pSrcBuffer);
-typedef Bool		(*DRI2SwapBuffersProcPtr)(DrawablePtr pDraw,
+typedef Bool		(*DRI2SwapBuffersProcPtr)(ScreenPtr pScreen,
 						  DRI2BufferPtr pFrontBuffer,
 						  DRI2BufferPtr pBackBuffer,
 						  void *data);
@@ -141,7 +141,7 @@ extern _X_EXPORT DRI2BufferPtr *DRI2GetBuffersWithFormat(DrawablePtr pDraw,
 	int *width, int *height, unsigned int *attachments, int count,
 	int *out_count);
 
-extern _X_EXPORT int DRI2SwapBuffers(DrawablePtr pDrawable);
+extern _X_EXPORT int DRI2SwapBuffers(DrawablePtr pDrawable, int interval);
 extern _X_EXPORT Bool DRI2WaitSwap(ClientPtr client, DrawablePtr pDrawable);
 extern _X_EXPORT void DRI2SwapComplete(void *data);
 
diff --git a/hw/xfree86/dri2/dri2ext.c b/hw/xfree86/dri2/dri2ext.c
index 9f5f389..d0fd341 100644
--- a/hw/xfree86/dri2/dri2ext.c
+++ b/hw/xfree86/dri2/dri2ext.c
@@ -339,7 +339,7 @@ ProcDRI2SwapBuffers(ClientPtr client)
     if (!validDrawable(client, stuff->drawable, &pDrawable, &status))
 	return status;
 
-    return DRI2SwapBuffers(pDrawable);
+    return DRI2SwapBuffers(pDrawable, 0); /* get swap interval... */
 }
 
 static int


More information about the xorg-commit mailing list