[PATCH 2/4] quartz/vmr9: Track the IVMRSurfaceAllocatorNotify9 reference count separately.

Zebediah Figura z.figura12 at gmail.com
Mon May 4 18:05:22 CDT 2020


Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
 dlls/quartz/tests/vmr9.c |  4 ++--
 dlls/quartz/vmr9.c       | 41 +++++++++++++++++++++++++++++++++-------
 2 files changed, 36 insertions(+), 9 deletions(-)

diff --git a/dlls/quartz/tests/vmr9.c b/dlls/quartz/tests/vmr9.c
index 4994829641b..c0fb6a6f34a 100644
--- a/dlls/quartz/tests/vmr9.c
+++ b/dlls/quartz/tests/vmr9.c
@@ -3271,10 +3271,10 @@ static void test_surface_allocator_notify_refcount(void)
     ok(hr == S_OK, "Got hr %#x.\n", hr);
 
     ref = IBaseFilter_Release(filter);
-    todo_wine ok(!ref, "Got outstanding refcount %d.\n", ref);
+    ok(!ref, "Got outstanding refcount %d.\n", ref);
     todo_wine ok(allocator_got_TerminateDevice == 1, "Got %u calls to TerminateDevice().\n",
             allocator_got_TerminateDevice);
-    todo_wine ok(allocator_refcount == 1, "Got outstanding refcount %d.\n", allocator_refcount);
+    ok(allocator_refcount == 1, "Got outstanding refcount %d.\n", allocator_refcount);
 
     ref = IVMRSurfaceAllocatorNotify9_Release(notify);
     ok(!ref, "Got outstanding refcount %d.\n", ref);
diff --git a/dlls/quartz/vmr9.c b/dlls/quartz/vmr9.c
index 93fc3417e7f..a8f88e3d04b 100644
--- a/dlls/quartz/vmr9.c
+++ b/dlls/quartz/vmr9.c
@@ -56,6 +56,12 @@ struct quartz_vmr
     IVMRWindowlessControl IVMRWindowlessControl_iface;
     IVMRWindowlessControl9 IVMRWindowlessControl9_iface;
 
+    /* Devil May Cry 3 releases the last IBaseFilter reference while still
+     * holding an IVMRSurfaceAllocatorNotify9 reference, and depends on
+     * IVMRSurfaceAllocator9::TerminateDevice() being called as a result.
+     * Native uses a separate reference count for IVMRSurfaceAllocatorNotify9. */
+    LONG IVMRSurfaceAllocatorNotify9_refcount;
+
     IOverlay IOverlay_iface;
 
     IVMRSurfaceAllocatorEx9 *allocator;
@@ -571,12 +577,23 @@ static HRESULT WINAPI VMR9_BreakConnect(struct strmbase_renderer *This)
     return hr;
 }
 
+static void vmr_free(struct quartz_vmr *filter)
+{
+    free(filter);
+    InterlockedDecrement(&object_locks);
+}
+
 static void vmr_destroy(struct strmbase_renderer *iface)
 {
     struct quartz_vmr *filter = impl_from_IBaseFilter(&iface->filter.IBaseFilter_iface);
 
     video_window_cleanup(&filter->baseControlWindow);
 
+    /* Devil May Cry 3 releases the IVMRSurfaceAllocatorNotify9 interface from
+     * TerminateDevice(). Artificially increase the reference count so that we
+     * don't free the filter yet. */
+    InterlockedIncrement(&filter->renderer.filter.refcount);
+
     if (filter->allocator)
         IVMRSurfaceAllocatorEx9_Release(filter->allocator);
     if (filter->presenter)
@@ -592,9 +609,8 @@ static void vmr_destroy(struct strmbase_renderer *iface)
     CloseHandle(filter->run_event);
     FreeLibrary(filter->hD3d9);
     strmbase_renderer_cleanup(&filter->renderer);
-    free(filter);
-
-    InterlockedDecrement(&object_locks);
+    if (!filter->IVMRSurfaceAllocatorNotify9_refcount)
+        vmr_free(filter);
 }
 
 static HRESULT vmr_query_interface(struct strmbase_renderer *iface, REFIID iid, void **out)
@@ -2044,14 +2060,25 @@ static HRESULT WINAPI VMR9SurfaceAllocatorNotify_QueryInterface(IVMRSurfaceAlloc
 
 static ULONG WINAPI VMR9SurfaceAllocatorNotify_AddRef(IVMRSurfaceAllocatorNotify9 *iface)
 {
-    struct quartz_vmr *This = impl_from_IVMRSurfaceAllocatorNotify9(iface);
-    return IUnknown_AddRef(This->renderer.filter.outer_unk);
+    struct quartz_vmr *filter = impl_from_IVMRSurfaceAllocatorNotify9(iface);
+    ULONG refcount = InterlockedIncrement(&filter->IVMRSurfaceAllocatorNotify9_refcount);
+
+    TRACE("%p increasing refcount to %u.\n", iface, refcount);
+
+    return refcount;
 }
 
 static ULONG WINAPI VMR9SurfaceAllocatorNotify_Release(IVMRSurfaceAllocatorNotify9 *iface)
 {
-    struct quartz_vmr *This = impl_from_IVMRSurfaceAllocatorNotify9(iface);
-    return IUnknown_Release(This->renderer.filter.outer_unk);
+    struct quartz_vmr *filter = impl_from_IVMRSurfaceAllocatorNotify9(iface);
+    ULONG refcount = InterlockedDecrement(&filter->IVMRSurfaceAllocatorNotify9_refcount);
+
+    TRACE("%p decreasing refcount to %u.\n", iface, refcount);
+
+    if (!refcount && !filter->renderer.filter.refcount)
+        vmr_free(filter);
+
+    return refcount;
 }
 
 static HRESULT WINAPI VMR9SurfaceAllocatorNotify_AdviseSurfaceAllocator(IVMRSurfaceAllocatorNotify9 *iface, DWORD_PTR id, IVMRSurfaceAllocator9 *alloc)
-- 
2.26.2




More information about the wine-devel mailing list