• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_fence.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 
38 static VkExternalFenceHandleTypeFlags
vk_sync_fence_import_types(const struct vk_sync_type * type)39 vk_sync_fence_import_types(const struct vk_sync_type *type)
40 {
41    VkExternalFenceHandleTypeFlags handle_types = 0;
42 
43    if (type->import_opaque_fd)
44       handle_types |= VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT;
45 
46    if (type->import_sync_file)
47       handle_types |= VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
48 
49    return handle_types;
50 }
51 
52 static VkExternalFenceHandleTypeFlags
vk_sync_fence_export_types(const struct vk_sync_type * type)53 vk_sync_fence_export_types(const struct vk_sync_type *type)
54 {
55    VkExternalFenceHandleTypeFlags handle_types = 0;
56 
57    if (type->export_opaque_fd)
58       handle_types |= VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT;
59 
60    if (type->export_sync_file)
61       handle_types |= VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
62 
63    return handle_types;
64 }
65 
66 static VkExternalFenceHandleTypeFlags
vk_sync_fence_handle_types(const struct vk_sync_type * type)67 vk_sync_fence_handle_types(const struct vk_sync_type *type)
68 {
69    return vk_sync_fence_export_types(type) &
70           vk_sync_fence_import_types(type);
71 }
72 
73 static const struct vk_sync_type *
get_fence_sync_type(struct vk_physical_device * pdevice,VkExternalFenceHandleTypeFlags handle_types)74 get_fence_sync_type(struct vk_physical_device *pdevice,
75                     VkExternalFenceHandleTypeFlags handle_types)
76 {
77    static const enum vk_sync_features req_features =
78       VK_SYNC_FEATURE_BINARY |
79       VK_SYNC_FEATURE_CPU_WAIT |
80       VK_SYNC_FEATURE_CPU_RESET;
81 
82    for (const struct vk_sync_type *const *t =
83         pdevice->supported_sync_types; *t; t++) {
84       if (req_features & ~(*t)->features)
85          continue;
86 
87       if (handle_types & ~vk_sync_fence_handle_types(*t))
88          continue;
89 
90       return *t;
91    }
92 
93    return NULL;
94 }
95 
96 VkResult
vk_fence_create(struct vk_device * device,const VkFenceCreateInfo * pCreateInfo,const VkAllocationCallbacks * pAllocator,struct vk_fence ** fence_out)97 vk_fence_create(struct vk_device *device,
98                 const VkFenceCreateInfo *pCreateInfo,
99                 const VkAllocationCallbacks *pAllocator,
100                 struct vk_fence **fence_out)
101 {
102    struct vk_fence *fence;
103 
104    assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_FENCE_CREATE_INFO);
105 
106    const VkExportFenceCreateInfo *export =
107       vk_find_struct_const(pCreateInfo->pNext, EXPORT_FENCE_CREATE_INFO);
108    VkExternalFenceHandleTypeFlags handle_types =
109       export ? export->handleTypes : 0;
110 
111    const struct vk_sync_type *sync_type =
112       get_fence_sync_type(device->physical, handle_types);
113    if (sync_type == NULL) {
114       /* We should always be able to get a fence type for internal */
115       assert(get_fence_sync_type(device->physical, 0) != NULL);
116       return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE,
117                        "Combination of external handle types is unsupported "
118                        "for VkFence creation.");
119    }
120 
121    /* Allocate a vk_fence + vk_sync implementation. Because the permanent
122     * field of vk_fence is the base field of the vk_sync implementation, we
123     * can make the 2 structures overlap.
124     */
125    size_t size = offsetof(struct vk_fence, permanent) + sync_type->size;
126    fence = vk_object_zalloc(device, pAllocator, size, VK_OBJECT_TYPE_FENCE);
127    if (fence == NULL)
128       return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
129 
130    enum vk_sync_flags sync_flags = 0;
131    if (handle_types)
132       sync_flags |= VK_SYNC_IS_SHAREABLE;
133 
134    bool signaled = pCreateInfo->flags & VK_FENCE_CREATE_SIGNALED_BIT;
135    VkResult result = vk_sync_init(device, &fence->permanent,
136                                   sync_type, sync_flags, signaled);
137    if (result != VK_SUCCESS) {
138       vk_object_free(device, pAllocator, fence);
139       return result;
140    }
141 
142    *fence_out = fence;
143 
144    return VK_SUCCESS;
145 }
146 
147 VKAPI_ATTR VkResult VKAPI_CALL
vk_common_CreateFence(VkDevice _device,const VkFenceCreateInfo * pCreateInfo,const VkAllocationCallbacks * pAllocator,VkFence * pFence)148 vk_common_CreateFence(VkDevice _device,
149                       const VkFenceCreateInfo *pCreateInfo,
150                       const VkAllocationCallbacks *pAllocator,
151                       VkFence *pFence)
152 {
153    VK_FROM_HANDLE(vk_device, device, _device);
154    struct vk_fence *fence;
155 
156    VkResult result = vk_fence_create(device, pCreateInfo, pAllocator, &fence);
157    if (result != VK_SUCCESS)
158       return result;
159 
160    *pFence = vk_fence_to_handle(fence);
161 
162    return VK_SUCCESS;
163 }
164 
165 void
vk_fence_reset_temporary(struct vk_device * device,struct vk_fence * fence)166 vk_fence_reset_temporary(struct vk_device *device,
167                          struct vk_fence *fence)
168 {
169    if (fence->temporary == NULL)
170       return;
171 
172    vk_sync_destroy(device, fence->temporary);
173    fence->temporary = NULL;
174 }
175 
176 void
vk_fence_destroy(struct vk_device * device,struct vk_fence * fence,const VkAllocationCallbacks * pAllocator)177 vk_fence_destroy(struct vk_device *device,
178                  struct vk_fence *fence,
179                  const VkAllocationCallbacks *pAllocator)
180 {
181    vk_fence_reset_temporary(device, fence);
182    vk_sync_finish(device, &fence->permanent);
183 
184    vk_object_free(device, pAllocator, fence);
185 }
186 
187 VKAPI_ATTR void VKAPI_CALL
vk_common_DestroyFence(VkDevice _device,VkFence _fence,const VkAllocationCallbacks * pAllocator)188 vk_common_DestroyFence(VkDevice _device,
189                        VkFence _fence,
190                        const VkAllocationCallbacks *pAllocator)
191 {
192    VK_FROM_HANDLE(vk_device, device, _device);
193    VK_FROM_HANDLE(vk_fence, fence, _fence);
194 
195    if (fence == NULL)
196       return;
197 
198    vk_fence_destroy(device, fence, pAllocator);
199 }
200 
201 VKAPI_ATTR VkResult VKAPI_CALL
vk_common_ResetFences(VkDevice _device,uint32_t fenceCount,const VkFence * pFences)202 vk_common_ResetFences(VkDevice _device,
203                       uint32_t fenceCount,
204                       const VkFence *pFences)
205 {
206    VK_FROM_HANDLE(vk_device, device, _device);
207 
208    for (uint32_t i = 0; i < fenceCount; i++) {
209       VK_FROM_HANDLE(vk_fence, fence, pFences[i]);
210 
211       /* From the Vulkan 1.2.194 spec:
212        *
213        *    "If any member of pFences currently has its payload imported with
214        *    temporary permanence, that fence’s prior permanent payload is
215        *    first restored. The remaining operations described therefore
216        *    operate on the restored payload."
217        */
218       vk_fence_reset_temporary(device, fence);
219 
220       VkResult result = vk_sync_reset(device, &fence->permanent);
221       if (result != VK_SUCCESS)
222          return result;
223    }
224 
225    return VK_SUCCESS;
226 }
227 
228 VKAPI_ATTR VkResult VKAPI_CALL
vk_common_GetFenceStatus(VkDevice _device,VkFence _fence)229 vk_common_GetFenceStatus(VkDevice _device,
230                          VkFence _fence)
231 {
232    VK_FROM_HANDLE(vk_device, device, _device);
233    VK_FROM_HANDLE(vk_fence, fence, _fence);
234 
235    if (vk_device_is_lost(device))
236       return VK_ERROR_DEVICE_LOST;
237 
238    VkResult result = vk_sync_wait(device, vk_fence_get_active_sync(fence),
239                                   0 /* wait_value */,
240                                   VK_SYNC_WAIT_COMPLETE,
241                                   0 /* abs_timeout_ns */);
242    if (result == VK_TIMEOUT)
243       return VK_NOT_READY;
244    else
245       return result;
246 }
247 
248 VKAPI_ATTR VkResult VKAPI_CALL
vk_common_WaitForFences(VkDevice _device,uint32_t fenceCount,const VkFence * pFences,VkBool32 waitAll,uint64_t timeout)249 vk_common_WaitForFences(VkDevice _device,
250                         uint32_t fenceCount,
251                         const VkFence *pFences,
252                         VkBool32 waitAll,
253                         uint64_t timeout)
254 {
255    VK_FROM_HANDLE(vk_device, device, _device);
256 
257    if (vk_device_is_lost(device))
258       return VK_ERROR_DEVICE_LOST;
259 
260    if (fenceCount == 0)
261       return VK_SUCCESS;
262 
263    uint64_t abs_timeout_ns = os_time_get_absolute_timeout(timeout);
264 
265    STACK_ARRAY(struct vk_sync_wait, waits, fenceCount);
266 
267    for (uint32_t i = 0; i < fenceCount; i++) {
268       VK_FROM_HANDLE(vk_fence, fence, pFences[i]);
269       waits[i] = (struct vk_sync_wait) {
270          .sync = vk_fence_get_active_sync(fence),
271          .stage_mask = ~(VkPipelineStageFlags2)0,
272       };
273    }
274 
275    enum vk_sync_wait_flags wait_flags = VK_SYNC_WAIT_COMPLETE;
276    if (!waitAll)
277       wait_flags |= VK_SYNC_WAIT_ANY;
278 
279    VkResult result = vk_sync_wait_many(device, fenceCount, waits,
280                                        wait_flags, abs_timeout_ns);
281 
282    STACK_ARRAY_FINISH(waits);
283 
284    VkResult device_status = vk_device_check_status(device);
285    if (device_status != VK_SUCCESS)
286       return device_status;
287 
288    return result;
289 }
290 
291 VKAPI_ATTR void VKAPI_CALL
vk_common_GetPhysicalDeviceExternalFenceProperties(VkPhysicalDevice physicalDevice,const VkPhysicalDeviceExternalFenceInfo * pExternalFenceInfo,VkExternalFenceProperties * pExternalFenceProperties)292 vk_common_GetPhysicalDeviceExternalFenceProperties(
293    VkPhysicalDevice physicalDevice,
294    const VkPhysicalDeviceExternalFenceInfo *pExternalFenceInfo,
295    VkExternalFenceProperties *pExternalFenceProperties)
296 {
297    VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice);
298 
299    assert(pExternalFenceInfo->sType ==
300           VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO);
301    const VkExternalFenceHandleTypeFlagBits handle_type =
302       pExternalFenceInfo->handleType;
303 
304    const struct vk_sync_type *sync_type =
305       get_fence_sync_type(pdevice, handle_type);
306    if (sync_type == NULL) {
307       pExternalFenceProperties->exportFromImportedHandleTypes = 0;
308       pExternalFenceProperties->compatibleHandleTypes = 0;
309       pExternalFenceProperties->externalFenceFeatures = 0;
310       return;
311    }
312 
313    VkExternalFenceHandleTypeFlagBits import =
314       vk_sync_fence_import_types(sync_type);
315    VkExternalFenceHandleTypeFlagBits export =
316       vk_sync_fence_export_types(sync_type);
317 
318    if (handle_type != VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT) {
319       const struct vk_sync_type *opaque_sync_type =
320          get_fence_sync_type(pdevice, VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT);
321 
322       /* If we're a different vk_sync_type than the one selected when only
323        * OPAQUE_FD is set, then we can't import/export OPAQUE_FD.  Put
324        * differently, there can only be one OPAQUE_FD sync type.
325        */
326       if (sync_type != opaque_sync_type) {
327          import &= ~VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT;
328          export &= ~VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT;
329       }
330    }
331 
332    VkExternalFenceHandleTypeFlags compatible = import & export;
333    VkExternalFenceFeatureFlags features = 0;
334    if (handle_type & export)
335       features |= VK_EXTERNAL_FENCE_FEATURE_EXPORTABLE_BIT;
336    if (handle_type & import)
337       features |= VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT;
338 
339    pExternalFenceProperties->exportFromImportedHandleTypes = export;
340    pExternalFenceProperties->compatibleHandleTypes = compatible;
341    pExternalFenceProperties->externalFenceFeatures = features;
342 }
343 
344 #ifndef _WIN32
345 
346 VKAPI_ATTR VkResult VKAPI_CALL
vk_common_ImportFenceFdKHR(VkDevice _device,const VkImportFenceFdInfoKHR * pImportFenceFdInfo)347 vk_common_ImportFenceFdKHR(VkDevice _device,
348                            const VkImportFenceFdInfoKHR *pImportFenceFdInfo)
349 {
350    VK_FROM_HANDLE(vk_device, device, _device);
351    VK_FROM_HANDLE(vk_fence, fence, pImportFenceFdInfo->fence);
352 
353    assert(pImportFenceFdInfo->sType ==
354           VK_STRUCTURE_TYPE_IMPORT_FENCE_FD_INFO_KHR);
355 
356    const int fd = pImportFenceFdInfo->fd;
357    const VkExternalFenceHandleTypeFlagBits handle_type =
358       pImportFenceFdInfo->handleType;
359 
360    struct vk_sync *temporary = NULL, *sync;
361    if (pImportFenceFdInfo->flags & VK_FENCE_IMPORT_TEMPORARY_BIT) {
362       const struct vk_sync_type *sync_type =
363          get_fence_sync_type(device->physical, handle_type);
364 
365       VkResult result = vk_sync_create(device, sync_type, 0 /* flags */,
366                                        0 /* initial_value */, &temporary);
367       if (result != VK_SUCCESS)
368          return result;
369 
370       sync = temporary;
371    } else {
372       sync = &fence->permanent;
373    }
374    assert(handle_type & vk_sync_fence_handle_types(sync->type));
375 
376    VkResult result;
377    switch (pImportFenceFdInfo->handleType) {
378    case VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT:
379       result = vk_sync_import_opaque_fd(device, sync, fd);
380       break;
381 
382    case VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT:
383       result = vk_sync_import_sync_file(device, sync, fd);
384       break;
385 
386    default:
387       result = vk_error(fence, VK_ERROR_INVALID_EXTERNAL_HANDLE);
388    }
389 
390    if (result != VK_SUCCESS) {
391       if (temporary != NULL)
392          vk_sync_destroy(device, temporary);
393       return result;
394    }
395 
396    /* From the Vulkan 1.2.194 spec:
397     *
398     *    "Importing a fence payload from a file descriptor transfers
399     *    ownership of the file descriptor from the application to the
400     *    Vulkan implementation. The application must not perform any
401     *    operations on the file descriptor after a successful import."
402     *
403     * If the import fails, we leave the file descriptor open.
404     */
405    if (fd != -1)
406       close(fd);
407 
408    if (temporary) {
409       vk_fence_reset_temporary(device, fence);
410       fence->temporary = temporary;
411    }
412 
413    return VK_SUCCESS;
414 }
415 
416 VKAPI_ATTR VkResult VKAPI_CALL
vk_common_GetFenceFdKHR(VkDevice _device,const VkFenceGetFdInfoKHR * pGetFdInfo,int * pFd)417 vk_common_GetFenceFdKHR(VkDevice _device,
418                         const VkFenceGetFdInfoKHR *pGetFdInfo,
419                         int *pFd)
420 {
421    VK_FROM_HANDLE(vk_device, device, _device);
422    VK_FROM_HANDLE(vk_fence, fence, pGetFdInfo->fence);
423 
424    assert(pGetFdInfo->sType == VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR);
425 
426    struct vk_sync *sync = vk_fence_get_active_sync(fence);
427 
428    VkResult result;
429    switch (pGetFdInfo->handleType) {
430    case VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT:
431       result = vk_sync_export_opaque_fd(device, sync, pFd);
432       if (unlikely(result != VK_SUCCESS))
433          return result;
434       break;
435 
436    case VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT:
437       /* There's no direct spec quote for this but the same rules as for
438        * semaphore export apply.  We can't export a sync file from a fence
439        * if the fence event hasn't been submitted to the kernel yet.
440        */
441       if (vk_device_supports_threaded_submit(device)) {
442          result = vk_sync_wait(device, sync, 0,
443                                VK_SYNC_WAIT_PENDING,
444                                UINT64_MAX);
445          if (unlikely(result != VK_SUCCESS))
446             return result;
447       }
448 
449       result = vk_sync_export_sync_file(device, sync, pFd);
450       if (unlikely(result != VK_SUCCESS))
451          return result;
452 
453       /* From the Vulkan 1.2.194 spec:
454        *
455        *    "Export operations have the same transference as the specified
456        *    handle type’s import operations. Additionally, exporting a fence
457        *    payload to a handle with copy transference has the same side
458        *    effects on the source fence’s payload as executing a fence reset
459        *    operation."
460        *
461        * In other words, exporting a sync file also resets the fence.  We
462        * only care about this for the permanent payload because the temporary
463        * payload will be destroyed below.
464        */
465       if (sync == &fence->permanent) {
466          result = vk_sync_reset(device, sync);
467          if (unlikely(result != VK_SUCCESS))
468             return result;
469       }
470       break;
471 
472    default:
473       unreachable("Invalid fence export handle type");
474    }
475 
476    /* From the Vulkan 1.2.194 spec:
477     *
478     *    "Export operations have the same transference as the specified
479     *    handle type’s import operations. [...]  If the fence was using a
480     *    temporarily imported payload, the fence’s prior permanent payload
481     *    will be restored.
482     */
483    vk_fence_reset_temporary(device, fence);
484 
485    return VK_SUCCESS;
486 }
487 
488 #endif /* !defined(_WIN32) */
489