• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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