Skip to content

Commit db495d5

Browse files
committed
feat(winemetal, dxmt): introduce SharedEventListener
Fix fence event not set for server-side synchronization
1 parent 23f9550 commit db495d5

File tree

7 files changed

+150
-20
lines changed

7 files changed

+150
-20
lines changed

src/d3d11/d3d11_fence.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ class MTLD3D11FenceImpl : public MTLD3D11DeviceChild<MTLD3D11Fence> {
4242

4343
HRESULT STDMETHODCALLTYPE SetEventOnCompletion(UINT64 Value,
4444
HANDLE Event) final {
45-
MTLSharedEvent_setWin32EventAtValue(event.handle, Event, Value);
45+
auto shared_event_listener = this->m_parent->GetDXMTDevice().queue().GetSharedEventListener();
46+
MTLSharedEvent_setWin32EventAtValue(event.handle, shared_event_listener, Event, Value);
4647
return S_OK;
4748
};
4849
};

src/dxmt/dxmt_command_queue.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ CommandQueue::CommandQueue(WMT::Device device) :
1919
finishThread([this]() { this->WaitForFinishThread(); }),
2020
device(device),
2121
commandQueue(device.newCommandQueue(kCommandChunkCount)),
22+
shared_event_listener(SharedEventListener_create()),
23+
event_listener_thread([this]() { SharedEventListener_start(this->shared_event_listener); }),
2224
staging_allocator({
2325
device, WMTResourceOptionCPUCacheModeWriteCombined | WMTResourceHazardTrackingModeUntracked |
2426
WMTResourceStorageModeShared
@@ -56,12 +58,14 @@ CommandQueue::~CommandQueue() {
5658
ready_for_encode.notify_one();
5759
ready_for_commit++;
5860
ready_for_commit.notify_one();
61+
SharedEventListener_destroy(shared_event_listener);
5962
encodeThread.join();
6063
finishThread.join();
6164
for (unsigned i = 0; i < kCommandChunkCount; i++) {
6265
auto &chunk = chunks[i];
6366
chunk.reset();
6467
};
68+
event_listener_thread.join();
6569
TRACE("Destructed command queue");
6670
}
6771

src/dxmt/dxmt_command_queue.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ class CommandQueue {
155155
WMT::Device device;
156156
WMT::Reference<WMT::CommandQueue> commandQueue;
157157

158+
obj_handle_t shared_event_listener;
159+
dxmt::thread event_listener_thread;
160+
158161
friend class CommandChunk;
159162
uint64_t
160163
GetNextEncoderId() {
@@ -211,6 +214,10 @@ class CommandQueue {
211214
return event.signaledValue();
212215
};
213216

217+
obj_handle_t GetSharedEventListener() {
218+
return shared_event_listener;
219+
}
220+
214221
/**
215222
This is not thread-safe!
216223
CurrentChunk & CommitCurrentChunk should be called on the same thread

src/winemetal/unix/winemetal_unix.c

Lines changed: 91 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
#include <stdatomic.h>
12
#include <dlfcn.h>
23
#import <Cocoa/Cocoa.h>
34
#import <ColorSync/ColorSync.h>
5+
#import <CoreFoundation/CFRunLoop.h>
46
#import <Metal/Metal.h>
57
#import <MetalFX/MetalFX.h>
68
#import <QuartzCore/QuartzCore.h>
@@ -2269,31 +2271,102 @@ _MTLSharedEvent_signalValue(void *obj) {
22692271
}
22702272

22712273
#ifndef DXMT_NATIVE
2274+
2275+
typedef struct {
2276+
_Atomic(CFRunLoopRef) runloop_ref;
2277+
MTLSharedEventListener *shared_listener;
2278+
} *shared_event_listener_t;
2279+
22722280
extern NTSTATUS NtSetEvent(void *handle, void *prev_state);
22732281

22742282
static NTSTATUS
22752283
_MTLSharedEvent_setWin32EventAtValue(void *obj) {
2276-
static MTLSharedEventListener *shared_listener = nil;
2277-
static dispatch_once_t pred;
2278-
dispatch_once(&pred, ^{
2279-
shared_listener = [[MTLSharedEventListener alloc] init];
2280-
});
2284+
struct unixcall_mtlsharedevent_setevent *params = obj;
2285+
void *nt_event_handle = (shared_event_listener_t)params->event_handle;
2286+
shared_event_listener_t q = (shared_event_listener_t)params->shared_event_listener;
2287+
[(id<MTLSharedEvent>)params->shared_event
2288+
notifyListener:q->shared_listener
2289+
atValue:params->value
2290+
block:^(id<MTLSharedEvent> _e, uint64_t _v) {
2291+
// NOTE: must ensure no more notification comes after listener been destroyed.
2292+
while (!atomic_load_explicit(&q->runloop_ref, memory_order_acquire)) {
2293+
#if defined(__x86_64__)
2294+
_mm_pause();
2295+
#elif defined(__aarch64__)
2296+
__asm__ __volatile__("yield");
2297+
#endif
2298+
}
2299+
CFRunLoopPerformBlock(q->runloop_ref, kCFRunLoopCommonModes, ^{
2300+
NtSetEvent(nt_event_handle, NULL);
2301+
});
2302+
CFRunLoopWakeUp(q->runloop_ref);
2303+
}];
2304+
return STATUS_SUCCESS;
2305+
}
22812306

2282-
struct unixcall_generic_obj_obj_uint64_noret *params = obj;
2283-
void *nt_event_handle = (void *)params->arg0;
2284-
[(id<MTLSharedEvent>)params->handle notifyListener:shared_listener
2285-
atValue:params->arg1
2286-
block:^(id<MTLSharedEvent> _e, uint64_t _v) {
2287-
NtSetEvent(nt_event_handle, NULL);
2288-
}];
2307+
static NTSTATUS
2308+
_SharedEventListener_start(void *obj) {
2309+
struct unixcall_generic_obj_noret *params = obj;
2310+
shared_event_listener_t q = (shared_event_listener_t)params->handle;
2311+
CFRunLoopRef uninited = NULL;
2312+
if (q && atomic_compare_exchange_strong(&q->runloop_ref, &uninited, CFRunLoopGetCurrent())) {
2313+
/* Add a dummy source so the runloop stays running */
2314+
CFRunLoopSourceContext source_context = {0};
2315+
CFRunLoopSourceRef source = CFRunLoopSourceCreate(NULL, 0, &source_context);
2316+
CFRunLoopAddSource(q->runloop_ref, source, kCFRunLoopCommonModes);
2317+
CFRunLoopRun();
2318+
}
2319+
return STATUS_SUCCESS;
2320+
}
2321+
2322+
static NTSTATUS
2323+
_SharedEventListener_create(void *obj) {
2324+
struct unixcall_generic_obj_ret *params = obj;
2325+
shared_event_listener_t q = malloc(sizeof(*q));
2326+
if (q) {
2327+
q->runloop_ref = NULL;
2328+
q->shared_listener = [[MTLSharedEventListener alloc] init];
2329+
}
2330+
params->ret = (obj_handle_t)q;
2331+
return STATUS_SUCCESS;
2332+
}
2333+
2334+
static NTSTATUS
2335+
_SharedEventListener_destroy(void *obj) {
2336+
struct unixcall_generic_obj_noret *params = obj;
2337+
shared_event_listener_t q = (shared_event_listener_t)params->handle;
2338+
if (q && q->runloop_ref) {
2339+
CFRunLoopStop(q->runloop_ref);
2340+
q->runloop_ref = NULL;
2341+
[q->shared_listener release];
2342+
q->shared_listener = nil;
2343+
free(q);
2344+
}
22892345
return STATUS_SUCCESS;
22902346
}
2347+
22912348
#else
22922349
static NTSTATUS
22932350
_MTLSharedEvent_setWin32EventAtValue(void *obj) {
22942351
// nop
22952352
return STATUS_SUCCESS;
22962353
}
2354+
2355+
static NTSTATUS
2356+
_SharedEventListener_start(void *obj) {
2357+
return STATUS_SUCCESS;
2358+
}
2359+
2360+
static NTSTATUS
2361+
_SharedEventListener_create(void *obj) {
2362+
return STATUS_SUCCESS;
2363+
}
2364+
2365+
static NTSTATUS
2366+
_SharedEventListener_destroy(void *obj) {
2367+
return STATUS_SUCCESS;
2368+
}
2369+
22972370
#endif
22982371

22992372
static NTSTATUS
@@ -2426,6 +2499,9 @@ const void *__wine_unix_call_funcs[] = {
24262499
&_MTLDevice_newFence,
24272500
&_MTLDevice_newEvent,
24282501
&_MTLBuffer_updateContents,
2502+
&_SharedEventListener_create,
2503+
&_SharedEventListener_start,
2504+
&_SharedEventListener_destroy,
24292505
};
24302506

24312507
#ifndef DXMT_NATIVE
@@ -2538,5 +2614,8 @@ const void *__wine_unix_call_wow64_funcs[] = {
25382614
&_MTLDevice_newFence,
25392615
&_MTLDevice_newEvent,
25402616
&_MTLBuffer_updateContents,
2617+
&_SharedEventListener_create,
2618+
&_SharedEventListener_start,
2619+
&_SharedEventListener_destroy,
25412620
};
25422621
#endif

src/winemetal/winemetal.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1736,7 +1736,9 @@ WINEMETAL_API void MTLCommandBuffer_encodeWaitForEvent(obj_handle_t cmdbuf, obj_
17361736

17371737
WINEMETAL_API void MTLSharedEvent_signalValue(obj_handle_t event, uint64_t value);
17381738

1739-
WINEMETAL_API void MTLSharedEvent_setWin32EventAtValue(obj_handle_t event, void *nt_event_handle, uint64_t at_value);
1739+
WINEMETAL_API void MTLSharedEvent_setWin32EventAtValue(
1740+
obj_handle_t event, obj_handle_t shared_event_listener, void *nt_event_handle, uint64_t at_value
1741+
);
17401742

17411743
WINEMETAL_API obj_handle_t MTLDevice_newFence(obj_handle_t device);
17421744

@@ -1745,4 +1747,10 @@ WINEMETAL_API obj_handle_t MTLDevice_newEvent(obj_handle_t device);
17451747
WINEMETAL_API void
17461748
MTLBuffer_updateContents(obj_handle_t buffer, uint64_t offset, struct WMTConstMemoryPointer data, uint64_t length);
17471749

1750+
WINEMETAL_API obj_handle_t SharedEventListener_create();
1751+
1752+
WINEMETAL_API void SharedEventListener_start(obj_handle_t shared_event_listener);
1753+
1754+
WINEMETAL_API void SharedEventListener_destroy(obj_handle_t shared_event_listener);
1755+
17481756
#endif

src/winemetal/winemetal_thunks.c

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -898,11 +898,14 @@ MTLSharedEvent_signalValue(obj_handle_t event, uint64_t value) {
898898
}
899899

900900
WINEMETAL_API void
901-
MTLSharedEvent_setWin32EventAtValue(obj_handle_t event, void *nt_event_handle, uint64_t at_value) {
902-
struct unixcall_generic_obj_obj_uint64_noret params;
903-
params.handle = event;
904-
params.arg0 = (obj_handle_t)PtrToUInt64(nt_event_handle);
905-
params.arg1 = at_value;
901+
MTLSharedEvent_setWin32EventAtValue(
902+
obj_handle_t event, obj_handle_t shared_event_listener, void *nt_event_handle, uint64_t at_value
903+
) {
904+
struct unixcall_mtlsharedevent_setevent params;
905+
params.shared_event = event;
906+
params.shared_event_listener = shared_event_listener;
907+
params.event_handle = (obj_handle_t)PtrToUInt64(nt_event_handle);
908+
params.value = at_value;
906909
UNIX_CALL(104, &params);
907910
}
908911

@@ -932,4 +935,25 @@ MTLBuffer_updateContents(obj_handle_t buffer, uint64_t offset, struct WMTConstMe
932935
params.data = data;
933936
params.length = length;
934937
UNIX_CALL(107, &params);
935-
}
938+
}
939+
940+
WINEMETAL_API obj_handle_t
941+
SharedEventListener_create() {
942+
struct unixcall_generic_obj_ret params;
943+
UNIX_CALL(108, &params);
944+
return params.ret;
945+
}
946+
947+
WINEMETAL_API void
948+
SharedEventListener_start(obj_handle_t event_queue) {
949+
struct unixcall_generic_obj_noret params;
950+
params.handle = event_queue;
951+
UNIX_CALL(109, &params);
952+
}
953+
954+
WINEMETAL_API void
955+
SharedEventListener_destroy(obj_handle_t event_queue) {
956+
struct unixcall_generic_obj_noret params;
957+
params.handle = event_queue;
958+
UNIX_CALL(110, &params);
959+
}

src/winemetal/winemetal_thunks.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,13 @@ struct unixcall_mtlbuffer_updatecontents {
276276
uint64_t length;
277277
};
278278

279+
struct unixcall_mtlsharedevent_setevent {
280+
obj_handle_t shared_event;
281+
obj_handle_t event_handle;
282+
obj_handle_t shared_event_listener;
283+
uint64_t value;
284+
};
285+
279286
#pragma pack(pop)
280287

281288
#endif

0 commit comments

Comments
 (0)