[PATCH v5 0/3] MR9777: mfplat: Implement mf d3d12 synchronization object.
I'm also working on tests & implementation for d3d12-backed IMFMediaBuffers and sample allocators, submitting this here to receive some feedback while working on more. Sorry if the tests are too involved, perhaps they could be simplified, but since there is very little documentation on this I had to figure out a lot of minor details using tests. The use case is Fuga Melodies of Steel 3 which uses the UE5 electra decoder plugin. It expects d3d12 support in media foundation (sample allocator, media buffer, sync object). This game doesn't require implementing proper d3d12 video decoding, only that frame data is stored in a IMFMediaBuffer that is backed by a ID3D12Resource. Once finished, this feature would presumably help with other UE5 games that have similar usage patterns as well (All the code for it is in UE5, which is source-available). Other real world examples are limited, but I believe chromium and mesa have some optional code using this feature. MFCreateD3D12SynchronizationObject is (presumably) used by MFCreateDXGISurfaceBuffer to create a synchronization object for the buffer (I already have some tests that query and use the synchronization object from a d3d12-backed mediabuffer, which I will submit together with the other d3d12 buffer tests.) -- v5: mfplat: Implement mf d3d12 synchronization object. mfplat/tests: Add tests for mf d3d12 synchronization object. https://gitlab.winehq.org/wine/wine/-/merge_requests/9777
From: Charlotte Pabst <cpabst@codeweavers.com> --- include/mfd3d12.idl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/mfd3d12.idl b/include/mfd3d12.idl index 2098e6d405a..d6124d4a279 100644 --- a/include/mfd3d12.idl +++ b/include/mfd3d12.idl @@ -40,8 +40,11 @@ interface IMFD3D12SynchronizationObjectCommands : IUnknown interface IMFD3D12SynchronizationObject : IUnknown { HRESULT SignalEventOnFinalResourceRelease(HANDLE hEvent); + HRESULT Reset(); } +cpp_quote("HRESULT WINAPI MFCreateD3D12SynchronizationObject(ID3D12Device *device, REFIID riid, void **obj);") + typedef enum MF_MT_D3D_RESOURCE_VERSION_ENUM { MF_D3D11_RESOURCE, @@ -58,6 +61,8 @@ cpp_quote("EXTERN_GUID(MF_MT_D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, 0x82c85 cpp_quote("EXTERN_GUID(MF_MT_D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE, 0xba06bfac, 0xffe3, 0x474a, 0xab, 0x55, 0x16, 0x1e, 0xe4, 0x41, 0x7a, 0x2e);") cpp_quote("EXTERN_GUID(MF_MT_D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER, 0xa6a1e439, 0x2f96, 0x4ab5, 0x98, 0xdc, 0xad, 0xf7, 0x49, 0x73, 0x50, 0x5d);") cpp_quote("EXTERN_GUID(MF_MT_D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS, 0xa4940b2, 0xcfd6, 0x4738, 0x9d, 0x02, 0x98, 0x11, 0x37, 0x34, 0x01, 0x5a);") +cpp_quote("EXTERN_GUID(MF_MT_D3D12_RESOURCE_DIMENSION, 0x5f772624, 0x16ca, 0x4b89, 0x96, 0x51, 0x5d, 0xdf, 0x76, 0x9f, 0x8a, 0xb8);") cpp_quote("EXTERN_GUID(MF_SA_D3D12_HEAP_FLAGS, 0x496b3266, 0xd28f, 0x4f8c, 0x93, 0xa7, 0x4a, 0x59, 0x6b, 0x1a, 0x31, 0xa1);") cpp_quote("EXTERN_GUID(MF_SA_D3D12_HEAP_TYPE, 0x56f26a76, 0xbbc1, 0x4ce0, 0xbb, 0x11, 0xe2, 0x23, 0x68, 0xd8, 0x74, 0xed);") cpp_quote("EXTERN_GUID(MF_SA_D3D12_CLEAR_VALUE, 0x86ba9a39, 0x526, 0x495d, 0x9a, 0xb5, 0x54, 0xec, 0x9f, 0xad, 0x6f, 0xc3);") +cpp_quote("EXTERN_GUID(MF_SA_D3D12_AWARE, 0x77f0bacb, 0x17a8, 0x4a50, 0x9a, 0x7d, 0xa5, 0xcc, 0x9, 0xd3, 0x9d, 0x44);") -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9777
From: Charlotte Pabst <cpabst@codeweavers.com> --- dlls/mfplat/tests/mfplat.c | 579 +++++++++++++++++++++++++++++++++++++ 1 file changed, 579 insertions(+) diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index e28fece1418..1f121515d9b 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -561,6 +561,7 @@ static HRESULT (WINAPI *pMFLockSharedWorkQueue)(const WCHAR *name, LONG base_pri static HRESULT (WINAPI *pMFLockDXGIDeviceManager)(UINT *token, IMFDXGIDeviceManager **manager); static HRESULT (WINAPI *pMFUnlockDXGIDeviceManager)(void); static HRESULT (WINAPI *pMFInitVideoFormat_RGB)(MFVIDEOFORMAT *format, DWORD width, DWORD height, DWORD d3dformat); +static HRESULT (WINAPI *pMFCreateD3D12SynchronizationObject)(ID3D12Device *device, REFIID riid, void **obj); static HRESULT (WINAPI *pRtwqStartup)(void); static HRESULT (WINAPI *pRtwqShutdown)(void); @@ -1774,6 +1775,7 @@ static void init_functions(void) X(MFTUnregisterLocal); X(MFTUnregisterLocalByCLSID); X(MFUnlockDXGIDeviceManager); + X(MFCreateD3D12SynchronizationObject); if ((mod = LoadLibraryA("d3d11.dll"))) { @@ -11415,6 +11417,582 @@ notsupported: ok(!refcount, "Unexpected device refcount %u.\n", refcount); } +static DWORD WINAPI test_d3d12_sync_object_thread(LPVOID param) +{ + HRESULT hr = IMFD3D12SynchronizationObjectCommands_SignalEventOnResourceReady(param, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + return 0; +} + +static void test_d3d12_sync_object(void) +{ + IMFD3D12SynchronizationObject *sync_obj; + IMFD3D12SynchronizationObjectCommands *sync_cmd; + IUnknown *unk; + ID3D12Device *device, *device2; + ID3D12CommandQueue *queue, *queue2; + ID3D12Fence *fence, *fence2; + HANDLE event, event2, event3, event4, thread; + D3D12_COMMAND_QUEUE_DESC queue_desc = { .Type = D3D12_COMMAND_LIST_TYPE_DIRECT }; + DWORD status; + unsigned int refcount; + HRESULT hr; + + /* d3d12 */ + if (!(device = create_d3d12_device())) + { + skip("Failed to create a D3D12 device, skipping tests.\n"); + return; + } + + if (!pMFCreateD3D12SynchronizationObject) + { + todo_wine + win_skip("MFCreateD3D12SynchronizationObject() is not available.\n"); + goto notsupported; + } + + hr = ID3D12Device_CreateCommandQueue(device, &queue_desc, &IID_ID3D12CommandQueue, (void **) &queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ID3D12Device_CreateCommandQueue(device, &queue_desc, &IID_ID3D12CommandQueue, (void **) &queue2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + event = CreateEventA(NULL, FALSE, FALSE, NULL); + event2 = CreateEventA(NULL, FALSE, FALSE, NULL); + + /* MFCreateD3D12SynchronizationObject */ + + hr = pMFCreateD3D12SynchronizationObject(device, &IID_IUnknown, NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = pMFCreateD3D12SynchronizationObject(device, &IID_IUnknown, (void **) &unk); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IUnknown_QueryInterface(unk, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IUnknown_QueryInterface(unk, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IUnknown_Release(unk); + IMFD3D12SynchronizationObject_Release(sync_obj); + IMFD3D12SynchronizationObjectCommands_Release(sync_cmd); + + hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObject_QueryInterface(sync_obj, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFD3D12SynchronizationObject_Release(sync_obj); + IMFD3D12SynchronizationObjectCommands_Release(sync_cmd); + + hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFD3D12SynchronizationObject_Release(sync_obj); + IMFD3D12SynchronizationObjectCommands_Release(sync_cmd); + + /* invalid arguments */ + + hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReadyWait(sync_cmd, NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceRelease(sync_cmd, NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObject_SignalEventOnFinalResourceRelease(sync_obj, NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + IMFD3D12SynchronizationObject_Release(sync_obj); + IMFD3D12SynchronizationObjectCommands_Release(sync_cmd); + + /* EnqueueResourceReady / EnqueueResourceReadyWait */ + + hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ID3D12Fence_SetEventOnCompletion(fence2, 1, event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ID3D12CommandQueue_Wait(queue, fence, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReadyWait(sync_cmd, queue2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ID3D12CommandQueue_Signal(queue2, fence2, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + status = WaitForSingleObject(event, 100); + ok(status == WAIT_TIMEOUT, "got %#lx.\n", status); + hr = ID3D12Fence_Signal(fence, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + status = WaitForSingleObject(event, 100); + ok(status == WAIT_OBJECT_0, "got %#lx.\n", status); + + IMFD3D12SynchronizationObjectCommands_Release(sync_cmd); + ID3D12Fence_Release(fence); + ID3D12Fence_Release(fence2); + + /* EnqueueResourceReady / SignalEventOnResourceReady */ + + hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObjectCommands_SignalEventOnResourceReady(sync_cmd, event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_SignalEventOnResourceReady(sync_cmd, event2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + status = WaitForSingleObject(event, 100); + ok(status == WAIT_TIMEOUT, "got %#lx.\n", status); + status = WaitForSingleObject(event2, 100); + ok(status == WAIT_TIMEOUT, "got %#lx.\n", status); + + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + status = WaitForSingleObject(event, 100); + ok(status == WAIT_OBJECT_0, "got %#lx.\n", status); + status = WaitForSingleObject(event2, 100); + ok(status == WAIT_OBJECT_0, "got %#lx.\n", status); + + IMFD3D12SynchronizationObjectCommands_Release(sync_cmd); + + /* multiple pending EnqueueResourceReady */ + + hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ID3D12CommandQueue_Wait(queue, fence, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ID3D12CommandQueue_Wait(queue2, fence2, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObjectCommands_SignalEventOnResourceReady(sync_cmd, event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + status = WaitForSingleObject(event, 100); + ok(status == WAIT_TIMEOUT, "got %#lx.\n", status); + + hr = ID3D12Fence_Signal(fence, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + status = WaitForSingleObject(event, 100); + ok(status == WAIT_OBJECT_0, "got %#lx.\n", status); + + hr = ID3D12Fence_Signal(fence2, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFD3D12SynchronizationObjectCommands_Release(sync_cmd); + ID3D12Fence_Release(fence); + ID3D12Fence_Release(fence2); + + /* SignalEventOnResourceReady(NULL) */ + + hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + thread = CreateThread(NULL, 0, test_d3d12_sync_object_thread, sync_cmd, 0, NULL); + + status = WaitForSingleObject(thread, 100); + ok(status == WAIT_TIMEOUT, "got %#lx.\n", status); + + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + status = WaitForSingleObject(thread, 100); + ok(status == WAIT_OBJECT_0, "got %#lx.\n", status); + + CloseHandle(thread); + IMFD3D12SynchronizationObjectCommands_Release(sync_cmd); + + /* EnqueueResourceReady works cross-device */ + + device2 = create_d3d12_device(); + hr = pMFCreateD3D12SynchronizationObject(device2, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObjectCommands_SignalEventOnResourceReady(sync_cmd, event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + status = WaitForSingleObject(event, 100); + ok(status == WAIT_OBJECT_0, "got %#lx.\n", status); + + IMFD3D12SynchronizationObjectCommands_Release(sync_cmd); + ID3D12Device_Release(device2); + + /* EnqueueResourceRelease depends on MFStartup */ + + hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceRelease(sync_cmd, queue); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Failed to start up, hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceRelease(sync_cmd, queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFD3D12SynchronizationObjectCommands_Release(sync_cmd); + + /* SignalEventOnFinalResourceRelease signals immediately if no Release enqueued */ + + hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObject_SignalEventOnFinalResourceRelease(sync_obj, event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + status = WaitForSingleObject(event, 100); + ok(status == WAIT_OBJECT_0, "got %#lx.\n", status); + + IMFD3D12SynchronizationObject_Release(sync_obj); + + /* EnqueueResourceRelease / SignalEventOnFinalResourceRelease */ + + hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + for (UINT64 gen = 1; gen <= 5; gen++) + { + hr = ID3D12CommandQueue_Wait(queue, fence, gen); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceRelease(sync_cmd, queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObject_SignalEventOnFinalResourceRelease(sync_obj, event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + status = WaitForSingleObject(event, 100); + ok(status == WAIT_TIMEOUT, "got %#lx.\n", status); + + hr = ID3D12CommandQueue_Wait(queue2, fence2, gen); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceRelease(sync_cmd, queue2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ID3D12Fence_Signal(fence, gen); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + status = WaitForSingleObject(event, 100); + ok(status == WAIT_TIMEOUT, "got %#lx.\n", status); + + hr = ID3D12Fence_Signal(fence2, gen); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + status = WaitForSingleObject(event, 100); + ok(status == WAIT_OBJECT_0, "got %#lx.\n", status); + + hr = IMFD3D12SynchronizationObject_Reset(sync_obj); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + + IMFD3D12SynchronizationObject_Release(sync_obj); + IMFD3D12SynchronizationObjectCommands_Release(sync_cmd); + ID3D12Fence_Release(fence); + ID3D12Fence_Release(fence2); + + /* SignalEventOnFinalResourceRelease only tracks one event */ + + hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ID3D12CommandQueue_Wait(queue, fence, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceRelease(sync_cmd, queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObject_SignalEventOnFinalResourceRelease(sync_obj, event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObject_SignalEventOnFinalResourceRelease(sync_obj, event2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ID3D12Fence_Signal(fence, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + status = WaitForSingleObject(event, 100); + ok(status == WAIT_TIMEOUT, "got %#lx.\n", status); + status = WaitForSingleObject(event2, 100); + ok(status == WAIT_OBJECT_0, "got %#lx.\n", status); + + IMFD3D12SynchronizationObject_Release(sync_obj); + IMFD3D12SynchronizationObjectCommands_Release(sync_cmd); + ID3D12Fence_Release(fence); + + /* SignalEventOnFinalResourceRelease with duplicated event */ + + hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + event3 = CreateEventA(NULL, FALSE, FALSE, NULL); + + hr = ID3D12CommandQueue_Wait(queue, fence, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceRelease(sync_cmd, queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObject_SignalEventOnFinalResourceRelease(sync_obj, event3); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + DuplicateHandle(GetCurrentProcess(), event3, GetCurrentProcess(), &event4, 0, FALSE, DUPLICATE_SAME_ACCESS); + CloseHandle(event3); + + hr = ID3D12Fence_Signal(fence, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + status = WaitForSingleObject(event4, 100); + ok(status == WAIT_TIMEOUT, "got %#lx.\n", status); + + IMFD3D12SynchronizationObject_Release(sync_obj); + IMFD3D12SynchronizationObjectCommands_Release(sync_cmd); + ID3D12Fence_Release(fence); + CloseHandle(event4); + + /* SignalEventOnFinalResourceRelease is not affected by pending Ready */ + + hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ID3D12CommandQueue_Wait(queue, fence, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObject_SignalEventOnFinalResourceRelease(sync_obj, event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + status = WaitForSingleObject(event, 100); + ok(status == WAIT_OBJECT_0, "got %#lx.\n", status); + + /* unblock queue */ + hr = ID3D12Fence_Signal(fence, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFD3D12SynchronizationObject_Release(sync_obj); + IMFD3D12SynchronizationObjectCommands_Release(sync_cmd); + ID3D12Fence_Release(fence); + + /* Reset errors if Release pending */ + + hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ID3D12CommandQueue_Wait(queue, fence, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceRelease(sync_cmd, queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObject_Reset(sync_obj); + ok(hr == MF_E_UNEXPECTED, "Unexpected hr %#lx.\n", hr); + + hr = ID3D12Fence_Signal(fence, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObject_SignalEventOnFinalResourceRelease(sync_obj, event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + status = WaitForSingleObject(event, 100); + ok(status == WAIT_OBJECT_0, "got %#lx.\n", status); + + hr = IMFD3D12SynchronizationObject_Reset(sync_obj); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFD3D12SynchronizationObject_Release(sync_obj); + IMFD3D12SynchronizationObjectCommands_Release(sync_cmd); + ID3D12Fence_Release(fence); + + /* Reset clears Ready */ + + hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObjectCommands_SignalEventOnResourceReady(sync_cmd, event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + status = WaitForSingleObject(event, 100); + ok(status == WAIT_OBJECT_0, "got %#lx.\n", status); + + hr = IMFD3D12SynchronizationObject_Reset(sync_obj); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObjectCommands_SignalEventOnResourceReady(sync_cmd, event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + status = WaitForSingleObject(event, 100); + ok(status == WAIT_TIMEOUT, "got %#lx.\n", status); + + IMFD3D12SynchronizationObject_Release(sync_obj); + IMFD3D12SynchronizationObjectCommands_Release(sync_cmd); + CloseHandle(event4); + + /* ReadyWait before Reset is signaled by Ready after Reset */ + + hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReadyWait(sync_cmd, queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ID3D12CommandQueue_Signal(queue, fence, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObject_Reset(sync_obj); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + ID3D12Fence_SetEventOnCompletion(fence, 1, event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + status = WaitForSingleObject(event, 100); + ok(status == WAIT_OBJECT_0, "got %#lx.\n", status); + + IMFD3D12SynchronizationObject_Release(sync_obj); + IMFD3D12SynchronizationObjectCommands_Release(sync_cmd); + ID3D12Fence_Release(fence); + + /* Ready after Reset is cleared by deferred Ready before Reset */ + + hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ID3D12CommandQueue_Wait(queue, fence, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ID3D12CommandQueue_Signal(queue, fence2, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObject_Reset(sync_obj); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObjectCommands_SignalEventOnResourceReady(sync_cmd, event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + status = WaitForSingleObject(event, 100); + ok(status == WAIT_OBJECT_0, "got %#lx.\n", status); + + hr = ID3D12Fence_Signal(fence, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ID3D12Fence_SetEventOnCompletion(fence2, 1, event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + status = WaitForSingleObject(event, 100); + ok(status == WAIT_OBJECT_0, "got %#lx.\n", status); + + hr = IMFD3D12SynchronizationObjectCommands_SignalEventOnResourceReady(sync_cmd, event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + status = WaitForSingleObject(event, 100); + ok(status == WAIT_TIMEOUT, "got %#lx.\n", status); + + IMFD3D12SynchronizationObject_Release(sync_obj); + IMFD3D12SynchronizationObjectCommands_Release(sync_cmd); + ID3D12Fence_Release(fence); + ID3D12Fence_Release(fence2); + + /* ReadyWait before Reset is signaled by deferred Ready before Reset */ + + hr = pMFCreateD3D12SynchronizationObject(device, &IID_IMFD3D12SynchronizationObjectCommands, (void **) &sync_cmd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_QueryInterface(sync_cmd, &IID_IMFD3D12SynchronizationObject, (void **) &sync_obj); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ID3D12CommandQueue_Wait(queue, fence, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReady(sync_cmd, queue); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObjectCommands_EnqueueResourceReadyWait(sync_cmd, queue2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ID3D12CommandQueue_Signal(queue2, fence2, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFD3D12SynchronizationObject_Reset(sync_obj); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ID3D12Fence_Signal(fence, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + ID3D12Fence_SetEventOnCompletion(fence2, 1, event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + status = WaitForSingleObject(event, 100); + ok(status == WAIT_OBJECT_0, "got %#lx.\n", status); + + IMFD3D12SynchronizationObject_Release(sync_obj); + IMFD3D12SynchronizationObjectCommands_Release(sync_cmd); + ID3D12Fence_Release(fence); + ID3D12Fence_Release(fence2); + + hr = MFShutdown(); + ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr); + + ID3D12CommandQueue_Release(queue); + ID3D12CommandQueue_Release(queue2); + CloseHandle(event); + CloseHandle(event2); + +notsupported: + refcount = ID3D12Device_Release(device); + ok(!refcount, "Unexpected device refcount %u.\n", refcount); +} + static void test_sample_allocator_sysmem(void) { IMFVideoSampleAllocatorNotify test_notify = { &test_notify_callback_vtbl }; @@ -14017,6 +14595,7 @@ START_TEST(mfplat) test_MFMapDXGIFormatToDX9Format(); test_d3d11_surface_buffer(); test_d3d12_surface_buffer(); + test_d3d12_sync_object(); test_sample_allocator_sysmem(); test_sample_allocator_d3d9(); test_sample_allocator_d3d11(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9777
From: Charlotte Pabst <cpabst@codeweavers.com> --- dlls/mfplat/main.c | 399 +++++++++++++++++++++++++++++++++++++ dlls/mfplat/mfplat.spec | 1 + dlls/mfplat/tests/mfplat.c | 1 - 3 files changed, 400 insertions(+), 1 deletion(-) diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index da9dce416ba..4c2f7284095 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -8849,6 +8849,405 @@ HRESULT WINAPI CreatePropertyStore(IPropertyStore **store) return S_OK; } +struct d3d12_sync_object_release +{ + struct list entry; + ID3D12Fence *fence; + HANDLE event; +}; + +struct d3d12_sync_object +{ + IMFD3D12SynchronizationObject IMFD3D12SynchronizationObject_iface; + IMFD3D12SynchronizationObjectCommands IMFD3D12SynchronizationObjectCommands_iface; + IRtwqAsyncCallback async_release_iface; + LONG refcount; + CRITICAL_SECTION cs; + ID3D12Device *device; + ID3D12Fence *ready_fence; + UINT64 generation; + unsigned int release_wait; + HANDLE final_release_event; + struct list release_freelist; + struct list *release_freelist_cursor; +}; + +static struct d3d12_sync_object *impl_from_IMFD3D12SynchronizationObject(IMFD3D12SynchronizationObject *iface) +{ + return CONTAINING_RECORD(iface, struct d3d12_sync_object, IMFD3D12SynchronizationObject_iface); +} + +static struct d3d12_sync_object *impl_from_IMFD3D12SynchronizationObjectCommands(IMFD3D12SynchronizationObjectCommands *iface) +{ + return CONTAINING_RECORD(iface, struct d3d12_sync_object, IMFD3D12SynchronizationObjectCommands_iface); +} + +static struct d3d12_sync_object *d3d12_sync_object_from_IRtwqAsyncCallback(IRtwqAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct d3d12_sync_object, async_release_iface); +} + +static HRESULT WINAPI d3d12_sync_object_QueryInterface(IMFD3D12SynchronizationObject *iface, REFIID riid, void **obj) +{ + struct d3d12_sync_object *syncobj = impl_from_IMFD3D12SynchronizationObject(iface); + + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFD3D12SynchronizationObject) || IsEqualIID(riid, &IID_IUnknown)) + *obj = &syncobj->IMFD3D12SynchronizationObject_iface; + else if (IsEqualIID(riid, &IID_IMFD3D12SynchronizationObjectCommands)) + *obj = &syncobj->IMFD3D12SynchronizationObjectCommands_iface; + else + { + *obj = NULL; + WARN("Unsupported interface %s.\n", debugstr_guid(riid)); + return E_NOINTERFACE; + } + + IMFD3D12SynchronizationObject_AddRef(&syncobj->IMFD3D12SynchronizationObject_iface); + return S_OK; +} + +static ULONG WINAPI d3d12_sync_object_AddRef(IMFD3D12SynchronizationObject *iface) +{ + struct d3d12_sync_object *syncobj = impl_from_IMFD3D12SynchronizationObject(iface); + ULONG refcount = InterlockedIncrement(&syncobj->refcount); + + TRACE("%p, refcount %ld.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI d3d12_sync_object_Release(IMFD3D12SynchronizationObject *iface) +{ + struct d3d12_sync_object *syncobj = impl_from_IMFD3D12SynchronizationObject(iface); + ULONG refcount = InterlockedDecrement(&syncobj->refcount); + + TRACE("%p, refcount %ld.\n", iface, refcount); + + if (!refcount) + { + struct d3d12_sync_object_release *cursor, *cursor2; + + LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &syncobj->release_freelist, + struct d3d12_sync_object_release, entry) + { + if (cursor->fence) + ID3D12Fence_Release(cursor->fence); + CloseHandle(cursor->event); + free(cursor); + } + + ID3D12Device_Release(syncobj->device); + ID3D12Fence_Release(syncobj->ready_fence); + DeleteCriticalSection(&syncobj->cs); + free(syncobj); + } + + return refcount; +} + +static HRESULT WINAPI d3d12_sync_object_SignalEventOnFinalResourceRelease(IMFD3D12SynchronizationObject *iface, HANDLE event) +{ + struct d3d12_sync_object *syncobj = impl_from_IMFD3D12SynchronizationObject(iface); + + TRACE("%p, %p.\n", iface, event); + + if (!event) + return E_INVALIDARG; + + EnterCriticalSection(&syncobj->cs); + if (syncobj->release_wait) + syncobj->final_release_event = event; + else + SetEvent(event); + LeaveCriticalSection(&syncobj->cs); + + return S_OK; +} + +static HRESULT WINAPI d3d12_sync_object_Reset(IMFD3D12SynchronizationObject *iface) +{ + struct d3d12_sync_object *syncobj = impl_from_IMFD3D12SynchronizationObject(iface); + HRESULT hr = S_OK; + + TRACE("%p.\n", iface); + + EnterCriticalSection(&syncobj->cs); + if (syncobj->release_wait) + hr = MF_E_UNEXPECTED; + else + { + ++syncobj->generation; + syncobj->release_freelist_cursor = &syncobj->release_freelist; + } + LeaveCriticalSection(&syncobj->cs); + + return hr; +} + +static HRESULT WINAPI d3d12_sync_object_commands_QueryInterface(IMFD3D12SynchronizationObjectCommands *iface, REFIID riid, void **obj) +{ + return d3d12_sync_object_QueryInterface(&impl_from_IMFD3D12SynchronizationObjectCommands(iface)->IMFD3D12SynchronizationObject_iface, riid, obj); +} + +static ULONG WINAPI d3d12_sync_object_commands_AddRef(IMFD3D12SynchronizationObjectCommands *iface) +{ + return d3d12_sync_object_AddRef(&impl_from_IMFD3D12SynchronizationObjectCommands(iface)->IMFD3D12SynchronizationObject_iface); +} + +static ULONG WINAPI d3d12_sync_object_commands_Release(IMFD3D12SynchronizationObjectCommands *iface) +{ + return d3d12_sync_object_Release(&impl_from_IMFD3D12SynchronizationObjectCommands(iface)->IMFD3D12SynchronizationObject_iface); +} + +static HRESULT WINAPI d3d12_sync_object_commands_EnqueueResourceReady(IMFD3D12SynchronizationObjectCommands *iface, ID3D12CommandQueue *producer_queue) +{ + struct d3d12_sync_object *syncobj = impl_from_IMFD3D12SynchronizationObjectCommands(iface); + HRESULT hr; + + TRACE("%p, %p.\n", iface, producer_queue); + + if (!producer_queue) + return E_INVALIDARG; + + EnterCriticalSection(&syncobj->cs); + hr = ID3D12CommandQueue_Signal(producer_queue, syncobj->ready_fence, syncobj->generation); + LeaveCriticalSection(&syncobj->cs); + + return hr; +} + +static HRESULT WINAPI d3d12_sync_object_commands_EnqueueResourceReadyWait(IMFD3D12SynchronizationObjectCommands *iface, ID3D12CommandQueue *consumer_queue) +{ + struct d3d12_sync_object *syncobj = impl_from_IMFD3D12SynchronizationObjectCommands(iface); + HRESULT hr; + + TRACE("%p, %p.\n", iface, consumer_queue); + + if (!consumer_queue) + return E_INVALIDARG; + + EnterCriticalSection(&syncobj->cs); + hr = ID3D12CommandQueue_Wait(consumer_queue, syncobj->ready_fence, syncobj->generation); + LeaveCriticalSection(&syncobj->cs); + + return hr; +} + +static HRESULT WINAPI d3d12_sync_object_commands_SignalEventOnResourceReady(IMFD3D12SynchronizationObjectCommands *iface, HANDLE event) +{ + struct d3d12_sync_object *syncobj = impl_from_IMFD3D12SynchronizationObjectCommands(iface); + HRESULT hr; + UINT64 generation; + + TRACE("%p, %p.\n", iface, event); + + EnterCriticalSection(&syncobj->cs); + generation = syncobj->generation; + LeaveCriticalSection(&syncobj->cs); + + hr = ID3D12Fence_SetEventOnCompletion(syncobj->ready_fence, generation, event); + return hr; +} + +static HRESULT d3d12_sync_object_create_release(struct d3d12_sync_object *syncobj, struct d3d12_sync_object_release **out) +{ + struct list *next; + struct d3d12_sync_object_release *relobj; + HRESULT hr; + + next = list_next(&syncobj->release_freelist, syncobj->release_freelist_cursor); + if (next) + { + relobj = LIST_ENTRY(next, struct d3d12_sync_object_release, entry); + } + else + { + relobj = calloc(1, sizeof(*relobj)); + if (!relobj) + return E_OUTOFMEMORY; + relobj->event = CreateEventA(NULL, FALSE, FALSE, NULL); + list_add_after(syncobj->release_freelist_cursor, &relobj->entry); + } + + if (!relobj->fence) + { + hr = ID3D12Device_CreateFence(syncobj->device, 0, D3D12_FENCE_FLAG_SHARED, &IID_ID3D12Fence, (void **) &relobj->fence); + if (FAILED(hr)) + return hr; + } + + *out = relobj; + return S_OK; +} + +static HRESULT WINAPI d3d12_sync_object_commands_EnqueueResourceRelease(IMFD3D12SynchronizationObjectCommands *iface, ID3D12CommandQueue *consumer_queue) +{ + struct d3d12_sync_object *syncobj = impl_from_IMFD3D12SynchronizationObjectCommands(iface); + struct d3d12_sync_object_release *relobj; + IRtwqAsyncResult *result; + RTWQWORKITEM_KEY key; + HRESULT hr; + + TRACE("%p, %p.\n", iface, consumer_queue); + + if (!consumer_queue) + return E_INVALIDARG; + + EnterCriticalSection(&syncobj->cs); + hr = d3d12_sync_object_create_release(syncobj, &relobj); + if (FAILED(hr)) + goto end; + + hr = RtwqCreateAsyncResult(NULL, &syncobj->async_release_iface, NULL, &result); + if (FAILED(hr)) + goto end; + + hr = RtwqPutWaitingWorkItem(relobj->event, 0, result, &key); + IRtwqAsyncResult_Release(result); + if (FAILED(hr)) + goto end; + + hr = ID3D12CommandQueue_Signal(consumer_queue, relobj->fence, syncobj->generation); + if (FAILED(hr)) + { + RtwqCancelWorkItem(key); + goto end; + } + + hr = ID3D12Fence_SetEventOnCompletion(relobj->fence, syncobj->generation, relobj->event); + if (FAILED(hr)) + { + RtwqCancelWorkItem(key); + /* can't re-use fence since queue might signal it */ + ID3D12Fence_Release(relobj->fence); + relobj->fence = NULL; + goto end; + } + + ++syncobj->release_wait; + syncobj->release_freelist_cursor = &relobj->entry; + +end: + LeaveCriticalSection(&syncobj->cs); + return hr; +} + +static HRESULT WINAPI d3d12_sync_object_async_release_QueryInterface(IRtwqAsyncCallback *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IRtwqAsyncCallback)) + { + *obj = iface; + IRtwqAsyncCallback_AddRef(iface); + return S_OK; + } + + *obj = NULL; + WARN("Unsupported interface %s.\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI d3d12_sync_object_async_release_AddRef(IRtwqAsyncCallback *iface) +{ + return d3d12_sync_object_AddRef(&d3d12_sync_object_from_IRtwqAsyncCallback(iface)->IMFD3D12SynchronizationObject_iface); +} + +static ULONG WINAPI d3d12_sync_object_async_release_Release(IRtwqAsyncCallback *iface) +{ + return d3d12_sync_object_Release(&d3d12_sync_object_from_IRtwqAsyncCallback(iface)->IMFD3D12SynchronizationObject_iface); +} + +static HRESULT WINAPI d3d12_sync_object_async_release_GetParameters(IRtwqAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI d3d12_sync_object_async_release_Invoke(IRtwqAsyncCallback *iface, IRtwqAsyncResult *result) +{ + struct d3d12_sync_object *syncobj = d3d12_sync_object_from_IRtwqAsyncCallback(iface); + + TRACE("%p, %p.\n", iface, result); + + EnterCriticalSection(&syncobj->cs); + if (!--syncobj->release_wait && syncobj->final_release_event) + { + SetEvent(syncobj->final_release_event); + syncobj->final_release_event = NULL; + } + LeaveCriticalSection(&syncobj->cs); + return S_OK; +} + +static const IMFD3D12SynchronizationObjectVtbl d3d12_sync_object_vtbl = +{ + d3d12_sync_object_QueryInterface, + d3d12_sync_object_AddRef, + d3d12_sync_object_Release, + d3d12_sync_object_SignalEventOnFinalResourceRelease, + d3d12_sync_object_Reset, +}; + +static const IMFD3D12SynchronizationObjectCommandsVtbl d3d12_sync_object_commands_vtbl = +{ + d3d12_sync_object_commands_QueryInterface, + d3d12_sync_object_commands_AddRef, + d3d12_sync_object_commands_Release, + d3d12_sync_object_commands_EnqueueResourceReady, + d3d12_sync_object_commands_EnqueueResourceReadyWait, + d3d12_sync_object_commands_SignalEventOnResourceReady, + d3d12_sync_object_commands_EnqueueResourceRelease, +}; + +static const IRtwqAsyncCallbackVtbl d3d12_sync_object_async_release_vtbl = +{ + d3d12_sync_object_async_release_QueryInterface, + d3d12_sync_object_async_release_AddRef, + d3d12_sync_object_async_release_Release, + d3d12_sync_object_async_release_GetParameters, + d3d12_sync_object_async_release_Invoke, +}; + +/*********************************************************************** + * MFCreateD3D12SynchronizationObject (mfplat.@) + */ + +HRESULT WINAPI MFCreateD3D12SynchronizationObject(ID3D12Device *device, REFIID riid, void **obj) +{ + HRESULT hr; + struct d3d12_sync_object *syncobj; + + if (!obj) + return E_INVALIDARG; + + syncobj = calloc(1, sizeof(*syncobj)); + if (!syncobj) + return E_OUTOFMEMORY; + + hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_SHARED, &IID_ID3D12Fence, (void **) &syncobj->ready_fence); + if (FAILED(hr)) + { + free(syncobj); + return hr; + } + + syncobj->refcount = 1; + syncobj->IMFD3D12SynchronizationObject_iface.lpVtbl = &d3d12_sync_object_vtbl; + syncobj->IMFD3D12SynchronizationObjectCommands_iface.lpVtbl = &d3d12_sync_object_commands_vtbl; + syncobj->async_release_iface.lpVtbl = &d3d12_sync_object_async_release_vtbl; + InitializeCriticalSection(&syncobj->cs); + ID3D12Device_AddRef(device); + syncobj->device = device; + syncobj->generation = 1; + list_init(&syncobj->release_freelist); + syncobj->release_freelist_cursor = &syncobj->release_freelist; + + hr = IMFD3D12SynchronizationObject_QueryInterface(&syncobj->IMFD3D12SynchronizationObject_iface, riid, obj); + IMFD3D12SynchronizationObject_Release(&syncobj->IMFD3D12SynchronizationObject_iface); + + return hr; +} + struct shared_dxgi_manager { IMFDXGIDeviceManager *manager; diff --git a/dlls/mfplat/mfplat.spec b/dlls/mfplat/mfplat.spec index 229230cd899..9f653d70df3 100644 --- a/dlls/mfplat/mfplat.spec +++ b/dlls/mfplat/mfplat.spec @@ -45,6 +45,7 @@ @ stdcall MFCreateAttributes(ptr long) @ stdcall MFCreateAudioMediaType(ptr ptr) @ stdcall MFCreateCollection(ptr) +@ stdcall MFCreateD3D12SynchronizationObject(ptr ptr ptr) @ stdcall MFCreateDXGIDeviceManager(ptr ptr) @ stdcall MFCreateDXGISurfaceBuffer(ptr ptr long long ptr) @ stdcall MFCreateDXSurfaceBuffer(ptr ptr long ptr) diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 1f121515d9b..727aac53a19 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -11447,7 +11447,6 @@ static void test_d3d12_sync_object(void) if (!pMFCreateD3D12SynchronizationObject) { - todo_wine win_skip("MFCreateD3D12SynchronizationObject() is not available.\n"); goto notsupported; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9777
On Tue Apr 21 13:10:24 2026 +0000, Giovanni Mascellani wrote:
You treat `final_release_event` as a borrowed reference, i.e. assuming that the caller won't destroy the handle it passed here until everything is done. This might or might not be correct, and it doesn't seem that tests are testing this. The caller might immediately destroy the handle and then wait for the event through a different duplicated handle, in which case you'd need to make your own duplicate here. Seems like the current behavior is correct, I added a test for it.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9777#note_139132
participants (2)
-
Charlotte Pabst -
Charlotte Pabst (@CharlottePabst)