Blob Blame Raw
From 20edb1d61db80f5fedb4d3eea60c67d438502238 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Kristian=20H=C3=B8gsberg?= <krh@redhat.com>
Date: Wed, 29 Jul 2009 08:26:14 -0400
Subject: [PATCH] DRI2 Page Flipping

---
 glx/glxcontext.h          |    4 ++
 glx/glxdri2.c             |   47 ++++++++++++++----
 glx/glxext.c              |    3 +
 glx/glxserver.h           |   18 +++----
 hw/xfree86/dri2/dri2.c    |  118 +++++++++++++++++++++++++++++++++++++++++++-
 hw/xfree86/dri2/dri2.h    |   12 ++++-
 hw/xfree86/dri2/dri2ext.c |   25 +++++++++-
 7 files changed, 200 insertions(+), 27 deletions(-)

diff --git a/glx/glxcontext.h b/glx/glxcontext.h
index 70a1411..79bc083 100644
--- a/glx/glxcontext.h
+++ b/glx/glxcontext.h
@@ -55,6 +55,10 @@ struct __GLXcontext {
 				     unsigned long mask);
     int            (*forceCurrent)  (__GLXcontext *context);
 
+    Bool           (*wait)          (__GLXcontext *context,
+				     __GLXclientState *cl,
+				     int *error);
+
     __GLXtextureFromPixmap *textureFromPixmap;
 
     /*
diff --git a/glx/glxdri2.c b/glx/glxdri2.c
index ed7fb4c..4b89c31 100644
--- a/glx/glxdri2.c
+++ b/glx/glxdri2.c
@@ -70,6 +70,7 @@ struct __GLXDRIscreen {
 
     const __DRIcoreExtension *core;
     const __DRIdri2Extension *dri2;
+    const __DRI2flushExtension *flush;
     const __DRIcopySubBufferExtension *copySubBuffer;
     const __DRIswapControlExtension *swapControl;
     const __DRItexBufferExtension *texBuffer;
@@ -132,17 +133,6 @@ __glXDRIdrawableCopySubBuffer(__GLXdrawable *drawable,
 		   DRI2BufferFrontLeft, DRI2BufferBackLeft);
 }
 
-static GLboolean
-__glXDRIdrawableSwapBuffers(__GLXdrawable *drawable)
-{
-    __GLXDRIdrawable *private = (__GLXDRIdrawable *) drawable;
-
-    __glXDRIdrawableCopySubBuffer(drawable, 0, 0,
-				  private->width, private->height);
-
-    return TRUE;
-}
-
 static void
 __glXDRIdrawableWaitX(__GLXdrawable *drawable)
 {
@@ -177,6 +167,20 @@ __glXDRIdrawableWaitGL(__GLXdrawable *drawable)
 		   DRI2BufferFrontLeft, DRI2BufferFakeFrontLeft);
 }
 
+static GLboolean
+__glXDRIdrawableSwapBuffers(__GLXdrawable *drawable)
+{
+    __GLXDRIdrawable *priv = (__GLXDRIdrawable *) drawable;
+    __GLXDRIscreen *screen = priv->screen;
+
+    (*screen->flush->flushInvalidate)(priv->driDrawable);
+
+    if (DRI2SwapBuffers(drawable->pDraw) != Success)
+	return FALSE;
+
+    return TRUE;
+}
+
 static int
 __glXDRIdrawableSwapInterval(__GLXdrawable *drawable, int interval)
 {
@@ -241,6 +245,18 @@ __glXDRIcontextForceCurrent(__GLXcontext *baseContext)
 					read->driDrawable);
 }
 
+static Bool
+__glXDRIcontextWait(__GLXcontext *baseContext,
+		    __GLXclientState *cl, int *error)
+{
+    if (DRI2WaitSwap(cl->client, baseContext->drawPriv->pDraw)) {
+	*error = cl->client->noClientException;
+	return TRUE;
+    }
+
+    return FALSE;
+}
+
 #ifdef __DRI_TEX_BUFFER
 
 static int
@@ -346,6 +362,7 @@ __glXDRIscreenCreateContext(__GLXscreen *baseScreen,
     context->base.copy              = __glXDRIcontextCopy;
     context->base.forceCurrent      = __glXDRIcontextForceCurrent;
     context->base.textureFromPixmap = &__glXDRItextureFromPixmap;
+    context->base.wait              = __glXDRIcontextWait;
 
     context->driContext =
 	(*screen->dri2->createNewContext)(screen->driScreen,
@@ -581,6 +598,14 @@ initializeExtensions(__GLXDRIscreen *screen)
 	    LogMessage(X_INFO, "AIGLX: GLX_EXT_texture_from_pixmap backed by buffer objects\n");
 	}
 #endif
+
+#ifdef __DRI2_FLUSH
+	if (strcmp(extensions[i]->name, __DRI2_FLUSH) == 0 &&
+	    extensions[i]->version >= __DRI2_FLUSH_VERSION) {
+		screen->flush = (__DRI2flushExtension *) extensions[i];
+	}
+#endif
+
 	/* Ignore unknown extensions */
     }
 }
diff --git a/glx/glxext.c b/glx/glxext.c
index 19d70d4..f57ccf5 100644
--- a/glx/glxext.c
+++ b/glx/glxext.c
@@ -439,6 +439,9 @@ __GLXcontext *__glXForceCurrent(__GLXclientState *cl, GLXContextTag tag,
     	}
     }
     
+    if (cx->wait && (*cx->wait)(cx, cl, error))
+	return NULL;
+
     if (cx == __glXLastContext) {
 	/* No need to re-bind */
 	return cx;
diff --git a/glx/glxserver.h b/glx/glxserver.h
index 46c9382..3e62782 100644
--- a/glx/glxserver.h
+++ b/glx/glxserver.h
@@ -56,7 +56,14 @@
 #include <GL/gl.h>
 #include <GL/glxproto.h>
 
-/* For glxscreens.h */
+/*
+** GLX resources.
+*/
+typedef XID GLXContextID;
+typedef XID GLXPixmap;
+typedef XID GLXDrawable;
+
+typedef struct __GLXclientStateRec __GLXclientState;
 typedef struct __GLXdrawable __GLXdrawable;
 typedef struct __GLXcontext __GLXcontext;
 
@@ -75,15 +82,6 @@ typedef struct __GLXcontext __GLXcontext;
 #define False 0
 #endif
 
-/*
-** GLX resources.
-*/
-typedef XID GLXContextID;
-typedef XID GLXPixmap;
-typedef XID GLXDrawable;
-
-typedef struct __GLXclientStateRec __GLXclientState;
-
 extern __GLXscreen *glxGetScreen(ScreenPtr pScreen);
 extern __GLXclientState *glxGetClient(ClientPtr pClient);
 
diff --git a/hw/xfree86/dri2/dri2.c b/hw/xfree86/dri2/dri2.c
index d15ced1..7b9fb23 100644
--- a/hw/xfree86/dri2/dri2.c
+++ b/hw/xfree86/dri2/dri2.c
@@ -38,6 +38,7 @@
 #include "xf86Module.h"
 #include "scrnintstr.h"
 #include "windowstr.h"
+#include "dixstruct.h"
 #include "dri2.h"
 #include "xf86VGAarbiter.h"
 
@@ -56,7 +57,8 @@ typedef struct _DRI2Drawable {
     int			 height;
     DRI2BufferPtr	*buffers;
     int			 bufferCount;
-    unsigned int	 pendingSequence;
+    unsigned int	 swapPending;
+    ClientPtr		 blockedClient;
 } DRI2DrawableRec, *DRI2DrawablePtr;
 
 typedef struct _DRI2Screen {
@@ -68,6 +70,7 @@ typedef struct _DRI2Screen {
     DRI2CreateBufferProcPtr	 CreateBuffer;
     DRI2DestroyBufferProcPtr	 DestroyBuffer;
     DRI2CopyRegionProcPtr	 CopyRegion;
+    DRI2SwapBuffersProcPtr	 SwapBuffers;
 
     HandleExposuresProcPtr       HandleExposures;
 } DRI2ScreenRec, *DRI2ScreenPtr;
@@ -119,6 +122,8 @@ DRI2CreateDrawable(DrawablePtr pDraw)
     pPriv->height = pDraw->height;
     pPriv->buffers = NULL;
     pPriv->bufferCount = 0;
+    pPriv->swapPending = FALSE;
+    pPriv->blockedClient = NULL;
 
     if (pDraw->type == DRAWABLE_WINDOW)
     {
@@ -338,6 +343,106 @@ DRI2CopyRegion(DrawablePtr pDraw, RegionPtr pRegion,
     return Success;
 }
 
+static Bool
+DRI2FlipCheck(DrawablePtr pDraw)
+{
+    ScreenPtr pScreen = pDraw->pScreen;
+    WindowPtr pWin, pRoot;
+    PixmapPtr pWinPixmap, pRootPixmap;
+
+    if (pDraw->type == DRAWABLE_PIXMAP)
+	return TRUE;
+
+    pRoot = WindowTable[pScreen->myNum];
+    pRootPixmap = pScreen->GetWindowPixmap(pRoot);
+
+    pWin = (WindowPtr) pDraw;
+    pWinPixmap = pScreen->GetWindowPixmap(pWin);
+    if (pRootPixmap != pWinPixmap)
+	return FALSE;
+    if (!REGION_EQUAL(pScreen, &pWin->clipList, &pRoot->winSize))
+	return FALSE;
+
+    return TRUE;
+}
+
+int
+DRI2SwapBuffers(DrawablePtr pDraw)
+{
+    DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
+    DRI2DrawablePtr pPriv;
+    DRI2BufferPtr   pDestBuffer, pSrcBuffer;
+    int		    i;
+    BoxRec	    box;
+    RegionRec	    region;
+
+    pPriv = DRI2GetDrawable(pDraw);
+    if (pPriv == NULL)
+	return BadDrawable;
+
+    pDestBuffer = NULL;
+    pSrcBuffer = NULL;
+    for (i = 0; i < pPriv->bufferCount; i++)
+    {
+	if (pPriv->buffers[i]->attachment == DRI2BufferFrontLeft)
+	    pDestBuffer = pPriv->buffers[i];
+	if (pPriv->buffers[i]->attachment == DRI2BufferBackLeft)
+	    pSrcBuffer = pPriv->buffers[i];
+    }
+    if (pSrcBuffer == NULL || pDestBuffer == NULL)
+	return BadValue;
+
+    if (DRI2FlipCheck(pDraw) &&
+	(*ds->SwapBuffers)(pDraw, pDestBuffer, pSrcBuffer, pPriv))
+    {
+	pPriv->swapPending = TRUE;
+	return Success;
+    }
+
+    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);
+}
+
+Bool
+DRI2WaitSwap(ClientPtr client, DrawablePtr pDrawable)
+{
+    DRI2DrawablePtr pPriv = DRI2GetDrawable(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) {
+	ResetCurrentRequest(client);
+	client->sequence--;
+	IgnoreClient(client);
+	pPriv->blockedClient = client;
+	return TRUE;
+    }
+
+    return FALSE;
+}
+
+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
 DRI2DestroyDrawable(DrawablePtr pDraw)
 {
@@ -363,7 +468,11 @@ DRI2DestroyDrawable(DrawablePtr pDraw)
 	xfree(pPriv->buffers);
     }
 
-    xfree(pPriv);
+    /* 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)
+	xfree(pPriv);
 
     if (pDraw->type == DRAWABLE_WINDOW)
     {
@@ -421,7 +530,7 @@ DRI2ScreenInit(ScreenPtr pScreen, DRI2InfoPtr info)
         return FALSE;
     }
 
-    ds = xalloc(sizeof *ds);
+    ds = xcalloc(1, sizeof *ds);
     if (!ds)
 	return FALSE;
 
@@ -433,6 +542,9 @@ DRI2ScreenInit(ScreenPtr pScreen, DRI2InfoPtr info)
     ds->DestroyBuffer  = info->DestroyBuffer;
     ds->CopyRegion     = info->CopyRegion;
 
+    if (info->version >= 4)
+	ds->SwapBuffers = info->SwapBuffers;
+
     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 175471a..42bdb09 100644
--- a/hw/xfree86/dri2/dri2.h
+++ b/hw/xfree86/dri2/dri2.h
@@ -58,6 +58,10 @@ typedef void		(*DRI2CopyRegionProcPtr)(DrawablePtr pDraw,
 						 RegionPtr pRegion,
 						 DRI2BufferPtr pDestBuffer,
 						 DRI2BufferPtr pSrcBuffer);
+typedef Bool		(*DRI2SwapBuffersProcPtr)(DrawablePtr pDraw,
+						  DRI2BufferPtr pFrontBuffer,
+						  DRI2BufferPtr pBackBuffer,
+						  void *data);
 
 typedef void		(*DRI2WaitProcPtr)(WindowPtr pWin,
 					   unsigned int sequence);
@@ -71,7 +75,7 @@ typedef void		(*DRI2DestroyBufferProcPtr)(DrawablePtr pDraw,
 /**
  * Version of the DRI2InfoRec structure defined in this header
  */
-#define DRI2INFOREC_VERSION 3
+#define DRI2INFOREC_VERSION 4
 
 typedef struct {
     unsigned int version;	/**< Version of this struct */
@@ -82,7 +86,7 @@ typedef struct {
     DRI2CreateBufferProcPtr	CreateBuffer;
     DRI2DestroyBufferProcPtr	DestroyBuffer;
     DRI2CopyRegionProcPtr	CopyRegion;
-    DRI2WaitProcPtr		Wait;
+    DRI2SwapBuffersProcPtr	SwapBuffers;
 
 }  DRI2InfoRec, *DRI2InfoPtr;
 
@@ -137,4 +141,8 @@ 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 Bool DRI2WaitSwap(ClientPtr client, DrawablePtr pDrawable);
+extern _X_EXPORT void DRI2SwapComplete(void *data);
+
 #endif
diff --git a/hw/xfree86/dri2/dri2ext.c b/hw/xfree86/dri2/dri2ext.c
index 029dce8..9f5f389 100644
--- a/hw/xfree86/dri2/dri2ext.c
+++ b/hw/xfree86/dri2/dri2ext.c
@@ -80,7 +80,7 @@ ProcDRI2QueryVersion(ClientPtr client)
     rep.length = 0;
     rep.sequenceNumber = client->sequence;
     rep.majorVersion = 1;
-    rep.minorVersion = 1;
+    rep.minorVersion = 2;
 
     if (client->swapped) {
     	swaps(&rep.sequenceNumber, n);
@@ -253,6 +253,9 @@ 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);
@@ -276,6 +279,9 @@ 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);
@@ -322,6 +328,21 @@ ProcDRI2CopyRegion(ClientPtr client)
 }
 
 static int
+ProcDRI2SwapBuffers(ClientPtr client)
+{
+    REQUEST(xDRI2SwapBuffersReq);
+    DrawablePtr pDrawable;
+    int status;
+
+    REQUEST_SIZE_MATCH(xDRI2SwapBuffersReq);
+
+    if (!validDrawable(client, stuff->drawable, &pDrawable, &status))
+	return status;
+
+    return DRI2SwapBuffers(pDrawable);
+}
+
+static int
 ProcDRI2Dispatch (ClientPtr client)
 {
     REQUEST(xReq);
@@ -349,6 +370,8 @@ ProcDRI2Dispatch (ClientPtr client)
 	return ProcDRI2CopyRegion(client);
     case X_DRI2GetBuffersWithFormat:
 	return ProcDRI2GetBuffersWithFormat(client);
+    case X_DRI2SwapBuffers:
+	return ProcDRI2SwapBuffers(client);
     default:
 	return BadRequest;
     }
-- 
1.6.0.6