[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
Giovanni Mascellani (@giomasce) commented about dlls/mfplat/tests/mfplat.c:
+ + 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 */ Unless I'm missing something, you don't seem to check what happens when you enqueue many `ResourceReady` one after the other. Does it model an event (i.e., many `ResourceReady` count as just one) or some kind of reference count like for `ResourceRelease`?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9777#note_137212
Giovanni Mascellani (@giomasce) commented about dlls/mfplat/main.c:
+ * 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); It seems that `D3D12_FENCE_FLAG_SHARED` is not currently implemented in vkd3d. Is it needed for the "EnqueueResourceReady works cross-device" test?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9777#note_137213
Giovanni Mascellani (@giomasce) commented about dlls/mfplat/main.c:
+ 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; 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.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9777#note_137214
Giovanni Mascellani (@giomasce) commented about dlls/mfplat/main.c:
+ 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); `SetEventOnCompletion()` has a special behavior when passed a NULL event: it just waits for the fence to be completed. It would probably be good to check whether this behavior is expected for `SignalEventOnResourceReady()` and either check that the event is not NULL if it's not or implement it without holding the critical section forever if it is.
(relatedly, other APIs might return with `E_POINTER` when passed a NULL pointer, rather than crashing) -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9777#note_137215
Giovanni Mascellani (@giomasce) commented about dlls/mfplat/main.c:
+ 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); This didn't have `D3D12_FENCE_FLAG_SHARED` before, now it has. Can you elaborate on why?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9777#note_137216
Giovanni Mascellani (@giomasce) commented about dlls/mfplat/main.c:
+{ + 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); It doesn't seem this event is ever reset after being used; so the second time you use `struct d3d12_sync_object_release` the event will already be set.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9777#note_137217
On Tue Apr 21 13:10:25 2026 +0000, Nikolay Sivov wrote:
@CharlottePabst in case you're waiting, I'll still need someone familiar with d3d12 concepts to effectively review this. Does this block other missing parts to get d3d12 buffers working? Right now we can't create such buffers at all. I had a look and it mostly seems fine, but I added some comments.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9777#note_137218
On Tue Apr 21 13:10:25 2026 +0000, Giovanni Mascellani wrote:
It doesn't seem this event is ever reset after being used; so the second time you use `struct d3d12_sync_object_release` the event will already be set. The event is created with bManualReset=FALSE, so RtwqPutWaitingWorkItem() (indirectly) resets it after the event becomes signaled. But perhaps we shouldn't rely on that - it seems unclear to me if it could be considered documented behavior. It seems like a reasonable expectation tho(?) - the threadpool worker/waitqueue has to wait on the event in some way and then become released after it fires.
It also seems like we use bManualReset=FALSE together with MFPutWaitingWorkItem() in dlls/mf/sar.c. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9777#note_137228
On Tue Apr 21 13:10:25 2026 +0000, Giovanni Mascellani wrote:
This didn't have `D3D12_FENCE_FLAG_SHARED` before, now it has. Can you elaborate on why? I believe it was a performance vs. correctness tradeoff. To my understanding D3D12_FENCE_FLAG_SHARED is correct, but Paul told me that creating a fence with this flag would be quite expensive (and it is a bit of a niche use case), and so I dropped it - but with the freelist (which came later), fence creation would be cached. It probably doesn't make much sense to change it in this commit in hindsight, perhaps we could also squash the freelist commit and the additional test commit into the initial implementation.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9777#note_137237
On Tue Apr 21 13:10:24 2026 +0000, Giovanni Mascellani wrote:
It seems that `D3D12_FENCE_FLAG_SHARED` is not currently implemented in vkd3d. Is it needed for the "EnqueueResourceReady works cross-device" test? This is the intent, yes. My understanding is that D3D12_FENCE_FLAG_SHARED enables cross-ID3D12Device usage of the fence. It seems like the test still passes as is however - I'm unsure why.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9777#note_137238
On Tue Apr 21 17:57:50 2026 +0000, Charlotte Pabst wrote:
This is the intent, yes. My understanding is that D3D12_FENCE_FLAG_SHARED enables cross-ID3D12Device usage of the fence. It seems like the test still passes as is however - I'm unsure why. Hmm, that's puzzling. I looked around on the internet, and there isn't much, but the only traces I could find is that `D3D12_FENCE_FLAG_SHARED` allows you to use `CreateSharedHandle()` and `OpenSharedHandle()` on that thing, something you're not doing anyway. I can't see any trace of the fact that it allows using a fence object itself sharing it between two different devices.
[However I did some tests](https://gitlab.winehq.org/giomasce/wine/-/commit/b0ec97e668e93de98c1cdb031ff...), and that seems to work anyway, even between two different adaptors, even without the flag, and it doesn't trigger any validation error. Of course it might be that it works by chance and the validation layer just doesn't catch that. All in all I don't really know what to think. My feeling is that the flag is irrelevant for correctness (either this is fine even without the flags, or it's not good even with the flag). OTOH there's still wide room to belive I'm not correct and using the flag anyway is safer, even if likely less performant. I won't take a hard stance here. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9777#note_137323
On Tue Apr 21 17:58:26 2026 +0000, Charlotte Pabst wrote:
I believe it was a performance vs. correctness tradeoff. To my understanding D3D12_FENCE_FLAG_SHARED is correct, but Paul told me that creating a fence with this flag would be quite expensive (and it is a bit of a niche use case), and so I dropped it - but with the freelist (which came later), fence creation would be cached. It probably doesn't make much sense to change it in this commit in hindsight, perhaps we could also squash the freelist commit and the additional test commit into the initial implementation. Yeah, irrespective of the `D3D12_FENCE_FLAG_SHARED`, it doesn't make a lot of sense to commit a certain implementation and then replace with another one immediately after.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9777#note_137325
On Wed Apr 22 10:06:39 2026 +0000, Charlotte Pabst wrote:
The event is created with bManualReset=FALSE, so RtwqPutWaitingWorkItem() (indirectly) resets it after the event becomes signaled. But perhaps we shouldn't rely on that - it seems unclear to me if it could be considered documented behavior. It seems like a reasonable expectation tho(?) - the threadpool worker/waitqueue has to wait on the event in some way and then become released after it fires. It also seems like we use bManualReset=FALSE together with MFPutWaitingWorkItem() in dlls/mf/sar.c. Oh yes, my bad here: I always get confused with manual reset, and I interpret it as "automatic reset", so I invert its meaning. It's not the first time. I think it makes sense that passing the automatic reset event to `RtwqPutWaitingWorkItem()` will indeed reset it.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9777#note_137326
participants (3)
-
Charlotte Pabst -
Charlotte Pabst (@CharlottePabst) -
Giovanni Mascellani (@giomasce)