See bug 10758. If you create a depth-stencil surface with Direct3D 9 on 
Windows, select it into a D3D9 device with SetDepthStencilSurface() and then 
release it (so its reference count goes to zero), the surface will not be 
deleted. It will persist until its external reference count is zero /and/ it 
has been deselected with SetDepthStencilSurface() or Reset(). However, Wine's 
D3D implementation will delete the surface as soon as its refcount goes to 
zero, even if it is still selected into the device. This causes a crash with 
Myst IV.

Attached is a patch that adds a private reference count to 
IDirect3DSurface9_Impl, plus a test case for it. The patch fixes the problem 
with Myst IV; could one of the resident D3D gurus have a look at it and see if 
I've got the approach right or if there's something else we should be doing?

We'll probably also have to make the equivalent change in D3D8 as well, but I 
haven't considered that.

Cheers, jim
From 880f9c714ec701b071da012fc4009bb51a011765 Mon Sep 17 00:00:00 2001
From: Jim Cameron <[EMAIL PROTECTED]>
Date: Thu, 9 Oct 2008 22:11:14 +0100
Subject: Protect current depth-stencil surface from deletion

---
 dlls/d3d9/device.c  |   20 ++++++++++++++++++++
 dlls/d3d9/surface.c |    2 +-
 2 files changed, 21 insertions(+), 1 deletions(-)

diff --git a/dlls/d3d9/device.c b/dlls/d3d9/device.c
index 8aab230..36306d5 100644
--- a/dlls/d3d9/device.c
+++ b/dlls/d3d9/device.c
@@ -715,12 +715,32 @@ static HRESULT  WINAPI  IDirect3DDevice9Impl_GetRenderTarget(LPDIRECT3DDEVICE9EX
 static HRESULT  WINAPI  IDirect3DDevice9Impl_SetDepthStencilSurface(LPDIRECT3DDEVICE9EX iface, IDirect3DSurface9* pZStencilSurface) {
     IDirect3DDevice9Impl *This = (IDirect3DDevice9Impl *)iface;
     IDirect3DSurface9Impl *pSurface;
+    IDirect3DSurface9Impl *pPrev = NULL;
+    IWineD3DSurface *pWineD3DSurface;
     HRESULT hr;
     TRACE("(%p) Relay\n" , This);
 
     pSurface = (IDirect3DSurface9Impl*)pZStencilSurface;
     EnterCriticalSection(&d3d9_cs);
+    /* Retrieve the previous depth-stencil surface, if it exists */
+    hr = IWineD3DDevice_GetDepthStencilSurface(This->WineD3DDevice,&pWineD3DSurface);
+    if (hr == WINED3D_OK) {
+        IWineD3DSurface_GetParent(pWineD3DSurface,(IUnknown**)&pPrev);
+        IWineD3DSurface_Release(pWineD3DSurface);
+    }
     hr = IWineD3DDevice_SetDepthStencilSurface(This->WineD3DDevice, NULL==pSurface ? NULL : pSurface->wineD3DSurface);
+    if (hr == WINED3D_OK) {
+        /* Protect user-supplied surfaces from deletion while they are
+           selected into the device. */
+        if (pSurface != NULL && !pSurface->isImplicit) {
+            InterlockedIncrement(&pSurface->refPrivate);
+        }
+        if (pPrev != NULL && !pPrev->isImplicit) {
+            InterlockedDecrement(&pPrev->refPrivate);
+        }
+    }
+    /* If there was no external reference, will be deleted */
+    if (pPrev != NULL) IUnknown_Release((IUnknown*) pPrev);
     LeaveCriticalSection(&d3d9_cs);
     return hr;
 }
diff --git a/dlls/d3d9/surface.c b/dlls/d3d9/surface.c
index 54558b4..8787380 100644
--- a/dlls/d3d9/surface.c
+++ b/dlls/d3d9/surface.c
@@ -77,7 +77,7 @@ static ULONG WINAPI IDirect3DSurface9Impl_Release(LPDIRECT3DSURFACE9 iface) {
 
         if (ref == 0) {
             if (This->parentDevice) IUnknown_Release(This->parentDevice);
-            if (!This->isImplicit) {
+            if (!This->isImplicit && This->refPrivate == 0) {
                 EnterCriticalSection(&d3d9_cs);
                 IWineD3DSurface_Release(This->wineD3DSurface);
                 LeaveCriticalSection(&d3d9_cs);
-- 
1.5.4.3

From 7e5407dd53a6bf894dd62a920a6f94cc9935f7a1 Mon Sep 17 00:00:00 2001
From: Jim Cameron <[EMAIL PROTECTED]>
Date: Thu, 9 Oct 2008 22:11:41 +0100
Subject: Test for depth-stencil surface deletion

---
 dlls/d3d9/tests/device.c |   15 +++++++++++++++
 1 files changed, 15 insertions(+), 0 deletions(-)

diff --git a/dlls/d3d9/tests/device.c b/dlls/d3d9/tests/device.c
index d28da88..6d47306 100644
--- a/dlls/d3d9/tests/device.c
+++ b/dlls/d3d9/tests/device.c
@@ -1493,6 +1493,21 @@ static void test_depthstenciltest(void)
     ok(hr == D3D_OK, "IDirect3DDevice9_GetRenderState failed with %08x\n", hr);
     ok(state == D3DZB_FALSE, "D3DRS_ZENABLE is %s\n", state == D3DZB_FALSE ? "D3DZB_FALSE" : (state == D3DZB_TRUE ? "D3DZB_TRUE" : "D3DZB_USEW"));
 
+    /* Test if a user-supplied depth-stencil surface survives being selected and then released. */
+    hr = IDirect3DDevice9_CreateDepthStencilSurface
+        (pDevice, d3dpp.BackBufferWidth, d3dpp.BackBufferHeight,
+         d3dpp.AutoDepthStencilFormat, D3DMULTISAMPLE_NONE, 0, FALSE,
+         &pDepthStencil, NULL);
+    ok(hr == D3D_OK && pDepthStencil != NULL, "IDirect3DDevice9_CreateDepthStencilSurface failed with %08x\n", hr);
+    hr = IDirect3DDevice9_SetDepthStencilSurface(pDevice, pDepthStencil);
+    ok(hr == D3D_OK, "IDirect3DDevice9_SetDepthStencilSurface failed with %08x\n", hr);
+    /* Surface should remain valid, but with refcount of zero */
+    CHECK_RELEASE_REFCOUNT(pDepthStencil, 0);
+    CHECK_ADDREF_REFCOUNT(pDepthStencil, 1);
+    /* Select it back out so it's deleted cleanly */
+    hr = IDirect3DDevice9_SetDepthStencilSurface(pDevice, NULL);
+    ok(hr == D3D_OK, "IDirect3DDevice9_SetDepthStencilSurface failed with %08x\n", hr);
+
 cleanup:
     if(pDepthStencil) IDirect3DSurface9_Release(pDepthStencil);
     if(pD3d) IDirect3D9_Release(pD3d);
-- 
1.5.4.3



Reply via email to