~mmach/netext73/mesa-haswell

« back to all changes in this revision

Viewing changes to src/vulkan/runtime/vk_sync_timeline.c

  • Committer: mmach
  • Date: 2022-09-22 19:56:13 UTC
  • Revision ID: netbit73@gmail.com-20220922195613-wtik9mmy20tmor0i
2022-09-22 21:17:09

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright © 2021 Intel 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 "vk_sync_timeline.h"
25
 
 
26
 
#include <inttypes.h>
27
 
 
28
 
#include "util/os_time.h"
29
 
#include "util/timespec.h"
30
 
 
31
 
#include "vk_alloc.h"
32
 
#include "vk_device.h"
33
 
#include "vk_log.h"
34
 
 
35
 
static struct vk_sync_timeline *
36
 
to_vk_sync_timeline(struct vk_sync *sync)
37
 
{
38
 
   assert(sync->type->init == vk_sync_timeline_init);
39
 
 
40
 
   return container_of(sync, struct vk_sync_timeline, sync);
41
 
}
42
 
 
43
 
static void
44
 
vk_sync_timeline_type_validate(const struct vk_sync_timeline_type *ttype)
45
 
{
46
 
   ASSERTED const enum vk_sync_features req_features =
47
 
      VK_SYNC_FEATURE_BINARY |
48
 
      VK_SYNC_FEATURE_GPU_WAIT |
49
 
      VK_SYNC_FEATURE_GPU_MULTI_WAIT |
50
 
      VK_SYNC_FEATURE_CPU_WAIT |
51
 
      VK_SYNC_FEATURE_CPU_RESET;
52
 
 
53
 
   assert(!(req_features & ~ttype->point_sync_type->features));
54
 
}
55
 
 
56
 
VkResult
57
 
vk_sync_timeline_init(struct vk_device *device,
58
 
                      struct vk_sync *sync,
59
 
                      uint64_t initial_value)
60
 
{
61
 
   struct vk_sync_timeline *timeline = to_vk_sync_timeline(sync);
62
 
   int ret;
63
 
 
64
 
   ASSERTED const struct vk_sync_timeline_type *ttype =
65
 
      container_of(timeline->sync.type, struct vk_sync_timeline_type, sync);
66
 
   vk_sync_timeline_type_validate(ttype);
67
 
 
68
 
   ret = mtx_init(&timeline->mutex, mtx_plain);
69
 
   if (ret != thrd_success)
70
 
      return vk_errorf(device, VK_ERROR_UNKNOWN, "mtx_init failed");
71
 
 
72
 
   ret = cnd_init(&timeline->cond);
73
 
   if (ret != thrd_success) {
74
 
      mtx_destroy(&timeline->mutex);
75
 
      return vk_errorf(device, VK_ERROR_UNKNOWN, "cnd_init failed");
76
 
   }
77
 
 
78
 
   timeline->highest_past =
79
 
      timeline->highest_pending = initial_value;
80
 
   list_inithead(&timeline->pending_points);
81
 
   list_inithead(&timeline->free_points);
82
 
 
83
 
   return VK_SUCCESS;
84
 
}
85
 
 
86
 
static void
87
 
vk_sync_timeline_finish(struct vk_device *device,
88
 
                        struct vk_sync *sync)
89
 
{
90
 
   struct vk_sync_timeline *timeline = to_vk_sync_timeline(sync);
91
 
 
92
 
   list_for_each_entry_safe(struct vk_sync_timeline_point, point,
93
 
                            &timeline->free_points, link) {
94
 
      list_del(&point->link);
95
 
      vk_sync_finish(device, &point->sync);
96
 
      vk_free(&device->alloc, point);
97
 
   }
98
 
   list_for_each_entry_safe(struct vk_sync_timeline_point, point,
99
 
                            &timeline->pending_points, link) {
100
 
      list_del(&point->link);
101
 
      vk_sync_finish(device, &point->sync);
102
 
      vk_free(&device->alloc, point);
103
 
   }
104
 
 
105
 
   cnd_destroy(&timeline->cond);
106
 
   mtx_destroy(&timeline->mutex);
107
 
}
108
 
 
109
 
static struct vk_sync_timeline_point *
110
 
vk_sync_timeline_first_point(struct vk_sync_timeline *timeline)
111
 
{
112
 
   struct vk_sync_timeline_point *point =
113
 
      list_first_entry(&timeline->pending_points,
114
 
                       struct vk_sync_timeline_point, link);
115
 
 
116
 
   assert(point->value <= timeline->highest_pending);
117
 
   assert(point->value > timeline->highest_past);
118
 
 
119
 
   return point;
120
 
}
121
 
 
122
 
static VkResult
123
 
vk_sync_timeline_gc_locked(struct vk_device *device,
124
 
                           struct vk_sync_timeline *timeline,
125
 
                           bool drain);
126
 
 
127
 
static VkResult
128
 
vk_sync_timeline_alloc_point_locked(struct vk_device *device,
129
 
                                    struct vk_sync_timeline *timeline,
130
 
                                    uint64_t value,
131
 
                                    struct vk_sync_timeline_point **point_out)
132
 
{
133
 
   struct vk_sync_timeline_point *point;
134
 
   VkResult result;
135
 
 
136
 
   result = vk_sync_timeline_gc_locked(device, timeline, false);
137
 
   if (unlikely(result != VK_SUCCESS))
138
 
      return result;
139
 
 
140
 
   if (list_is_empty(&timeline->free_points)) {
141
 
      const struct vk_sync_timeline_type *ttype =
142
 
         container_of(timeline->sync.type, struct vk_sync_timeline_type, sync);
143
 
      const struct vk_sync_type *point_sync_type = ttype->point_sync_type;
144
 
 
145
 
      size_t size = offsetof(struct vk_sync_timeline_point, sync) +
146
 
                    point_sync_type->size;
147
 
 
148
 
      point = vk_zalloc(&device->alloc, size, 8,
149
 
                        VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
150
 
      if (!point)
151
 
         return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
152
 
 
153
 
      point->timeline = timeline;
154
 
 
155
 
      result = vk_sync_init(device, &point->sync, point_sync_type,
156
 
                            0 /* flags */, 0 /* initial_value */);
157
 
      if (unlikely(result != VK_SUCCESS)) {
158
 
         vk_free(&device->alloc, point);
159
 
         return result;
160
 
      }
161
 
   } else {
162
 
      point = list_first_entry(&timeline->free_points,
163
 
                               struct vk_sync_timeline_point, link);
164
 
 
165
 
      if (point->sync.type->reset) {
166
 
         result = vk_sync_reset(device, &point->sync);
167
 
         if (unlikely(result != VK_SUCCESS))
168
 
            return result;
169
 
      }
170
 
 
171
 
      list_del(&point->link);
172
 
   }
173
 
 
174
 
   point->value = value;
175
 
   *point_out = point;
176
 
 
177
 
   return VK_SUCCESS;
178
 
}
179
 
 
180
 
VkResult
181
 
vk_sync_timeline_alloc_point(struct vk_device *device,
182
 
                             struct vk_sync_timeline *timeline,
183
 
                             uint64_t value,
184
 
                             struct vk_sync_timeline_point **point_out)
185
 
{
186
 
   VkResult result;
187
 
 
188
 
   mtx_lock(&timeline->mutex);
189
 
   result = vk_sync_timeline_alloc_point_locked(device, timeline, value, point_out);
190
 
   mtx_unlock(&timeline->mutex);
191
 
 
192
 
   return result;
193
 
}
194
 
 
195
 
static void
196
 
vk_sync_timeline_point_free_locked(struct vk_sync_timeline *timeline,
197
 
                                   struct vk_sync_timeline_point *point)
198
 
{
199
 
   assert(point->refcount == 0 && !point->pending);
200
 
   list_add(&point->link, &timeline->free_points);
201
 
}
202
 
 
203
 
void
204
 
vk_sync_timeline_point_free(struct vk_device *device,
205
 
                            struct vk_sync_timeline_point *point)
206
 
{
207
 
   struct vk_sync_timeline *timeline = point->timeline;
208
 
 
209
 
   mtx_lock(&timeline->mutex);
210
 
   vk_sync_timeline_point_free_locked(timeline, point);
211
 
   mtx_unlock(&timeline->mutex);
212
 
}
213
 
 
214
 
static void
215
 
vk_sync_timeline_point_ref(struct vk_sync_timeline_point *point)
216
 
{
217
 
   point->refcount++;
218
 
}
219
 
 
220
 
static void
221
 
vk_sync_timeline_point_unref(struct vk_sync_timeline *timeline,
222
 
                             struct vk_sync_timeline_point *point)
223
 
{
224
 
   assert(point->refcount > 0);
225
 
   point->refcount--;
226
 
   if (point->refcount == 0 && !point->pending)
227
 
      vk_sync_timeline_point_free_locked(timeline, point);
228
 
}
229
 
 
230
 
static void
231
 
vk_sync_timeline_point_complete(struct vk_sync_timeline *timeline,
232
 
                                struct vk_sync_timeline_point *point)
233
 
{
234
 
   if (!point->pending)
235
 
      return;
236
 
 
237
 
   assert(timeline->highest_past < point->value);
238
 
   timeline->highest_past = point->value;
239
 
 
240
 
   point->pending = false;
241
 
   list_del(&point->link);
242
 
 
243
 
   if (point->refcount == 0)
244
 
      vk_sync_timeline_point_free_locked(timeline, point);
245
 
}
246
 
 
247
 
static VkResult
248
 
vk_sync_timeline_gc_locked(struct vk_device *device,
249
 
                           struct vk_sync_timeline *timeline,
250
 
                           bool drain)
251
 
{
252
 
   list_for_each_entry_safe(struct vk_sync_timeline_point, point,
253
 
                            &timeline->pending_points, link) {
254
 
      /* timeline->higest_pending is only incremented once submission has
255
 
       * happened. If this point has a greater serial, it means the point
256
 
       * hasn't been submitted yet.
257
 
       */
258
 
      if (point->value > timeline->highest_pending)
259
 
         return VK_SUCCESS;
260
 
 
261
 
      /* If someone is waiting on this time point, consider it busy and don't
262
 
       * try to recycle it. There's a slim possibility that it's no longer
263
 
       * busy by the time we look at it but we would be recycling it out from
264
 
       * under a waiter and that can lead to weird races.
265
 
       *
266
 
       * We walk the list in-order so if this time point is still busy so is
267
 
       * every following time point
268
 
       */
269
 
      assert(point->refcount >= 0);
270
 
      if (point->refcount > 0 && !drain)
271
 
         return VK_SUCCESS;
272
 
 
273
 
      /* Garbage collect any signaled point. */
274
 
      VkResult result = vk_sync_wait(device, &point->sync, 0,
275
 
                                     VK_SYNC_WAIT_COMPLETE,
276
 
                                     0 /* abs_timeout_ns */);
277
 
      if (result == VK_TIMEOUT) {
278
 
         /* We walk the list in-order so if this time point is still busy so
279
 
          * is every following time point
280
 
          */
281
 
         return VK_SUCCESS;
282
 
      } else if (result != VK_SUCCESS) {
283
 
         return result;
284
 
      }
285
 
 
286
 
      vk_sync_timeline_point_complete(timeline, point);
287
 
   }
288
 
 
289
 
   return VK_SUCCESS;
290
 
}
291
 
 
292
 
VkResult
293
 
vk_sync_timeline_point_install(struct vk_device *device,
294
 
                               struct vk_sync_timeline_point *point)
295
 
{
296
 
   struct vk_sync_timeline *timeline = point->timeline;
297
 
 
298
 
   mtx_lock(&timeline->mutex);
299
 
 
300
 
   assert(point->value > timeline->highest_pending);
301
 
   timeline->highest_pending = point->value;
302
 
 
303
 
   assert(point->refcount == 0);
304
 
   point->pending = true;
305
 
   list_addtail(&point->link, &timeline->pending_points);
306
 
 
307
 
   int ret = cnd_broadcast(&timeline->cond);
308
 
 
309
 
   mtx_unlock(&timeline->mutex);
310
 
 
311
 
   if (ret == thrd_error)
312
 
      return vk_errorf(device, VK_ERROR_UNKNOWN, "cnd_broadcast failed");
313
 
 
314
 
   return VK_SUCCESS;
315
 
}
316
 
 
317
 
static VkResult
318
 
vk_sync_timeline_get_point_locked(struct vk_device *device,
319
 
                                  struct vk_sync_timeline *timeline,
320
 
                                  uint64_t wait_value,
321
 
                                  struct vk_sync_timeline_point **point_out)
322
 
{
323
 
   if (timeline->highest_past >= wait_value) {
324
 
      /* Nothing to wait on */
325
 
      *point_out = NULL;
326
 
      return VK_SUCCESS;
327
 
   }
328
 
 
329
 
   list_for_each_entry(struct vk_sync_timeline_point, point,
330
 
                       &timeline->pending_points, link) {
331
 
      if (point->value >= wait_value) {
332
 
         vk_sync_timeline_point_ref(point);
333
 
         *point_out = point;
334
 
         return VK_SUCCESS;
335
 
      }
336
 
   }
337
 
 
338
 
   return VK_NOT_READY;
339
 
}
340
 
 
341
 
VkResult
342
 
vk_sync_timeline_get_point(struct vk_device *device,
343
 
                           struct vk_sync_timeline *timeline,
344
 
                           uint64_t wait_value,
345
 
                           struct vk_sync_timeline_point **point_out)
346
 
{
347
 
   mtx_lock(&timeline->mutex);
348
 
   VkResult result = vk_sync_timeline_get_point_locked(device, timeline,
349
 
                                                  wait_value, point_out);
350
 
   mtx_unlock(&timeline->mutex);
351
 
 
352
 
   return result;
353
 
}
354
 
 
355
 
void
356
 
vk_sync_timeline_point_release(struct vk_device *device,
357
 
                               struct vk_sync_timeline_point *point)
358
 
{
359
 
   struct vk_sync_timeline *timeline = point->timeline;
360
 
 
361
 
   mtx_lock(&timeline->mutex);
362
 
   vk_sync_timeline_point_unref(timeline, point);
363
 
   mtx_unlock(&timeline->mutex);
364
 
}
365
 
 
366
 
static VkResult
367
 
vk_sync_timeline_signal_locked(struct vk_device *device,
368
 
                               struct vk_sync_timeline *timeline,
369
 
                               uint64_t value)
370
 
{
371
 
   VkResult result = vk_sync_timeline_gc_locked(device, timeline, true);
372
 
   if (unlikely(result != VK_SUCCESS))
373
 
      return result;
374
 
 
375
 
   if (unlikely(value <= timeline->highest_past)) {
376
 
      return vk_device_set_lost(device, "Timeline values must only ever "
377
 
                                        "strictly increase.");
378
 
   }
379
 
 
380
 
   assert(list_is_empty(&timeline->pending_points));
381
 
   assert(timeline->highest_pending == timeline->highest_past);
382
 
   timeline->highest_pending = timeline->highest_past = value;
383
 
 
384
 
   int ret = cnd_broadcast(&timeline->cond);
385
 
   if (ret == thrd_error)
386
 
      return vk_errorf(device, VK_ERROR_UNKNOWN, "cnd_broadcast failed");
387
 
 
388
 
   return VK_SUCCESS;
389
 
}
390
 
 
391
 
static VkResult
392
 
vk_sync_timeline_signal(struct vk_device *device,
393
 
                        struct vk_sync *sync,
394
 
                        uint64_t value)
395
 
{
396
 
   struct vk_sync_timeline *timeline = to_vk_sync_timeline(sync);
397
 
 
398
 
   mtx_lock(&timeline->mutex);
399
 
   VkResult result = vk_sync_timeline_signal_locked(device, timeline, value);
400
 
   mtx_unlock(&timeline->mutex);
401
 
 
402
 
   return result;
403
 
}
404
 
 
405
 
static VkResult
406
 
vk_sync_timeline_get_value(struct vk_device *device,
407
 
                           struct vk_sync *sync,
408
 
                           uint64_t *value)
409
 
{
410
 
   struct vk_sync_timeline *timeline = to_vk_sync_timeline(sync);
411
 
 
412
 
   mtx_lock(&timeline->mutex);
413
 
   VkResult result = vk_sync_timeline_gc_locked(device, timeline, true);
414
 
   mtx_unlock(&timeline->mutex);
415
 
 
416
 
   if (result != VK_SUCCESS)
417
 
      return result;
418
 
 
419
 
   *value = timeline->highest_past;
420
 
 
421
 
   return VK_SUCCESS;
422
 
}
423
 
 
424
 
static VkResult
425
 
vk_sync_timeline_wait_locked(struct vk_device *device,
426
 
                             struct vk_sync_timeline *timeline,
427
 
                             uint64_t wait_value,
428
 
                             enum vk_sync_wait_flags wait_flags,
429
 
                             uint64_t abs_timeout_ns)
430
 
{
431
 
   /* Wait on the queue_submit condition variable until the timeline has a
432
 
    * time point pending that's at least as high as wait_value.
433
 
    */
434
 
   uint64_t now_ns = os_time_get_nano();
435
 
   while (timeline->highest_pending < wait_value) {
436
 
      if (now_ns >= abs_timeout_ns)
437
 
         return VK_TIMEOUT;
438
 
 
439
 
      int ret;
440
 
      if (abs_timeout_ns >= INT64_MAX) {
441
 
         /* Common infinite wait case */
442
 
         ret = cnd_wait(&timeline->cond, &timeline->mutex);
443
 
      } else {
444
 
         /* This is really annoying.  The C11 threads API uses CLOCK_REALTIME
445
 
          * while all our absolute timeouts are in CLOCK_MONOTONIC.  Best
446
 
          * thing we can do is to convert and hope the system admin doesn't
447
 
          * change the time out from under us.
448
 
          */
449
 
         uint64_t rel_timeout_ns = abs_timeout_ns - now_ns;
450
 
 
451
 
         struct timespec now_ts, abs_timeout_ts;
452
 
         timespec_get(&now_ts, TIME_UTC);
453
 
         if (timespec_add_nsec(&abs_timeout_ts, &now_ts, rel_timeout_ns)) {
454
 
            /* Overflowed; may as well be infinite */
455
 
            ret = cnd_wait(&timeline->cond, &timeline->mutex);
456
 
         } else {
457
 
            ret = cnd_timedwait(&timeline->cond, &timeline->mutex,
458
 
                                &abs_timeout_ts);
459
 
         }
460
 
      }
461
 
      if (ret == thrd_error)
462
 
         return vk_errorf(device, VK_ERROR_UNKNOWN, "cnd_timedwait failed");
463
 
 
464
 
      /* We don't trust the timeout condition on cnd_timedwait() because of
465
 
       * the potential clock issues caused by using CLOCK_REALTIME.  Instead,
466
 
       * update now_ns, go back to the top of the loop, and re-check.
467
 
       */
468
 
      now_ns = os_time_get_nano();
469
 
   }
470
 
 
471
 
   if (wait_flags & VK_SYNC_WAIT_PENDING)
472
 
      return VK_SUCCESS;
473
 
 
474
 
   VkResult result = vk_sync_timeline_gc_locked(device, timeline, false);
475
 
   if (result != VK_SUCCESS)
476
 
      return result;
477
 
 
478
 
   while (timeline->highest_past < wait_value) {
479
 
      struct vk_sync_timeline_point *point = vk_sync_timeline_first_point(timeline);
480
 
 
481
 
      /* Drop the lock while we wait. */
482
 
      vk_sync_timeline_point_ref(point);
483
 
      mtx_unlock(&timeline->mutex);
484
 
 
485
 
      result = vk_sync_wait(device, &point->sync, 0,
486
 
                            VK_SYNC_WAIT_COMPLETE,
487
 
                            abs_timeout_ns);
488
 
 
489
 
      /* Pick the mutex back up */
490
 
      mtx_lock(&timeline->mutex);
491
 
      vk_sync_timeline_point_unref(timeline, point);
492
 
 
493
 
      /* This covers both VK_TIMEOUT and VK_ERROR_DEVICE_LOST */
494
 
      if (result != VK_SUCCESS)
495
 
         return result;
496
 
 
497
 
      vk_sync_timeline_point_complete(timeline, point);
498
 
   }
499
 
 
500
 
   return VK_SUCCESS;
501
 
}
502
 
 
503
 
static VkResult
504
 
vk_sync_timeline_wait(struct vk_device *device,
505
 
                      struct vk_sync *sync,
506
 
                      uint64_t wait_value,
507
 
                      enum vk_sync_wait_flags wait_flags,
508
 
                      uint64_t abs_timeout_ns)
509
 
{
510
 
   struct vk_sync_timeline *timeline = to_vk_sync_timeline(sync);
511
 
 
512
 
   mtx_lock(&timeline->mutex);
513
 
   VkResult result = vk_sync_timeline_wait_locked(device, timeline,
514
 
                                             wait_value, wait_flags,
515
 
                                             abs_timeout_ns);
516
 
   mtx_unlock(&timeline->mutex);
517
 
 
518
 
   return result;
519
 
}
520
 
 
521
 
struct vk_sync_timeline_type
522
 
vk_sync_timeline_get_type(const struct vk_sync_type *point_sync_type)
523
 
{
524
 
   return (struct vk_sync_timeline_type) {
525
 
      .sync = {
526
 
         .size = sizeof(struct vk_sync_timeline),
527
 
         .features = VK_SYNC_FEATURE_TIMELINE |
528
 
                     VK_SYNC_FEATURE_GPU_WAIT |
529
 
                     VK_SYNC_FEATURE_CPU_WAIT |
530
 
                     VK_SYNC_FEATURE_CPU_SIGNAL |
531
 
                     VK_SYNC_FEATURE_WAIT_ANY |
532
 
                     VK_SYNC_FEATURE_WAIT_PENDING,
533
 
         .init = vk_sync_timeline_init,
534
 
         .finish = vk_sync_timeline_finish,
535
 
         .signal = vk_sync_timeline_signal,
536
 
         .get_value = vk_sync_timeline_get_value,
537
 
         .wait = vk_sync_timeline_wait,
538
 
      },
539
 
      .point_sync_type = point_sync_type,
540
 
   };
541
 
}