| /* |
| * Copyright © 2021 Intel Corporation |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| * IN THE SOFTWARE. |
| */ |
| |
| #include "vk_fence.h" |
| |
| #include "util/os_time.h" |
| |
| #ifndef _WIN32 |
| #include <unistd.h> |
| #endif |
| |
| #include "vk_common_entrypoints.h" |
| #include "vk_device.h" |
| #include "vk_log.h" |
| #include "vk_physical_device.h" |
| #include "vk_util.h" |
| |
| static VkExternalFenceHandleTypeFlags |
| vk_sync_fence_import_types(const struct vk_sync_type *type) |
| { |
| VkExternalFenceHandleTypeFlags handle_types = 0; |
| |
| if (type->import_opaque_fd) |
| handle_types |= VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT; |
| |
| if (type->import_sync_file) |
| handle_types |= VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT; |
| |
| return handle_types; |
| } |
| |
| static VkExternalFenceHandleTypeFlags |
| vk_sync_fence_export_types(const struct vk_sync_type *type) |
| { |
| VkExternalFenceHandleTypeFlags handle_types = 0; |
| |
| if (type->export_opaque_fd) |
| handle_types |= VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT; |
| |
| if (type->export_sync_file) |
| handle_types |= VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT; |
| |
| return handle_types; |
| } |
| |
| static VkExternalFenceHandleTypeFlags |
| vk_sync_fence_handle_types(const struct vk_sync_type *type) |
| { |
| return vk_sync_fence_export_types(type) & |
| vk_sync_fence_import_types(type); |
| } |
| |
| static const struct vk_sync_type * |
| get_fence_sync_type(struct vk_physical_device *pdevice, |
| VkExternalFenceHandleTypeFlags handle_types) |
| { |
| static const enum vk_sync_features req_features = |
| VK_SYNC_FEATURE_BINARY | |
| VK_SYNC_FEATURE_CPU_WAIT | |
| VK_SYNC_FEATURE_CPU_RESET; |
| |
| for (const struct vk_sync_type *const *t = |
| pdevice->supported_sync_types; *t; t++) { |
| if (req_features & ~(*t)->features) |
| continue; |
| |
| if (handle_types & ~vk_sync_fence_handle_types(*t)) |
| continue; |
| |
| return *t; |
| } |
| |
| return NULL; |
| } |
| |
| VkResult |
| vk_fence_create(struct vk_device *device, |
| const VkFenceCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| struct vk_fence **fence_out) |
| { |
| struct vk_fence *fence; |
| |
| assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_FENCE_CREATE_INFO); |
| |
| const VkExportFenceCreateInfo *export = |
| vk_find_struct_const(pCreateInfo->pNext, EXPORT_FENCE_CREATE_INFO); |
| VkExternalFenceHandleTypeFlags handle_types = |
| export ? export->handleTypes : 0; |
| |
| const struct vk_sync_type *sync_type = |
| get_fence_sync_type(device->physical, handle_types); |
| if (sync_type == NULL) { |
| /* We should always be able to get a fence type for internal */ |
| assert(get_fence_sync_type(device->physical, 0) != NULL); |
| return vk_errorf(device, VK_ERROR_INVALID_EXTERNAL_HANDLE, |
| "Combination of external handle types is unsupported " |
| "for VkFence creation."); |
| } |
| |
| /* Allocate a vk_fence + vk_sync implementation. Because the permanent |
| * field of vk_fence is the base field of the vk_sync implementation, we |
| * can make the 2 structures overlap. |
| */ |
| size_t size = offsetof(struct vk_fence, permanent) + sync_type->size; |
| fence = vk_object_zalloc(device, pAllocator, size, VK_OBJECT_TYPE_FENCE); |
| if (fence == NULL) |
| return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); |
| |
| enum vk_sync_flags sync_flags = 0; |
| if (handle_types) |
| sync_flags |= VK_SYNC_IS_SHAREABLE; |
| |
| bool signaled = pCreateInfo->flags & VK_FENCE_CREATE_SIGNALED_BIT; |
| VkResult result = vk_sync_init(device, &fence->permanent, |
| sync_type, sync_flags, signaled); |
| if (result != VK_SUCCESS) { |
| vk_object_free(device, pAllocator, fence); |
| return result; |
| } |
| |
| *fence_out = fence; |
| |
| return VK_SUCCESS; |
| } |
| |
| VKAPI_ATTR VkResult VKAPI_CALL |
| vk_common_CreateFence(VkDevice _device, |
| const VkFenceCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkFence *pFence) |
| { |
| VK_FROM_HANDLE(vk_device, device, _device); |
| struct vk_fence *fence; |
| |
| VkResult result = vk_fence_create(device, pCreateInfo, pAllocator, &fence); |
| if (result != VK_SUCCESS) |
| return result; |
| |
| *pFence = vk_fence_to_handle(fence); |
| |
| return VK_SUCCESS; |
| } |
| |
| void |
| vk_fence_reset_temporary(struct vk_device *device, |
| struct vk_fence *fence) |
| { |
| if (fence->temporary == NULL) |
| return; |
| |
| vk_sync_destroy(device, fence->temporary); |
| fence->temporary = NULL; |
| } |
| |
| void |
| vk_fence_destroy(struct vk_device *device, |
| struct vk_fence *fence, |
| const VkAllocationCallbacks *pAllocator) |
| { |
| vk_fence_reset_temporary(device, fence); |
| vk_sync_finish(device, &fence->permanent); |
| |
| vk_object_free(device, pAllocator, fence); |
| } |
| |
| VKAPI_ATTR void VKAPI_CALL |
| vk_common_DestroyFence(VkDevice _device, |
| VkFence _fence, |
| const VkAllocationCallbacks *pAllocator) |
| { |
| VK_FROM_HANDLE(vk_device, device, _device); |
| VK_FROM_HANDLE(vk_fence, fence, _fence); |
| |
| if (fence == NULL) |
| return; |
| |
| vk_fence_destroy(device, fence, pAllocator); |
| } |
| |
| VKAPI_ATTR VkResult VKAPI_CALL |
| vk_common_ResetFences(VkDevice _device, |
| uint32_t fenceCount, |
| const VkFence *pFences) |
| { |
| VK_FROM_HANDLE(vk_device, device, _device); |
| |
| for (uint32_t i = 0; i < fenceCount; i++) { |
| VK_FROM_HANDLE(vk_fence, fence, pFences[i]); |
| |
| /* From the Vulkan 1.2.194 spec: |
| * |
| * "If any member of pFences currently has its payload imported with |
| * temporary permanence, that fence’s prior permanent payload is |
| * first restored. The remaining operations described therefore |
| * operate on the restored payload." |
| */ |
| vk_fence_reset_temporary(device, fence); |
| |
| VkResult result = vk_sync_reset(device, &fence->permanent); |
| if (result != VK_SUCCESS) |
| return result; |
| } |
| |
| return VK_SUCCESS; |
| } |
| |
| VKAPI_ATTR VkResult VKAPI_CALL |
| vk_common_GetFenceStatus(VkDevice _device, |
| VkFence _fence) |
| { |
| VK_FROM_HANDLE(vk_device, device, _device); |
| VK_FROM_HANDLE(vk_fence, fence, _fence); |
| |
| if (vk_device_is_lost(device)) |
| return VK_ERROR_DEVICE_LOST; |
| |
| VkResult result = vk_sync_wait(device, vk_fence_get_active_sync(fence), |
| 0 /* wait_value */, |
| VK_SYNC_WAIT_COMPLETE, |
| 0 /* abs_timeout_ns */); |
| if (result == VK_TIMEOUT) |
| return VK_NOT_READY; |
| else |
| return result; |
| } |
| |
| VKAPI_ATTR VkResult VKAPI_CALL |
| vk_common_WaitForFences(VkDevice _device, |
| uint32_t fenceCount, |
| const VkFence *pFences, |
| VkBool32 waitAll, |
| uint64_t timeout) |
| { |
| VK_FROM_HANDLE(vk_device, device, _device); |
| |
| if (vk_device_is_lost(device)) |
| return VK_ERROR_DEVICE_LOST; |
| |
| if (fenceCount == 0) |
| return VK_SUCCESS; |
| |
| uint64_t abs_timeout_ns = os_time_get_absolute_timeout(timeout); |
| |
| STACK_ARRAY(struct vk_sync_wait, waits, fenceCount); |
| |
| for (uint32_t i = 0; i < fenceCount; i++) { |
| VK_FROM_HANDLE(vk_fence, fence, pFences[i]); |
| waits[i] = (struct vk_sync_wait) { |
| .sync = vk_fence_get_active_sync(fence), |
| .stage_mask = ~(VkPipelineStageFlags2)0, |
| }; |
| } |
| |
| enum vk_sync_wait_flags wait_flags = VK_SYNC_WAIT_COMPLETE; |
| if (!waitAll) |
| wait_flags |= VK_SYNC_WAIT_ANY; |
| |
| VkResult result = vk_sync_wait_many(device, fenceCount, waits, |
| wait_flags, abs_timeout_ns); |
| |
| STACK_ARRAY_FINISH(waits); |
| |
| VkResult device_status = vk_device_check_status(device); |
| if (device_status != VK_SUCCESS) |
| return device_status; |
| |
| return result; |
| } |
| |
| VKAPI_ATTR void VKAPI_CALL |
| vk_common_GetPhysicalDeviceExternalFenceProperties( |
| VkPhysicalDevice physicalDevice, |
| const VkPhysicalDeviceExternalFenceInfo *pExternalFenceInfo, |
| VkExternalFenceProperties *pExternalFenceProperties) |
| { |
| VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice); |
| |
| assert(pExternalFenceInfo->sType == |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO); |
| const VkExternalFenceHandleTypeFlagBits handle_type = |
| pExternalFenceInfo->handleType; |
| |
| const struct vk_sync_type *sync_type = |
| get_fence_sync_type(pdevice, handle_type); |
| if (sync_type == NULL) { |
| pExternalFenceProperties->exportFromImportedHandleTypes = 0; |
| pExternalFenceProperties->compatibleHandleTypes = 0; |
| pExternalFenceProperties->externalFenceFeatures = 0; |
| return; |
| } |
| |
| VkExternalFenceHandleTypeFlagBits import = |
| vk_sync_fence_import_types(sync_type); |
| VkExternalFenceHandleTypeFlagBits export = |
| vk_sync_fence_export_types(sync_type); |
| |
| if (handle_type != VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT) { |
| const struct vk_sync_type *opaque_sync_type = |
| get_fence_sync_type(pdevice, VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT); |
| |
| /* If we're a different vk_sync_type than the one selected when only |
| * OPAQUE_FD is set, then we can't import/export OPAQUE_FD. Put |
| * differently, there can only be one OPAQUE_FD sync type. |
| */ |
| if (sync_type != opaque_sync_type) { |
| import &= ~VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT; |
| export &= ~VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT; |
| } |
| } |
| |
| VkExternalFenceHandleTypeFlags compatible = import & export; |
| VkExternalFenceFeatureFlags features = 0; |
| if (handle_type & export) |
| features |= VK_EXTERNAL_FENCE_FEATURE_EXPORTABLE_BIT; |
| if (handle_type & import) |
| features |= VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT; |
| |
| pExternalFenceProperties->exportFromImportedHandleTypes = export; |
| pExternalFenceProperties->compatibleHandleTypes = compatible; |
| pExternalFenceProperties->externalFenceFeatures = features; |
| } |
| |
| #ifndef _WIN32 |
| |
| VKAPI_ATTR VkResult VKAPI_CALL |
| vk_common_ImportFenceFdKHR(VkDevice _device, |
| const VkImportFenceFdInfoKHR *pImportFenceFdInfo) |
| { |
| VK_FROM_HANDLE(vk_device, device, _device); |
| VK_FROM_HANDLE(vk_fence, fence, pImportFenceFdInfo->fence); |
| |
| assert(pImportFenceFdInfo->sType == |
| VK_STRUCTURE_TYPE_IMPORT_FENCE_FD_INFO_KHR); |
| |
| const int fd = pImportFenceFdInfo->fd; |
| const VkExternalFenceHandleTypeFlagBits handle_type = |
| pImportFenceFdInfo->handleType; |
| |
| struct vk_sync *temporary = NULL, *sync; |
| if (pImportFenceFdInfo->flags & VK_FENCE_IMPORT_TEMPORARY_BIT) { |
| const struct vk_sync_type *sync_type = |
| get_fence_sync_type(device->physical, handle_type); |
| |
| VkResult result = vk_sync_create(device, sync_type, 0 /* flags */, |
| 0 /* initial_value */, &temporary); |
| if (result != VK_SUCCESS) |
| return result; |
| |
| sync = temporary; |
| } else { |
| sync = &fence->permanent; |
| } |
| assert(handle_type & vk_sync_fence_handle_types(sync->type)); |
| |
| VkResult result; |
| switch (pImportFenceFdInfo->handleType) { |
| case VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT: |
| result = vk_sync_import_opaque_fd(device, sync, fd); |
| break; |
| |
| case VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT: |
| result = vk_sync_import_sync_file(device, sync, fd); |
| break; |
| |
| default: |
| result = vk_error(fence, VK_ERROR_INVALID_EXTERNAL_HANDLE); |
| } |
| |
| if (result != VK_SUCCESS) { |
| if (temporary != NULL) |
| vk_sync_destroy(device, temporary); |
| return result; |
| } |
| |
| /* From the Vulkan 1.2.194 spec: |
| * |
| * "Importing a fence payload from a file descriptor transfers |
| * ownership of the file descriptor from the application to the |
| * Vulkan implementation. The application must not perform any |
| * operations on the file descriptor after a successful import." |
| * |
| * If the import fails, we leave the file descriptor open. |
| */ |
| if (fd != -1) |
| close(fd); |
| |
| if (temporary) { |
| vk_fence_reset_temporary(device, fence); |
| fence->temporary = temporary; |
| } |
| |
| return VK_SUCCESS; |
| } |
| |
| VKAPI_ATTR VkResult VKAPI_CALL |
| vk_common_GetFenceFdKHR(VkDevice _device, |
| const VkFenceGetFdInfoKHR *pGetFdInfo, |
| int *pFd) |
| { |
| VK_FROM_HANDLE(vk_device, device, _device); |
| VK_FROM_HANDLE(vk_fence, fence, pGetFdInfo->fence); |
| |
| assert(pGetFdInfo->sType == VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR); |
| |
| struct vk_sync *sync = vk_fence_get_active_sync(fence); |
| |
| VkResult result; |
| switch (pGetFdInfo->handleType) { |
| case VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT: |
| result = vk_sync_export_opaque_fd(device, sync, pFd); |
| if (unlikely(result != VK_SUCCESS)) |
| return result; |
| break; |
| |
| case VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT: |
| /* There's no direct spec quote for this but the same rules as for |
| * semaphore export apply. We can't export a sync file from a fence |
| * if the fence event hasn't been submitted to the kernel yet. |
| */ |
| if (vk_device_supports_threaded_submit(device)) { |
| result = vk_sync_wait(device, sync, 0, |
| VK_SYNC_WAIT_PENDING, |
| UINT64_MAX); |
| if (unlikely(result != VK_SUCCESS)) |
| return result; |
| } |
| |
| result = vk_sync_export_sync_file(device, sync, pFd); |
| if (unlikely(result != VK_SUCCESS)) |
| return result; |
| |
| /* From the Vulkan 1.2.194 spec: |
| * |
| * "Export operations have the same transference as the specified |
| * handle type’s import operations. Additionally, exporting a fence |
| * payload to a handle with copy transference has the same side |
| * effects on the source fence’s payload as executing a fence reset |
| * operation." |
| * |
| * In other words, exporting a sync file also resets the fence. We |
| * only care about this for the permanent payload because the temporary |
| * payload will be destroyed below. |
| */ |
| if (sync == &fence->permanent) { |
| result = vk_sync_reset(device, sync); |
| if (unlikely(result != VK_SUCCESS)) |
| return result; |
| } |
| break; |
| |
| default: |
| unreachable("Invalid fence export handle type"); |
| } |
| |
| /* From the Vulkan 1.2.194 spec: |
| * |
| * "Export operations have the same transference as the specified |
| * handle type’s import operations. [...] If the fence was using a |
| * temporarily imported payload, the fence’s prior permanent payload |
| * will be restored. |
| */ |
| vk_fence_reset_temporary(device, fence); |
| |
| return VK_SUCCESS; |
| } |
| |
| #endif /* !defined(_WIN32) */ |