diff --git a/spa/plugins/vulkan/vulkan-compute-utils.c b/spa/plugins/vulkan/vulkan-compute-utils.c index 09d0657a5..ef5f17cf4 100644 --- a/spa/plugins/vulkan/vulkan-compute-utils.c +++ b/spa/plugins/vulkan/vulkan-compute-utils.c @@ -247,39 +247,23 @@ static int createCommandBuffer(struct vulkan_compute_state *s) return 0; } +/** runCommandBuffer + * The return value of this functions means the following: + * ret < 0: Error + * ret = 0: queueSubmit was succsessful, but manual synchronization is required + * ret = 1: queueSubmit was succsessful and buffers can be released without synchronization + */ static int runCommandBuffer(struct vulkan_compute_state *s) { + VULKAN_INSTANCE_FUNCTION(vkQueueSubmit2KHR); + VULKAN_INSTANCE_FUNCTION(vkGetSemaphoreFdKHR); + static const VkCommandBufferBeginInfo beginInfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, }; VK_CHECK_RESULT(vkBeginCommandBuffer(s->commandBuffer, &beginInfo)); - VkImageMemoryBarrier barrier[s->n_streams]; - uint32_t i; - - for (i = 0; i < s->n_streams; i++) { - struct vulkan_stream *p = &s->streams[i]; - - barrier[i]= (VkImageMemoryBarrier) { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcAccessMask = 0, - .dstAccessMask = 0, - .image = p->buffers[p->current_buffer_id].image, - }; - } - - vkCmdPipelineBarrier(s->commandBuffer, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - 0, 0, NULL, 0, NULL, - s->n_streams, barrier); - vkCmdBindPipeline(s->commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, s->pipeline); vkCmdPushConstants (s->commandBuffer, s->pipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, @@ -291,19 +275,135 @@ static int runCommandBuffer(struct vulkan_compute_state *s) (uint32_t)ceil(s->constants.width / (float)WORKGROUP_SIZE), (uint32_t)ceil(s->constants.height / (float)WORKGROUP_SIZE), 1); + VkImageMemoryBarrier acquire_barrier[s->n_streams]; + VkImageMemoryBarrier release_barrier[s->n_streams]; + VkSemaphoreSubmitInfo semaphore_wait_info[s->n_streams]; + uint32_t semaphore_wait_info_len = 0; + VkSemaphoreSubmitInfo semaphore_signal_info[1]; + uint32_t semaphore_signal_info_len = 0; + + uint32_t i; + for (i = 0; i < s->n_streams; i++) { + struct vulkan_stream *p = &s->streams[i]; + struct vulkan_buffer *current_buffer = &p->buffers[p->current_buffer_id]; + + VkAccessFlags access_flags; + if (p->direction == SPA_DIRECTION_INPUT) { + access_flags = VK_ACCESS_SHADER_READ_BIT; + } else { + access_flags = VK_ACCESS_SHADER_WRITE_BIT; + } + + acquire_barrier[i]= (VkImageMemoryBarrier) { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, + .dstQueueFamilyIndex = s->base.queueFamilyIndex, + .image = current_buffer->image, + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcAccessMask = 0, + .dstAccessMask = access_flags, + .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .subresourceRange.levelCount = 1, + .subresourceRange.layerCount = 1, + }; + + release_barrier[i]= (VkImageMemoryBarrier) { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcQueueFamilyIndex = s->base.queueFamilyIndex, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, + .image = current_buffer->image, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcAccessMask = access_flags, + .dstAccessMask = 0, + .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .subresourceRange.levelCount = 1, + .subresourceRange.layerCount = 1, + }; + + if (vulkan_sync_foreign_dmabuf(&s->base, current_buffer) < 0) { + spa_log_warn(s->log, "Failed to wait for foreign buffer DMA-BUF fence"); + } else { + if (current_buffer->foreign_semaphore != VK_NULL_HANDLE) { + semaphore_wait_info[semaphore_wait_info_len++] = (VkSemaphoreSubmitInfo) { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, + .semaphore = current_buffer->foreign_semaphore, + .stageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + }; + } + } + } + + vkCmdPipelineBarrier(s->commandBuffer, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + 0, 0, NULL, 0, NULL, + s->n_streams, acquire_barrier); + + vkCmdPipelineBarrier(s->commandBuffer, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, + 0, 0, NULL, 0, NULL, + s->n_streams, release_barrier); + VK_CHECK_RESULT(vkEndCommandBuffer(s->commandBuffer)); VK_CHECK_RESULT(vkResetFences(s->base.device, 1, &s->fence)); - const VkSubmitInfo submitInfo = { - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, - .commandBufferCount = 1, - .pCommandBuffers = &s->commandBuffer, + if (s->pipelineSemaphore == VK_NULL_HANDLE) { + VkExportSemaphoreCreateInfo export_info = { + .sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO, + .handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + }; + VkSemaphoreCreateInfo semaphore_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + .pNext = &export_info, + }; + VK_CHECK_RESULT(vkCreateSemaphore(s->base.device, &semaphore_info, NULL, &s->pipelineSemaphore)); + } + + semaphore_signal_info[semaphore_signal_info_len++] = (VkSemaphoreSubmitInfo) { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, + .semaphore = s->pipelineSemaphore, }; - VK_CHECK_RESULT(vkQueueSubmit(s->base.queue, 1, &submitInfo, s->fence)); + + VkCommandBufferSubmitInfoKHR commandBufferInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, + .commandBuffer = s->commandBuffer, + }; + + const VkSubmitInfo2KHR submitInfo = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2_KHR, + .commandBufferInfoCount = 1, + .pCommandBufferInfos = &commandBufferInfo, + .waitSemaphoreInfoCount = semaphore_wait_info_len, + .pWaitSemaphoreInfos = semaphore_wait_info, + .signalSemaphoreInfoCount = semaphore_signal_info_len, + .pSignalSemaphoreInfos = semaphore_signal_info, + }; + VK_CHECK_RESULT(vkQueueSubmit2KHR(s->base.queue, 1, &submitInfo, s->fence)); s->started = true; - return 0; + VkSemaphoreGetFdInfoKHR get_fence_fd_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR, + .semaphore = s->pipelineSemaphore, + .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + }; + int sync_file_fd = -1; + VK_CHECK_RESULT(vkGetSemaphoreFdKHR(s->base.device, &get_fence_fd_info, &sync_file_fd)); + + int ret = 1; + for (uint32_t i = 0; i < s->n_streams; i++) { + struct vulkan_stream *p = &s->streams[i]; + + if (!vulkan_sync_export_dmabuf(&s->base, &p->buffers[p->current_buffer_id], sync_file_fd)) { + ret = 0; + } + } + close(sync_file_fd); + + return ret; } static void clear_buffers(struct vulkan_compute_state *s, struct vulkan_stream *p) diff --git a/spa/plugins/vulkan/vulkan-compute-utils.h b/spa/plugins/vulkan/vulkan-compute-utils.h index f66b36750..d24a68e77 100644 --- a/spa/plugins/vulkan/vulkan-compute-utils.h +++ b/spa/plugins/vulkan/vulkan-compute-utils.h @@ -41,6 +41,7 @@ struct vulkan_compute_state { VkCommandBuffer commandBuffer; VkFence fence; + VkSemaphore pipelineSemaphore; unsigned int initialized:1; unsigned int prepared:1; unsigned int started:1; diff --git a/spa/plugins/vulkan/vulkan-types.h b/spa/plugins/vulkan/vulkan-types.h index 45a281f2b..bcc8adaed 100644 --- a/spa/plugins/vulkan/vulkan-types.h +++ b/spa/plugins/vulkan/vulkan-types.h @@ -30,6 +30,7 @@ struct vulkan_buffer { VkImage image; VkImageView view; VkDeviceMemory memory; + VkSemaphore foreign_semaphore; }; struct vulkan_stream { @@ -67,5 +68,7 @@ struct vulkan_base { uint32_t formatInfoCount; struct vulkan_format_info *formatInfos; + bool implicit_sync_interop; + unsigned int initialized:1; }; diff --git a/spa/plugins/vulkan/vulkan-utils.c b/spa/plugins/vulkan/vulkan-utils.c index 03340a2b8..cea99e596 100644 --- a/spa/plugins/vulkan/vulkan-utils.c +++ b/spa/plugins/vulkan/vulkan-utils.c @@ -10,6 +10,7 @@ #include #include #include +#include #if !defined(__FreeBSD__) && !defined(__MidnightBSD__) #include #endif @@ -26,6 +27,7 @@ #include #include "vulkan-utils.h" +#include "dmabuf.h" //#define ENABLE_VALIDATION @@ -201,10 +203,16 @@ static int createDevice(struct vulkan_base *s, struct vulkan_base_info *info) .queueCount = 1, .pQueuePriorities = (const float[]) { 1.0f } }; + const VkPhysicalDeviceSynchronization2FeaturesKHR sync2_features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES_KHR, + .synchronization2 = VK_TRUE, + }; static const char * const extensions[] = { VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, + VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, + VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME, VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME }; const VkDeviceCreateInfo deviceCreateInfo = { @@ -213,6 +221,7 @@ static int createDevice(struct vulkan_base *s, struct vulkan_base_info *info) .pQueueCreateInfos = &queueCreateInfo, .enabledExtensionCount = SPA_N_ELEMENTS(extensions), .ppEnabledExtensionNames = extensions, + .pNext = &sync2_features, }; VK_CHECK_RESULT(vkCreateDevice(s->physicalDevice, &deviceCreateInfo, NULL, &s->device)); @@ -329,6 +338,60 @@ static void destroyFormatInfo(struct vulkan_base *s) s->formatInfoCount = 0; } +int vulkan_sync_foreign_dmabuf(struct vulkan_base *s, struct vulkan_buffer *vk_buf) +{ + VULKAN_INSTANCE_FUNCTION(vkImportSemaphoreFdKHR); + + if (!s->implicit_sync_interop) { + struct pollfd pollfd = { + .fd = vk_buf->fd, + .events = POLLIN, + }; + int timeout_ms = 1000; + int ret = poll(&pollfd, 1, timeout_ms); + if (ret < 0) { + spa_log_error(s->log, "Failed to wait for DMA-BUF fence"); + return -1; + } else if (ret == 0) { + spa_log_error(s->log, "Timed out waiting for DMA-BUF fence"); + return -1; + } + return 0; + } + + int sync_file_fd = dmabuf_export_sync_file(s->log, vk_buf->fd, DMA_BUF_SYNC_READ); + if (sync_file_fd < 0) { + spa_log_error(s->log, "Failed to extract for DMA-BUF fence"); + return -1; + } + + if (vk_buf->foreign_semaphore == VK_NULL_HANDLE) { + VkSemaphoreCreateInfo semaphore_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + }; + VK_CHECK_RESULT_WITH_CLEANUP(vkCreateSemaphore(s->device, &semaphore_info, NULL, &vk_buf->foreign_semaphore), close(sync_file_fd)); + } + + VkImportSemaphoreFdInfoKHR import_info = { + .sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR, + .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + .flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT_KHR, + .semaphore = vk_buf->foreign_semaphore, + .fd = sync_file_fd, + }; + VK_CHECK_RESULT_WITH_CLEANUP(vkImportSemaphoreFdKHR(s->device, &import_info), close(sync_file_fd)); + + return 0; +} + +bool vulkan_sync_export_dmabuf(struct vulkan_base *s, struct vulkan_buffer *vk_buf, int sync_file_fd) +{ + if (!s->implicit_sync_interop) + return false; + + return dmabuf_import_sync_file(s->log, vk_buf->fd, DMA_BUF_SYNC_WRITE, sync_file_fd); +} + int vulkan_commandPool_create(struct vulkan_base *s, VkCommandPool *commandPool) { const VkCommandPoolCreateInfo commandPoolCreateInfo = { @@ -720,6 +783,7 @@ int vulkan_base_init(struct vulkan_base *s, struct vulkan_base_info *info) CHECK(findPhysicalDevice(s)); CHECK(createDevice(s, info)); CHECK(queryFormatInfo(s, info)); + s->implicit_sync_interop = dmabuf_check_sync_file_import_export(s->log); s->initialized = true; } return 0; diff --git a/spa/plugins/vulkan/vulkan-utils.h b/spa/plugins/vulkan/vulkan-utils.h index 0576debc3..879cc6ff0 100644 --- a/spa/plugins/vulkan/vulkan-utils.h +++ b/spa/plugins/vulkan/vulkan-utils.h @@ -20,6 +20,16 @@ return _r; \ } \ } +#define VK_CHECK_RESULT_WITH_CLEANUP(f, c) \ +{ \ + VkResult _result = (f); \ + int _r = -vkresult_to_errno(_result); \ + if (_result != VK_SUCCESS) { \ + spa_log_error(s->log, "error: %d (%d %s)", _result, _r, spa_strerror(_r)); \ + (c); \ + return _r; \ + } \ +} #define VK_CHECK_RESULT_LOOP(f) \ { \ VkResult _result = (f); \ @@ -52,6 +62,9 @@ struct external_dmabuf_info { struct spa_buffer *spa_buf; }; +int vulkan_sync_foreign_dmabuf(struct vulkan_base *s, struct vulkan_buffer *vk_buf); +bool vulkan_sync_export_dmabuf(struct vulkan_base *s, struct vulkan_buffer *vk_buf, int sync_file_fd); + int vulkan_fixate_modifier(struct vulkan_base *s, struct dmabuf_fixation_info *info, uint64_t *modifier); int vulkan_create_dmabuf(struct vulkan_base *s, struct external_dmabuf_info *info, struct vulkan_buffer *vk_buf); int vulkan_import_dmabuf(struct vulkan_base *s, struct external_dmabuf_info *info, struct vulkan_buffer *vk_buf);