Blob Blame History Raw
From 69cbca0b83a86ad1ab266314e76304ca4e35f57b Mon Sep 17 00:00:00 2001
From: Owen Taylor <otaylor@redhat.com>
Date: Fri, 30 Apr 2010 13:17:18 -0400
Subject: [PATCH] Merge fixes for DRI drawable handling from master
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

commit a92b2c2c8dd1e86ee852168146f01bdf72bfe2d0
Author: Kristian Høgsberg <krh@bitplanet.net>
Date:   Fri Apr 16 05:55:35 2010 -0400

glx: Drop DestroyWindow hook

Now that glx doesn't call DRI2DestroyDrawable anymore, we don't need to
force a specific resource destruction order in the DestroyWindow hook.

Signed-off-by: Kristian Høgsberg <krh@bitplanet.net>
Reviewed-by: Michel Dänzer <michel@daenzer.net>

https://bugs.freedesktop.org/show_bug.cgi?id=26394
Signed-off-by: Keith Packard <keithp@keithp.com>

commit 2b3f755254675933475b068ff20bd322ffb8631f
Author: Kristian Høgsberg <krh@bitplanet.net>
Date:   Thu, 29 Apr 2010 16:36:10 -0400

dri2: Take an XID for tracking the DRI2 drawable

Some pixmaps (window pixmaps and scratch pixmaps) don't have the
drawable->id set and thus DRI2 gets confused when using that field
for looking up the DRI2 drawable.  Go back to using privates for getting
at the DRI2 drawable from a DrawablePtr.  We need to keep the resource
tracking in place so we can remove the DRI2 drawable when the X resource
it was created for goes away.  Additionally, we also now track the DRI2
drawable using a client XID so we can reclaim the DRI2 drawable even if
the client goes before the drawable and doesn't destroy the DRI2 drawable.

commit 1da1f33f2dd5b437dd56cd9f5d6782de4ad5a1bc
Author: Kristian Høgsberg <krh@bitplanet.net>
Date:   Fri Apr 16 05:55:34 2010 -0400

DRI2: Track DRI2 drawables as resources, not privates

The main motivation here is to have the resource system clean up the
DRI2 drawable automatically so glx doesn't have to.  Right now, the
glx drawable resource must be destroyed before the X drawable, so that
calling DRI2DestroyDrawable doesn't crash.  By making the DRI2
drawable a resource, GLX doesn't have to worry about that and the
resource destruction order becomes irrelevant.

https://bugs.freedesktop.org/show_bug.cgi?id=26394

Signed-off-by: Kristian Høgsberg <krh@bitplanet.net>
Signed-off-by: Keith Packard <keithp@keithp.com>

commit f0006aa58f6cf7552a239e169ff6e7e4fda532f4
Author: Kristian Høgsberg <krh@bitplanet.net>
Date:   Fri Apr 16 05:55:32 2010 -0400

glx: Track GLX 1.3 style GLX drawables under their X drawable ID as well

This ensures that the DrawableGone callback gets called as necessary
when the X drawable goes away.  Otherwise, using a GLX drawable
(say, glXSwapBuffers) in indirect mode after the X drawable has been
destroyed will crash the server.

Signed-off-by: Kristian Høgsberg <krh@bitplanet.net>
Reviewed-by: Michel Dänzer <michel@daenzer.net>
Signed-off-by: Keith Packard <keithp@keithp.com>

commit 22da7aa9d743deee198aaf6df5d370a446db9763
Author: Kristian Høgsberg <krh@bitplanet.net>
Date:   Fri Apr 16 05:55:33 2010 -0400

glx: Let the resource system destroy pixmaps

GLX pbuffers are implemented using a pixmap allocated by the server.
With the change to DRI2 to track DRI2 drawables as resources, we need to make
sure that every drawable we create a DRI2 drawable for has an XID.  By
using the XID of the pbuffer, the resource system will automatically
reclaim the hidden pixmap and the DRI2 drawable when the pbuffer is
destroyed or the client exits.

Signed-off-by: Kristian Høgsberg <krh@bitplanet.net>
Signed-off-by: Keith Packard <keithp@keithp.com>
---
 glx/glxcmds.c             |   60 ++++++++-----
 glx/glxdri.c              |    8 +-
 glx/glxdri2.c             |   17 ++--
 glx/glxdriswrast.c        |    8 +-
 glx/glxext.c              |   11 +++
 glx/glxscreens.c          |   28 ------
 glx/glxscreens.h          |    7 +-
 hw/xfree86/dri2/dri2.c    |  215 +++++++++++++++++++++++++++------------------
 hw/xfree86/dri2/dri2.h    |    3 +-
 hw/xfree86/dri2/dri2ext.c |   24 +-----
 include/list.h            |    6 ++
 11 files changed, 209 insertions(+), 178 deletions(-)

diff --git a/glx/glxcmds.c b/glx/glxcmds.c
index 77afbf4..ec3bbe6 100644
--- a/glx/glxcmds.c
+++ b/glx/glxcmds.c
@@ -161,7 +161,11 @@ validGlxDrawable(ClientPtr client, XID id, int type, int access_mode,
 	return FALSE;
     }
 
+    /* If the ID of the glx drawable we looked up doesn't match the id
+     * we looked for, it's because we looked it up under the X
+     * drawable ID (see DoCreateGLXDrawable). */
     if (rc == BadValue ||
+	(*drawable)->drawId != id ||
 	(type != GLX_DRAWABLE_ANY && type != (*drawable)->type)) {
 	client->errorValue = id;
 	switch (type) {
@@ -508,8 +512,9 @@ __glXGetDrawable(__GLXcontext *glxc, GLXDrawable drawId, ClientPtr client,
     if (!validGlxFBConfigForWindow(client, glxc->config, pDraw, error))
 	return NULL;
 
-    pGlxDraw = glxc->pGlxScreen->createDrawable(glxc->pGlxScreen,
-						pDraw, GLX_DRAWABLE_WINDOW,
+    pGlxDraw = glxc->pGlxScreen->createDrawable(client, glxc->pGlxScreen,
+						pDraw, drawId,
+						GLX_DRAWABLE_WINDOW,
 						drawId, glxc->config);
 
     /* since we are creating the drawablePrivate, drawId should be new */
@@ -1097,28 +1102,20 @@ __glXDrawableInit(__GLXdrawable *drawable,
 void
 __glXDrawableRelease(__GLXdrawable *drawable)
 {
-    ScreenPtr pScreen = drawable->pDraw->pScreen;
-
-    switch (drawable->type) {
-    case GLX_DRAWABLE_PIXMAP:
-    case GLX_DRAWABLE_PBUFFER:
-	(*pScreen->DestroyPixmap)((PixmapPtr) drawable->pDraw);
-	break;
-    }
 }
 
 static int 
-DoCreateGLXDrawable(ClientPtr client, __GLXscreen *pGlxScreen, __GLXconfig *config,
-		    DrawablePtr pDraw, XID glxDrawableId, int type)
+DoCreateGLXDrawable(ClientPtr client, __GLXscreen *pGlxScreen,
+		    __GLXconfig *config, DrawablePtr pDraw, XID drawableId,
+		    XID glxDrawableId, int type)
 {
     __GLXdrawable *pGlxDraw;
 
-    LEGAL_NEW_RESOURCE(glxDrawableId, client);
-
     if (pGlxScreen->pScreen != pDraw->pScreen)
 	return BadMatch;
 
-    pGlxDraw = pGlxScreen->createDrawable(pGlxScreen, pDraw, type,
+    pGlxDraw = pGlxScreen->createDrawable(client, pGlxScreen, pDraw,
+					  drawableId, type,
 					  glxDrawableId, config);
     if (pGlxDraw == NULL)
 	return BadAlloc;
@@ -1128,6 +1125,15 @@ DoCreateGLXDrawable(ClientPtr client, __GLXscreen *pGlxScreen, __GLXconfig *conf
 	return BadAlloc;
     }
 
+    /* Add the glx drawable under the XID of the underlying X drawable
+     * too.  That way we'll get a callback in DrawableGone and can
+     * clean up properly when the drawable is destroyed. */
+    if (drawableId != glxDrawableId &&
+	!AddResource(pDraw->id, __glXDrawableRes, pGlxDraw)) {
+	pGlxDraw->destroy (pGlxDraw);
+	return BadAlloc;
+    }
+
     return Success;
 }
 
@@ -1138,6 +1144,8 @@ DoCreateGLXPixmap(ClientPtr client, __GLXscreen *pGlxScreen, __GLXconfig *config
     DrawablePtr pDraw;
     int err;
 
+    LEGAL_NEW_RESOURCE(glxDrawableId, client);
+
     err = dixLookupDrawable(&pDraw, drawableId, client, 0, DixAddAccess);
     if (err != Success) {
 	client->errorValue = drawableId;
@@ -1148,12 +1156,9 @@ DoCreateGLXPixmap(ClientPtr client, __GLXscreen *pGlxScreen, __GLXconfig *config
 	return BadPixmap;
     }
 
-    err = DoCreateGLXDrawable(client, pGlxScreen, config, pDraw,
+    err = DoCreateGLXDrawable(client, pGlxScreen, config, pDraw, drawableId,
 			      glxDrawableId, GLX_DRAWABLE_PIXMAP);
 
-    if (err == Success)
-	((PixmapPtr) pDraw)->refcnt++;
-
     return err;
 }
 
@@ -1294,6 +1299,8 @@ DoCreatePbuffer(ClientPtr client, int screenNum, XID fbconfigId,
     PixmapPtr		 pPixmap;
     int			 err;
 
+    LEGAL_NEW_RESOURCE(glxDrawableId, client);
+
     if (!validGlxScreen(client, screenNum, &pGlxScreen, &err))
 	return err;
     if (!validGlxFBConfig(client, pGlxScreen, fbconfigId, &config, &err))
@@ -1304,8 +1311,16 @@ DoCreatePbuffer(ClientPtr client, int screenNum, XID fbconfigId,
 						    width, height, config->rgbBits, 0);
     __glXleaveServer(GL_FALSE);
 
+    /* Assign the pixmap the same id as the pbuffer and add it as a
+     * resource so it and the DRI2 drawable will be reclaimed when the
+     * pbuffer is destroyed. */
+    pPixmap->drawable.id = glxDrawableId;
+    if (!AddResource(pPixmap->drawable.id, RT_PIXMAP, pPixmap))
+	return BadAlloc;
+
     return DoCreateGLXDrawable(client, pGlxScreen, config, &pPixmap->drawable,
-			       glxDrawableId, GLX_DRAWABLE_PBUFFER);
+			       glxDrawableId, glxDrawableId,
+			       GLX_DRAWABLE_PBUFFER);
 }
 
 int __glXDisp_CreatePbuffer(__GLXclientState *cl, GLbyte *pc)
@@ -1411,6 +1426,8 @@ int __glXDisp_CreateWindow(__GLXclientState *cl, GLbyte *pc)
     DrawablePtr		 pDraw;
     int			 err;
 
+    LEGAL_NEW_RESOURCE(req->glxwindow, client);
+
     if (!validGlxScreen(client, req->screen, &pGlxScreen, &err))
 	return err;
     if (!validGlxFBConfig(client, pGlxScreen, req->fbconfig, &config, &err))
@@ -1426,7 +1443,8 @@ int __glXDisp_CreateWindow(__GLXclientState *cl, GLbyte *pc)
 	return err;
 
     return DoCreateGLXDrawable(client, pGlxScreen, config,
-			       pDraw, req->glxwindow, GLX_DRAWABLE_WINDOW);
+			       pDraw, req->window,
+			       req->glxwindow, GLX_DRAWABLE_WINDOW);
 }
 
 int __glXDisp_DestroyWindow(__GLXclientState *cl, GLbyte *pc)
diff --git a/glx/glxdri.c b/glx/glxdri.c
index 30b820c..51b602d 100644
--- a/glx/glxdri.c
+++ b/glx/glxdri.c
@@ -682,10 +682,12 @@ __glXDRIscreenCreateContext(__GLXscreen *baseScreen,
 }
 
 static __GLXdrawable *
-__glXDRIscreenCreateDrawable(__GLXscreen *screen,
+__glXDRIscreenCreateDrawable(ClientPtr client,
+			     __GLXscreen *screen,
 			     DrawablePtr pDraw,
-			     int type,
 			     XID drawId,
+			     int type,
+			     XID glxDrawId,
 			     __GLXconfig *glxConfig)
 {
     __GLXDRIscreen *driScreen = (__GLXDRIscreen *) screen;
@@ -699,7 +701,7 @@ __glXDRIscreenCreateDrawable(__GLXscreen *screen,
 	return NULL;
 
     if (!__glXDrawableInit(&private->base, screen,
-			   pDraw, type, drawId, glxConfig)) {
+			   pDraw, type, glxDrawId, glxConfig)) {
         xfree(private);
 	return NULL;
     }
diff --git a/glx/glxdri2.c b/glx/glxdri2.c
index bc37285..5bdeedb 100644
--- a/glx/glxdri2.c
+++ b/glx/glxdri2.c
@@ -105,11 +105,6 @@ __glXDRIdrawableDestroy(__GLXdrawable *drawable)
     
     (*core->destroyDrawable)(private->driDrawable);
 
-    /* If the X window was destroyed, the dri DestroyWindow hook will
-     * aready have taken care of this, so only call if pDraw isn't NULL. */
-    if (drawable->pDraw != NULL)
-	DRI2DestroyDrawable(drawable->pDraw);
-
     __glXDrawableRelease(drawable);
 
     xfree(private);
@@ -435,10 +430,12 @@ __glXDRIscreenCreateContext(__GLXscreen *baseScreen,
 }
 
 static __GLXdrawable *
-__glXDRIscreenCreateDrawable(__GLXscreen *screen,
+__glXDRIscreenCreateDrawable(ClientPtr client,
+			     __GLXscreen *screen,
 			     DrawablePtr pDraw,
-			     int type,
 			     XID drawId,
+			     int type,
+			     XID glxDrawId,
 			     __GLXconfig *glxConfig)
 {
     __GLXDRIscreen *driScreen = (__GLXDRIscreen *) screen;
@@ -451,7 +448,7 @@ __glXDRIscreenCreateDrawable(__GLXscreen *screen,
 
     private->screen = driScreen;
     if (!__glXDrawableInit(&private->base, screen,
-			   pDraw, type, drawId, glxConfig)) {
+			   pDraw, type, glxDrawId, glxConfig)) {
         xfree(private);
 	return NULL;
     }
@@ -462,7 +459,7 @@ __glXDRIscreenCreateDrawable(__GLXscreen *screen,
     private->base.waitGL	= __glXDRIdrawableWaitGL;
     private->base.waitX		= __glXDRIdrawableWaitX;
 
-    if (DRI2CreateDrawable(pDraw)) {
+    if (DRI2CreateDrawable(client, pDraw, drawId)) {
 	    xfree(private);
 	    return NULL;
     }
@@ -725,7 +722,7 @@ __glXDRIscreenProbe(ScreenPtr pScreen)
 		screen->core = (const __DRIcoreExtension *) extensions[i];
 	}
         if (strcmp(extensions[i]->name, __DRI_DRI2) == 0 &&
-	    extensions[i]->version >= __DRI_DRI2_VERSION) {
+	    extensions[i]->version >= 1) {
 		screen->dri2 = (const __DRIdri2Extension *) extensions[i];
 	}
     }
diff --git a/glx/glxdriswrast.c b/glx/glxdriswrast.c
index c647d83..6a34393 100644
--- a/glx/glxdriswrast.c
+++ b/glx/glxdriswrast.c
@@ -301,10 +301,12 @@ glxChangeGC(GCPtr gc, BITS32 mask, CARD32 val)
 }
 
 static __GLXdrawable *
-__glXDRIscreenCreateDrawable(__GLXscreen *screen,
+__glXDRIscreenCreateDrawable(ClientPtr client,
+			     __GLXscreen *screen,
 			     DrawablePtr pDraw,
-			     int type,
 			     XID drawId,
+			     int type,
+			     XID glxDrawId,
 			     __GLXconfig *glxConfig)
 {
     __GLXDRIscreen *driScreen = (__GLXDRIscreen *) screen;
@@ -319,7 +321,7 @@ __glXDRIscreenCreateDrawable(__GLXscreen *screen,
 
     private->screen = driScreen;
     if (!__glXDrawableInit(&private->base, screen,
-			   pDraw, type, drawId, glxConfig)) {
+			   pDraw, type, glxDrawId, glxConfig)) {
         xfree(private);
 	return NULL;
     }
diff --git a/glx/glxext.c b/glx/glxext.c
index 59bcfbe..89e58b0 100644
--- a/glx/glxext.c
+++ b/glx/glxext.c
@@ -126,6 +126,17 @@ static Bool DrawableGone(__GLXdrawable *glxPriv, XID xid)
 {
     __GLXcontext *c;
 
+    /* If this drawable was created using glx 1.3 drawable
+     * constructors, we added it as a glx drawable resource under both
+     * its glx drawable ID and it X drawable ID.  Remove the other
+     * resource now so we don't a callback for freed memory. */
+    if (glxPriv->drawId != glxPriv->pDraw->id) {
+	if (xid == glxPriv->drawId)
+	    FreeResourceByType(glxPriv->pDraw->id, __glXDrawableRes, TRUE);
+	else
+	    FreeResourceByType(glxPriv->drawId, __glXDrawableRes, TRUE);
+    }
+
     for (c = glxAllContexts; c; c = c->next) {
 	if (c->isCurrent && (c->drawPriv == glxPriv || c->readPriv == glxPriv)) {
 	    int i;
diff --git a/glx/glxscreens.c b/glx/glxscreens.c
index 58d8ee0..b75aea6 100644
--- a/glx/glxscreens.c
+++ b/glx/glxscreens.c
@@ -215,7 +215,6 @@ glxCloseScreen (int index, ScreenPtr pScreen)
     __GLXscreen *pGlxScreen = glxGetScreen(pScreen);
 
     pScreen->CloseScreen = pGlxScreen->CloseScreen;
-    pScreen->DestroyWindow = pGlxScreen->DestroyWindow;
 
     pGlxScreen->destroy(pGlxScreen);
 
@@ -347,31 +346,6 @@ pickFBConfig(__GLXscreen *pGlxScreen, VisualPtr visual)
     return best;
 }
 
-static Bool
-glxDestroyWindow(WindowPtr pWin)
-{
-    ScreenPtr pScreen = pWin->drawable.pScreen;
-    __GLXscreen *pGlxScreen = glxGetScreen(pScreen);
-    Bool retval = TRUE;
-
-    FreeResource(pWin->drawable.id, FALSE);
-
-    /* call lower wrapped functions */
-    if (pGlxScreen->DestroyWindow) {
-	/* unwrap */
-	pScreen->DestroyWindow = pGlxScreen->DestroyWindow;
-
-	/* call lower layers */
-	retval = (*pScreen->DestroyWindow)(pWin);
-
-	/* rewrap */
-	pGlxScreen->DestroyWindow = pScreen->DestroyWindow;
-	pScreen->DestroyWindow = glxDestroyWindow;
-    }
-
-    return retval;
-}
-
 void __glXScreenInit(__GLXscreen *pGlxScreen, ScreenPtr pScreen)
 {
     __GLXconfig *m;
@@ -394,8 +368,6 @@ void __glXScreenInit(__GLXscreen *pGlxScreen, ScreenPtr pScreen)
 
     pGlxScreen->CloseScreen = pScreen->CloseScreen;
     pScreen->CloseScreen = glxCloseScreen;
-    pGlxScreen->DestroyWindow = pScreen->DestroyWindow;
-    pScreen->DestroyWindow = glxDestroyWindow;
 
     i = 0;
     for (m = pGlxScreen->fbconfigs; m != NULL; m = m->next) {
diff --git a/glx/glxscreens.h b/glx/glxscreens.h
index bff4363..861e03c 100644
--- a/glx/glxscreens.h
+++ b/glx/glxscreens.h
@@ -134,10 +134,12 @@ struct __GLXscreen {
 				    __GLXconfig *modes,
 				    __GLXcontext *shareContext);
 
-    __GLXdrawable *(*createDrawable)(__GLXscreen *context,
+    __GLXdrawable *(*createDrawable)(ClientPtr client,
+				     __GLXscreen *context,
 				     DrawablePtr pDraw,
-				     int type,
 				     XID drawId,
+				     int type,
+				     XID glxDrawId,
 				     __GLXconfig *modes);
     int            (*swapInterval)  (__GLXdrawable *drawable,
 				     int interval);
@@ -173,7 +175,6 @@ struct __GLXscreen {
     /*@}*/
 
     Bool (*CloseScreen)(int index, ScreenPtr pScreen);
-    Bool (*DestroyWindow)(WindowPtr pWindow);
 };
 
 
diff --git a/hw/xfree86/dri2/dri2.c b/hw/xfree86/dri2/dri2.c
index 2bdb733..11442d0 100644
--- a/hw/xfree86/dri2/dri2.c
+++ b/hw/xfree86/dri2/dri2.c
@@ -37,6 +37,7 @@
 #include <errno.h>
 #include <xf86drm.h>
 #include "xf86Module.h"
+#include "list.h"
 #include "scrnintstr.h"
 #include "windowstr.h"
 #include "dixstruct.h"
@@ -48,15 +49,20 @@
 CARD8 dri2_major; /* version of DRI2 supported by DDX */
 CARD8 dri2_minor;
 
-static int dri2ScreenPrivateKeyIndex;
+static int           dri2ScreenPrivateKeyIndex;
 static DevPrivateKey dri2ScreenPrivateKey = &dri2ScreenPrivateKeyIndex;
 static int dri2WindowPrivateKeyIndex;
 static DevPrivateKey dri2WindowPrivateKey = &dri2WindowPrivateKeyIndex;
 static int dri2PixmapPrivateKeyIndex;
 static DevPrivateKey dri2PixmapPrivateKey = &dri2PixmapPrivateKeyIndex;
+static RESTYPE       dri2DrawableRes;
+
+typedef struct _DRI2Screen *DRI2ScreenPtr;
 
 typedef struct _DRI2Drawable {
-    unsigned int	 refCount;
+    DRI2ScreenPtr        dri2_screen;
+    DrawablePtr		 drawable;
+    struct list		 reference_list;
     int			 width;
     int			 height;
     DRI2BufferPtr	*buffers;
@@ -73,9 +79,9 @@ typedef struct _DRI2Drawable {
     int			 swap_limit; /* for N-buffering */
 } DRI2DrawableRec, *DRI2DrawablePtr;
 
-typedef struct _DRI2Screen *DRI2ScreenPtr;
-
 typedef struct _DRI2Screen {
+    ScreenPtr			 screen;
+    int				 refcnt;
     unsigned int		 numDrivers;
     const char			**driverNames;
     const char			*deviceName;
@@ -101,45 +107,33 @@ DRI2GetScreen(ScreenPtr pScreen)
 static DRI2DrawablePtr
 DRI2GetDrawable(DrawablePtr pDraw)
 {
-    WindowPtr		  pWin;
-    PixmapPtr		  pPixmap;
+    WindowPtr pWin;
+    PixmapPtr pPixmap;
 
-    if (!pDraw)
-	return NULL;
-
-    if (pDraw->type == DRAWABLE_WINDOW)
-    {
+    if (pDraw->type == DRAWABLE_WINDOW) {
 	pWin = (WindowPtr) pDraw;
 	return dixLookupPrivate(&pWin->devPrivates, dri2WindowPrivateKey);
-    }
-    else
-    {
+    } else {
 	pPixmap = (PixmapPtr) pDraw;
 	return dixLookupPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey);
     }
 }
 
-int
-DRI2CreateDrawable(DrawablePtr pDraw)
+static DRI2DrawablePtr
+DRI2AllocateDrawable(DrawablePtr pDraw)
 {
     DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
-    WindowPtr	    pWin;
-    PixmapPtr	    pPixmap;
     DRI2DrawablePtr pPriv;
     CARD64          ust;
-
-    pPriv = DRI2GetDrawable(pDraw);
-    if (pPriv != NULL)
-    {
-	pPriv->refCount++;
-	return Success;
-    }
+    WindowPtr pWin;
+    PixmapPtr pPixmap;
 
     pPriv = xalloc(sizeof *pPriv);
     if (pPriv == NULL)
-	return BadAlloc;
+	return NULL;
 
-    pPriv->refCount = 1;
+    pPriv->dri2_screen = ds;
+    pPriv->drawable = pDraw;
     pPriv->width = pDraw->width;
     pPriv->height = pDraw->height;
     pPriv->buffers = NULL;
@@ -157,44 +151,131 @@ DRI2CreateDrawable(DrawablePtr pDraw)
     pPriv->swap_limit = 1; /* default to double buffering */
     pPriv->last_swap_msc = 0;
     pPriv->last_swap_ust = 0;
+    list_init(&pPriv->reference_list);
 
-    if (pDraw->type == DRAWABLE_WINDOW)
-    {
+    if (pDraw->type == DRAWABLE_WINDOW) {
 	pWin = (WindowPtr) pDraw;
 	dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, pPriv);
-    }
-    else
-    {
+    } else {
 	pPixmap = (PixmapPtr) pDraw;
 	dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, pPriv);
     }
 
+    return pPriv;
+}
+
+typedef struct DRI2DrawableRefRec {
+    XID id;
+    XID dri2_id;
+    struct list link;
+} DRI2DrawableRefRec, *DRI2DrawableRefPtr;
+
+static DRI2DrawableRefPtr
+DRI2LookupDrawableRef(DRI2DrawablePtr pPriv, XID id)
+{
+    DRI2DrawableRefPtr ref;
+
+    list_for_each_entry(ref, &pPriv->reference_list, link) {
+	if (ref->id == id)
+	    return ref;
+    }
+    
+    return NULL;
+}
+
+static int
+DRI2AddDrawableRef(DRI2DrawablePtr pPriv, XID id, XID dri2_id)
+{
+    DRI2DrawableRefPtr ref;
+
+    ref = malloc(sizeof *ref);
+    if (ref == NULL)
+	return BadAlloc;
+	
+    if (!AddResource(dri2_id, dri2DrawableRes, pPriv))
+	return BadAlloc;
+    if (!DRI2LookupDrawableRef(pPriv, id))
+	if (!AddResource(id, dri2DrawableRes, pPriv))
+	    return BadAlloc;
+
+    ref->id = id;
+    ref->dri2_id = dri2_id; 
+    list_add(&ref->link, &pPriv->reference_list);
+
     return Success;
 }
 
-static void
-DRI2FreeDrawable(DrawablePtr pDraw)
+int
+DRI2CreateDrawable(ClientPtr client, DrawablePtr pDraw, XID id)
 {
     DRI2DrawablePtr pPriv;
-    WindowPtr  	    pWin;
-    PixmapPtr	    pPixmap;
+    XID dri2_id;
+    int rc;
 
     pPriv = DRI2GetDrawable(pDraw);
     if (pPriv == NULL)
-	return;
+	pPriv = DRI2AllocateDrawable(pDraw);
+    if (pPriv == NULL)
+	return BadAlloc;
+    
+    dri2_id = FakeClientID(client->index);
+    rc = DRI2AddDrawableRef(pPriv, id, dri2_id);
+    if (rc != Success)
+	return rc;
 
-    xfree(pPriv);
+    return Success;
+}
 
-    if (pDraw->type == DRAWABLE_WINDOW)
-    {
+static int DRI2DrawableGone(pointer p, XID id)
+{
+    DRI2DrawablePtr pPriv = p;
+    DRI2ScreenPtr   ds = pPriv->dri2_screen;
+    DRI2DrawableRefPtr ref, next;
+    WindowPtr pWin;
+    PixmapPtr pPixmap;
+    DrawablePtr pDraw;
+    int i;
+
+    list_for_each_entry_safe(ref, next, &pPriv->reference_list, link) {
+	if (ref->dri2_id == id) {
+	    list_del(&ref->link);
+	    /* If this was the last ref under this X drawable XID,
+	     * unregister the X drawable resource. */
+	    if (!DRI2LookupDrawableRef(pPriv, ref->id))
+		FreeResourceByType(ref->id, dri2DrawableRes, TRUE);
+	    free(ref);
+	    break;
+	}
+
+	if (ref->id == id) {
+	    list_del(&ref->link);
+	    FreeResourceByType(ref->dri2_id, dri2DrawableRes, TRUE);
+	    free(ref);
+	}
+    }
+
+    if (!list_is_empty(&pPriv->reference_list))
+	return Success;
+
+    pDraw = pPriv->drawable;
+    if (pDraw->type == DRAWABLE_WINDOW) {
 	pWin = (WindowPtr) pDraw;
 	dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, NULL);
-    }
-    else
-    {
+    } else {
 	pPixmap = (PixmapPtr) pDraw;
 	dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, NULL);
     }
+
+    if (pPriv->buffers != NULL) {
+	for (i = 0; i < pPriv->bufferCount; i++)
+	    (*ds->DestroyBuffer)(pDraw, pPriv->buffers[i]);
+
+	xfree(pPriv->buffers);
+    }
+
+    xfree(pPriv);
+
+    return Success;
 }
 
 static int
@@ -505,10 +586,6 @@ DRI2WaitMSCComplete(ClientPtr client, DrawablePtr pDraw, int frame,
 
     pPriv->blockedClient = NULL;
     pPriv->blockedOnMsc = FALSE;
-
-    /* If there's still a swap pending, let DRI2SwapComplete free it */
-    if (pPriv->refCount == 0 && pPriv->swapsPending == 0)
-	DRI2FreeDrawable(pDraw);
 }
 
 static void
@@ -576,13 +653,6 @@ DRI2SwapComplete(ClientPtr client, DrawablePtr pDraw, int frame,
     pPriv->last_swap_ust = ust;
 
     DRI2WakeClient(client, pDraw, frame, tv_sec, tv_usec);
-
-    /*
-     * It's normal for the app to have exited with a swap outstanding, but
-     * don't free the drawable until they're all complete.
-     */
-    if (pPriv->swapsPending == 0 && pPriv->refCount == 0)
-	DRI2FreeDrawable(pDraw);
 }
 
 Bool
@@ -750,7 +820,7 @@ DRI2WaitMSC(ClientPtr client, DrawablePtr pDraw, CARD64 target_msc,
     Bool ret;
 
     pPriv = DRI2GetDrawable(pDraw);
-    if (pPriv == NULL || pPriv->refCount == 0)
+    if (pPriv == NULL)
 	return BadDrawable;
 
     /* Old DDX just completes immediately */
@@ -774,7 +844,7 @@ DRI2WaitSBC(ClientPtr client, DrawablePtr pDraw, CARD64 target_sbc,
     DRI2DrawablePtr pPriv;
 
     pPriv = DRI2GetDrawable(pDraw);
-    if (pPriv == NULL || pPriv->refCount == 0)
+    if (pPriv == NULL)
 	return BadDrawable;
 
     /* target_sbc == 0 means to block until all pending swaps are
@@ -800,36 +870,6 @@ DRI2WaitSBC(ClientPtr client, DrawablePtr pDraw, CARD64 target_sbc,
     return Success;
 }
 
-void
-DRI2DestroyDrawable(DrawablePtr pDraw)
-{
-    DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
-    DRI2DrawablePtr pPriv;
-
-    pPriv = DRI2GetDrawable(pDraw);
-    if (pPriv == NULL)
-	return;
-
-    pPriv->refCount--;
-    if (pPriv->refCount > 0)
-	return;
-
-    if (pPriv->buffers != NULL) {
-	int i;
-
-	for (i = 0; i < pPriv->bufferCount; i++)
-	    (*ds->DestroyBuffer)(pDraw, pPriv->buffers[i]);
-
-	xfree(pPriv->buffers);
-    }
-
-    /* If the window is destroyed while we have a swap or wait 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->swapsPending && !pPriv->blockedClient)
-	DRI2FreeDrawable(pDraw);
-}
-
 Bool
 DRI2HasSwapControl(ScreenPtr pScreen)
 {
@@ -890,6 +930,7 @@ DRI2ScreenInit(ScreenPtr pScreen, DRI2InfoPtr info)
     if (!ds)
 	return FALSE;
 
+    ds->screen         = pScreen;
     ds->fd	       = info->fd;
     ds->deviceName     = info->deviceName;
     dri2_major         = 1;
@@ -961,6 +1002,8 @@ DRI2Setup(pointer module, pointer opts, int *errmaj, int *errmin)
 {
     static Bool setupDone = FALSE;
 
+    dri2DrawableRes = CreateNewResourceType(DRI2DrawableGone, "DRI2Drawable");
+
     if (!setupDone)
     {
 	setupDone = TRUE;
diff --git a/hw/xfree86/dri2/dri2.h b/hw/xfree86/dri2/dri2.h
index ce8a5df..5415a0b 100644
--- a/hw/xfree86/dri2/dri2.h
+++ b/hw/xfree86/dri2/dri2.h
@@ -198,7 +198,8 @@ extern _X_EXPORT Bool DRI2Connect(ScreenPtr pScreen,
 
 extern _X_EXPORT Bool DRI2Authenticate(ScreenPtr pScreen, drm_magic_t magic);
 
-extern _X_EXPORT int DRI2CreateDrawable(DrawablePtr pDraw);
+extern _X_EXPORT int DRI2CreateDrawable(ClientPtr client,
+					DrawablePtr pDraw, XID id);
 
 extern _X_EXPORT void DRI2DestroyDrawable(DrawablePtr pDraw);
 
diff --git a/hw/xfree86/dri2/dri2ext.c b/hw/xfree86/dri2/dri2ext.c
index 094d54d..58eaa10 100644
--- a/hw/xfree86/dri2/dri2ext.c
+++ b/hw/xfree86/dri2/dri2ext.c
@@ -51,7 +51,6 @@
 #include "xf86Module.h"
 
 static ExtensionEntry	*dri2Extension;
-static RESTYPE		 dri2DrawableRes;
 
 static Bool
 validDrawable(ClientPtr client, XID drawable, Mask access_mode,
@@ -168,15 +167,10 @@ ProcDRI2CreateDrawable(ClientPtr client)
 		       &pDrawable, &status))
 	return status;
 
-    status = DRI2CreateDrawable(pDrawable);
+    status = DRI2CreateDrawable(client, pDrawable, stuff->drawable);
     if (status != Success)
 	return status;
 
-    if (!AddResource(stuff->drawable, dri2DrawableRes, pDrawable)) {
-	DRI2DestroyDrawable(pDrawable);
-	return BadAlloc;
-    }
-
     return client->noClientException;
 }
 
@@ -192,8 +186,6 @@ ProcDRI2DestroyDrawable(ClientPtr client)
 		       &pDrawable, &status))
 	return status;
 
-    FreeResourceByType(stuff->drawable, dri2DrawableRes, FALSE);
-
     return client->noClientException;
 }
 
@@ -627,25 +619,11 @@ SProcDRI2Dispatch (ClientPtr client)
     }
 }
 
-static int DRI2DrawableGone(pointer p, XID id)
-{
-    DrawablePtr pDrawable = p;
-
-    DRI2DestroyDrawable(pDrawable);
-
-    return Success;
-}
-
 int DRI2EventBase;
 
 static void
 DRI2ExtensionInit(void)
 {
-    dri2DrawableRes = CreateNewResourceType(DRI2DrawableGone, "DRI2Drawable");
-
-    if (!dri2DrawableRes)
-	return;
-
     dri2Extension = AddExtension(DRI2_NAME,
 				 DRI2NumberEvents,
 				 DRI2NumberErrors,
diff --git a/include/list.h b/include/list.h
index a126a65..89dc29d 100644
--- a/include/list.h
+++ b/include/list.h
@@ -94,4 +94,10 @@ list_is_empty(struct list *head)
 	 &pos->member != (head);					\
 	 pos = __container_of(pos->member.next, pos, member))
 
+#define list_for_each_entry_safe(pos, next, head, member)		\
+    for (pos = __container_of((head)->next, pos, member),		\
+	 next = __container_of(pos->member.next, pos, member);		\
+	 &pos->member != (head);					\
+	 pos = next, next = __container_of(next->member.next, next, member))
+
 #endif
-- 
1.7.0.1