1 /*
2 * Copyright © 2022 Collabora Ltd.
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 "lvp_private.h"
25 #include "util/timespec.h"
26
27 static void
lvp_pipe_sync_validate(ASSERTED struct lvp_pipe_sync * sync)28 lvp_pipe_sync_validate(ASSERTED struct lvp_pipe_sync *sync)
29 {
30 if (sync->signaled)
31 assert(sync->fence == NULL);
32 }
33
34 static VkResult
lvp_pipe_sync_init(UNUSED struct vk_device * vk_device,struct vk_sync * vk_sync,uint64_t initial_value)35 lvp_pipe_sync_init(UNUSED struct vk_device *vk_device,
36 struct vk_sync *vk_sync,
37 uint64_t initial_value)
38 {
39 struct lvp_pipe_sync *sync = vk_sync_as_lvp_pipe_sync(vk_sync);
40
41 mtx_init(&sync->lock, mtx_plain);
42 cnd_init(&sync->changed);
43 sync->signaled = (initial_value != 0);
44 sync->fence = NULL;
45
46 return VK_SUCCESS;
47 }
48
49 static void
lvp_pipe_sync_finish(struct vk_device * vk_device,struct vk_sync * vk_sync)50 lvp_pipe_sync_finish(struct vk_device *vk_device,
51 struct vk_sync *vk_sync)
52 {
53 struct lvp_device *device = container_of(vk_device, struct lvp_device, vk);
54 struct lvp_pipe_sync *sync = vk_sync_as_lvp_pipe_sync(vk_sync);
55
56 lvp_pipe_sync_validate(sync);
57 if (sync->fence)
58 device->pscreen->fence_reference(device->pscreen, &sync->fence, NULL);
59 cnd_destroy(&sync->changed);
60 mtx_destroy(&sync->lock);
61 }
62
63 void
lvp_pipe_sync_signal_with_fence(struct lvp_device * device,struct lvp_pipe_sync * sync,struct pipe_fence_handle * fence)64 lvp_pipe_sync_signal_with_fence(struct lvp_device *device,
65 struct lvp_pipe_sync *sync,
66 struct pipe_fence_handle *fence)
67 {
68 mtx_lock(&sync->lock);
69 lvp_pipe_sync_validate(sync);
70 sync->signaled = fence == NULL;
71 device->pscreen->fence_reference(device->pscreen, &sync->fence, fence);
72 cnd_broadcast(&sync->changed);
73 mtx_unlock(&sync->lock);
74 }
75
76 static VkResult
lvp_pipe_sync_signal(struct vk_device * vk_device,struct vk_sync * vk_sync,uint64_t value)77 lvp_pipe_sync_signal(struct vk_device *vk_device,
78 struct vk_sync *vk_sync,
79 uint64_t value)
80 {
81 struct lvp_device *device = container_of(vk_device, struct lvp_device, vk);
82 struct lvp_pipe_sync *sync = vk_sync_as_lvp_pipe_sync(vk_sync);
83
84 mtx_lock(&sync->lock);
85 lvp_pipe_sync_validate(sync);
86 sync->signaled = true;
87 if (sync->fence)
88 device->pscreen->fence_reference(device->pscreen, &sync->fence, NULL);
89 cnd_broadcast(&sync->changed);
90 mtx_unlock(&sync->lock);
91
92 return VK_SUCCESS;
93 }
94
95 static VkResult
lvp_pipe_sync_reset(struct vk_device * vk_device,struct vk_sync * vk_sync)96 lvp_pipe_sync_reset(struct vk_device *vk_device,
97 struct vk_sync *vk_sync)
98 {
99 struct lvp_device *device = container_of(vk_device, struct lvp_device, vk);
100 struct lvp_pipe_sync *sync = vk_sync_as_lvp_pipe_sync(vk_sync);
101
102 mtx_lock(&sync->lock);
103 lvp_pipe_sync_validate(sync);
104 sync->signaled = false;
105 if (sync->fence)
106 device->pscreen->fence_reference(device->pscreen, &sync->fence, NULL);
107 cnd_broadcast(&sync->changed);
108 mtx_unlock(&sync->lock);
109
110 return VK_SUCCESS;
111 }
112
113 static VkResult
lvp_pipe_sync_move(struct vk_device * vk_device,struct vk_sync * vk_dst,struct vk_sync * vk_src)114 lvp_pipe_sync_move(struct vk_device *vk_device,
115 struct vk_sync *vk_dst,
116 struct vk_sync *vk_src)
117 {
118 struct lvp_device *device = container_of(vk_device, struct lvp_device, vk);
119 struct lvp_pipe_sync *dst = vk_sync_as_lvp_pipe_sync(vk_dst);
120 struct lvp_pipe_sync *src = vk_sync_as_lvp_pipe_sync(vk_src);
121
122 /* Pull the fence out of the source */
123 mtx_lock(&src->lock);
124 struct pipe_fence_handle *fence = src->fence;
125 bool signaled = src->signaled;
126 src->fence = NULL;
127 src->signaled = false;
128 cnd_broadcast(&src->changed);
129 mtx_unlock(&src->lock);
130
131 mtx_lock(&dst->lock);
132 if (dst->fence)
133 device->pscreen->fence_reference(device->pscreen, &dst->fence, NULL);
134 dst->fence = fence;
135 dst->signaled = signaled;
136 cnd_broadcast(&dst->changed);
137 mtx_unlock(&dst->lock);
138
139 return VK_SUCCESS;
140 }
141
142 static VkResult
lvp_pipe_sync_wait_locked(struct lvp_device * device,struct lvp_pipe_sync * sync,uint64_t wait_value,enum vk_sync_wait_flags wait_flags,uint64_t abs_timeout_ns)143 lvp_pipe_sync_wait_locked(struct lvp_device *device,
144 struct lvp_pipe_sync *sync,
145 uint64_t wait_value,
146 enum vk_sync_wait_flags wait_flags,
147 uint64_t abs_timeout_ns)
148 {
149 assert(!(wait_flags & VK_SYNC_WAIT_ANY));
150
151 lvp_pipe_sync_validate(sync);
152
153 uint64_t now_ns = os_time_get_nano();
154 while (!sync->signaled && !sync->fence) {
155 if (now_ns >= abs_timeout_ns)
156 return VK_TIMEOUT;
157
158 int ret;
159 if (abs_timeout_ns >= INT64_MAX) {
160 /* Common infinite wait case */
161 ret = cnd_wait(&sync->changed, &sync->lock);
162 } else {
163 /* This is really annoying. The C11 threads API uses CLOCK_REALTIME
164 * while all our absolute timeouts are in CLOCK_MONOTONIC. Best
165 * thing we can do is to convert and hope the system admin doesn't
166 * change the time out from under us.
167 */
168 uint64_t rel_timeout_ns = abs_timeout_ns - now_ns;
169
170 struct timespec now_ts, abs_timeout_ts;
171 timespec_get(&now_ts, TIME_UTC);
172 if (timespec_add_nsec(&abs_timeout_ts, &now_ts, rel_timeout_ns)) {
173 /* Overflowed; may as well be infinite */
174 ret = cnd_wait(&sync->changed, &sync->lock);
175 } else {
176 ret = cnd_timedwait(&sync->changed, &sync->lock, &abs_timeout_ts);
177 }
178 }
179 if (ret == thrd_error)
180 return vk_errorf(device, VK_ERROR_UNKNOWN, "cnd_timedwait failed");
181
182 lvp_pipe_sync_validate(sync);
183
184 /* We don't trust the timeout condition on cnd_timedwait() because of
185 * the potential clock issues caused by using CLOCK_REALTIME. Instead,
186 * update now_ns, go back to the top of the loop, and re-check.
187 */
188 now_ns = os_time_get_nano();
189 }
190
191 if (sync->signaled || (wait_flags & VK_SYNC_WAIT_PENDING))
192 return VK_SUCCESS;
193
194 /* Grab a reference before we drop the lock */
195 struct pipe_fence_handle *fence = NULL;
196 device->pscreen->fence_reference(device->pscreen, &fence, sync->fence);
197
198 mtx_unlock(&sync->lock);
199
200 uint64_t rel_timeout_ns =
201 now_ns >= abs_timeout_ns ? 0 : abs_timeout_ns - now_ns;
202 bool complete = device->pscreen->fence_finish(device->pscreen, NULL,
203 fence, rel_timeout_ns);
204
205 device->pscreen->fence_reference(device->pscreen, &fence, NULL);
206
207 mtx_lock(&sync->lock);
208
209 lvp_pipe_sync_validate(sync);
210
211 if (!complete)
212 return VK_TIMEOUT;
213
214 if (sync->fence == fence) {
215 device->pscreen->fence_reference(device->pscreen, &sync->fence, NULL);
216 sync->signaled = true;
217 }
218
219 return VK_SUCCESS;
220 }
221
222 static VkResult
lvp_pipe_sync_wait(struct vk_device * vk_device,struct vk_sync * vk_sync,uint64_t wait_value,enum vk_sync_wait_flags wait_flags,uint64_t abs_timeout_ns)223 lvp_pipe_sync_wait(struct vk_device *vk_device,
224 struct vk_sync *vk_sync,
225 uint64_t wait_value,
226 enum vk_sync_wait_flags wait_flags,
227 uint64_t abs_timeout_ns)
228 {
229 struct lvp_device *device = container_of(vk_device, struct lvp_device, vk);
230 struct lvp_pipe_sync *sync = vk_sync_as_lvp_pipe_sync(vk_sync);
231
232 mtx_lock(&sync->lock);
233
234 VkResult result = lvp_pipe_sync_wait_locked(device, sync, wait_value,
235 wait_flags, abs_timeout_ns);
236
237 mtx_unlock(&sync->lock);
238
239 return result;
240 }
241
242 const struct vk_sync_type lvp_pipe_sync_type = {
243 .size = sizeof(struct lvp_pipe_sync),
244 .features = VK_SYNC_FEATURE_BINARY |
245 VK_SYNC_FEATURE_GPU_WAIT |
246 VK_SYNC_FEATURE_GPU_MULTI_WAIT |
247 VK_SYNC_FEATURE_CPU_WAIT |
248 VK_SYNC_FEATURE_CPU_RESET |
249 VK_SYNC_FEATURE_CPU_SIGNAL |
250 VK_SYNC_FEATURE_WAIT_PENDING,
251 .init = lvp_pipe_sync_init,
252 .finish = lvp_pipe_sync_finish,
253 .signal = lvp_pipe_sync_signal,
254 .reset = lvp_pipe_sync_reset,
255 .move = lvp_pipe_sync_move,
256 .wait = lvp_pipe_sync_wait,
257 };
258