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)