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_semaphore.h"
25
26#include "util/os_time.h"
27
28#ifndef _WIN32
29#include <unistd.h>
30#endif
31
32#include "vk_common_entrypoints.h"
33#include "vk_device.h"
34#include "vk_log.h"
35#include "vk_physical_device.h"
36#include "vk_util.h"
37
38static VkExternalSemaphoreHandleTypeFlags
39vk_sync_semaphore_import_types(const struct vk_sync_type *type,
40                               VkSemaphoreType semaphore_type)
41{
42   VkExternalSemaphoreHandleTypeFlags handle_types = 0;
43
44   if (type->import_opaque_fd)
45      handle_types |= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
46
47   if (type->export_sync_file && semaphore_type == VK_SEMAPHORE_TYPE_BINARY)
48      handle_types |= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
49
50   return handle_types;
51}
52
53static VkExternalSemaphoreHandleTypeFlags
54vk_sync_semaphore_export_types(const struct vk_sync_type *type,
55                               VkSemaphoreType semaphore_type)
56{
57   VkExternalSemaphoreHandleTypeFlags handle_types = 0;
58
59   if (type->export_opaque_fd)
60      handle_types |= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
61
62   if (type->export_sync_file && semaphore_type == VK_SEMAPHORE_TYPE_BINARY)
63      handle_types |= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
64
65   return handle_types;
66}
67
68static VkExternalSemaphoreHandleTypeFlags
69vk_sync_semaphore_handle_types(const struct vk_sync_type *type,
70                               VkSemaphoreType semaphore_type)
71{
72   return vk_sync_semaphore_export_types(type, semaphore_type) &
73          vk_sync_semaphore_import_types(type, semaphore_type);
74}
75
76static const struct vk_sync_type *
77get_semaphore_sync_type(struct vk_physical_device *pdevice,
78                        VkSemaphoreType semaphore_type,
79                        VkExternalSemaphoreHandleTypeFlags handle_types)
80{
81   assert(semaphore_type == VK_SEMAPHORE_TYPE_BINARY ||
82          semaphore_type == VK_SEMAPHORE_TYPE_TIMELINE);
83
84   enum vk_sync_features req_features = VK_SYNC_FEATURE_GPU_WAIT;
85   if (semaphore_type == VK_SEMAPHORE_TYPE_TIMELINE) {
86      req_features |= VK_SYNC_FEATURE_TIMELINE |
87                      VK_SYNC_FEATURE_CPU_WAIT;
88   } else {
89      req_features |= VK_SYNC_FEATURE_BINARY;
90   }
91
92   for (const struct vk_sync_type *const *t =
93        pdevice->supported_sync_types; *t; t++) {
94      if (req_features & ~(*t)->features)
95         continue;
96
97      if (handle_types & ~vk_sync_semaphore_handle_types(*t, semaphore_type))
98         continue;
99
100      return *t;
101   }
102
103   return NULL;
104}
105
106static VkSemaphoreType
107get_semaphore_type(const void *pNext, uint64_t *initial_value)
108{
109   const VkSemaphoreTypeCreateInfo *type_info =
110      vk_find_struct_const(pNext, SEMAPHORE_TYPE_CREATE_INFO);
111
112   if (!type_info)
113      return VK_SEMAPHORE_TYPE_BINARY;
114
115   if (initial_value)
116      *initial_value = type_info->initialValue;
117   return type_info->semaphoreType;
118}
119
120VKAPI_ATTR VkResult VKAPI_CALL
121vk_common_CreateSemaphore(VkDevice _device,
122                          const VkSemaphoreCreateInfo *pCreateInfo,
123                          const VkAllocationCallbacks *pAllocator,
124                          VkSemaphore *pSemaphore)
125{
126   VK_FROM_HANDLE(vk_device, device, _device);
127   struct vk_semaphore *semaphore;
128
129   assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO);
130
131   uint64_t initial_value = 0;
132   const VkSemaphoreType semaphore_type =
133      get_semaphore_type(pCreateInfo->pNext, &initial_value);
134
135   if (semaphore_type == VK_SEMAPHORE_TYPE_TIMELINE)
136      assert(device->timeline_mode != VK_DEVICE_TIMELINE_MODE_NONE);
137
138   const VkExportSemaphoreCreateInfo *export =
139      vk_find_struct_const(pCreateInfo->pNext, EXPORT_SEMAPHORE_CREATE_INFO);
140   VkExternalSemaphoreHandleTypeFlags handle_types =
141      export ? export->handleTypes : 0;
142
143   const struct vk_sync_type *sync_type =
144      get_semaphore_sync_type(device->physical, semaphore_type, handle_types);
145   if (sync_type == NULL) {
146      /* We should always be able to get a semaphore type for internal */
147      assert(get_semaphore_sync_type(device->physical, semaphore_type, 0) != NULL);
148      return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
149                       "Combination of external handle types is unsupported "
150                       "for VkSemaphore creation.");
151   }
152
153   /* If the timeline mode is ASSISTED, then any permanent binary semaphore
154    * types need to be able to support move.  We don't require this for
155    * temporary unless that temporary is also used as a semaphore signal
156    * operation which is much trickier to assert early.
157    */
158   if (semaphore_type == VK_SEMAPHORE_TYPE_BINARY &&
159       vk_device_supports_threaded_submit(device))
160      assert(sync_type->move);
161
162   /* Allocate a vk_semaphore + vk_sync implementation. Because the permanent
163    * field of vk_semaphore is the base field of the vk_sync implementation,
164    * we can make the 2 structures overlap.
165    */
166   size_t size = offsetof(struct vk_semaphore, permanent) + sync_type->size;
167   semaphore = vk_object_zalloc(device, pAllocator, size,
168                                VK_OBJECT_TYPE_SEMAPHORE);
169   if (semaphore == NULL)
170      return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
171
172   semaphore->type = semaphore_type;
173
174   enum vk_sync_flags sync_flags = 0;
175   if (semaphore_type == VK_SEMAPHORE_TYPE_TIMELINE)
176      sync_flags |= VK_SYNC_IS_TIMELINE;
177   if (handle_types)
178      sync_flags |= VK_SYNC_IS_SHAREABLE;
179
180   VkResult result = vk_sync_init(device, &semaphore->permanent,
181                                  sync_type, sync_flags, initial_value);
182   if (result != VK_SUCCESS) {
183      vk_object_free(device, pAllocator, semaphore);
184      return result;
185   }
186
187   *pSemaphore = vk_semaphore_to_handle(semaphore);
188
189   return VK_SUCCESS;
190}
191
192void
193vk_semaphore_reset_temporary(struct vk_device *device,
194                             struct vk_semaphore *semaphore)
195{
196   if (semaphore->temporary == NULL)
197      return;
198
199   vk_sync_destroy(device, semaphore->temporary);
200   semaphore->temporary = NULL;
201}
202
203VKAPI_ATTR void VKAPI_CALL
204vk_common_DestroySemaphore(VkDevice _device,
205                           VkSemaphore _semaphore,
206                           const VkAllocationCallbacks *pAllocator)
207{
208   VK_FROM_HANDLE(vk_device, device, _device);
209   VK_FROM_HANDLE(vk_semaphore, semaphore, _semaphore);
210
211   if (semaphore == NULL)
212      return;
213
214   vk_semaphore_reset_temporary(device, semaphore);
215   vk_sync_finish(device, &semaphore->permanent);
216
217   vk_object_free(device, pAllocator, semaphore);
218}
219
220VKAPI_ATTR void VKAPI_CALL
221vk_common_GetPhysicalDeviceExternalSemaphoreProperties(
222   VkPhysicalDevice physicalDevice,
223   const VkPhysicalDeviceExternalSemaphoreInfo *pExternalSemaphoreInfo,
224   VkExternalSemaphoreProperties *pExternalSemaphoreProperties)
225{
226   VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice);
227
228   assert(pExternalSemaphoreInfo->sType ==
229          VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO);
230   const VkExternalSemaphoreHandleTypeFlagBits handle_type =
231      pExternalSemaphoreInfo->handleType;
232
233   const VkSemaphoreType semaphore_type =
234      get_semaphore_type(pExternalSemaphoreInfo->pNext, NULL);
235
236   const struct vk_sync_type *sync_type =
237      get_semaphore_sync_type(pdevice, semaphore_type, handle_type);
238   if (sync_type == NULL) {
239      pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0;
240      pExternalSemaphoreProperties->compatibleHandleTypes = 0;
241      pExternalSemaphoreProperties->externalSemaphoreFeatures = 0;
242      return;
243   }
244
245   VkExternalSemaphoreHandleTypeFlagBits import =
246      vk_sync_semaphore_import_types(sync_type, semaphore_type);
247   VkExternalSemaphoreHandleTypeFlagBits export =
248      vk_sync_semaphore_export_types(sync_type, semaphore_type);
249
250   if (handle_type != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT) {
251      const struct vk_sync_type *opaque_sync_type =
252         get_semaphore_sync_type(pdevice, semaphore_type,
253                                 VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT);
254
255      /* If we're a different vk_sync_type than the one selected when only
256       * OPAQUE_FD is set, then we can't import/export OPAQUE_FD.  Put
257       * differently, there can only be one OPAQUE_FD sync type.
258       */
259      if (sync_type != opaque_sync_type) {
260         import &= ~VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
261         export &= ~VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
262      }
263   }
264
265   VkExternalSemaphoreHandleTypeFlags compatible = import & export;
266   VkExternalSemaphoreFeatureFlags features = 0;
267   if (handle_type & export)
268      features |= VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT;
269   if (handle_type & import)
270      features |= VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT;
271
272   pExternalSemaphoreProperties->exportFromImportedHandleTypes = export;
273   pExternalSemaphoreProperties->compatibleHandleTypes = compatible;
274   pExternalSemaphoreProperties->externalSemaphoreFeatures = features;
275}
276
277VKAPI_ATTR VkResult VKAPI_CALL
278vk_common_GetSemaphoreCounterValue(VkDevice _device,
279                                   VkSemaphore _semaphore,
280                                   uint64_t *pValue)
281{
282   VK_FROM_HANDLE(vk_device, device, _device);
283   VK_FROM_HANDLE(vk_semaphore, semaphore, _semaphore);
284
285   if (vk_device_is_lost(device))
286      return VK_ERROR_DEVICE_LOST;
287
288   struct vk_sync *sync = vk_semaphore_get_active_sync(semaphore);
289   return vk_sync_get_value(device, sync, pValue);
290}
291
292VKAPI_ATTR VkResult VKAPI_CALL
293vk_common_WaitSemaphores(VkDevice _device,
294                         const VkSemaphoreWaitInfo *pWaitInfo,
295                         uint64_t timeout)
296{
297   VK_FROM_HANDLE(vk_device, device, _device);
298
299   if (vk_device_is_lost(device))
300      return VK_ERROR_DEVICE_LOST;
301
302   if (pWaitInfo->semaphoreCount == 0)
303      return VK_SUCCESS;
304
305   uint64_t abs_timeout_ns = os_time_get_absolute_timeout(timeout);
306
307   const uint32_t wait_count = pWaitInfo->semaphoreCount;
308   STACK_ARRAY(struct vk_sync_wait, waits, pWaitInfo->semaphoreCount);
309
310   for (uint32_t i = 0; i < wait_count; i++) {
311      VK_FROM_HANDLE(vk_semaphore, semaphore, pWaitInfo->pSemaphores[i]);
312      assert(semaphore->type == VK_SEMAPHORE_TYPE_TIMELINE);
313
314      waits[i] = (struct vk_sync_wait) {
315         .sync = vk_semaphore_get_active_sync(semaphore),
316         .stage_mask = ~(VkPipelineStageFlags2)0,
317         .wait_value = pWaitInfo->pValues[i],
318      };
319   }
320
321   enum vk_sync_wait_flags wait_flags = VK_SYNC_WAIT_COMPLETE;
322   if (pWaitInfo->flags & VK_SEMAPHORE_WAIT_ANY_BIT)
323      wait_flags |= VK_SYNC_WAIT_ANY;
324
325   VkResult result = vk_sync_wait_many(device, wait_count, waits,
326                                       wait_flags, abs_timeout_ns);
327
328   STACK_ARRAY_FINISH(waits);
329
330   VkResult device_status = vk_device_check_status(device);
331   if (device_status != VK_SUCCESS)
332      return device_status;
333
334   return result;
335}
336
337VKAPI_ATTR VkResult VKAPI_CALL
338vk_common_SignalSemaphore(VkDevice _device,
339                          const VkSemaphoreSignalInfo *pSignalInfo)
340{
341   VK_FROM_HANDLE(vk_device, device, _device);
342   VK_FROM_HANDLE(vk_semaphore, semaphore, pSignalInfo->semaphore);
343   struct vk_sync *sync = vk_semaphore_get_active_sync(semaphore);
344   VkResult result;
345
346   /* From the Vulkan 1.2.194 spec:
347    *
348    *    UID-VkSemaphoreSignalInfo-semaphore-03257
349    *
350    *    "semaphore must have been created with a VkSemaphoreType of
351    *    VK_SEMAPHORE_TYPE_TIMELINE."
352    */
353   assert(semaphore->type == VK_SEMAPHORE_TYPE_TIMELINE);
354
355   /* From the Vulkan 1.2.194 spec:
356    *
357    *    VUID-VkSemaphoreSignalInfo-value-03258
358    *
359    *    "value must have a value greater than the current value of the
360    *    semaphore"
361    *
362    * Since 0 is the lowest possible semaphore timeline value, we can assert
363    * that a non-zero signal value is provided.
364    */
365   if (unlikely(pSignalInfo->value == 0)) {
366      return vk_device_set_lost(device,
367         "Tried to signal a timeline with value 0");
368   }
369
370   result = vk_sync_signal(device, sync, pSignalInfo->value);
371   if (unlikely(result != VK_SUCCESS))
372      return result;
373
374   if (device->submit_mode == VK_QUEUE_SUBMIT_MODE_DEFERRED) {
375      result = vk_device_flush(device);
376      if (unlikely(result != VK_SUCCESS))
377         return result;
378   }
379
380   return VK_SUCCESS;
381}
382
383#ifndef _WIN32
384
385VKAPI_ATTR VkResult VKAPI_CALL
386vk_common_ImportSemaphoreFdKHR(VkDevice _device,
387                               const VkImportSemaphoreFdInfoKHR *pImportSemaphoreFdInfo)
388{
389   VK_FROM_HANDLE(vk_device, device, _device);
390   VK_FROM_HANDLE(vk_semaphore, semaphore, pImportSemaphoreFdInfo->semaphore);
391
392   assert(pImportSemaphoreFdInfo->sType ==
393          VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR);
394
395   const int fd = pImportSemaphoreFdInfo->fd;
396   const VkExternalSemaphoreHandleTypeFlagBits handle_type =
397      pImportSemaphoreFdInfo->handleType;
398
399   struct vk_sync *temporary = NULL, *sync;
400   if (pImportSemaphoreFdInfo->flags & VK_SEMAPHORE_IMPORT_TEMPORARY_BIT) {
401      /* From the Vulkan 1.2.194 spec:
402       *
403       *    VUID-VkImportSemaphoreFdInfoKHR-flags-03323
404       *
405       *    "If flags contains VK_SEMAPHORE_IMPORT_TEMPORARY_BIT, the
406       *    VkSemaphoreTypeCreateInfo::semaphoreType field of the semaphore
407       *    from which handle or name was exported must not be
408       *    VK_SEMAPHORE_TYPE_TIMELINE"
409       */
410      if (unlikely(semaphore->type == VK_SEMAPHORE_TYPE_TIMELINE)) {
411         return vk_errorf(device, VK_ERROR_UNKNOWN,
412                          "Cannot temporarily import into a timeline "
413                          "semaphore");
414      }
415
416      const struct vk_sync_type *sync_type =
417         get_semaphore_sync_type(device->physical, semaphore->type, handle_type);
418
419      VkResult result = vk_sync_create(device, sync_type, 0 /* flags */,
420                                       0 /* initial_value */, &temporary);
421      if (result != VK_SUCCESS)
422         return result;
423
424      sync = temporary;
425   } else {
426      sync = &semaphore->permanent;
427   }
428   assert(handle_type &
429          vk_sync_semaphore_handle_types(sync->type, semaphore->type));
430
431   VkResult result;
432   switch (pImportSemaphoreFdInfo->handleType) {
433   case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT:
434      result = vk_sync_import_opaque_fd(device, sync, fd);
435      break;
436
437   case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT:
438      result = vk_sync_import_sync_file(device, sync, fd);
439      break;
440
441   default:
442      result = vk_error(semaphore, VK_ERROR_INVALID_EXTERNAL_HANDLE);
443   }
444
445   if (result != VK_SUCCESS) {
446      if (temporary != NULL)
447         vk_sync_destroy(device, temporary);
448      return result;
449   }
450
451   /* From the Vulkan 1.2.194 spec:
452    *
453    *    "Importing a semaphore payload from a file descriptor transfers
454    *    ownership of the file descriptor from the application to the Vulkan
455    *    implementation. The application must not perform any operations on
456    *    the file descriptor after a successful import."
457    *
458    * If the import fails, we leave the file descriptor open.
459    */
460   if (fd != -1)
461      close(fd);
462
463   /* From a spec correctness point of view, we could probably replace the
464    * semaphore's temporary payload with the new vk_sync at the top.  However,
465    * we choose to be nice to applications and only replace the semaphore if
466    * the import succeeded.
467    */
468   if (temporary) {
469      vk_semaphore_reset_temporary(device, semaphore);
470      semaphore->temporary = temporary;
471   }
472
473   return VK_SUCCESS;
474}
475
476VKAPI_ATTR VkResult VKAPI_CALL
477vk_common_GetSemaphoreFdKHR(VkDevice _device,
478                            const VkSemaphoreGetFdInfoKHR *pGetFdInfo,
479                            int *pFd)
480{
481   VK_FROM_HANDLE(vk_device, device, _device);
482   VK_FROM_HANDLE(vk_semaphore, semaphore, pGetFdInfo->semaphore);
483
484   assert(pGetFdInfo->sType == VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR);
485
486   struct vk_sync *sync = vk_semaphore_get_active_sync(semaphore);
487
488   VkResult result;
489   switch (pGetFdInfo->handleType) {
490   case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT:
491      result = vk_sync_export_opaque_fd(device, sync, pFd);
492      if (result != VK_SUCCESS)
493         return result;
494      break;
495
496   case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT:
497      /* From the Vulkan 1.2.194 spec:
498       *
499       *    VUID-VkSemaphoreGetFdInfoKHR-handleType-03253
500       *
501       *    "If handleType refers to a handle type with copy payload
502       *    transference semantics, semaphore must have been created with a
503       *    VkSemaphoreType of VK_SEMAPHORE_TYPE_BINARY."
504       */
505      if (unlikely(semaphore->type != VK_SEMAPHORE_TYPE_BINARY)) {
506         return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
507                          "Cannot export a timeline semaphore as SYNC_FD");
508      }
509
510      /* From the Vulkan 1.2.194 spec:
511       *    VUID-VkSemaphoreGetFdInfoKHR-handleType-03254
512       *
513       *    "If handleType refers to a handle type with copy payload
514       *    transference semantics, semaphore must have an associated
515       *    semaphore signal operation that has been submitted for execution
516       *    and any semaphore signal operations on which it depends (if any)
517       *    must have also been submitted for execution."
518       *
519       * If we have real timelines, it's possible that the time point doesn't
520       * exist yet and is waiting for one of our submit threads to trigger.
521       * However, thanks to the above bit of spec text, that wait should never
522       * block for long.
523       */
524      if (vk_device_supports_threaded_submit(device)) {
525         result = vk_sync_wait(device, sync, 0,
526                               VK_SYNC_WAIT_PENDING,
527                               UINT64_MAX);
528         if (unlikely(result != VK_SUCCESS))
529            return result;
530      }
531
532      result = vk_sync_export_sync_file(device, sync, pFd);
533      if (unlikely(result != VK_SUCCESS))
534         return result;
535
536      /* From the Vulkan 1.2.194 spec:
537       *
538       *    "Export operations have the same transference as the specified
539       *    handle type’s import operations. Additionally, exporting a
540       *    semaphore payload to a handle with copy transference has the same
541       *    side effects on the source semaphore’s payload as executing a
542       *    semaphore wait operation."
543       *
544       * In other words, exporting a sync file also resets the semaphore.  We
545       * only care about this for the permanent payload because the temporary
546       * payload will be destroyed below.
547       */
548      if (sync == &semaphore->permanent) {
549         result = vk_sync_reset(device, sync);
550         if (unlikely(result != VK_SUCCESS))
551            return result;
552      }
553      break;
554
555   default:
556      unreachable("Invalid semaphore export handle type");
557   }
558
559   /* From the Vulkan 1.2.194 spec:
560    *
561    *    "Export operations have the same transference as the specified
562    *    handle type’s import operations. [...] If the semaphore was using
563    *    a temporarily imported payload, the semaphore’s prior permanent
564    *    payload will be restored."
565    */
566   vk_semaphore_reset_temporary(device, semaphore);
567
568   return VK_SUCCESS;
569}
570
571#endif /* !defined(_WIN32) */
572