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 assert(!(sync->flags & VK_SYNC_IS_SHAREABLE));
47
48 if (FAILED(ID3D12Device1_CreateFence(ddev->dev, initial_value,
49 D3D12_FENCE_FLAG_NONE,
50 &IID_ID3D12Fence,
51 (void **)&dsync->fence)))
52 return vk_error(device, VK_ERROR_OUT_OF_DEVICE_MEMORY);
53
54 return VK_SUCCESS;
55 }
56
57 static void
dzn_sync_finish(struct vk_device * device,struct vk_sync * sync)58 dzn_sync_finish(struct vk_device *device,
59 struct vk_sync *sync)
60 {
61 struct dzn_sync *dsync = container_of(sync, struct dzn_sync, vk);
62
63 ID3D12Fence_Release(dsync->fence);
64 }
65
66 static VkResult
dzn_sync_signal(struct vk_device * device,struct vk_sync * sync,uint64_t value)67 dzn_sync_signal(struct vk_device *device,
68 struct vk_sync *sync,
69 uint64_t value)
70 {
71 struct dzn_sync *dsync = container_of(sync, struct dzn_sync, vk);
72
73 if (!(sync->flags & VK_SYNC_IS_TIMELINE))
74 value = 1;
75
76 if (FAILED(ID3D12Fence_Signal(dsync->fence, value)))
77 return vk_error(device, VK_ERROR_OUT_OF_DEVICE_MEMORY);
78
79 return VK_SUCCESS;
80 }
81
82 static VkResult
dzn_sync_get_value(struct vk_device * device,struct vk_sync * sync,uint64_t * value)83 dzn_sync_get_value(struct vk_device *device,
84 struct vk_sync *sync,
85 uint64_t *value)
86 {
87 struct dzn_sync *dsync = container_of(sync, struct dzn_sync, vk);
88
89 *value = ID3D12Fence_GetCompletedValue(dsync->fence);
90 return VK_SUCCESS;
91 }
92
93 static VkResult
dzn_sync_reset(struct vk_device * device,struct vk_sync * sync)94 dzn_sync_reset(struct vk_device *device,
95 struct vk_sync *sync)
96 {
97 struct dzn_sync *dsync = container_of(sync, struct dzn_sync, vk);
98
99 if (FAILED(ID3D12Fence_Signal(dsync->fence, 0)))
100 return vk_error(device, VK_ERROR_OUT_OF_DEVICE_MEMORY);
101
102 return VK_SUCCESS;
103 }
104
105 static VkResult
dzn_sync_move(struct vk_device * device,struct vk_sync * dst,struct vk_sync * src)106 dzn_sync_move(struct vk_device *device,
107 struct vk_sync *dst,
108 struct vk_sync *src)
109 {
110 struct dzn_device *ddev = container_of(device, struct dzn_device, vk);
111 struct dzn_sync *ddst = container_of(dst, struct dzn_sync, vk);
112 struct dzn_sync *dsrc = container_of(src, struct dzn_sync, vk);
113 ID3D12Fence *new_fence;
114
115 if (FAILED(ID3D12Device1_CreateFence(ddev->dev, 0,
116 D3D12_FENCE_FLAG_NONE,
117 &IID_ID3D12Fence,
118 (void **)&new_fence)))
119 return vk_error(device, VK_ERROR_OUT_OF_DEVICE_MEMORY);
120
121 ID3D12Fence_Release(ddst->fence);
122 ddst->fence = dsrc->fence;
123 dsrc->fence = new_fence;
124 return VK_SUCCESS;
125 }
126
127 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)128 dzn_sync_wait(struct vk_device *device,
129 uint32_t wait_count,
130 const struct vk_sync_wait *waits,
131 enum vk_sync_wait_flags wait_flags,
132 uint64_t abs_timeout_ns)
133 {
134 struct dzn_device *ddev = container_of(device, struct dzn_device, vk);
135
136 #ifdef _WIN32
137 HANDLE event = CreateEventA(NULL, FALSE, FALSE, NULL);
138 if (event == NULL)
139 return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
140 #else
141 int event_fd = eventfd(0, EFD_CLOEXEC);
142 if (event_fd == -1)
143 return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
144 /* The D3D12 event-based APIs in WSL expect an eventfd file descriptor cast to HANDLE */
145 HANDLE event = (HANDLE)(intptr_t)event_fd;
146 #endif
147
148 STACK_ARRAY(ID3D12Fence *, fences, wait_count);
149 STACK_ARRAY(uint64_t, values, wait_count);
150
151 for (uint32_t i = 0; i < wait_count; i++) {
152 struct dzn_sync *sync = container_of(waits[i].sync, struct dzn_sync, vk);
153
154 fences[i] = sync->fence;
155 values[i] = (sync->vk.flags & VK_SYNC_IS_TIMELINE) ? waits[i].wait_value : 1;
156 }
157
158 D3D12_MULTIPLE_FENCE_WAIT_FLAGS flags =
159 (wait_flags & VK_SYNC_WAIT_ANY) ?
160 D3D12_MULTIPLE_FENCE_WAIT_FLAG_ANY :
161 D3D12_MULTIPLE_FENCE_WAIT_FLAG_ALL;
162
163 if (FAILED(ID3D12Device1_SetEventOnMultipleFenceCompletion(ddev->dev,
164 fences,
165 values,
166 wait_count,
167 flags,
168 event))) {
169 STACK_ARRAY_FINISH(fences);
170 STACK_ARRAY_FINISH(values);
171 #ifdef _WIN32
172 CloseHandle(event);
173 #else
174 close(event_fd);
175 #endif
176 return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
177 }
178
179 #ifdef _WIN32
180 DWORD timeout_ms;
181 static const DWORD timeout_infinite = INFINITE;
182 #else
183 int timeout_ms;
184 static const int timeout_infinite = -1;
185 #endif
186
187 if (abs_timeout_ns == OS_TIMEOUT_INFINITE) {
188 timeout_ms = timeout_infinite;
189 } else {
190 uint64_t cur_time = os_time_get_nano();
191 uint64_t rel_timeout_ns =
192 abs_timeout_ns > cur_time ? abs_timeout_ns - cur_time : 0;
193
194 timeout_ms = (rel_timeout_ns / 1000000) + (rel_timeout_ns % 1000000 ? 1 : 0);
195 }
196
197 #ifdef _WIN32
198 DWORD res =
199 WaitForSingleObject(event, timeout_ms);
200
201 CloseHandle(event);
202 VkResult ret = VK_SUCCESS;
203 if (res == WAIT_TIMEOUT)
204 ret = VK_TIMEOUT;
205 else if (res != WAIT_OBJECT_0)
206 ret = vk_error(device, VK_ERROR_UNKNOWN);
207 #else
208 VkResult ret = sync_wait(event_fd, timeout_ms) != 0 ? VK_TIMEOUT : VK_SUCCESS;
209 close(event_fd);
210 #endif
211
212 STACK_ARRAY_FINISH(fences);
213 STACK_ARRAY_FINISH(values);
214
215 return ret;
216 }
217
218 const struct vk_sync_type dzn_sync_type = {
219 .size = sizeof(struct dzn_sync),
220 .features = (enum vk_sync_features)
221 (VK_SYNC_FEATURE_TIMELINE |
222 VK_SYNC_FEATURE_GPU_WAIT |
223 VK_SYNC_FEATURE_CPU_WAIT |
224 VK_SYNC_FEATURE_CPU_SIGNAL |
225 VK_SYNC_FEATURE_WAIT_ANY |
226 VK_SYNC_FEATURE_WAIT_BEFORE_SIGNAL),
227
228 .init = dzn_sync_init,
229 .finish = dzn_sync_finish,
230 .signal = dzn_sync_signal,
231 .get_value = dzn_sync_get_value,
232 .reset = dzn_sync_reset,
233 .move = dzn_sync_move,
234 .wait_many = dzn_sync_wait,
235 };
236