1/*
2 * Copyright 2019 Google LLC
3 * SPDX-License-Identifier: MIT
4 *
5 * based in part on anv and radv which are:
6 * Copyright © 2015 Intel Corporation
7 * Copyright © 2016 Red Hat.
8 * Copyright © 2016 Bas Nieuwenhuizen
9 */
10
11#include "vn_queue.h"
12
13#include "util/libsync.h"
14#include "venus-protocol/vn_protocol_driver_event.h"
15#include "venus-protocol/vn_protocol_driver_fence.h"
16#include "venus-protocol/vn_protocol_driver_queue.h"
17#include "venus-protocol/vn_protocol_driver_semaphore.h"
18
19#include "vn_device.h"
20#include "vn_device_memory.h"
21#include "vn_renderer.h"
22#include "vn_wsi.h"
23
24/* queue commands */
25
26void
27vn_GetDeviceQueue2(VkDevice device,
28                   const VkDeviceQueueInfo2 *pQueueInfo,
29                   VkQueue *pQueue)
30{
31   struct vn_device *dev = vn_device_from_handle(device);
32
33   for (uint32_t i = 0; i < dev->queue_count; i++) {
34      struct vn_queue *queue = &dev->queues[i];
35      if (queue->family == pQueueInfo->queueFamilyIndex &&
36          queue->index == pQueueInfo->queueIndex &&
37          queue->flags == pQueueInfo->flags) {
38         *pQueue = vn_queue_to_handle(queue);
39         return;
40      }
41   }
42   unreachable("bad queue family/index");
43}
44
45static void
46vn_semaphore_reset_wsi(struct vn_device *dev, struct vn_semaphore *sem);
47
48struct vn_queue_submission {
49   VkStructureType batch_type;
50   VkQueue queue;
51   uint32_t batch_count;
52   union {
53      const void *batches;
54      const VkSubmitInfo *submit_batches;
55      const VkBindSparseInfo *bind_sparse_batches;
56   };
57   VkFence fence;
58
59   uint32_t wait_semaphore_count;
60   uint32_t wait_wsi_count;
61
62   struct {
63      void *storage;
64
65      union {
66         void *batches;
67         VkSubmitInfo *submit_batches;
68         VkBindSparseInfo *bind_sparse_batches;
69      };
70      VkSemaphore *semaphores;
71   } temp;
72};
73
74static void
75vn_queue_submission_count_batch_semaphores(struct vn_queue_submission *submit,
76                                           uint32_t batch_index)
77{
78   union {
79      const VkSubmitInfo *submit_batch;
80      const VkBindSparseInfo *bind_sparse_batch;
81   } u;
82   const VkSemaphore *wait_sems;
83   uint32_t wait_count;
84   switch (submit->batch_type) {
85   case VK_STRUCTURE_TYPE_SUBMIT_INFO:
86      u.submit_batch = &submit->submit_batches[batch_index];
87      wait_sems = u.submit_batch->pWaitSemaphores;
88      wait_count = u.submit_batch->waitSemaphoreCount;
89      break;
90   case VK_STRUCTURE_TYPE_BIND_SPARSE_INFO:
91      u.bind_sparse_batch = &submit->bind_sparse_batches[batch_index];
92      wait_sems = u.bind_sparse_batch->pWaitSemaphores;
93      wait_count = u.bind_sparse_batch->waitSemaphoreCount;
94      break;
95   default:
96      unreachable("unexpected batch type");
97      break;
98   }
99
100   submit->wait_semaphore_count += wait_count;
101   for (uint32_t i = 0; i < wait_count; i++) {
102      struct vn_semaphore *sem = vn_semaphore_from_handle(wait_sems[i]);
103      const struct vn_sync_payload *payload = sem->payload;
104
105      if (payload->type == VN_SYNC_TYPE_WSI_SIGNALED)
106         submit->wait_wsi_count++;
107   }
108}
109
110static void
111vn_queue_submission_count_semaphores(struct vn_queue_submission *submit)
112{
113   submit->wait_semaphore_count = 0;
114   submit->wait_wsi_count = 0;
115
116   for (uint32_t i = 0; i < submit->batch_count; i++)
117      vn_queue_submission_count_batch_semaphores(submit, i);
118}
119
120static VkResult
121vn_queue_submission_alloc_storage(struct vn_queue_submission *submit)
122{
123   struct vn_queue *queue = vn_queue_from_handle(submit->queue);
124   const VkAllocationCallbacks *alloc = &queue->device->base.base.alloc;
125   size_t alloc_size = 0;
126   size_t semaphores_offset = 0;
127
128   /* we want to filter out VN_SYNC_TYPE_WSI_SIGNALED wait semaphores */
129   if (submit->wait_wsi_count) {
130      switch (submit->batch_type) {
131      case VK_STRUCTURE_TYPE_SUBMIT_INFO:
132         alloc_size += sizeof(VkSubmitInfo) * submit->batch_count;
133         break;
134      case VK_STRUCTURE_TYPE_BIND_SPARSE_INFO:
135         alloc_size += sizeof(VkBindSparseInfo) * submit->batch_count;
136         break;
137      default:
138         unreachable("unexpected batch type");
139         break;
140      }
141
142      semaphores_offset = alloc_size;
143      alloc_size += sizeof(*submit->temp.semaphores) *
144                    (submit->wait_semaphore_count - submit->wait_wsi_count);
145   }
146
147   if (!alloc_size) {
148      submit->temp.storage = NULL;
149      return VK_SUCCESS;
150   }
151
152   submit->temp.storage = vk_alloc(alloc, alloc_size, VN_DEFAULT_ALIGN,
153                                   VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
154   if (!submit->temp.storage)
155      return VK_ERROR_OUT_OF_HOST_MEMORY;
156
157   submit->temp.batches = submit->temp.storage;
158   submit->temp.semaphores = submit->temp.storage + semaphores_offset;
159
160   return VK_SUCCESS;
161}
162
163static uint32_t
164vn_queue_submission_filter_batch_wsi_semaphores(
165   struct vn_queue_submission *submit,
166   uint32_t batch_index,
167   uint32_t sem_base)
168{
169   struct vn_queue *queue = vn_queue_from_handle(submit->queue);
170
171   union {
172      VkSubmitInfo *submit_batch;
173      VkBindSparseInfo *bind_sparse_batch;
174   } u;
175   const VkSemaphore *src_sems;
176   uint32_t src_count;
177   switch (submit->batch_type) {
178   case VK_STRUCTURE_TYPE_SUBMIT_INFO:
179      u.submit_batch = &submit->temp.submit_batches[batch_index];
180      src_sems = u.submit_batch->pWaitSemaphores;
181      src_count = u.submit_batch->waitSemaphoreCount;
182      break;
183   case VK_STRUCTURE_TYPE_BIND_SPARSE_INFO:
184      u.bind_sparse_batch = &submit->temp.bind_sparse_batches[batch_index];
185      src_sems = u.bind_sparse_batch->pWaitSemaphores;
186      src_count = u.bind_sparse_batch->waitSemaphoreCount;
187      break;
188   default:
189      unreachable("unexpected batch type");
190      break;
191   }
192
193   VkSemaphore *dst_sems = &submit->temp.semaphores[sem_base];
194   uint32_t dst_count = 0;
195
196   /* filter out VN_SYNC_TYPE_WSI_SIGNALED wait semaphores */
197   for (uint32_t i = 0; i < src_count; i++) {
198      struct vn_semaphore *sem = vn_semaphore_from_handle(src_sems[i]);
199      const struct vn_sync_payload *payload = sem->payload;
200
201      if (payload->type == VN_SYNC_TYPE_WSI_SIGNALED)
202         vn_semaphore_reset_wsi(queue->device, sem);
203      else
204         dst_sems[dst_count++] = src_sems[i];
205   }
206
207   switch (submit->batch_type) {
208   case VK_STRUCTURE_TYPE_SUBMIT_INFO:
209      u.submit_batch->pWaitSemaphores = dst_sems;
210      u.submit_batch->waitSemaphoreCount = dst_count;
211      break;
212   case VK_STRUCTURE_TYPE_BIND_SPARSE_INFO:
213      u.bind_sparse_batch->pWaitSemaphores = dst_sems;
214      u.bind_sparse_batch->waitSemaphoreCount = dst_count;
215      break;
216   default:
217      break;
218   }
219
220   return dst_count;
221}
222
223static void
224vn_queue_submission_setup_batches(struct vn_queue_submission *submit)
225{
226   if (!submit->temp.storage)
227      return;
228
229   /* make a copy because we need to filter out WSI semaphores */
230   if (submit->wait_wsi_count) {
231      switch (submit->batch_type) {
232      case VK_STRUCTURE_TYPE_SUBMIT_INFO:
233         memcpy(submit->temp.submit_batches, submit->submit_batches,
234                sizeof(submit->submit_batches[0]) * submit->batch_count);
235         submit->submit_batches = submit->temp.submit_batches;
236         break;
237      case VK_STRUCTURE_TYPE_BIND_SPARSE_INFO:
238         memcpy(submit->temp.bind_sparse_batches, submit->bind_sparse_batches,
239                sizeof(submit->bind_sparse_batches[0]) * submit->batch_count);
240         submit->bind_sparse_batches = submit->temp.bind_sparse_batches;
241         break;
242      default:
243         unreachable("unexpected batch type");
244         break;
245      }
246   }
247
248   uint32_t wait_sem_base = 0;
249   for (uint32_t i = 0; i < submit->batch_count; i++) {
250      if (submit->wait_wsi_count) {
251         wait_sem_base += vn_queue_submission_filter_batch_wsi_semaphores(
252            submit, i, wait_sem_base);
253      }
254   }
255}
256
257static VkResult
258vn_queue_submission_prepare_submit(struct vn_queue_submission *submit,
259                                   VkQueue queue,
260                                   uint32_t batch_count,
261                                   const VkSubmitInfo *submit_batches,
262                                   VkFence fence)
263{
264   submit->batch_type = VK_STRUCTURE_TYPE_SUBMIT_INFO;
265   submit->queue = queue;
266   submit->batch_count = batch_count;
267   submit->submit_batches = submit_batches;
268   submit->fence = fence;
269
270   vn_queue_submission_count_semaphores(submit);
271
272   VkResult result = vn_queue_submission_alloc_storage(submit);
273   if (result != VK_SUCCESS)
274      return result;
275
276   vn_queue_submission_setup_batches(submit);
277
278   return VK_SUCCESS;
279}
280
281static VkResult
282vn_queue_submission_prepare_bind_sparse(
283   struct vn_queue_submission *submit,
284   VkQueue queue,
285   uint32_t batch_count,
286   const VkBindSparseInfo *bind_sparse_batches,
287   VkFence fence)
288{
289   submit->batch_type = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO;
290   submit->queue = queue;
291   submit->batch_count = batch_count;
292   submit->bind_sparse_batches = bind_sparse_batches;
293   submit->fence = fence;
294
295   vn_queue_submission_count_semaphores(submit);
296
297   VkResult result = vn_queue_submission_alloc_storage(submit);
298   if (result != VK_SUCCESS)
299      return result;
300
301   vn_queue_submission_setup_batches(submit);
302
303   return VK_SUCCESS;
304}
305
306static void
307vn_queue_submission_cleanup(struct vn_queue_submission *submit)
308{
309   struct vn_queue *queue = vn_queue_from_handle(submit->queue);
310   const VkAllocationCallbacks *alloc = &queue->device->base.base.alloc;
311
312   vk_free(alloc, submit->temp.storage);
313}
314
315static inline uint32_t
316vn_queue_family_array_index(struct vn_queue *queue)
317{
318   for (uint32_t i = 0; i < queue->device->queue_family_count; i++) {
319      if (queue->device->queue_families[i] == queue->family)
320         return i;
321   }
322   unreachable("invalid queue");
323}
324
325static VkResult
326vn_queue_submit(struct vn_instance *instance,
327                VkQueue queue_handle,
328                uint32_t batch_count,
329                const VkSubmitInfo *batches,
330                VkFence fence_handle,
331                bool sync_submit)
332{
333   /* skip no-op submit */
334   if (!batch_count && fence_handle == VK_NULL_HANDLE)
335      return VK_SUCCESS;
336
337   if (sync_submit) {
338      return vn_call_vkQueueSubmit(instance, queue_handle, batch_count,
339                                   batches, fence_handle);
340   }
341
342   vn_async_vkQueueSubmit(instance, queue_handle, batch_count, batches,
343                          fence_handle);
344   return VK_SUCCESS;
345}
346
347VkResult
348vn_QueueSubmit(VkQueue _queue,
349               uint32_t submitCount,
350               const VkSubmitInfo *pSubmits,
351               VkFence _fence)
352{
353   VN_TRACE_FUNC();
354   struct vn_queue *queue = vn_queue_from_handle(_queue);
355   struct vn_device *dev = queue->device;
356   struct vn_fence *fence = vn_fence_from_handle(_fence);
357   const bool external_fence = fence && fence->is_external;
358   const bool feedback_fence = fence && fence->feedback.slot;
359   struct vn_queue_submission submit;
360   const struct vn_device_memory *wsi_mem = NULL;
361   bool sync_submit;
362   VkResult result;
363
364   result = vn_queue_submission_prepare_submit(&submit, _queue, submitCount,
365                                               pSubmits, _fence);
366   if (result != VK_SUCCESS)
367      return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
368
369   if (submit.batch_count == 1) {
370      const struct wsi_memory_signal_submit_info *info = vk_find_struct_const(
371         submit.submit_batches[0].pNext, WSI_MEMORY_SIGNAL_SUBMIT_INFO_MESA);
372      if (info) {
373         wsi_mem = vn_device_memory_from_handle(info->memory);
374         assert(!wsi_mem->base_memory && wsi_mem->base_bo);
375      }
376   }
377
378   /* force synchronous submission if any of the below applies:
379    * - struct wsi_memory_signal_submit_info
380    * - fence is an external fence
381    * - NO_ASYNC_QUEUE_SUBMIT perf option enabled
382    */
383   sync_submit = wsi_mem || external_fence || VN_PERF(NO_ASYNC_QUEUE_SUBMIT);
384
385   /* if the original submission involves a feedback fence:
386    * - defer the feedback fence to another submit to avoid deep copy
387    * - defer the potential sync_submit to the feedback fence submission
388    */
389   result = vn_queue_submit(dev->instance, submit.queue, submit.batch_count,
390                            submit.submit_batches,
391                            feedback_fence ? VK_NULL_HANDLE : submit.fence,
392                            !feedback_fence && sync_submit);
393   if (result != VK_SUCCESS) {
394      vn_queue_submission_cleanup(&submit);
395      return vn_error(dev->instance, result);
396   }
397
398   /* TODO intercept original submit batches to append the fence feedback cmd
399    * with a per-queue cached submission builder to avoid transient allocs.
400    *
401    * vn_queue_submission bits must be fixed for VkTimelineSemaphoreSubmitInfo
402    * before adding timeline semaphore feedback.
403    */
404   if (feedback_fence) {
405      const uint32_t feedback_cmd_index = vn_queue_family_array_index(queue);
406      const VkSubmitInfo info = {
407         .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
408         .pNext = NULL,
409         .waitSemaphoreCount = 0,
410         .pWaitSemaphores = NULL,
411         .pWaitDstStageMask = NULL,
412         .commandBufferCount = 1,
413         .pCommandBuffers = &fence->feedback.commands[feedback_cmd_index],
414      };
415      result = vn_queue_submit(dev->instance, submit.queue, 1, &info,
416                               submit.fence, sync_submit);
417      if (result != VK_SUCCESS) {
418         vn_queue_submission_cleanup(&submit);
419         return vn_error(dev->instance, result);
420      }
421   }
422
423   if (wsi_mem) {
424      /* XXX this is always false and kills the performance */
425      if (dev->instance->renderer->info.has_implicit_fencing) {
426         vn_renderer_submit(dev->renderer, &(const struct vn_renderer_submit){
427                                              .bos = &wsi_mem->base_bo,
428                                              .bo_count = 1,
429                                           });
430      } else {
431         if (VN_DEBUG(WSI)) {
432            static uint32_t ratelimit;
433            if (ratelimit < 10) {
434               vn_log(dev->instance,
435                      "forcing vkQueueWaitIdle before presenting");
436               ratelimit++;
437            }
438         }
439
440         vn_QueueWaitIdle(submit.queue);
441      }
442   }
443
444   vn_queue_submission_cleanup(&submit);
445
446   return VK_SUCCESS;
447}
448
449VkResult
450vn_QueueBindSparse(VkQueue _queue,
451                   uint32_t bindInfoCount,
452                   const VkBindSparseInfo *pBindInfo,
453                   VkFence fence)
454{
455   VN_TRACE_FUNC();
456   struct vn_queue *queue = vn_queue_from_handle(_queue);
457   struct vn_device *dev = queue->device;
458
459   /* TODO allow sparse resource along with sync feedback */
460   assert(VN_PERF(NO_FENCE_FEEDBACK));
461
462   struct vn_queue_submission submit;
463   VkResult result = vn_queue_submission_prepare_bind_sparse(
464      &submit, _queue, bindInfoCount, pBindInfo, fence);
465   if (result != VK_SUCCESS)
466      return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
467
468   result = vn_call_vkQueueBindSparse(
469      dev->instance, submit.queue, submit.batch_count,
470      submit.bind_sparse_batches, submit.fence);
471   if (result != VK_SUCCESS) {
472      vn_queue_submission_cleanup(&submit);
473      return vn_error(dev->instance, result);
474   }
475
476   vn_queue_submission_cleanup(&submit);
477
478   return VK_SUCCESS;
479}
480
481VkResult
482vn_QueueWaitIdle(VkQueue _queue)
483{
484   VN_TRACE_FUNC();
485   struct vn_queue *queue = vn_queue_from_handle(_queue);
486   VkDevice device = vn_device_to_handle(queue->device);
487
488   VkResult result = vn_QueueSubmit(_queue, 0, NULL, queue->wait_fence);
489   if (result != VK_SUCCESS)
490      return result;
491
492   result = vn_WaitForFences(device, 1, &queue->wait_fence, true, UINT64_MAX);
493   vn_ResetFences(device, 1, &queue->wait_fence);
494
495   return vn_result(queue->device->instance, result);
496}
497
498/* fence commands */
499
500static void
501vn_sync_payload_release(struct vn_device *dev,
502                        struct vn_sync_payload *payload)
503{
504   payload->type = VN_SYNC_TYPE_INVALID;
505}
506
507static VkResult
508vn_fence_init_payloads(struct vn_device *dev,
509                       struct vn_fence *fence,
510                       bool signaled,
511                       const VkAllocationCallbacks *alloc)
512{
513   fence->permanent.type = VN_SYNC_TYPE_DEVICE_ONLY;
514   fence->temporary.type = VN_SYNC_TYPE_INVALID;
515   fence->payload = &fence->permanent;
516
517   return VK_SUCCESS;
518}
519
520void
521vn_fence_signal_wsi(struct vn_device *dev, struct vn_fence *fence)
522{
523   struct vn_sync_payload *temp = &fence->temporary;
524
525   vn_sync_payload_release(dev, temp);
526   temp->type = VN_SYNC_TYPE_WSI_SIGNALED;
527   fence->payload = temp;
528}
529
530static VkResult
531vn_fence_feedback_init(struct vn_device *dev,
532                       struct vn_fence *fence,
533                       bool signaled,
534                       const VkAllocationCallbacks *alloc)
535{
536   VkDevice dev_handle = vn_device_to_handle(dev);
537   struct vn_feedback_slot *slot;
538   VkCommandBuffer *cmd_handles;
539   VkResult result;
540
541   if (fence->is_external)
542      return VK_SUCCESS;
543
544   /* Fence feedback implementation relies on vkWaitForFences to cover the gap
545    * between feedback slot signaling and the actual fence signal operation.
546    */
547   if (unlikely(!dev->instance->renderer->info.allow_vk_wait_syncs))
548      return VK_SUCCESS;
549
550   if (VN_PERF(NO_FENCE_FEEDBACK))
551      return VK_SUCCESS;
552
553   slot = vn_feedback_pool_alloc(&dev->feedback_pool, VN_FEEDBACK_TYPE_FENCE);
554   if (!slot)
555      return VK_ERROR_OUT_OF_HOST_MEMORY;
556
557   vn_feedback_set_status(slot, signaled ? VK_SUCCESS : VK_NOT_READY);
558
559   cmd_handles =
560      vk_zalloc(alloc, sizeof(*cmd_handles) * dev->queue_family_count,
561                VN_DEFAULT_ALIGN, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
562   if (!cmd_handles) {
563      vn_feedback_pool_free(&dev->feedback_pool, slot);
564      return VK_ERROR_OUT_OF_HOST_MEMORY;
565   }
566
567   for (uint32_t i = 0; i < dev->queue_family_count; i++) {
568      result = vn_feedback_fence_cmd_alloc(dev_handle, &dev->cmd_pools[i],
569                                           slot, &cmd_handles[i]);
570      if (result != VK_SUCCESS) {
571         for (uint32_t j = 0; j < i; j++) {
572            vn_feedback_fence_cmd_free(dev_handle, &dev->cmd_pools[j],
573                                       cmd_handles[j]);
574         }
575         break;
576      }
577   }
578
579   if (result != VK_SUCCESS) {
580      vk_free(alloc, cmd_handles);
581      vn_feedback_pool_free(&dev->feedback_pool, slot);
582      return result;
583   }
584
585   fence->feedback.slot = slot;
586   fence->feedback.commands = cmd_handles;
587
588   return VK_SUCCESS;
589}
590
591static void
592vn_fence_feedback_fini(struct vn_device *dev,
593                       struct vn_fence *fence,
594                       const VkAllocationCallbacks *alloc)
595{
596   VkDevice dev_handle = vn_device_to_handle(dev);
597
598   if (!fence->feedback.slot)
599      return;
600
601   for (uint32_t i = 0; i < dev->queue_family_count; i++) {
602      vn_feedback_fence_cmd_free(dev_handle, &dev->cmd_pools[i],
603                                 fence->feedback.commands[i]);
604   }
605
606   vn_feedback_pool_free(&dev->feedback_pool, fence->feedback.slot);
607
608   vk_free(alloc, fence->feedback.commands);
609}
610
611VkResult
612vn_CreateFence(VkDevice device,
613               const VkFenceCreateInfo *pCreateInfo,
614               const VkAllocationCallbacks *pAllocator,
615               VkFence *pFence)
616{
617   VN_TRACE_FUNC();
618   struct vn_device *dev = vn_device_from_handle(device);
619   const VkAllocationCallbacks *alloc =
620      pAllocator ? pAllocator : &dev->base.base.alloc;
621   const bool signaled = pCreateInfo->flags & VK_FENCE_CREATE_SIGNALED_BIT;
622   VkResult result;
623
624   struct vn_fence *fence = vk_zalloc(alloc, sizeof(*fence), VN_DEFAULT_ALIGN,
625                                      VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
626   if (!fence)
627      return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
628
629   vn_object_base_init(&fence->base, VK_OBJECT_TYPE_FENCE, &dev->base);
630
631   const struct VkExportFenceCreateInfo *export_info =
632      vk_find_struct_const(pCreateInfo->pNext, EXPORT_FENCE_CREATE_INFO);
633   VkFenceCreateInfo local_create_info;
634   if (export_info) {
635      local_create_info = *pCreateInfo;
636      local_create_info.pNext = NULL;
637      pCreateInfo = &local_create_info;
638
639      fence->is_external = !!export_info->handleTypes;
640   }
641
642   result = vn_fence_init_payloads(dev, fence, signaled, alloc);
643   if (result != VK_SUCCESS)
644      goto out_object_base_fini;
645
646   result = vn_fence_feedback_init(dev, fence, signaled, alloc);
647   if (result != VK_SUCCESS)
648      goto out_payloads_fini;
649
650   *pFence = vn_fence_to_handle(fence);
651   vn_async_vkCreateFence(dev->instance, device, pCreateInfo, NULL, pFence);
652
653   return VK_SUCCESS;
654
655out_payloads_fini:
656   vn_sync_payload_release(dev, &fence->permanent);
657   vn_sync_payload_release(dev, &fence->temporary);
658
659out_object_base_fini:
660   vn_object_base_fini(&fence->base);
661   vk_free(alloc, fence);
662   return vn_error(dev->instance, result);
663}
664
665void
666vn_DestroyFence(VkDevice device,
667                VkFence _fence,
668                const VkAllocationCallbacks *pAllocator)
669{
670   VN_TRACE_FUNC();
671   struct vn_device *dev = vn_device_from_handle(device);
672   struct vn_fence *fence = vn_fence_from_handle(_fence);
673   const VkAllocationCallbacks *alloc =
674      pAllocator ? pAllocator : &dev->base.base.alloc;
675
676   if (!fence)
677      return;
678
679   vn_async_vkDestroyFence(dev->instance, device, _fence, NULL);
680
681   vn_fence_feedback_fini(dev, fence, alloc);
682
683   vn_sync_payload_release(dev, &fence->permanent);
684   vn_sync_payload_release(dev, &fence->temporary);
685
686   vn_object_base_fini(&fence->base);
687   vk_free(alloc, fence);
688}
689
690VkResult
691vn_ResetFences(VkDevice device, uint32_t fenceCount, const VkFence *pFences)
692{
693   VN_TRACE_FUNC();
694   struct vn_device *dev = vn_device_from_handle(device);
695
696   /* TODO if the fence is shared-by-ref, this needs to be synchronous */
697   if (false)
698      vn_call_vkResetFences(dev->instance, device, fenceCount, pFences);
699   else
700      vn_async_vkResetFences(dev->instance, device, fenceCount, pFences);
701
702   for (uint32_t i = 0; i < fenceCount; i++) {
703      struct vn_fence *fence = vn_fence_from_handle(pFences[i]);
704      struct vn_sync_payload *perm = &fence->permanent;
705
706      vn_sync_payload_release(dev, &fence->temporary);
707
708      assert(perm->type == VN_SYNC_TYPE_DEVICE_ONLY);
709      fence->payload = perm;
710
711      if (fence->feedback.slot)
712         vn_feedback_reset_status(fence->feedback.slot);
713   }
714
715   return VK_SUCCESS;
716}
717
718VkResult
719vn_GetFenceStatus(VkDevice device, VkFence _fence)
720{
721   struct vn_device *dev = vn_device_from_handle(device);
722   struct vn_fence *fence = vn_fence_from_handle(_fence);
723   struct vn_sync_payload *payload = fence->payload;
724
725   VkResult result;
726   switch (payload->type) {
727   case VN_SYNC_TYPE_DEVICE_ONLY:
728      if (fence->feedback.slot) {
729         result = vn_feedback_get_status(fence->feedback.slot);
730         if (result == VK_SUCCESS) {
731            /* When fence feedback slot gets signaled, the real fence
732             * signal operation follows after but the signaling isr can be
733             * deferred or preempted. To avoid theoretical racing, we let
734             * the renderer wait for the fence. This also helps resolve
735             * synchronization validation errors, because the layer no
736             * longer sees any fence status checks and falsely believes the
737             * caller does not sync.
738             */
739            vn_async_vkWaitForFences(dev->instance, device, 1, &_fence,
740                                     VK_TRUE, UINT64_MAX);
741         }
742      } else {
743         result = vn_call_vkGetFenceStatus(dev->instance, device, _fence);
744      }
745      break;
746   case VN_SYNC_TYPE_WSI_SIGNALED:
747      result = VK_SUCCESS;
748      break;
749   default:
750      unreachable("unexpected fence payload type");
751      break;
752   }
753
754   return vn_result(dev->instance, result);
755}
756
757static VkResult
758vn_find_first_signaled_fence(VkDevice device,
759                             const VkFence *fences,
760                             uint32_t count)
761{
762   for (uint32_t i = 0; i < count; i++) {
763      VkResult result = vn_GetFenceStatus(device, fences[i]);
764      if (result == VK_SUCCESS || result < 0)
765         return result;
766   }
767   return VK_NOT_READY;
768}
769
770static VkResult
771vn_remove_signaled_fences(VkDevice device, VkFence *fences, uint32_t *count)
772{
773   uint32_t cur = 0;
774   for (uint32_t i = 0; i < *count; i++) {
775      VkResult result = vn_GetFenceStatus(device, fences[i]);
776      if (result != VK_SUCCESS) {
777         if (result < 0)
778            return result;
779         fences[cur++] = fences[i];
780      }
781   }
782
783   *count = cur;
784   return cur ? VK_NOT_READY : VK_SUCCESS;
785}
786
787static VkResult
788vn_update_sync_result(VkResult result, int64_t abs_timeout, uint32_t *iter)
789{
790   switch (result) {
791   case VK_NOT_READY:
792      if (abs_timeout != OS_TIMEOUT_INFINITE &&
793          os_time_get_nano() >= abs_timeout)
794         result = VK_TIMEOUT;
795      else
796         vn_relax(iter, "client");
797      break;
798   default:
799      assert(result == VK_SUCCESS || result < 0);
800      break;
801   }
802
803   return result;
804}
805
806VkResult
807vn_WaitForFences(VkDevice device,
808                 uint32_t fenceCount,
809                 const VkFence *pFences,
810                 VkBool32 waitAll,
811                 uint64_t timeout)
812{
813   VN_TRACE_FUNC();
814   struct vn_device *dev = vn_device_from_handle(device);
815   const VkAllocationCallbacks *alloc = &dev->base.base.alloc;
816
817   const int64_t abs_timeout = os_time_get_absolute_timeout(timeout);
818   VkResult result = VK_NOT_READY;
819   uint32_t iter = 0;
820   if (fenceCount > 1 && waitAll) {
821      VkFence local_fences[8];
822      VkFence *fences = local_fences;
823      if (fenceCount > ARRAY_SIZE(local_fences)) {
824         fences =
825            vk_alloc(alloc, sizeof(*fences) * fenceCount, VN_DEFAULT_ALIGN,
826                     VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
827         if (!fences)
828            return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
829      }
830      memcpy(fences, pFences, sizeof(*fences) * fenceCount);
831
832      while (result == VK_NOT_READY) {
833         result = vn_remove_signaled_fences(device, fences, &fenceCount);
834         result = vn_update_sync_result(result, abs_timeout, &iter);
835      }
836
837      if (fences != local_fences)
838         vk_free(alloc, fences);
839   } else {
840      while (result == VK_NOT_READY) {
841         result = vn_find_first_signaled_fence(device, pFences, fenceCount);
842         result = vn_update_sync_result(result, abs_timeout, &iter);
843      }
844   }
845
846   return vn_result(dev->instance, result);
847}
848
849static VkResult
850vn_create_sync_file(struct vn_device *dev, int *out_fd)
851{
852   struct vn_renderer_sync *sync;
853   VkResult result = vn_renderer_sync_create(dev->renderer, 0,
854                                             VN_RENDERER_SYNC_BINARY, &sync);
855   if (result != VK_SUCCESS)
856      return vn_error(dev->instance, result);
857
858   const struct vn_renderer_submit submit = {
859      .batches =
860         &(const struct vn_renderer_submit_batch){
861            .syncs = &sync,
862            .sync_values = &(const uint64_t){ 1 },
863            .sync_count = 1,
864         },
865      .batch_count = 1,
866   };
867   result = vn_renderer_submit(dev->renderer, &submit);
868   if (result != VK_SUCCESS) {
869      vn_renderer_sync_destroy(dev->renderer, sync);
870      return vn_error(dev->instance, result);
871   }
872
873   *out_fd = vn_renderer_sync_export_syncobj(dev->renderer, sync, true);
874   vn_renderer_sync_destroy(dev->renderer, sync);
875
876   return *out_fd >= 0 ? VK_SUCCESS : VK_ERROR_TOO_MANY_OBJECTS;
877}
878
879VkResult
880vn_ImportFenceFdKHR(VkDevice device,
881                    const VkImportFenceFdInfoKHR *pImportFenceFdInfo)
882{
883   VN_TRACE_FUNC();
884   struct vn_device *dev = vn_device_from_handle(device);
885   struct vn_fence *fence = vn_fence_from_handle(pImportFenceFdInfo->fence);
886   ASSERTED const bool sync_file = pImportFenceFdInfo->handleType ==
887                                   VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
888   const int fd = pImportFenceFdInfo->fd;
889
890   /* TODO update fence->is_external after we support opaque fd import */
891   assert(dev->instance->experimental.globalFencing);
892   assert(sync_file);
893   if (fd >= 0) {
894      if (sync_wait(fd, -1))
895         return vn_error(dev->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
896
897      close(fd);
898   }
899
900   /* abuse VN_SYNC_TYPE_WSI_SIGNALED */
901   vn_fence_signal_wsi(dev, fence);
902
903   return VK_SUCCESS;
904}
905
906VkResult
907vn_GetFenceFdKHR(VkDevice device,
908                 const VkFenceGetFdInfoKHR *pGetFdInfo,
909                 int *pFd)
910{
911   VN_TRACE_FUNC();
912   struct vn_device *dev = vn_device_from_handle(device);
913   struct vn_fence *fence = vn_fence_from_handle(pGetFdInfo->fence);
914   const bool sync_file =
915      pGetFdInfo->handleType == VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
916   struct vn_sync_payload *payload = fence->payload;
917
918   assert(dev->instance->experimental.globalFencing);
919   assert(sync_file);
920   int fd = -1;
921   if (payload->type == VN_SYNC_TYPE_DEVICE_ONLY) {
922      VkResult result = vn_create_sync_file(dev, &fd);
923      if (result != VK_SUCCESS)
924         return vn_error(dev->instance, result);
925   }
926
927   if (sync_file) {
928      vn_sync_payload_release(dev, &fence->temporary);
929      fence->payload = &fence->permanent;
930
931      /* XXX implies reset operation on the host fence */
932   }
933
934   *pFd = fd;
935   return VK_SUCCESS;
936}
937
938/* semaphore commands */
939
940static VkResult
941vn_semaphore_init_payloads(struct vn_device *dev,
942                           struct vn_semaphore *sem,
943                           uint64_t initial_val,
944                           const VkAllocationCallbacks *alloc)
945{
946   sem->permanent.type = VN_SYNC_TYPE_DEVICE_ONLY;
947   sem->temporary.type = VN_SYNC_TYPE_INVALID;
948   sem->payload = &sem->permanent;
949
950   return VK_SUCCESS;
951}
952
953static void
954vn_semaphore_reset_wsi(struct vn_device *dev, struct vn_semaphore *sem)
955{
956   struct vn_sync_payload *perm = &sem->permanent;
957
958   vn_sync_payload_release(dev, &sem->temporary);
959
960   sem->payload = perm;
961}
962
963void
964vn_semaphore_signal_wsi(struct vn_device *dev, struct vn_semaphore *sem)
965{
966   struct vn_sync_payload *temp = &sem->temporary;
967
968   vn_sync_payload_release(dev, temp);
969   temp->type = VN_SYNC_TYPE_WSI_SIGNALED;
970   sem->payload = temp;
971}
972
973VkResult
974vn_CreateSemaphore(VkDevice device,
975                   const VkSemaphoreCreateInfo *pCreateInfo,
976                   const VkAllocationCallbacks *pAllocator,
977                   VkSemaphore *pSemaphore)
978{
979   VN_TRACE_FUNC();
980   struct vn_device *dev = vn_device_from_handle(device);
981   const VkAllocationCallbacks *alloc =
982      pAllocator ? pAllocator : &dev->base.base.alloc;
983
984   struct vn_semaphore *sem = vk_zalloc(alloc, sizeof(*sem), VN_DEFAULT_ALIGN,
985                                        VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
986   if (!sem)
987      return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
988
989   vn_object_base_init(&sem->base, VK_OBJECT_TYPE_SEMAPHORE, &dev->base);
990
991   const VkSemaphoreTypeCreateInfo *type_info =
992      vk_find_struct_const(pCreateInfo->pNext, SEMAPHORE_TYPE_CREATE_INFO);
993   uint64_t initial_val = 0;
994   if (type_info && type_info->semaphoreType == VK_SEMAPHORE_TYPE_TIMELINE) {
995      sem->type = VK_SEMAPHORE_TYPE_TIMELINE;
996      initial_val = type_info->initialValue;
997   } else {
998      sem->type = VK_SEMAPHORE_TYPE_BINARY;
999   }
1000
1001   VkResult result = vn_semaphore_init_payloads(dev, sem, initial_val, alloc);
1002   if (result != VK_SUCCESS) {
1003      vn_object_base_fini(&sem->base);
1004      vk_free(alloc, sem);
1005      return vn_error(dev->instance, result);
1006   }
1007
1008   VkSemaphore sem_handle = vn_semaphore_to_handle(sem);
1009   vn_async_vkCreateSemaphore(dev->instance, device, pCreateInfo, NULL,
1010                              &sem_handle);
1011
1012   *pSemaphore = sem_handle;
1013
1014   return VK_SUCCESS;
1015}
1016
1017void
1018vn_DestroySemaphore(VkDevice device,
1019                    VkSemaphore semaphore,
1020                    const VkAllocationCallbacks *pAllocator)
1021{
1022   VN_TRACE_FUNC();
1023   struct vn_device *dev = vn_device_from_handle(device);
1024   struct vn_semaphore *sem = vn_semaphore_from_handle(semaphore);
1025   const VkAllocationCallbacks *alloc =
1026      pAllocator ? pAllocator : &dev->base.base.alloc;
1027
1028   if (!sem)
1029      return;
1030
1031   vn_async_vkDestroySemaphore(dev->instance, device, semaphore, NULL);
1032
1033   vn_sync_payload_release(dev, &sem->permanent);
1034   vn_sync_payload_release(dev, &sem->temporary);
1035
1036   vn_object_base_fini(&sem->base);
1037   vk_free(alloc, sem);
1038}
1039
1040VkResult
1041vn_GetSemaphoreCounterValue(VkDevice device,
1042                            VkSemaphore semaphore,
1043                            uint64_t *pValue)
1044{
1045   VN_TRACE_FUNC();
1046   struct vn_device *dev = vn_device_from_handle(device);
1047   struct vn_semaphore *sem = vn_semaphore_from_handle(semaphore);
1048   ASSERTED struct vn_sync_payload *payload = sem->payload;
1049
1050   assert(payload->type == VN_SYNC_TYPE_DEVICE_ONLY);
1051   return vn_call_vkGetSemaphoreCounterValue(dev->instance, device, semaphore,
1052                                             pValue);
1053}
1054
1055VkResult
1056vn_SignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo *pSignalInfo)
1057{
1058   VN_TRACE_FUNC();
1059   struct vn_device *dev = vn_device_from_handle(device);
1060
1061   /* TODO if the semaphore is shared-by-ref, this needs to be synchronous */
1062   if (false)
1063      vn_call_vkSignalSemaphore(dev->instance, device, pSignalInfo);
1064   else
1065      vn_async_vkSignalSemaphore(dev->instance, device, pSignalInfo);
1066
1067   return VK_SUCCESS;
1068}
1069
1070static VkResult
1071vn_find_first_signaled_semaphore(VkDevice device,
1072                                 const VkSemaphore *semaphores,
1073                                 const uint64_t *values,
1074                                 uint32_t count)
1075{
1076   for (uint32_t i = 0; i < count; i++) {
1077      uint64_t val = 0;
1078      VkResult result =
1079         vn_GetSemaphoreCounterValue(device, semaphores[i], &val);
1080      if (result != VK_SUCCESS || val >= values[i])
1081         return result;
1082   }
1083   return VK_NOT_READY;
1084}
1085
1086static VkResult
1087vn_remove_signaled_semaphores(VkDevice device,
1088                              VkSemaphore *semaphores,
1089                              uint64_t *values,
1090                              uint32_t *count)
1091{
1092   uint32_t cur = 0;
1093   for (uint32_t i = 0; i < *count; i++) {
1094      uint64_t val = 0;
1095      VkResult result =
1096         vn_GetSemaphoreCounterValue(device, semaphores[i], &val);
1097      if (result != VK_SUCCESS)
1098         return result;
1099      if (val < values[i])
1100         semaphores[cur++] = semaphores[i];
1101   }
1102
1103   *count = cur;
1104   return cur ? VK_NOT_READY : VK_SUCCESS;
1105}
1106
1107VkResult
1108vn_WaitSemaphores(VkDevice device,
1109                  const VkSemaphoreWaitInfo *pWaitInfo,
1110                  uint64_t timeout)
1111{
1112   VN_TRACE_FUNC();
1113   struct vn_device *dev = vn_device_from_handle(device);
1114   const VkAllocationCallbacks *alloc = &dev->base.base.alloc;
1115
1116   const int64_t abs_timeout = os_time_get_absolute_timeout(timeout);
1117   VkResult result = VK_NOT_READY;
1118   uint32_t iter = 0;
1119   if (pWaitInfo->semaphoreCount > 1 &&
1120       !(pWaitInfo->flags & VK_SEMAPHORE_WAIT_ANY_BIT)) {
1121      uint32_t semaphore_count = pWaitInfo->semaphoreCount;
1122      VkSemaphore local_semaphores[8];
1123      uint64_t local_values[8];
1124      VkSemaphore *semaphores = local_semaphores;
1125      uint64_t *values = local_values;
1126      if (semaphore_count > ARRAY_SIZE(local_semaphores)) {
1127         semaphores = vk_alloc(
1128            alloc, (sizeof(*semaphores) + sizeof(*values)) * semaphore_count,
1129            VN_DEFAULT_ALIGN, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
1130         if (!semaphores)
1131            return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
1132
1133         values = (uint64_t *)&semaphores[semaphore_count];
1134      }
1135      memcpy(semaphores, pWaitInfo->pSemaphores,
1136             sizeof(*semaphores) * semaphore_count);
1137      memcpy(values, pWaitInfo->pValues, sizeof(*values) * semaphore_count);
1138
1139      while (result == VK_NOT_READY) {
1140         result = vn_remove_signaled_semaphores(device, semaphores, values,
1141                                                &semaphore_count);
1142         result = vn_update_sync_result(result, abs_timeout, &iter);
1143      }
1144
1145      if (semaphores != local_semaphores)
1146         vk_free(alloc, semaphores);
1147   } else {
1148      while (result == VK_NOT_READY) {
1149         result = vn_find_first_signaled_semaphore(
1150            device, pWaitInfo->pSemaphores, pWaitInfo->pValues,
1151            pWaitInfo->semaphoreCount);
1152         result = vn_update_sync_result(result, abs_timeout, &iter);
1153      }
1154   }
1155
1156   return vn_result(dev->instance, result);
1157}
1158
1159VkResult
1160vn_ImportSemaphoreFdKHR(
1161   VkDevice device, const VkImportSemaphoreFdInfoKHR *pImportSemaphoreFdInfo)
1162{
1163   VN_TRACE_FUNC();
1164   struct vn_device *dev = vn_device_from_handle(device);
1165   struct vn_semaphore *sem =
1166      vn_semaphore_from_handle(pImportSemaphoreFdInfo->semaphore);
1167   ASSERTED const bool sync_file =
1168      pImportSemaphoreFdInfo->handleType ==
1169      VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
1170   const int fd = pImportSemaphoreFdInfo->fd;
1171
1172   assert(dev->instance->experimental.globalFencing);
1173   assert(sync_file);
1174   if (fd >= 0) {
1175      if (sync_wait(fd, -1))
1176         return vn_error(dev->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
1177
1178      close(fd);
1179   }
1180
1181   /* abuse VN_SYNC_TYPE_WSI_SIGNALED */
1182   vn_semaphore_signal_wsi(dev, sem);
1183
1184   return VK_SUCCESS;
1185}
1186
1187VkResult
1188vn_GetSemaphoreFdKHR(VkDevice device,
1189                     const VkSemaphoreGetFdInfoKHR *pGetFdInfo,
1190                     int *pFd)
1191{
1192   VN_TRACE_FUNC();
1193   struct vn_device *dev = vn_device_from_handle(device);
1194   struct vn_semaphore *sem = vn_semaphore_from_handle(pGetFdInfo->semaphore);
1195   const bool sync_file =
1196      pGetFdInfo->handleType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
1197   struct vn_sync_payload *payload = sem->payload;
1198
1199   assert(dev->instance->experimental.globalFencing);
1200   assert(sync_file);
1201   int fd = -1;
1202   if (payload->type == VN_SYNC_TYPE_DEVICE_ONLY) {
1203      VkResult result = vn_create_sync_file(dev, &fd);
1204      if (result != VK_SUCCESS)
1205         return vn_error(dev->instance, result);
1206   }
1207
1208   if (sync_file) {
1209      vn_sync_payload_release(dev, &sem->temporary);
1210      sem->payload = &sem->permanent;
1211
1212      /* XXX implies wait operation on the host semaphore */
1213   }
1214
1215   *pFd = fd;
1216   return VK_SUCCESS;
1217}
1218
1219/* event commands */
1220
1221static VkResult
1222vn_event_feedback_init(struct vn_device *dev, struct vn_event *ev)
1223{
1224   struct vn_feedback_slot *slot;
1225
1226   if (VN_PERF(NO_EVENT_FEEDBACK))
1227      return VK_SUCCESS;
1228
1229   slot = vn_feedback_pool_alloc(&dev->feedback_pool, VN_FEEDBACK_TYPE_EVENT);
1230   if (!slot)
1231      return VK_ERROR_OUT_OF_HOST_MEMORY;
1232
1233   /* newly created event object is in the unsignaled state */
1234   vn_feedback_set_status(slot, VK_EVENT_RESET);
1235
1236   ev->feedback_slot = slot;
1237
1238   return VK_SUCCESS;
1239}
1240
1241static inline void
1242vn_event_feedback_fini(struct vn_device *dev, struct vn_event *ev)
1243{
1244   if (ev->feedback_slot)
1245      vn_feedback_pool_free(&dev->feedback_pool, ev->feedback_slot);
1246}
1247
1248VkResult
1249vn_CreateEvent(VkDevice device,
1250               const VkEventCreateInfo *pCreateInfo,
1251               const VkAllocationCallbacks *pAllocator,
1252               VkEvent *pEvent)
1253{
1254   VN_TRACE_FUNC();
1255   struct vn_device *dev = vn_device_from_handle(device);
1256   const VkAllocationCallbacks *alloc =
1257      pAllocator ? pAllocator : &dev->base.base.alloc;
1258
1259   struct vn_event *ev = vk_zalloc(alloc, sizeof(*ev), VN_DEFAULT_ALIGN,
1260                                   VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
1261   if (!ev)
1262      return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
1263
1264   vn_object_base_init(&ev->base, VK_OBJECT_TYPE_EVENT, &dev->base);
1265
1266   /* feedback is only needed to speed up host operations */
1267   if (!(pCreateInfo->flags & VK_EVENT_CREATE_DEVICE_ONLY_BIT)) {
1268      VkResult result = vn_event_feedback_init(dev, ev);
1269      if (result != VK_SUCCESS)
1270         return vn_error(dev->instance, result);
1271   }
1272
1273   VkEvent ev_handle = vn_event_to_handle(ev);
1274   vn_async_vkCreateEvent(dev->instance, device, pCreateInfo, NULL,
1275                          &ev_handle);
1276
1277   *pEvent = ev_handle;
1278
1279   return VK_SUCCESS;
1280}
1281
1282void
1283vn_DestroyEvent(VkDevice device,
1284                VkEvent event,
1285                const VkAllocationCallbacks *pAllocator)
1286{
1287   VN_TRACE_FUNC();
1288   struct vn_device *dev = vn_device_from_handle(device);
1289   struct vn_event *ev = vn_event_from_handle(event);
1290   const VkAllocationCallbacks *alloc =
1291      pAllocator ? pAllocator : &dev->base.base.alloc;
1292
1293   if (!ev)
1294      return;
1295
1296   vn_async_vkDestroyEvent(dev->instance, device, event, NULL);
1297
1298   vn_event_feedback_fini(dev, ev);
1299
1300   vn_object_base_fini(&ev->base);
1301   vk_free(alloc, ev);
1302}
1303
1304VkResult
1305vn_GetEventStatus(VkDevice device, VkEvent event)
1306{
1307   VN_TRACE_FUNC();
1308   struct vn_device *dev = vn_device_from_handle(device);
1309   struct vn_event *ev = vn_event_from_handle(event);
1310   VkResult result;
1311
1312   if (ev->feedback_slot)
1313      result = vn_feedback_get_status(ev->feedback_slot);
1314   else
1315      result = vn_call_vkGetEventStatus(dev->instance, device, event);
1316
1317   return vn_result(dev->instance, result);
1318}
1319
1320VkResult
1321vn_SetEvent(VkDevice device, VkEvent event)
1322{
1323   VN_TRACE_FUNC();
1324   struct vn_device *dev = vn_device_from_handle(device);
1325   struct vn_event *ev = vn_event_from_handle(event);
1326
1327   if (ev->feedback_slot) {
1328      vn_feedback_set_status(ev->feedback_slot, VK_EVENT_SET);
1329      vn_async_vkSetEvent(dev->instance, device, event);
1330   } else {
1331      VkResult result = vn_call_vkSetEvent(dev->instance, device, event);
1332      if (result != VK_SUCCESS)
1333         return vn_error(dev->instance, result);
1334   }
1335
1336   return VK_SUCCESS;
1337}
1338
1339VkResult
1340vn_ResetEvent(VkDevice device, VkEvent event)
1341{
1342   VN_TRACE_FUNC();
1343   struct vn_device *dev = vn_device_from_handle(device);
1344   struct vn_event *ev = vn_event_from_handle(event);
1345
1346   if (ev->feedback_slot) {
1347      vn_feedback_reset_status(ev->feedback_slot);
1348      vn_async_vkResetEvent(dev->instance, device, event);
1349   } else {
1350      VkResult result = vn_call_vkResetEvent(dev->instance, device, event);
1351      if (result != VK_SUCCESS)
1352         return vn_error(dev->instance, result);
1353   }
1354
1355   return VK_SUCCESS;
1356}
1357