diff --git a/src/intel/vulkan/anv_batch_chain.c b/src/intel/vulkan/anv_batch_chain.c index ad76dc1fc0f..52ecfe89027 100644 --- a/src/intel/vulkan/anv_batch_chain.c +++ b/src/intel/vulkan/anv_batch_chain.c @@ -1402,8 +1402,9 @@ anv_cmd_buffer_execbuf(struct anv_device *device, VkResult result = VK_SUCCESS; for (uint32_t i = 0; i < num_in_semaphores; i++) { ANV_FROM_HANDLE(anv_semaphore, semaphore, in_semaphores[i]); - assert(semaphore->temporary.type == ANV_SEMAPHORE_TYPE_NONE); - struct anv_semaphore_impl *impl = &semaphore->permanent; + struct anv_semaphore_impl *impl = + semaphore->temporary.type != ANV_SEMAPHORE_TYPE_NONE ? + &semaphore->temporary : &semaphore->permanent; switch (impl->type) { case ANV_SEMAPHORE_TYPE_BO: @@ -1419,8 +1420,21 @@ anv_cmd_buffer_execbuf(struct anv_device *device, for (uint32_t i = 0; i < num_out_semaphores; i++) { ANV_FROM_HANDLE(anv_semaphore, semaphore, out_semaphores[i]); - assert(semaphore->temporary.type == ANV_SEMAPHORE_TYPE_NONE); - struct anv_semaphore_impl *impl = &semaphore->permanent; + + /* Under most circumstances, out fences won't be temporary. However, + * the spec does allow it for opaque_fd. From the Vulkan 1.0.53 spec: + * + * "If the import is temporary, the implementation must restore the + * semaphore to its prior permanent state after submitting the next + * semaphore wait operation." + * + * The spec says nothing whatsoever about signal operations on + * temporarily imported semaphores so it appears they are allowed. + * There are also CTS tests that require this to work. + */ + struct anv_semaphore_impl *impl = + semaphore->temporary.type != ANV_SEMAPHORE_TYPE_NONE ? + &semaphore->temporary : &semaphore->permanent; switch (impl->type) { case ANV_SEMAPHORE_TYPE_BO: @@ -1440,6 +1454,20 @@ anv_cmd_buffer_execbuf(struct anv_device *device, result = anv_device_execbuf(device, &execbuf.execbuf, execbuf.bos); + for (uint32_t i = 0; i < num_in_semaphores; i++) { + ANV_FROM_HANDLE(anv_semaphore, semaphore, in_semaphores[i]); + /* From the Vulkan 1.0.53 spec: + * + * "If the import is temporary, the implementation must restore the + * semaphore to its prior permanent state after submitting the next + * semaphore wait operation." + * + * This has to happen after the execbuf in case we close any syncobjs in + * the process. + */ + anv_semaphore_reset_temporary(device, semaphore); + } + anv_execbuf_finish(&execbuf, &device->alloc); return result; diff --git a/src/intel/vulkan/anv_extensions.py b/src/intel/vulkan/anv_extensions.py index ae222491b95..00186bc2c66 100644 --- a/src/intel/vulkan/anv_extensions.py +++ b/src/intel/vulkan/anv_extensions.py @@ -50,6 +50,9 @@ EXTENSIONS = [ Extension('VK_KHR_external_memory', 1, True), Extension('VK_KHR_external_memory_capabilities', 1, True), Extension('VK_KHR_external_memory_fd', 1, True), + Extension('VK_KHR_external_semaphore', 1, False), + Extension('VK_KHR_external_semaphore_capabilities', 1, False), + Extension('VK_KHR_external_semaphore_fd', 1, False), Extension('VK_KHR_get_memory_requirements2', 1, True), Extension('VK_KHR_get_physical_device_properties2', 1, True), Extension('VK_KHR_get_surface_capabilities2', 1, True), diff --git a/src/intel/vulkan/anv_private.h b/src/intel/vulkan/anv_private.h index c3644914328..b599db3b801 100644 --- a/src/intel/vulkan/anv_private.h +++ b/src/intel/vulkan/anv_private.h @@ -1765,6 +1765,9 @@ struct anv_semaphore { struct anv_semaphore_impl temporary; }; +void anv_semaphore_reset_temporary(struct anv_device *device, + struct anv_semaphore *semaphore); + struct anv_shader_module { unsigned char sha1[20]; uint32_t size; diff --git a/src/intel/vulkan/anv_queue.c b/src/intel/vulkan/anv_queue.c index 2c10e9d2f69..9a0789ca322 100644 --- a/src/intel/vulkan/anv_queue.c +++ b/src/intel/vulkan/anv_queue.c @@ -528,11 +528,38 @@ VkResult anv_CreateSemaphore( if (semaphore == NULL) return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY); - /* The DRM execbuffer ioctl always execute in-oder so long as you stay - * on the same ring. Since we don't expose the blit engine as a DMA - * queue, a dummy no-op semaphore is a perfectly valid implementation. - */ - semaphore->permanent.type = ANV_SEMAPHORE_TYPE_DUMMY; + const VkExportSemaphoreCreateInfoKHR *export = + vk_find_struct_const(pCreateInfo->pNext, EXPORT_SEMAPHORE_CREATE_INFO_KHR); + VkExternalSemaphoreHandleTypeFlagsKHR handleTypes = + export ? export->handleTypes : 0; + + if (handleTypes == 0) { + /* The DRM execbuffer ioctl always execute in-oder so long as you stay + * on the same ring. Since we don't expose the blit engine as a DMA + * queue, a dummy no-op semaphore is a perfectly valid implementation. + */ + semaphore->permanent.type = ANV_SEMAPHORE_TYPE_DUMMY; + } else if (handleTypes & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR) { + assert(handleTypes == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR); + + semaphore->permanent.type = ANV_SEMAPHORE_TYPE_BO; + VkResult result = anv_bo_cache_alloc(device, &device->bo_cache, + 4096, &semaphore->permanent.bo); + if (result != VK_SUCCESS) { + vk_free2(&device->alloc, pAllocator, semaphore); + return result; + } + + /* If we're going to use this as a fence, we need to *not* have the + * EXEC_OBJECT_ASYNC bit set. + */ + assert(!(semaphore->permanent.bo->flags & EXEC_OBJECT_ASYNC)); + } else { + assert(!"Unknown handle type"); + vk_free2(&device->alloc, pAllocator, semaphore); + return vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR); + } + semaphore->temporary.type = ANV_SEMAPHORE_TYPE_NONE; *pSemaphore = anv_semaphore_to_handle(semaphore); @@ -558,6 +585,17 @@ anv_semaphore_impl_cleanup(struct anv_device *device, unreachable("Invalid semaphore type"); } +void +anv_semaphore_reset_temporary(struct anv_device *device, + struct anv_semaphore *semaphore) +{ + if (semaphore->temporary.type == ANV_SEMAPHORE_TYPE_NONE) + return; + + anv_semaphore_impl_cleanup(device, &semaphore->temporary); + semaphore->temporary.type = ANV_SEMAPHORE_TYPE_NONE; +} + void anv_DestroySemaphore( VkDevice _device, VkSemaphore _semaphore, @@ -574,3 +612,109 @@ void anv_DestroySemaphore( vk_free2(&device->alloc, pAllocator, semaphore); } + +void anv_GetPhysicalDeviceExternalSemaphorePropertiesKHR( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceExternalSemaphoreInfoKHR* pExternalSemaphoreInfo, + VkExternalSemaphorePropertiesKHR* pExternalSemaphoreProperties) +{ + switch (pExternalSemaphoreInfo->handleType) { + case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR: + pExternalSemaphoreProperties->exportFromImportedHandleTypes = + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + pExternalSemaphoreProperties->compatibleHandleTypes = + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + pExternalSemaphoreProperties->externalSemaphoreFeatures = + VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHR | + VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHR; + break; + + default: + pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0; + pExternalSemaphoreProperties->compatibleHandleTypes = 0; + pExternalSemaphoreProperties->externalSemaphoreFeatures = 0; + } +} + +VkResult anv_ImportSemaphoreFdKHR( + VkDevice _device, + const VkImportSemaphoreFdInfoKHR* pImportSemaphoreFdInfo) +{ + ANV_FROM_HANDLE(anv_device, device, _device); + ANV_FROM_HANDLE(anv_semaphore, semaphore, pImportSemaphoreFdInfo->semaphore); + int fd = pImportSemaphoreFdInfo->fd; + + struct anv_semaphore_impl new_impl = { + .type = ANV_SEMAPHORE_TYPE_NONE, + }; + + switch (pImportSemaphoreFdInfo->handleType) { + case VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR: { + new_impl.type = ANV_SEMAPHORE_TYPE_BO; + + VkResult result = anv_bo_cache_import(device, &device->bo_cache, + fd, 4096, &new_impl.bo); + if (result != VK_SUCCESS) + return result; + + /* If we're going to use this as a fence, we need to *not* have the + * EXEC_OBJECT_ASYNC bit set. + */ + assert(!(new_impl.bo->flags & EXEC_OBJECT_ASYNC)); + + break; + } + + default: + return vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR); + } + + if (pImportSemaphoreFdInfo->flags & VK_SEMAPHORE_IMPORT_TEMPORARY_BIT_KHR) { + anv_semaphore_impl_cleanup(device, &semaphore->temporary); + semaphore->temporary = new_impl; + } else { + anv_semaphore_impl_cleanup(device, &semaphore->permanent); + semaphore->permanent = new_impl; + } + + return VK_SUCCESS; +} + +VkResult anv_GetSemaphoreFdKHR( + VkDevice _device, + const VkSemaphoreGetFdInfoKHR* pGetFdInfo, + int* pFd) +{ + ANV_FROM_HANDLE(anv_device, device, _device); + ANV_FROM_HANDLE(anv_semaphore, semaphore, pGetFdInfo->semaphore); + VkResult result; + + assert(pGetFdInfo->sType == VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR); + + struct anv_semaphore_impl *impl = + semaphore->temporary.type != ANV_SEMAPHORE_TYPE_NONE ? + &semaphore->temporary : &semaphore->permanent; + + switch (impl->type) { + case ANV_SEMAPHORE_TYPE_BO: + result = anv_bo_cache_export(device, &device->bo_cache, impl->bo, pFd); + if (result != VK_SUCCESS) + return result; + break; + + default: + return vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR); + } + + /* From the Vulkan 1.0.53 spec: + * + * "Export operations have the same transference as the specified handle + * type’s import operations. [...] If the semaphore was using a + * temporarily imported payload, the semaphore’s prior permanent payload + * will be restored. + */ + if (impl == &semaphore->temporary) + anv_semaphore_impl_cleanup(device, impl); + + return VK_SUCCESS; +}