diff options
author | Lynne <dev@lynne.ee> | 2024-09-20 05:28:19 +0200 |
---|---|---|
committer | Lynne <dev@lynne.ee> | 2024-09-22 02:11:08 +0200 |
commit | a577d313b2c14c855ab8aa69bbe3527bd7727212 (patch) | |
tree | d90868a14c912e13e59d4c38f3ca3c18063d7394 | |
parent | 2395444c80cce3879196a465fb76a33c357711f8 (diff) | |
download | ffmpeg-a577d313b2c14c855ab8aa69bbe3527bd7727212.tar.gz |
hwcontext_vulkan: add support for implicit DRM synchronization
More recent kernel versions allow for users to extract a sync_file
handle from a DMA-BUF, which can then be imported into Vulkan as a
binary semaphore.
This finally allows for synchronization between Vulkan and DMA-BUF
images, such as those from screen capture software, or VAAPI,
avoiding any corruption artifacts.
This is done fully asynchronously, where we use the kernel's
given binary semaphores as a dependency to increment the image's
usual VkSemaphores we allocate. The old imported binary semaphores
are cleaned up after execution as usual.
In the future, hwcontext_drm should receive support for explicitly
synchronized images as well, which would make the synchronization
more robust and portable.
-rw-r--r-- | libavutil/hwcontext_vulkan.c | 148 | ||||
-rw-r--r-- | libavutil/vulkan_functions.h | 1 |
2 files changed, 138 insertions, 11 deletions
diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c index 1de08c6181..23abd19eeb 100644 --- a/libavutil/hwcontext_vulkan.c +++ b/libavutil/hwcontext_vulkan.c @@ -61,6 +61,11 @@ #include "hwcontext_drm.h" #endif +#if HAVE_LINUX_DMA_BUF_H +#include <sys/ioctl.h> +#include <linux/dma-buf.h> +#endif + #if CONFIG_CUDA #include "hwcontext_cuda_internal.h" #include "cuda_check.h" @@ -2842,7 +2847,6 @@ static int vulkan_map_from_drm_frame_desc(AVHWFramesContext *hwfc, AVVkFrame **f VulkanDevicePriv *p = ctx->hwctx; AVVulkanDeviceContext *hwctx = &p->p; FFVulkanFunctions *vk = &p->vkctx.vkfn; - VulkanFramesPriv *fp = hwfc->hwctx; const AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)src->data[0]; VkBindImageMemoryInfo bind_info[AV_DRM_MAX_PLANES]; VkBindImagePlaneMemoryInfo plane_info[AV_DRM_MAX_PLANES]; @@ -2990,12 +2994,7 @@ static int vulkan_map_from_drm_frame_desc(AVHWFramesContext *hwfc, AVVkFrame **f goto fail; } - /* We'd import a semaphore onto the one we created using - * vkImportSemaphoreFdKHR but unfortunately neither DRM nor VAAPI - * offer us anything we could import and sync with, so instead - * just signal the semaphore we created. */ - - f->queue_family[i] = p->nb_img_qfs > 1 ? VK_QUEUE_FAMILY_IGNORED : p->img_qfs[0]; + f->queue_family[i] = VK_QUEUE_FAMILY_EXTERNAL; f->layout[i] = create_info.initialLayout; f->access[i] = 0x0; f->sem_value[i] = 0; @@ -3097,10 +3096,6 @@ static int vulkan_map_from_drm_frame_desc(AVHWFramesContext *hwfc, AVVkFrame **f goto fail; } - err = prepare_frame(hwfc, &fp->compute_exec, f, PREP_MODE_EXTERNAL_IMPORT); - if (err) - goto fail; - *frame = f; return 0; @@ -3111,6 +3106,133 @@ fail: return err; } +static int vulkan_map_from_drm_frame_sync(AVHWFramesContext *hwfc, AVFrame *dst, + const AVFrame *src, int flags) +{ + int err; + VkResult ret; + AVHWDeviceContext *ctx = hwfc->device_ctx; + VulkanDevicePriv *p = ctx->hwctx; + VulkanFramesPriv *fp = hwfc->hwctx; + AVVulkanDeviceContext *hwctx = &p->p; + FFVulkanFunctions *vk = &p->vkctx.vkfn; + + const AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)src->data[0]; + +#ifdef DMA_BUF_IOCTL_EXPORT_SYNC_FILE + if (p->vkctx.extensions & FF_VK_EXT_EXTERNAL_FD_SEM) { + VkCommandBuffer cmd_buf; + FFVkExecContext *exec; + VkImageMemoryBarrier2 img_bar[AV_NUM_DATA_POINTERS]; + VkSemaphore drm_sync_sem[AV_DRM_MAX_PLANES] = { 0 }; + int nb_img_bar = 0; + + for (int i = 0; i < desc->nb_objects; i++) { + VkSemaphoreTypeCreateInfo sem_type_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO, + .semaphoreType = VK_SEMAPHORE_TYPE_BINARY, + }; + VkSemaphoreCreateInfo sem_spawn = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + .pNext = &sem_type_info, + }; + VkImportSemaphoreFdInfoKHR import_info; + struct dma_buf_export_sync_file implicit_fd_info = { + .flags = DMA_BUF_SYNC_READ, + .fd = -1, + }; + + if (ioctl(desc->objects[i].fd, DMA_BUF_IOCTL_EXPORT_SYNC_FILE, + &implicit_fd_info)) { + err = AVERROR(errno); + av_log(hwctx, AV_LOG_ERROR, "Failed to retrieve implicit DRM sync file: %s\n", + av_err2str(err)); + for (; i >= 0; i--) + vk->DestroySemaphore(hwctx->act_dev, drm_sync_sem[i], hwctx->alloc); + return err; + } + + ret = vk->CreateSemaphore(hwctx->act_dev, &sem_spawn, + hwctx->alloc, &drm_sync_sem[i]); + if (ret != VK_SUCCESS) { + av_log(hwctx, AV_LOG_ERROR, "Failed to create semaphore: %s\n", + ff_vk_ret2str(ret)); + err = AVERROR_EXTERNAL; + for (; i >= 0; i--) + vk->DestroySemaphore(hwctx->act_dev, drm_sync_sem[i], hwctx->alloc); + return err; + } + + import_info = (VkImportSemaphoreFdInfoKHR) { + .sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR, + .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + .flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT, + .semaphore = drm_sync_sem[i], + .fd = implicit_fd_info.fd, + }; + + ret = vk->ImportSemaphoreFdKHR(hwctx->act_dev, &import_info); + if (ret != VK_SUCCESS) { + av_log(hwctx, AV_LOG_ERROR, "Failed to import semaphore: %s\n", + ff_vk_ret2str(ret)); + err = AVERROR_EXTERNAL; + for (; i >= 0; i--) + vk->DestroySemaphore(hwctx->act_dev, drm_sync_sem[i], hwctx->alloc); + return err; + } + } + + exec = ff_vk_exec_get(&fp->compute_exec); + cmd_buf = exec->buf; + + ff_vk_exec_start(&p->vkctx, exec); + + /* Ownership of semaphores is passed */ + err = ff_vk_exec_add_dep_bool_sem(&p->vkctx, exec, + drm_sync_sem, desc->nb_objects, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, 1); + if (err < 0) + return err; + + err = ff_vk_exec_add_dep_frame(&p->vkctx, exec, dst, + VK_PIPELINE_STAGE_2_NONE, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT); + if (err < 0) + return err; + + ff_vk_frame_barrier(&p->vkctx, exec, dst, img_bar, &nb_img_bar, + VK_PIPELINE_STAGE_2_NONE, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + ((flags & AV_HWFRAME_MAP_READ) ? + VK_ACCESS_2_SHADER_SAMPLED_READ_BIT : 0x0) | + ((flags & AV_HWFRAME_MAP_WRITE) ? + VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT : 0x0), + VK_IMAGE_LAYOUT_GENERAL, + VK_QUEUE_FAMILY_IGNORED); + + vk->CmdPipelineBarrier2(cmd_buf, &(VkDependencyInfo) { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pImageMemoryBarriers = img_bar, + .imageMemoryBarrierCount = nb_img_bar, + }); + + err = ff_vk_exec_submit(&p->vkctx, exec); + if (err < 0) + return err; + } else +#endif + { + AVVkFrame *f = (AVVkFrame *)dst->data[0]; + av_log(hwctx, AV_LOG_WARNING, "No support for synchronization when importing DMA-BUFs, " + "image may be corrupted.\n"); + err = prepare_frame(hwfc, &fp->compute_exec, f, PREP_MODE_EXTERNAL_IMPORT); + if (err) + return err; + } + + return 0; +} + static int vulkan_map_from_drm(AVHWFramesContext *hwfc, AVFrame *dst, const AVFrame *src, int flags) { @@ -3130,6 +3252,10 @@ static int vulkan_map_from_drm(AVHWFramesContext *hwfc, AVFrame *dst, if (err < 0) goto fail; + err = vulkan_map_from_drm_frame_sync(hwfc, dst, src, flags); + if (err < 0) + return err; + av_log(hwfc, AV_LOG_DEBUG, "Mapped DRM object to Vulkan!\n"); return 0; diff --git a/libavutil/vulkan_functions.h b/libavutil/vulkan_functions.h index d4697ec34c..6aeaf4e79a 100644 --- a/libavutil/vulkan_functions.h +++ b/libavutil/vulkan_functions.h @@ -114,6 +114,7 @@ typedef enum FFVulkanExtensions { \ /* Semaphores */ \ MACRO(1, 1, FF_VK_EXT_EXTERNAL_FD_SEM, GetSemaphoreFdKHR) \ + MACRO(1, 1, FF_VK_EXT_EXTERNAL_FD_SEM, ImportSemaphoreFdKHR) \ MACRO(1, 1, FF_VK_EXT_NO_FLAG, CreateSemaphore) \ MACRO(1, 1, FF_VK_EXT_NO_FLAG, WaitSemaphores) \ MACRO(1, 1, FF_VK_EXT_NO_FLAG, DestroySemaphore) \ |