1 /*
2 * Copyright © Microsoft Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include "d3d12_fence.h"
25
26 #include "d3d12_context.h"
27 #include "d3d12_screen.h"
28
29 #include "util/u_memory.h"
30
31 #include <dxguids/dxguids.h>
32
33 constexpr uint64_t NsPerMs = 1000000;
34 constexpr uint64_t MaxTimeoutInNs = (uint64_t)UINT_MAX * NsPerMs;
35
36 #ifdef _WIN32
37 static void
close_event(HANDLE event,int fd)38 close_event(HANDLE event, int fd)
39 {
40 if (event)
41 CloseHandle(event);
42 }
43
44 static HANDLE
create_event(int * fd)45 create_event(int *fd)
46 {
47 *fd = -1;
48 return CreateEvent(NULL, FALSE, FALSE, NULL);
49 }
50
51 static bool
wait_event(HANDLE event,int event_fd,uint64_t timeout_ns)52 wait_event(HANDLE event, int event_fd, uint64_t timeout_ns)
53 {
54 DWORD timeout_ms = (timeout_ns == PIPE_TIMEOUT_INFINITE || timeout_ns > MaxTimeoutInNs) ? INFINITE : timeout_ns / NsPerMs;
55 return WaitForSingleObject(event, timeout_ms) == WAIT_OBJECT_0;
56 }
57 #else
58 #include <sys/eventfd.h>
59 #include <poll.h>
60 #include <util/libsync.h>
61
62 static void
close_event(HANDLE event,int fd)63 close_event(HANDLE event, int fd)
64 {
65 if (fd != -1)
66 close(fd);
67 }
68
69 static HANDLE
create_event(int * fd)70 create_event(int *fd)
71 {
72 *fd = eventfd(0, 0);
73 return (HANDLE)(size_t)*fd;
74 }
75
76 static bool
wait_event(HANDLE event,int event_fd,uint64_t timeout_ns)77 wait_event(HANDLE event, int event_fd, uint64_t timeout_ns)
78 {
79 int timeout_ms = (timeout_ns == PIPE_TIMEOUT_INFINITE || timeout_ns > MaxTimeoutInNs) ? -1 : timeout_ns / NsPerMs;
80 return sync_wait(event_fd, timeout_ms) == 0;
81 }
82 #endif
83
84 static void
destroy_fence(struct d3d12_fence * fence)85 destroy_fence(struct d3d12_fence *fence)
86 {
87 close_event(fence->event, fence->event_fd);
88 FREE(fence);
89 }
90
91 struct d3d12_fence *
d3d12_create_fence(struct d3d12_screen * screen)92 d3d12_create_fence(struct d3d12_screen *screen)
93 {
94 struct d3d12_fence *ret = CALLOC_STRUCT(d3d12_fence);
95 if (!ret) {
96 debug_printf("CALLOC_STRUCT failed\n");
97 return NULL;
98 }
99
100 ret->cmdqueue_fence = screen->fence;
101 ret->value = ++screen->fence_value;
102 ret->event = create_event(&ret->event_fd);
103 if (FAILED(screen->fence->SetEventOnCompletion(ret->value, ret->event)))
104 goto fail;
105 if (FAILED(screen->cmdqueue->Signal(screen->fence, ret->value)))
106 goto fail;
107
108 pipe_reference_init(&ret->reference, 1);
109 return ret;
110
111 fail:
112 destroy_fence(ret);
113 return NULL;
114 }
115
116 struct d3d12_fence *
d3d12_open_fence(struct d3d12_screen * screen,HANDLE handle,const void * name)117 d3d12_open_fence(struct d3d12_screen *screen, HANDLE handle, const void *name)
118 {
119 struct d3d12_fence *ret = CALLOC_STRUCT(d3d12_fence);
120 if (!ret) {
121 debug_printf("CALLOC_STRUCT failed\n");
122 return NULL;
123 }
124
125 HANDLE handle_to_close = nullptr;
126 assert(!!handle ^ !!name);
127 if (name) {
128 screen->dev->OpenSharedHandleByName((LPCWSTR)name, GENERIC_ALL, &handle_to_close);
129 handle = handle_to_close;
130 }
131
132 screen->dev->OpenSharedHandle(handle, IID_PPV_ARGS(&ret->cmdqueue_fence));
133 if (!ret->cmdqueue_fence) {
134 free(ret);
135 return NULL;
136 }
137
138 /* A new value will be assigned later */
139 ret->value = 0;
140 pipe_reference_init(&ret->reference, 1);
141 return ret;
142 }
143
144 void
d3d12_fence_reference(struct d3d12_fence ** ptr,struct d3d12_fence * fence)145 d3d12_fence_reference(struct d3d12_fence **ptr, struct d3d12_fence *fence)
146 {
147 if (pipe_reference(&(*ptr)->reference, &fence->reference))
148 destroy_fence((struct d3d12_fence *)*ptr);
149
150 *ptr = fence;
151 }
152
153 static void
fence_reference(struct pipe_screen * pscreen,struct pipe_fence_handle ** pptr,struct pipe_fence_handle * pfence)154 fence_reference(struct pipe_screen *pscreen,
155 struct pipe_fence_handle **pptr,
156 struct pipe_fence_handle *pfence)
157 {
158 d3d12_fence_reference((struct d3d12_fence **)pptr, d3d12_fence(pfence));
159 }
160
161 bool
d3d12_fence_finish(struct d3d12_fence * fence,uint64_t timeout_ns)162 d3d12_fence_finish(struct d3d12_fence *fence, uint64_t timeout_ns)
163 {
164 if (fence->signaled)
165 return true;
166
167 bool complete = fence->cmdqueue_fence->GetCompletedValue() >= fence->value;
168 if (!complete && timeout_ns)
169 complete = wait_event(fence->event, fence->event_fd, timeout_ns);
170
171 fence->signaled = complete;
172 return complete;
173 }
174
175 static bool
fence_finish(struct pipe_screen * pscreen,struct pipe_context * pctx,struct pipe_fence_handle * pfence,uint64_t timeout_ns)176 fence_finish(struct pipe_screen *pscreen, struct pipe_context *pctx,
177 struct pipe_fence_handle *pfence, uint64_t timeout_ns)
178 {
179 bool ret = d3d12_fence_finish(d3d12_fence(pfence), timeout_ns);
180 if (ret && pctx) {
181 pctx = threaded_context_unwrap_sync(pctx);
182 struct d3d12_context *ctx = d3d12_context(pctx);
183 d3d12_foreach_submitted_batch(ctx, batch)
184 d3d12_reset_batch(ctx, batch, 0);
185 }
186 return ret;
187 }
188
189 void
d3d12_screen_fence_init(struct pipe_screen * pscreen)190 d3d12_screen_fence_init(struct pipe_screen *pscreen)
191 {
192 pscreen->fence_reference = fence_reference;
193 pscreen->fence_finish = fence_finish;
194 }
195