[PATCH v3 0/5] 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.) -- v3: mfplat: Use freelist for mfd3d12 syncobj release. mfplat/tests: Test that mfd3d12 syncobj release keeps working through multiple resets. mfplat: Implement mf d3d12 synchronization object. mfplat/tests: Add tests for mf d3d12 synchronization object. mfd3d12.h: Add some missing definitions. 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 | 455 +++++++++++++++++++++++++++++++++++++ 1 file changed, 455 insertions(+) diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index c9514293c49..0b1b4f75d97 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); @@ -1770,6 +1771,7 @@ static void init_functions(void) X(MFTUnregisterLocal); X(MFTUnregisterLocalByCLSID); X(MFUnlockDXGIDeviceManager); + X(MFCreateD3D12SynchronizationObject); if ((mod = LoadLibraryA("d3d11.dll"))) { @@ -11324,6 +11326,458 @@ notsupported: ok(!refcount, "Unexpected device refcount %u.\n", refcount); } +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; + 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); + + /* 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); + + /* 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); + + 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); + status = WaitForSingleObject(event, 100); + ok(status == WAIT_TIMEOUT, "got %#lx.\n", status); + + hr = ID3D12CommandQueue_Wait(queue2, fence2, 1); + 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, 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(fence2, 1); + 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); + + /* 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 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); + + /* 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 }; @@ -13969,6 +14423,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 01d9d2ddd89..445a074a561 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_async_release_params +{ + IUnknown IUnknown_iface; + LONG refcount; + ID3D12Fence *fence; + HANDLE event; +}; + +static struct d3d12_sync_object_async_release_params *d3d12_sync_object_async_release_params_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct d3d12_sync_object_async_release_params, IUnknown_iface); +} + +static HRESULT WINAPI d3d12_sync_object_async_release_params_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IUnknown_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_params_AddRef(IUnknown *iface) +{ + struct d3d12_sync_object_async_release_params *params = d3d12_sync_object_async_release_params_from_IUnknown(iface); + return InterlockedIncrement(¶ms->refcount); +} + +static ULONG WINAPI d3d12_sync_object_async_release_params_Release(IUnknown *iface) +{ + struct d3d12_sync_object_async_release_params *params = d3d12_sync_object_async_release_params_from_IUnknown(iface); + ULONG ref = InterlockedDecrement(¶ms->refcount); + if (!ref) + { + CloseHandle(params->event); + ID3D12Fence_Release(params->fence); + free(params); + } + return ref; +} + +static const IUnknownVtbl d3d12_sync_object_async_release_params_vtbl = +{ + d3d12_sync_object_async_release_params_QueryInterface, + d3d12_sync_object_async_release_params_AddRef, + d3d12_sync_object_async_release_params_Release, +}; + +static IUnknown *d3d12_sync_object_async_release_params_create(HANDLE event, ID3D12Fence *fence) +{ + struct d3d12_sync_object_async_release_params *params; + if (!(params = calloc(1, sizeof(*params)))) return NULL; + params->IUnknown_iface.lpVtbl = &d3d12_sync_object_async_release_params_vtbl; + params->refcount = 1; + params->event = event; + params->fence = fence; + return ¶ms->IUnknown_iface; +} + +struct d3d12_sync_object +{ + IMFD3D12SynchronizationObject IMFD3D12SynchronizationObject_iface; + IMFD3D12SynchronizationObjectCommands IMFD3D12SynchronizationObjectCommands_iface; + IRtwqAsyncCallback async_release_iface; + LONG refcount; + CRITICAL_SECTION cs; + ID3D12Fence *ready_fence; + UINT64 generation; + unsigned int release_wait; + HANDLE final_release_event; +}; + +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) + { + 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); + + 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; + 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); + + 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); + + 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; + + TRACE("%p, %p.\n", iface, event); + + EnterCriticalSection(&syncobj->cs); + hr = ID3D12Fence_SetEventOnCompletion(syncobj->ready_fence, syncobj->generation, event); + LeaveCriticalSection(&syncobj->cs); + + return hr; +} + +static HRESULT WINAPI d3d12_sync_object_commands_EnqueueResourceRelease(IMFD3D12SynchronizationObjectCommands *iface, ID3D12CommandQueue *consumer_queue) +{ + struct d3d12_sync_object *syncobj = impl_from_IMFD3D12SynchronizationObjectCommands(iface); + HRESULT hr; + HANDLE event; + ID3D12Device *device; + ID3D12Fence *fence; + IUnknown *params; + IRtwqAsyncResult *result; + + TRACE("%p, %p.\n", iface, consumer_queue); + + hr = ID3D12CommandQueue_GetDevice(consumer_queue, &IID_ID3D12Device, (void **) &device); + if (FAILED(hr)) + return hr; + + hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence); + ID3D12Device_Release(device); + if (FAILED(hr)) + return hr; + + hr = ID3D12CommandQueue_Signal(consumer_queue, fence, 1); + if (FAILED(hr)) + { + ID3D12Fence_Release(fence); + return hr; + } + + event = CreateEventA(NULL, FALSE, FALSE, NULL); + params = d3d12_sync_object_async_release_params_create(event, fence); + if (!params) + { + CloseHandle(event); + ID3D12Fence_Release(fence); + return E_OUTOFMEMORY; + } + + hr = ID3D12Fence_SetEventOnCompletion(fence, 1, event); + if (FAILED(hr)) + { + IUnknown_Release(params); + return hr; + } + + hr = RtwqCreateAsyncResult(NULL, &syncobj->async_release_iface, params, &result); + IUnknown_Release(params); + if (FAILED(hr)) + return hr; + + EnterCriticalSection(&syncobj->cs); + hr = RtwqPutWaitingWorkItem(event, 0, result, NULL); + IRtwqAsyncResult_Release(result); + if (SUCCEEDED(hr)) + ++syncobj->release_wait; + 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); + syncobj->generation = 1; + + 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 7b356f9f457..227ac4a24c7 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 0b1b4f75d97..79281a8fb4f 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -11349,7 +11349,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
From: Charlotte Pabst <cpabst@codeweavers.com> --- dlls/mfplat/tests/mfplat.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 79281a8fb4f..c0325e9a436 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -11503,7 +11503,9 @@ static void test_d3d12_sync_object(void) 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); + 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); @@ -11513,23 +11515,27 @@ static void test_d3d12_sync_object(void) status = WaitForSingleObject(event, 100); ok(status == WAIT_TIMEOUT, "got %#lx.\n", status); - hr = ID3D12CommandQueue_Wait(queue2, fence2, 1); + 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, 1); + 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, 1); + 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); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9777
From: Charlotte Pabst <cpabst@codeweavers.com> --- dlls/mfplat/main.c | 172 +++++++++++++++++++++------------------------ 1 file changed, 79 insertions(+), 93 deletions(-) diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index 445a074a561..98d9b5cf8f1 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -8849,70 +8849,13 @@ HRESULT WINAPI CreatePropertyStore(IPropertyStore **store) return S_OK; } -struct d3d12_sync_object_async_release_params +struct d3d12_sync_object_release { - IUnknown IUnknown_iface; - LONG refcount; + struct list entry; ID3D12Fence *fence; HANDLE event; }; -static struct d3d12_sync_object_async_release_params *d3d12_sync_object_async_release_params_from_IUnknown(IUnknown *iface) -{ - return CONTAINING_RECORD(iface, struct d3d12_sync_object_async_release_params, IUnknown_iface); -} - -static HRESULT WINAPI d3d12_sync_object_async_release_params_QueryInterface(IUnknown *iface, REFIID riid, void **obj) -{ - if (IsEqualIID(riid, &IID_IUnknown)) - { - *obj = iface; - IUnknown_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_params_AddRef(IUnknown *iface) -{ - struct d3d12_sync_object_async_release_params *params = d3d12_sync_object_async_release_params_from_IUnknown(iface); - return InterlockedIncrement(¶ms->refcount); -} - -static ULONG WINAPI d3d12_sync_object_async_release_params_Release(IUnknown *iface) -{ - struct d3d12_sync_object_async_release_params *params = d3d12_sync_object_async_release_params_from_IUnknown(iface); - ULONG ref = InterlockedDecrement(¶ms->refcount); - if (!ref) - { - CloseHandle(params->event); - ID3D12Fence_Release(params->fence); - free(params); - } - return ref; -} - -static const IUnknownVtbl d3d12_sync_object_async_release_params_vtbl = -{ - d3d12_sync_object_async_release_params_QueryInterface, - d3d12_sync_object_async_release_params_AddRef, - d3d12_sync_object_async_release_params_Release, -}; - -static IUnknown *d3d12_sync_object_async_release_params_create(HANDLE event, ID3D12Fence *fence) -{ - struct d3d12_sync_object_async_release_params *params; - if (!(params = calloc(1, sizeof(*params)))) return NULL; - params->IUnknown_iface.lpVtbl = &d3d12_sync_object_async_release_params_vtbl; - params->refcount = 1; - params->event = event; - params->fence = fence; - return ¶ms->IUnknown_iface; -} - struct d3d12_sync_object { IMFD3D12SynchronizationObject IMFD3D12SynchronizationObject_iface; @@ -8920,10 +8863,13 @@ struct d3d12_sync_object 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) @@ -8981,6 +8927,18 @@ static ULONG WINAPI d3d12_sync_object_Release(IMFD3D12SynchronizationObject *ifa 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); @@ -9016,7 +8974,10 @@ static HRESULT WINAPI d3d12_sync_object_Reset(IMFD3D12SynchronizationObject *ifa if (syncobj->release_wait) hr = MF_E_UNEXPECTED; else + { ++syncobj->generation; + syncobj->release_freelist_cursor = &syncobj->release_freelist; + } LeaveCriticalSection(&syncobj->cs); return hr; @@ -9079,62 +9040,83 @@ static HRESULT WINAPI d3d12_sync_object_commands_SignalEventOnResourceReady(IMFD 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); - HRESULT hr; - HANDLE event; - ID3D12Device *device; - ID3D12Fence *fence; - IUnknown *params; + struct d3d12_sync_object_release *relobj; IRtwqAsyncResult *result; + RTWQWORKITEM_KEY key; + HRESULT hr; TRACE("%p, %p.\n", iface, consumer_queue); - hr = ID3D12CommandQueue_GetDevice(consumer_queue, &IID_ID3D12Device, (void **) &device); + EnterCriticalSection(&syncobj->cs); + hr = d3d12_sync_object_create_release(syncobj, &relobj); if (FAILED(hr)) - return hr; + goto end; - hr = ID3D12Device_CreateFence(device, 0, 0, &IID_ID3D12Fence, (void **) &fence); - ID3D12Device_Release(device); + hr = RtwqCreateAsyncResult(NULL, &syncobj->async_release_iface, NULL, &result); if (FAILED(hr)) - return hr; + goto end; - hr = ID3D12CommandQueue_Signal(consumer_queue, fence, 1); + hr = RtwqPutWaitingWorkItem(relobj->event, 0, result, &key); + IRtwqAsyncResult_Release(result); if (FAILED(hr)) - { - ID3D12Fence_Release(fence); - return hr; - } + goto end; - event = CreateEventA(NULL, FALSE, FALSE, NULL); - params = d3d12_sync_object_async_release_params_create(event, fence); - if (!params) + hr = ID3D12CommandQueue_Signal(consumer_queue, relobj->fence, syncobj->generation); + if (FAILED(hr)) { - CloseHandle(event); - ID3D12Fence_Release(fence); - return E_OUTOFMEMORY; + RtwqCancelWorkItem(key); + goto end; } - hr = ID3D12Fence_SetEventOnCompletion(fence, 1, event); + hr = ID3D12Fence_SetEventOnCompletion(relobj->fence, syncobj->generation, relobj->event); if (FAILED(hr)) { - IUnknown_Release(params); - return hr; + RtwqCancelWorkItem(key); + /* can't re-use fence since queue might signal it */ + ID3D12Fence_Release(relobj->fence); + relobj->fence = NULL; + goto end; } - hr = RtwqCreateAsyncResult(NULL, &syncobj->async_release_iface, params, &result); - IUnknown_Release(params); - if (FAILED(hr)) - return hr; + ++syncobj->release_wait; + syncobj->release_freelist_cursor = &relobj->entry; - EnterCriticalSection(&syncobj->cs); - hr = RtwqPutWaitingWorkItem(event, 0, result, NULL); - IRtwqAsyncResult_Release(result); - if (SUCCEEDED(hr)) - ++syncobj->release_wait; +end: LeaveCriticalSection(&syncobj->cs); - return hr; } @@ -9240,7 +9222,11 @@ HRESULT WINAPI MFCreateD3D12SynchronizationObject(ID3D12Device *device, REFIID r 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); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9777
I'm not fully done with the buffers implementation yet if that's the question (working on it). But yes, d3d12 buffers require this feature. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9777#note_132032
participants (2)
-
Charlotte Pabst -
Charlotte Pabst (@CharlottePabst)