From 85a6a2461d8a68b9d1b5f6e0dfe88e17b6e793a0 Mon Sep 17 00:00:00 2001 From: Chad Versace Date: Tue, 11 Oct 2022 19:01:13 -0700 Subject: [PATCH] venus: Enable VK_KHR_synchronization2 Signed-off-by: Chad Versace Reviewed-by: Yiwei Zhang Reviewed-by: Ryan Neph Part-of: --- src/virtio/vulkan/vn_command_buffer.c | 219 +++++++++++++++++++++++++ src/virtio/vulkan/vn_feedback.c | 66 ++++++++ src/virtio/vulkan/vn_feedback.h | 6 + src/virtio/vulkan/vn_physical_device.c | 21 ++- src/virtio/vulkan/vn_queue.c | 165 +++++++++++++++++++ 5 files changed, 475 insertions(+), 2 deletions(-) diff --git a/src/virtio/vulkan/vn_command_buffer.c b/src/virtio/vulkan/vn_command_buffer.c index 2fe5375502e..43a41484b10 100644 --- a/src/virtio/vulkan/vn_command_buffer.c +++ b/src/virtio/vulkan/vn_command_buffer.c @@ -41,6 +41,24 @@ vn_image_memory_barrier_has_present_src( return false; } +static bool +vn_dependency_info_has_present_src(uint32_t dep_count, + const VkDependencyInfo *dep_infos) +{ + for (uint32_t i = 0; i < dep_count; i++) { + for (uint32_t j = 0; j < dep_infos[i].imageMemoryBarrierCount; j++) { + const VkImageMemoryBarrier2 *b = + &dep_infos[i].pImageMemoryBarriers[j]; + if (b->oldLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR || + b->newLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) { + return true; + } + } + } + + return false; +} + static void * vn_cmd_get_tmp_data(struct vn_command_buffer *cmd, size_t size) { @@ -207,6 +225,79 @@ vn_cmd_fix_image_memory_barrier(const struct vn_command_buffer *cmd, } } +/** See vn_cmd_fix_image_memory_barrier(). */ +static void +vn_cmd_fix_image_memory_barrier2(const struct vn_command_buffer *cmd, + VkImageMemoryBarrier2 *b) +{ + if (VN_PRESENT_SRC_INTERNAL_LAYOUT == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) + return; + + if (b->oldLayout != VK_IMAGE_LAYOUT_PRESENT_SRC_KHR && + b->newLayout != VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) + return; + + const struct vn_image *img = vn_image_from_handle(b->image); + assert(img->wsi.is_wsi); + + if (img->wsi.is_prime_blit_src || b->oldLayout == b->newLayout) { + if (b->oldLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) + b->oldLayout = VN_PRESENT_SRC_INTERNAL_LAYOUT; + if (b->newLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) + b->newLayout = VN_PRESENT_SRC_INTERNAL_LAYOUT; + return; + } + + if (b->oldLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) { + b->oldLayout = VN_PRESENT_SRC_INTERNAL_LAYOUT; + + /* no availability operation needed */ + b->srcStageMask = 0; + b->srcAccessMask = 0; + + if (img->sharing_mode == VK_SHARING_MODE_CONCURRENT) { + b->srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT; + b->dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + } else if (b->dstQueueFamilyIndex == b->srcQueueFamilyIndex || + b->dstQueueFamilyIndex == cmd->queue_family_index) { + b->srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT; + b->dstQueueFamilyIndex = cmd->queue_family_index; + } else { + /* The barrier also defines a queue family ownership transfer, and + * this is the one that gets submitted to the source queue family to + * release the ownership. Skip both the transfer and the transition. + */ + b->srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + b->dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + b->newLayout = b->oldLayout; + } + } else { + b->newLayout = VN_PRESENT_SRC_INTERNAL_LAYOUT; + + /* no visibility operation needed */ + b->dstStageMask = 0; + b->dstAccessMask = 0; + + if (img->sharing_mode == VK_SHARING_MODE_CONCURRENT) { + b->srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + b->dstQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT; + } else if (b->srcQueueFamilyIndex == b->dstQueueFamilyIndex || + b->srcQueueFamilyIndex == cmd->queue_family_index) { + b->srcQueueFamilyIndex = cmd->queue_family_index; + b->dstQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT; + } else { + /* The barrier also defines a queue family ownership transfer, and + * this is the one that gets submitted to the destination queue + * family to acquire the ownership. Skip both the transfer and the + * transition. + */ + b->srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + b->dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + b->oldLayout = b->newLayout; + } + } +} + static const VkImageMemoryBarrier * vn_cmd_wait_events_fix_image_memory_barriers( struct vn_command_buffer *cmd, @@ -288,6 +379,49 @@ vn_cmd_pipeline_barrier_fix_image_memory_barriers( return img_barriers; } +static const VkDependencyInfo * +vn_cmd_fix_dependency_infos(struct vn_command_buffer *cmd, + uint32_t dep_count, + const VkDependencyInfo *dep_infos) +{ + if (cmd->builder.render_pass || + !vn_dependency_info_has_present_src(dep_count, dep_infos)) + return dep_infos; + + uint32_t total_barrier_count = 0; + for (uint32_t i = 0; i < dep_count; i++) + total_barrier_count += dep_infos[i].imageMemoryBarrierCount; + + size_t tmp_size = dep_count * sizeof(VkDependencyInfo) + + total_barrier_count * sizeof(VkImageMemoryBarrier2); + void *tmp = vn_cmd_get_tmp_data(cmd, tmp_size); + if (!tmp) { + cmd->state = VN_COMMAND_BUFFER_STATE_INVALID; + return dep_infos; + } + + VkDependencyInfo *new_dep_infos = tmp; + tmp += dep_count * sizeof(VkDependencyInfo); + memcpy(new_dep_infos, dep_infos, dep_count * sizeof(VkDependencyInfo)); + + for (uint32_t i = 0; i < dep_count; i++) { + uint32_t barrier_count = dep_infos[i].imageMemoryBarrierCount; + + VkImageMemoryBarrier2 *new_barriers = tmp; + tmp += barrier_count * sizeof(VkImageMemoryBarrier2); + + memcpy(new_barriers, dep_infos[i].pImageMemoryBarriers, + barrier_count * sizeof(VkImageMemoryBarrier2)); + new_dep_infos[i].pImageMemoryBarriers = new_barriers; + + for (uint32_t j = 0; j < barrier_count; j++) { + vn_cmd_fix_image_memory_barrier2(cmd, &new_barriers[j]); + } + } + + return new_dep_infos; +} + static void vn_cmd_encode_memory_barriers(struct vn_command_buffer *cmd, VkPipelineStageFlags src_stage_mask, @@ -1312,6 +1446,43 @@ vn_CmdSetEvent(VkCommandBuffer commandBuffer, VK_EVENT_SET); } +static VkPipelineStageFlags2 +vn_dependency_info_collect_src_stage_mask(const VkDependencyInfo *dep_info) +{ + VkPipelineStageFlags2 mask = 0; + + for (uint32_t i = 0; i < dep_info->memoryBarrierCount; i++) + mask |= dep_info->pMemoryBarriers[i].srcStageMask; + + for (uint32_t i = 0; i < dep_info->bufferMemoryBarrierCount; i++) + mask |= dep_info->pBufferMemoryBarriers[i].srcStageMask; + + for (uint32_t i = 0; i < dep_info->imageMemoryBarrierCount; i++) + mask |= dep_info->pImageMemoryBarriers[i].srcStageMask; + + return mask; +} + +void +vn_CmdSetEvent2(VkCommandBuffer commandBuffer, + VkEvent event, + const VkDependencyInfo *pDependencyInfo) + +{ + struct vn_command_buffer *cmd = + vn_command_buffer_from_handle(commandBuffer); + + pDependencyInfo = vn_cmd_fix_dependency_infos(cmd, 1, pDependencyInfo); + + VN_CMD_ENQUEUE(vkCmdSetEvent2, commandBuffer, event, pDependencyInfo); + + VkPipelineStageFlags2 src_stage_mask = + vn_dependency_info_collect_src_stage_mask(pDependencyInfo); + + vn_feedback_event_cmd_record2(commandBuffer, event, src_stage_mask, + VK_EVENT_SET); +} + void vn_CmdResetEvent(VkCommandBuffer commandBuffer, VkEvent event, @@ -1323,6 +1494,16 @@ vn_CmdResetEvent(VkCommandBuffer commandBuffer, VK_EVENT_RESET); } +void +vn_CmdResetEvent2(VkCommandBuffer commandBuffer, + VkEvent event, + VkPipelineStageFlags2 stageMask) +{ + VN_CMD_ENQUEUE(vkCmdResetEvent2, commandBuffer, event, stageMask); + vn_feedback_event_cmd_record2(commandBuffer, event, stageMask, + VK_EVENT_RESET); +} + void vn_CmdWaitEvents(VkCommandBuffer commandBuffer, uint32_t eventCount, @@ -1357,6 +1538,22 @@ vn_CmdWaitEvents(VkCommandBuffer commandBuffer, } } +void +vn_CmdWaitEvents2(VkCommandBuffer commandBuffer, + uint32_t eventCount, + const VkEvent *pEvents, + const VkDependencyInfo *pDependencyInfos) +{ + struct vn_command_buffer *cmd = + vn_command_buffer_from_handle(commandBuffer); + + pDependencyInfos = + vn_cmd_fix_dependency_infos(cmd, eventCount, pDependencyInfos); + + VN_CMD_ENQUEUE(vkCmdWaitEvents2, commandBuffer, eventCount, pEvents, + pDependencyInfos); +} + void vn_CmdPipelineBarrier(VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, @@ -1382,6 +1579,18 @@ vn_CmdPipelineBarrier(VkCommandBuffer commandBuffer, pImageMemoryBarriers); } +void +vn_CmdPipelineBarrier2(VkCommandBuffer commandBuffer, + const VkDependencyInfo *pDependencyInfo) +{ + struct vn_command_buffer *cmd = + vn_command_buffer_from_handle(commandBuffer); + + pDependencyInfo = vn_cmd_fix_dependency_infos(cmd, 1, pDependencyInfo); + + VN_CMD_ENQUEUE(vkCmdPipelineBarrier2, commandBuffer, pDependencyInfo); +} + void vn_CmdBeginQuery(VkCommandBuffer commandBuffer, VkQueryPool queryPool, @@ -1419,6 +1628,16 @@ vn_CmdWriteTimestamp(VkCommandBuffer commandBuffer, queryPool, query); } +void +vn_CmdWriteTimestamp2(VkCommandBuffer commandBuffer, + VkPipelineStageFlagBits2 stage, + VkQueryPool queryPool, + uint32_t query) +{ + VN_CMD_ENQUEUE(vkCmdWriteTimestamp2, commandBuffer, stage, queryPool, + query); +} + void vn_CmdCopyQueryPoolResults(VkCommandBuffer commandBuffer, VkQueryPool queryPool, diff --git a/src/virtio/vulkan/vn_feedback.c b/src/virtio/vulkan/vn_feedback.c index 406aa095969..bc14d4babd7 100644 --- a/src/virtio/vulkan/vn_feedback.c +++ b/src/virtio/vulkan/vn_feedback.c @@ -264,6 +264,7 @@ vn_feedback_pool_free(struct vn_feedback_pool *pool, simple_mtx_unlock(&pool->mutex); } +/** See also vn_feedback_event_cmd_record2(). */ void vn_feedback_event_cmd_record(VkCommandBuffer cmd_handle, VkEvent ev_handle, @@ -322,6 +323,71 @@ vn_feedback_event_cmd_record(VkCommandBuffer cmd_handle, &buf_barrier_after, 0, NULL); } +/** See also vn_feedback_event_cmd_record(). */ +void +vn_feedback_event_cmd_record2(VkCommandBuffer cmd_h, + VkEvent event_h, + VkPipelineStageFlags2 src_stage_mask, + VkResult status) +{ + struct vn_event *event = vn_event_from_handle(event_h); + struct vn_feedback_slot *slot = event->feedback_slot; + + if (!slot) + return; + + STATIC_ASSERT(sizeof(*slot->status) == 4); + + const VkDependencyInfo dep_before = { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .dependencyFlags = 0, + .bufferMemoryBarrierCount = 1, + .pBufferMemoryBarriers = + (VkBufferMemoryBarrier2[]){ + { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, + .srcStageMask = src_stage_mask | VK_PIPELINE_STAGE_HOST_BIT | + VK_PIPELINE_STAGE_TRANSFER_BIT, + .srcAccessMask = + VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = slot->buffer, + .offset = slot->offset, + .size = 4, + }, + }, + }; + + const VkDependencyInfo dep_after = { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .dependencyFlags = 0, + .bufferMemoryBarrierCount = 1, + .pBufferMemoryBarriers = + (VkBufferMemoryBarrier2[]){ + { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, + .srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_HOST_BIT, + .dstAccessMask = + VK_ACCESS_HOST_READ_BIT | VK_ACCESS_HOST_WRITE_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = slot->buffer, + .offset = slot->offset, + .size = 4, + }, + }, + }; + + vn_CmdPipelineBarrier2(cmd_h, &dep_before); + vn_CmdFillBuffer(cmd_h, slot->buffer, slot->offset, 4, status); + vn_CmdPipelineBarrier2(cmd_h, &dep_after); +} + static VkResult vn_feedback_fence_cmd_record(VkCommandBuffer cmd_handle, struct vn_feedback_slot *slot) diff --git a/src/virtio/vulkan/vn_feedback.h b/src/virtio/vulkan/vn_feedback.h index 8f7870c894b..8e75e8a9f00 100644 --- a/src/virtio/vulkan/vn_feedback.h +++ b/src/virtio/vulkan/vn_feedback.h @@ -113,6 +113,12 @@ vn_feedback_event_cmd_record(VkCommandBuffer cmd_handle, VkPipelineStageFlags stage_mask, VkResult status); +void +vn_feedback_event_cmd_record2(VkCommandBuffer cmd_handle, + VkEvent event, + VkPipelineStageFlags2 src_stage_mask, + VkResult status); + VkResult vn_feedback_fence_cmd_alloc(VkDevice dev_handle, struct vn_feedback_cmd_pool *pool, diff --git a/src/virtio/vulkan/vn_physical_device.c b/src/virtio/vulkan/vn_physical_device.c index 15b0cf03072..cb05ccb1ead 100644 --- a/src/virtio/vulkan/vn_physical_device.c +++ b/src/virtio/vulkan/vn_physical_device.c @@ -116,6 +116,7 @@ vn_physical_device_init_features(struct vn_physical_device *physical_dev) shader_integer_dot_product; VkPhysicalDeviceShaderTerminateInvocationFeatures shader_terminate_invocation; + VkPhysicalDeviceSynchronization2Features synchronization2; VkPhysicalDeviceSubgroupSizeControlFeatures subgroup_size_control; VkPhysicalDeviceTextureCompressionASTCHDRFeatures texture_compression_astc_hdr; @@ -172,6 +173,7 @@ vn_physical_device_init_features(struct vn_physical_device *physical_dev) VN_ADD_EXT_TO_PNEXT_OF(features2, SHADER_INTEGER_DOT_PRODUCT_FEATURES, local_feats.shader_integer_dot_product, KHR_shader_integer_dot_product); VN_ADD_EXT_TO_PNEXT_OF(features2, SHADER_TERMINATE_INVOCATION_FEATURES, local_feats.shader_terminate_invocation, KHR_shader_terminate_invocation); VN_ADD_EXT_TO_PNEXT_OF(features2, SUBGROUP_SIZE_CONTROL_FEATURES, local_feats.subgroup_size_control, EXT_subgroup_size_control); + VN_ADD_EXT_TO_PNEXT_OF(features2, SYNCHRONIZATION_2_FEATURES, local_feats.synchronization2, KHR_synchronization2); VN_ADD_EXT_TO_PNEXT_OF(features2, TEXTURE_COMPRESSION_ASTC_HDR_FEATURES, local_feats.texture_compression_astc_hdr, EXT_texture_compression_astc_hdr); VN_ADD_EXT_TO_PNEXT_OF(features2, ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES, local_feats.zero_initialize_workgroup_memory, KHR_zero_initialize_workgroup_memory); } @@ -371,8 +373,9 @@ vn_physical_device_init_features(struct vn_physical_device *physical_dev) VN_SET_CORE_FIELD(vk13_feats, subgroupSizeControl, local_feats.subgroup_size_control); VN_SET_CORE_FIELD(vk13_feats, computeFullSubgroups, local_feats.subgroup_size_control); } - /* TODO(VK_KHR_synchronization2): Support extension */ - VN_SET_CORE_VALUE(vk13_feats, synchronization2, false); + if (exts->KHR_synchronization2) { + VN_SET_CORE_FIELD(vk13_feats, synchronization2, local_feats.synchronization2); + } if (exts->EXT_texture_compression_astc_hdr) { VN_SET_CORE_FIELD(vk13_feats, textureCompressionASTC_HDR, local_feats.texture_compression_astc_hdr); } @@ -933,6 +936,19 @@ vn_physical_device_init_external_semaphore_handles( VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; } #endif + + if (!(physical_dev->renderer_sync_fd_semaphore_features & + VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT)) { + /* Disable VK_KHR_synchronization2, because + * out implementation requires semaphore sync fd import. + */ + physical_dev->base.base.supported_extensions.KHR_synchronization2 = + false; + + /* Clamp to 1.2 because 1.3 requires VK_KHR_synchronization2. */ + physical_dev->properties.vulkan_1_0.apiVersion = MIN2( + VK_API_VERSION_1_2, physical_dev->properties.vulkan_1_0.apiVersion); + } } static void @@ -1044,6 +1060,7 @@ vn_physical_device_get_passthrough_extensions( .KHR_shader_integer_dot_product = true, .KHR_shader_non_semantic_info = true, .KHR_shader_terminate_invocation = true, + .KHR_synchronization2 = true, .KHR_zero_initialize_workgroup_memory = true, .EXT_4444_formats = true, .EXT_extended_dynamic_state = true, diff --git a/src/virtio/vulkan/vn_queue.c b/src/virtio/vulkan/vn_queue.c index 2844863fba7..2e359f322ae 100644 --- a/src/virtio/vulkan/vn_queue.c +++ b/src/virtio/vulkan/vn_queue.c @@ -525,6 +525,171 @@ vn_QueueSubmit(VkQueue queue_h, return VK_SUCCESS; } +static bool ATTRIBUTE_PURE +vn_submit_info2_has_external_signal_semaphore(uint32_t submit_count, + const VkSubmitInfo2 *submits) +{ + for (uint32_t i = 0; i < submit_count; i++) { + const VkSubmitInfo2 *submit = submits + i; + + for (uint32_t j = 0; j < submit->signalSemaphoreInfoCount; j++) { + VkSemaphore sem_h = submit->pSignalSemaphoreInfos[j].semaphore; + struct vn_semaphore *sem = vn_semaphore_from_handle(sem_h); + + if (sem->is_external) + return true; + } + } + + return false; +} + +static VkResult +vn_queue_submit2_import_semaphores(struct vn_device *dev, uint32_t submit_count, const VkSubmitInfo2 *submits) +{ + struct vn_instance *instance = dev->instance; + VkDevice dev_h = vn_device_to_handle(dev); + + assert(dev->physical_device->renderer_sync_fd_semaphore_features & + VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT); + + for (uint32_t i = 0; i < submit_count; i++) { + const VkSubmitInfo2 *submit = submits + i; + + for (uint32_t j = 0; j < submit->waitSemaphoreInfoCount; j++) { + VkSemaphore sem_h = submit->pWaitSemaphoreInfos[j].semaphore; + struct vn_semaphore *sem = vn_semaphore_from_handle(sem_h); + + if (sem->payload->type != VN_SYNC_TYPE_IMPORTED_SYNC_FD) + continue; + + if (!vn_semaphore_wait_external(dev, sem)) + return VK_ERROR_DEVICE_LOST; + + const VkImportSemaphoreResourceInfo100000MESA res_info = { + .sType = + VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_RESOURCE_INFO_100000_MESA, + .semaphore = sem_h, + .resourceId = 0, + }; + + vn_async_vkImportSemaphoreResource100000MESA(instance, dev_h, &res_info); + } + } + + return VK_SUCCESS; +} + +static VkResult +vn_queue_submit2_low(VkQueue queue_h, + uint32_t submit_count, + const VkSubmitInfo2 *submits, + VkFence fence_h, + bool sync) +{ + struct vn_queue *queue = vn_queue_from_handle(queue_h); + struct vn_instance *instance = queue->device->instance; + + if (!submit_count && !fence_h) + return VK_SUCCESS; + + if (sync || VN_PERF(NO_ASYNC_QUEUE_SUBMIT)) + return vn_call_vkQueueSubmit2(instance, queue_h, submit_count, submits, + fence_h); + + vn_async_vkQueueSubmit2(instance, queue_h, submit_count, submits, fence_h); + return VK_SUCCESS; +} + +static VkResult +vn_queue_submit2_feedback_fence(struct vn_queue *queue, + struct vn_fence *fence, + bool sync) +{ + VkQueue queue_h = vn_queue_to_handle(queue); + VkFence fence_h = vn_fence_to_handle(fence); + + assert(fence->feedback.slot); + + const VkSubmitInfo2 submit = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2, + .commandBufferInfoCount = 1, + .pCommandBufferInfos = + (VkCommandBufferSubmitInfo[]){ + { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, + .commandBuffer = vn_get_fence_feedback_cmd(queue, fence), + .deviceMask = 0, + }, + }, + }; + + return vn_queue_submit2_low(queue_h, 1, &submit, fence_h, sync); +} + +VkResult +vn_QueueSubmit2(VkQueue queue_h, + uint32_t submitCount, + const VkSubmitInfo2 *pSubmits, + VkFence fence_h) +{ + VN_TRACE_FUNC(); + struct vn_fence *fence = vn_fence_from_handle(fence_h); + struct vn_queue *queue = vn_queue_from_handle(queue_h); + struct vn_device *dev = queue->device; + struct vn_instance *instance = dev->instance; + VkResult result; + + assert(dev->physical_device->renderer_sync_fd_semaphore_features & + VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT); + + const struct wsi_memory_signal_submit_info *wsi_info = NULL; + if (submitCount == 1) + wsi_info = vk_find_struct_const(pSubmits[0].pNext, WSI_MEMORY_SIGNAL_SUBMIT_INFO_MESA); + + const bool has_external_fence = fence && fence->is_external; + const bool has_feedback_fence = fence && fence->feedback.slot; + assert(!has_external_fence || !has_feedback_fence); + + const bool sync = + has_external_fence || wsi_info || + vn_submit_info2_has_external_signal_semaphore(submitCount, pSubmits); + + result = vn_queue_submit2_import_semaphores(dev, submitCount, pSubmits); + if (result != VK_SUCCESS) + return vn_error(instance, result); + + result = + vn_queue_submit2_low(queue_h, submitCount, pSubmits, + has_feedback_fence ? VK_NULL_HANDLE : fence_h, + sync && !has_feedback_fence); + if (result != VK_SUCCESS) + return vn_error(instance, result); + + if (has_feedback_fence) { + result = vn_queue_submit2_feedback_fence(queue, fence, sync); + if (result != VK_SUCCESS) + return vn_error(instance, result); + } + + if (wsi_info) { + if (dev->instance->renderer->info.has_implicit_fencing) { + struct vn_device_memory *wsi_mem = + vn_device_memory_from_handle(wsi_info->memory); + assert(!wsi_mem->base_memory && wsi_mem->base_bo); + + vn_renderer_submit(dev->renderer, &(const struct vn_renderer_submit){ + .bos = &wsi_mem->base_bo, + .bo_count = 1, + }); + } else { + vn_queue_wait_idle_before_present(queue); + } + } + + return VK_SUCCESS; +} + VkResult vn_QueueBindSparse(VkQueue _queue, uint32_t bindInfoCount,