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 "dzn_private.h"
25
26 #include "vk_alloc.h"
27 #include "vk_debug_report.h"
28 #include "vk_util.h"
29
30 #include "util/macros.h"
31 #include "util/os_time.h"
32
33 #ifndef _WIN32
34 #include <sys/eventfd.h>
35 #include <libsync.h>
36 #endif
37
38 static VkResult
dzn_sync_init(struct vk_device * device,struct vk_sync * sync,uint64_t initial_value)39 dzn_sync_init(struct vk_device *device,
40 struct vk_sync *sync,
41 uint64_t initial_value)
42 {
43 struct dzn_sync *dsync = container_of(sync, struct dzn_sync, vk);
44 struct dzn_device *ddev = container_of(device, struct dzn_device, vk);
45
46 D3D12_FENCE_FLAGS flags = (sync->flags & VK_SYNC_IS_SHAREABLE) ?
47 D3D12_FENCE_FLAG_SHARED : D3D12_FENCE_FLAG_NONE;
48
49 if (FAILED(ID3D12Device1_CreateFence(ddev->dev, initial_value,
50 flags,
51 &IID_ID3D12Fence,
52 (void **)&dsync->fence)))
53 return vk_error(device, VK_ERROR_OUT_OF_DEVICE_MEMORY);
54
55 return VK_SUCCESS;
56 }
57
58 static void
dzn_sync_finish(struct vk_device * device,struct vk_sync * sync)59 dzn_sync_finish(struct vk_device *device,
60 struct vk_sync *sync)
61 {
62 struct dzn_sync *dsync = container_of(sync, struct dzn_sync, vk);
63
64 ID3D12Fence_Release(dsync->fence);
65
66 #ifdef _WIN32
67 if (dsync->export_handle)
68 CloseHandle(dsync->export_handle);
69 #endif
70 }
71
72 static VkResult
dzn_sync_signal(struct vk_device * device,struct vk_sync * sync,uint64_t value)73 dzn_sync_signal(struct vk_device *device,
74 struct vk_sync *sync,
75 uint64_t value)
76 {
77 struct dzn_sync *dsync = container_of(sync, struct dzn_sync, vk);
78
79 if (!(sync->flags & VK_SYNC_IS_TIMELINE))
80 value = 1;
81
82 if (FAILED(ID3D12Fence_Signal(dsync->fence, value)))
83 return vk_error(device, VK_ERROR_OUT_OF_DEVICE_MEMORY);
84
85 return VK_SUCCESS;
86 }
87
88 static VkResult
dzn_sync_get_value(struct vk_device * device,struct vk_sync * sync,uint64_t * value)89 dzn_sync_get_value(struct vk_device *device,
90 struct vk_sync *sync,
91 uint64_t *value)
92 {
93 struct dzn_sync *dsync = container_of(sync, struct dzn_sync, vk);
94
95 *value = ID3D12Fence_GetCompletedValue(dsync->fence);
96 return VK_SUCCESS;
97 }
98
99 static VkResult
dzn_sync_reset(struct vk_device * device,struct vk_sync * sync)100 dzn_sync_reset(struct vk_device *device,
101 struct vk_sync *sync)
102 {
103 struct dzn_sync *dsync = container_of(sync, struct dzn_sync, vk);
104
105 if (FAILED(ID3D12Fence_Signal(dsync->fence, 0)))
106 return vk_error(device, VK_ERROR_OUT_OF_DEVICE_MEMORY);
107
108 return VK_SUCCESS;
109 }
110
111 static VkResult
dzn_sync_move(struct vk_device * device,struct vk_sync * dst,struct vk_sync * src)112 dzn_sync_move(struct vk_device *device,
113 struct vk_sync *dst,
114 struct vk_sync *src)
115 {
116 struct dzn_device *ddev = container_of(device, struct dzn_device, vk);
117 struct dzn_sync *ddst = container_of(dst, struct dzn_sync, vk);
118 struct dzn_sync *dsrc = container_of(src, struct dzn_sync, vk);
119 ID3D12Fence *new_fence;
120
121 if (FAILED(ID3D12Device1_CreateFence(ddev->dev, 0,
122 D3D12_FENCE_FLAG_NONE,
123 &IID_ID3D12Fence,
124 (void **)&new_fence)))
125 return vk_error(device, VK_ERROR_OUT_OF_DEVICE_MEMORY);
126
127 ID3D12Fence_Release(ddst->fence);
128 ddst->fence = dsrc->fence;
129 dsrc->fence = new_fence;
130 return VK_SUCCESS;
131 }
132
133 static VkResult
dzn_sync_wait(struct vk_device * device,uint32_t wait_count,const struct vk_sync_wait * waits,enum vk_sync_wait_flags wait_flags,uint64_t abs_timeout_ns)134 dzn_sync_wait(struct vk_device *device,
135 uint32_t wait_count,
136 const struct vk_sync_wait *waits,
137 enum vk_sync_wait_flags wait_flags,
138 uint64_t abs_timeout_ns)
139 {
140 struct dzn_device *ddev = container_of(device, struct dzn_device, vk);
141
142 #ifdef _WIN32
143 HANDLE event = CreateEventA(NULL, false, false, NULL);
144 if (event == NULL)
145 return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
146 #else
147 int event_fd = eventfd(0, EFD_CLOEXEC);
148 if (event_fd == -1)
149 return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
150 /* The D3D12 event-based APIs in WSL expect an eventfd file descriptor cast to HANDLE */
151 HANDLE event = (HANDLE)(intptr_t)event_fd;
152 #endif
153
154 STACK_ARRAY(ID3D12Fence *, fences, wait_count);
155 STACK_ARRAY(uint64_t, values, wait_count);
156
157 for (uint32_t i = 0; i < wait_count; i++) {
158 struct dzn_sync *sync = container_of(waits[i].sync, struct dzn_sync, vk);
159
160 fences[i] = sync->fence;
161 values[i] = (sync->vk.flags & VK_SYNC_IS_TIMELINE) ? waits[i].wait_value : 1;
162 }
163
164 D3D12_MULTIPLE_FENCE_WAIT_FLAGS flags =
165 (wait_flags & VK_SYNC_WAIT_ANY) ?
166 D3D12_MULTIPLE_FENCE_WAIT_FLAG_ANY :
167 D3D12_MULTIPLE_FENCE_WAIT_FLAG_ALL;
168
169 if (FAILED(ID3D12Device1_SetEventOnMultipleFenceCompletion(ddev->dev,
170 fences,
171 values,
172 wait_count,
173 flags,
174 event))) {
175 STACK_ARRAY_FINISH(fences);
176 STACK_ARRAY_FINISH(values);
177 #ifdef _WIN32
178 CloseHandle(event);
179 #else
180 close(event_fd);
181 #endif
182 return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
183 }
184
185 #ifdef _WIN32
186 DWORD timeout_ms;
187 static const DWORD timeout_infinite = INFINITE;
188 #else
189 int timeout_ms;
190 static const int timeout_infinite = -1;
191 #endif
192
193 if (abs_timeout_ns == OS_TIMEOUT_INFINITE) {
194 timeout_ms = timeout_infinite;
195 } else {
196 uint64_t cur_time = os_time_get_nano();
197 uint64_t rel_timeout_ns =
198 abs_timeout_ns > cur_time ? abs_timeout_ns - cur_time : 0;
199
200 timeout_ms = (rel_timeout_ns / 1000000) + (rel_timeout_ns % 1000000 ? 1 : 0);
201 }
202
203 #ifdef _WIN32
204 DWORD res =
205 WaitForSingleObject(event, timeout_ms);
206
207 CloseHandle(event);
208 VkResult ret = VK_SUCCESS;
209 if (res == WAIT_TIMEOUT)
210 ret = VK_TIMEOUT;
211 else if (res != WAIT_OBJECT_0)
212 ret = vk_error(device, VK_ERROR_UNKNOWN);
213 #else
214 VkResult ret = sync_wait(event_fd, timeout_ms) != 0 ? VK_TIMEOUT : VK_SUCCESS;
215 close(event_fd);
216 #endif
217
218 STACK_ARRAY_FINISH(fences);
219 STACK_ARRAY_FINISH(values);
220
221 return ret;
222 }
223
224 #ifdef _WIN32
225 static VkResult
dzn_sync_import_win32_handle(struct vk_device * device,struct vk_sync * sync,HANDLE handle,LPCWSTR name)226 dzn_sync_import_win32_handle(struct vk_device *device, struct vk_sync *sync,
227 HANDLE handle, LPCWSTR name)
228 {
229 struct dzn_sync *dsync = container_of(sync, struct dzn_sync, vk);
230 struct dzn_device *ddev = container_of(device, struct dzn_device, vk);
231
232 dzn_sync_finish(device, sync);
233
234 HANDLE handle_to_close = NULL;
235 if (name) {
236 if (FAILED(ID3D12Device_OpenSharedHandleByName(ddev->dev, name, GENERIC_ALL, &handle_to_close)))
237 return vk_error(device, VK_ERROR_INVALID_EXTERNAL_HANDLE);
238 handle = handle_to_close;
239 }
240
241 HRESULT hr = ID3D12Device_OpenSharedHandle(ddev->dev, handle, &IID_ID3D12Fence, (void **)&dsync->fence);
242 if (handle_to_close)
243 CloseHandle(handle_to_close);
244 if (FAILED(hr))
245 return vk_error(device, VK_ERROR_INVALID_EXTERNAL_HANDLE);
246 return VK_SUCCESS;
247 }
248
249 static VkResult
dzn_sync_export_win32_handle(struct vk_device * device,struct vk_sync * sync,HANDLE * handle)250 dzn_sync_export_win32_handle(struct vk_device *device, struct vk_sync *sync, HANDLE *handle)
251 {
252 struct dzn_sync *dsync = container_of(sync, struct dzn_sync, vk);
253 if (dsync->export_handle) {
254 if (!DuplicateHandle(GetCurrentProcess(), dsync->export_handle,
255 GetCurrentProcess(), handle,
256 0, false, DUPLICATE_SAME_ACCESS))
257 return vk_error(device, VK_ERROR_OUT_OF_DEVICE_MEMORY);
258 return VK_SUCCESS;
259 }
260
261 struct dzn_device *ddev = container_of(device, struct dzn_device, vk);
262 if (FAILED(ID3D12Device_CreateSharedHandle(ddev->dev, (ID3D12DeviceChild *)dsync->fence,
263 NULL, GENERIC_ALL, NULL, handle)))
264 return vk_error(device, VK_ERROR_OUT_OF_DEVICE_MEMORY);
265 return VK_SUCCESS;
266 }
267
268 static VkResult
dzn_sync_prep_win32_export(struct vk_device * device,struct vk_sync * sync,const void * security_attributes,uint32_t access,const wchar_t * name)269 dzn_sync_prep_win32_export(struct vk_device *device, struct vk_sync *sync,
270 const void *security_attributes, uint32_t access,
271 const wchar_t *name)
272 {
273 struct dzn_sync *dsync = container_of(sync, struct dzn_sync, vk);
274 struct dzn_device *ddev = container_of(device, struct dzn_device, vk);
275 if (FAILED(ID3D12Device_CreateSharedHandle(ddev->dev, (ID3D12DeviceChild *)dsync->fence,
276 (const LPSECURITY_ATTRIBUTES)security_attributes,
277 GENERIC_ALL, name, &dsync->export_handle)))
278 return vk_error(device, VK_ERROR_OUT_OF_DEVICE_MEMORY);
279 return VK_SUCCESS;
280 }
281 #else
282 static VkResult
dzn_sync_import_opaque_fd(struct vk_device * device,struct vk_sync * sync,int fd)283 dzn_sync_import_opaque_fd(struct vk_device *device, struct vk_sync *sync,
284 int fd)
285 {
286 struct dzn_sync *dsync = container_of(sync, struct dzn_sync, vk);
287 struct dzn_device *ddev = container_of(device, struct dzn_device, vk);
288
289 dzn_sync_finish(device, sync);
290
291 HANDLE handle = (HANDLE)(intptr_t)fd;
292 if (FAILED(ID3D12Device_OpenSharedHandle(ddev->dev, handle, &IID_ID3D12Fence, (void **)&dsync->fence)))
293 return vk_error(device, VK_ERROR_INVALID_EXTERNAL_HANDLE);
294 return VK_SUCCESS;
295 }
296
297 static VkResult
dzn_sync_export_opaque_fd(struct vk_device * device,struct vk_sync * sync,int * fd)298 dzn_sync_export_opaque_fd(struct vk_device *device, struct vk_sync *sync, int *fd)
299 {
300 struct dzn_sync *dsync = container_of(sync, struct dzn_sync, vk);
301 struct dzn_device *ddev = container_of(device, struct dzn_device, vk);
302 HANDLE handle;
303 if (FAILED(ID3D12Device_CreateSharedHandle(ddev->dev, (ID3D12DeviceChild *)dsync->fence,
304 NULL, GENERIC_ALL, NULL, &handle)))
305 return vk_error(device, VK_ERROR_OUT_OF_DEVICE_MEMORY);
306 *fd = (int)(intptr_t)handle;
307 return VK_SUCCESS;
308 }
309 #endif
310
311 const struct vk_sync_type dzn_sync_type = {
312 .size = sizeof(struct dzn_sync),
313 .features = (enum vk_sync_features)
314 (VK_SYNC_FEATURE_TIMELINE |
315 VK_SYNC_FEATURE_GPU_WAIT |
316 VK_SYNC_FEATURE_CPU_WAIT |
317 VK_SYNC_FEATURE_CPU_SIGNAL |
318 VK_SYNC_FEATURE_WAIT_ANY |
319 VK_SYNC_FEATURE_WAIT_BEFORE_SIGNAL),
320
321 .init = dzn_sync_init,
322 .finish = dzn_sync_finish,
323 .signal = dzn_sync_signal,
324 .get_value = dzn_sync_get_value,
325 .reset = dzn_sync_reset,
326 .move = dzn_sync_move,
327 .wait_many = dzn_sync_wait,
328 #ifdef _WIN32
329 .import_win32_handle = dzn_sync_import_win32_handle,
330 .export_win32_handle = dzn_sync_export_win32_handle,
331 .set_win32_export_params = dzn_sync_prep_win32_export,
332 #else
333 .import_opaque_fd = dzn_sync_import_opaque_fd,
334 .export_opaque_fd = dzn_sync_export_opaque_fd,
335 #endif
336 };
337