Index: FFmpeg/libavutil/hwcontext_vulkan.c =================================================================== --- FFmpeg.orig/libavutil/hwcontext_vulkan.c +++ FFmpeg/libavutil/hwcontext_vulkan.c @@ -59,11 +59,47 @@ #include #include #include "hwcontext_drm.h" +#ifndef DRM_FORMAT_P010 +#define DRM_FORMAT_P010 fourcc_code('P', '0', '1', '0') +#define DRM_FORMAT_P012 fourcc_code('P', '0', '1', '2') +#define DRM_FORMAT_P016 fourcc_code('P', '0', '1', '6') +#endif +#ifndef DRM_FORMAT_P210 +#define DRM_FORMAT_P210 fourcc_code('P', '2', '1', '0') +#endif +#ifndef DRM_FORMAT_S010 +#define DRM_FORMAT_S010 fourcc_code('S', '0', '1', '0') +#define DRM_FORMAT_S210 fourcc_code('S', '2', '1', '0') +#define DRM_FORMAT_S410 fourcc_code('S', '4', '1', '0') +#define DRM_FORMAT_S012 fourcc_code('S', '0', '1', '2') +#define DRM_FORMAT_S212 fourcc_code('S', '2', '1', '2') +#define DRM_FORMAT_S412 fourcc_code('S', '4', '1', '2') +#define DRM_FORMAT_S016 fourcc_code('S', '0', '1', '6') +#define DRM_FORMAT_S216 fourcc_code('S', '2', '1', '6') +#define DRM_FORMAT_S416 fourcc_code('S', '4', '1', '6') +#endif #endif #if HAVE_LINUX_DMA_BUF_H #include #include +/* dma-buf export/import sync is only available in v6.0+ */ +#ifndef DMA_BUF_IOCTL_EXPORT_SYNC_FILE +struct dma_buf_export_sync_file { + uint32_t flags; + int32_t fd; +}; +#define DMA_BUF_IOCTL_EXPORT_SYNC_FILE \ + _IOWR(DMA_BUF_BASE, 2, struct dma_buf_export_sync_file) +#endif +#ifndef DMA_BUF_IOCTL_IMPORT_SYNC_FILE +struct dma_buf_import_sync_file { + uint32_t flags; + int32_t fd; +}; +#define DMA_BUF_IOCTL_IMPORT_SYNC_FILE \ + _IOW(DMA_BUF_BASE, 3, struct dma_buf_import_sync_file) +#endif #endif #if CONFIG_CUDA @@ -167,6 +203,11 @@ typedef struct VulkanDevicePriv { /* Maximum queues */ int limit_queues; + + /* DRM dma-buf sync file ioctl ret code, tested on first use. + * 0: success, other: fail (use fallback) */ + int drm_export_sync_file_ret; + int drm_import_sync_file_ret; } VulkanDevicePriv; typedef struct VulkanFramesPriv { @@ -1794,6 +1835,7 @@ static void vulkan_device_uninit(AVHWDev static int vulkan_device_create_internal(AVHWDeviceContext *ctx, VulkanDeviceSelection *dev_select, + int use_linear_images, int disable_multiplane, AVDictionary *opts, int flags) { @@ -1866,10 +1908,14 @@ static int vulkan_device_create_internal goto end; } - /* Tiled images setting, use them by default */ - opt_d = av_dict_get(opts, "linear_images", NULL, 0); - if (opt_d) - p->use_linear_images = strtol(opt_d->value, NULL, 10); + /* Tiled images setting, use them by default. + * The use_linear_images argument takes precedent over the option */ + p->use_linear_images = use_linear_images; + if (!p->use_linear_images) { + opt_d = av_dict_get(opts, "linear_images", NULL, 0); + if (opt_d) + p->use_linear_images = strtol(opt_d->value, NULL, 10); + } /* The disable_multiplane argument takes precedent over the option */ p->disable_multiplane = disable_multiplane; @@ -2167,7 +2213,7 @@ static int vulkan_device_create(AVHWDevi } } - return vulkan_device_create_internal(ctx, &dev_select, 0, opts, flags); + return vulkan_device_create_internal(ctx, &dev_select, 0, 0, opts, flags); } static int vulkan_device_derive(AVHWDeviceContext *ctx, @@ -2209,7 +2255,8 @@ static int vulkan_device_derive(AVHWDevi dev_select.vendor_id = 0x1002; } - return vulkan_device_create_internal(ctx, &dev_select, 0, opts, flags); + /* Linear multiplane image is used to ensure compatibility with VAAPI */ + return vulkan_device_create_internal(ctx, &dev_select, 1, 0, opts, flags); } #endif #if CONFIG_LIBDRM @@ -2242,7 +2289,14 @@ static int vulkan_device_derive(AVHWDevi drmFreeDevice(&drm_dev_info); - return vulkan_device_create_internal(ctx, &dev_select, 0, opts, flags); + /* Linear multiplane image is used to ensure compatibility with DRM */ + return vulkan_device_create_internal(ctx, &dev_select, 1, 0, opts, flags); + } +#endif +#if CONFIG_RKMPP + case AV_HWDEVICE_TYPE_RKMPP: { + /* Linear multiplane image is used to ensure compatibility with RKMPP */ + return vulkan_device_create_internal(ctx, &dev_select, 1, 0, opts, flags); } #endif #if CONFIG_CUDA @@ -2265,7 +2319,7 @@ static int vulkan_device_derive(AVHWDevi * CUDA is not able to import multiplane images, so always derive a * Vulkan device with multiplane disabled. */ - return vulkan_device_create_internal(ctx, &dev_select, 1, opts, flags); + return vulkan_device_create_internal(ctx, &dev_select, 0, 1, opts, flags); } #endif default: @@ -2874,6 +2928,8 @@ static void try_export_flags(AVHWFramesC .flags = (hwctx->tiling == VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT && has_mods) ? (hwctx->img_flags) : hwctx->img_flags ? hwctx->img_flags : (VkImageCreateFlags)(VK_IMAGE_CREATE_ALIAS_BIT), }; + VkPhysicalDeviceImageFormatInfo2 pinfo_fallback = pinfo; + pinfo_fallback.format = av_vkfmt_from_pixfmt(hwfc->sw_format)[0]; nb_mods = has_mods ? drm_mod_info->drmFormatModifierCount : 1; for (int i = 0; i < nb_mods; i++) { @@ -2881,7 +2937,10 @@ static void try_export_flags(AVHWFramesC phy_dev_mod_info.drmFormatModifier = drm_mod_info->pDrmFormatModifiers[i]; ret = vk->GetPhysicalDeviceImageFormatProperties2(dev_hwctx->phys_dev, - &pinfo, &props); + &pinfo, &props); + if (ret != VK_SUCCESS) + ret = vk->GetPhysicalDeviceImageFormatProperties2(dev_hwctx->phys_dev, + &pinfo_fallback, &props); if (has_mods) av_log(hwfc, AV_LOG_VERBOSE, "GetPhysicalDeviceImageFormatProperties2: mod[%d]=0x%llx -> %s\n", @@ -3285,28 +3344,65 @@ static void vulkan_unmap_from_drm(AVHWFr static const struct { uint32_t drm_fourcc; VkFormat vk_format; + int nb_planes; + uint32_t fallbacks[AV_DRM_MAX_PLANES]; } vulkan_drm_format_map[] = { - { DRM_FORMAT_R8, VK_FORMAT_R8_UNORM }, - { DRM_FORMAT_R16, VK_FORMAT_R16_UNORM }, - { DRM_FORMAT_GR88, VK_FORMAT_R8G8_UNORM }, - { DRM_FORMAT_RG88, VK_FORMAT_R8G8_UNORM }, - { DRM_FORMAT_GR1616, VK_FORMAT_R16G16_UNORM }, - { DRM_FORMAT_RG1616, VK_FORMAT_R16G16_UNORM }, - { DRM_FORMAT_ARGB8888, VK_FORMAT_B8G8R8A8_UNORM }, - { DRM_FORMAT_XRGB8888, VK_FORMAT_B8G8R8A8_UNORM }, - { DRM_FORMAT_ABGR8888, VK_FORMAT_R8G8B8A8_UNORM }, - { DRM_FORMAT_XBGR8888, VK_FORMAT_R8G8B8A8_UNORM }, - { DRM_FORMAT_ARGB2101010, VK_FORMAT_A2B10G10R10_UNORM_PACK32 }, - { DRM_FORMAT_ABGR2101010, VK_FORMAT_A2R10G10B10_UNORM_PACK32 }, - { DRM_FORMAT_XRGB2101010, VK_FORMAT_A2B10G10R10_UNORM_PACK32 }, - { DRM_FORMAT_XBGR2101010, VK_FORMAT_A2R10G10B10_UNORM_PACK32 }, + { DRM_FORMAT_R8, VK_FORMAT_R8_UNORM, 1, { DRM_FORMAT_R8 } }, + { DRM_FORMAT_R16, VK_FORMAT_R16_UNORM, 1, { DRM_FORMAT_R16 } }, + { DRM_FORMAT_GR88, VK_FORMAT_R8G8_UNORM, 1, { DRM_FORMAT_GR88 } }, + { DRM_FORMAT_RG88, VK_FORMAT_R8G8_UNORM, 1, { DRM_FORMAT_RG88 } }, + { DRM_FORMAT_GR1616, VK_FORMAT_R16G16_UNORM, 1, { DRM_FORMAT_GR1616 } }, + { DRM_FORMAT_RG1616, VK_FORMAT_R16G16_UNORM, 1, { DRM_FORMAT_RG1616 } }, + { DRM_FORMAT_ARGB8888, VK_FORMAT_B8G8R8A8_UNORM, 1, { DRM_FORMAT_ARGB8888 } }, + { DRM_FORMAT_XRGB8888, VK_FORMAT_B8G8R8A8_UNORM, 1, { DRM_FORMAT_XRGB8888 } }, + { DRM_FORMAT_ABGR8888, VK_FORMAT_R8G8B8A8_UNORM, 1, { DRM_FORMAT_ABGR8888 } }, + { DRM_FORMAT_XBGR8888, VK_FORMAT_R8G8B8A8_UNORM, 1, { DRM_FORMAT_XBGR8888 } }, + { DRM_FORMAT_ARGB2101010, VK_FORMAT_A2B10G10R10_UNORM_PACK32, 1, { DRM_FORMAT_ARGB2101010 } }, + { DRM_FORMAT_ABGR2101010, VK_FORMAT_A2R10G10B10_UNORM_PACK32, 1, { DRM_FORMAT_ABGR2101010 } }, + { DRM_FORMAT_XRGB2101010, VK_FORMAT_A2B10G10R10_UNORM_PACK32, 1, { DRM_FORMAT_XRGB2101010 } }, + { DRM_FORMAT_XBGR2101010, VK_FORMAT_A2R10G10B10_UNORM_PACK32, 1, { DRM_FORMAT_XBGR2101010 } }, // All these DRM_FORMATs were added in the same libdrm commit. #ifdef DRM_FORMAT_XYUV8888 - { DRM_FORMAT_XYUV8888, VK_FORMAT_R8G8B8A8_UNORM }, - { DRM_FORMAT_XVYU2101010, VK_FORMAT_A2R10G10B10_UNORM_PACK32 } , - { DRM_FORMAT_XVYU12_16161616, VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16 } , - { DRM_FORMAT_XVYU16161616, VK_FORMAT_R16G16B16A16_UNORM } , + { DRM_FORMAT_XYUV8888, VK_FORMAT_R8G8B8A8_UNORM, 1, { DRM_FORMAT_XYUV8888 } }, + { DRM_FORMAT_XVYU2101010, VK_FORMAT_A2R10G10B10_UNORM_PACK32, 1, { DRM_FORMAT_XVYU2101010 } }, + { DRM_FORMAT_XVYU12_16161616, VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16, 1, { DRM_FORMAT_XVYU12_16161616 } }, + { DRM_FORMAT_XVYU16161616, VK_FORMAT_R16G16B16A16_UNORM, 1, { DRM_FORMAT_XVYU16161616 } }, +#endif + + { DRM_FORMAT_RGB888, VK_FORMAT_R8G8B8_UNORM, 1, { DRM_FORMAT_RGB888 } }, + { DRM_FORMAT_BGR888, VK_FORMAT_B8G8R8_UNORM, 1, { DRM_FORMAT_BGR888 } }, + + { DRM_FORMAT_NV12, VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, 2, { DRM_FORMAT_R8, DRM_FORMAT_GR88 } }, + { DRM_FORMAT_NV16, VK_FORMAT_G8_B8R8_2PLANE_422_UNORM, 2, { DRM_FORMAT_R8, DRM_FORMAT_GR88 } }, + { DRM_FORMAT_NV24, VK_FORMAT_G8_B8R8_2PLANE_444_UNORM, 2, { DRM_FORMAT_R8, DRM_FORMAT_GR88 } }, + + // All these DRM_FORMATs were added in the same libdrm commit. +#ifdef DRM_FORMAT_P010 + { DRM_FORMAT_P010, VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, 2, { DRM_FORMAT_R16, DRM_FORMAT_GR1616 } }, + { DRM_FORMAT_P012, VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, 2, { DRM_FORMAT_R16, DRM_FORMAT_GR1616 } }, + { DRM_FORMAT_P016, VK_FORMAT_G16_B16R16_2PLANE_420_UNORM, 2, { DRM_FORMAT_R16, DRM_FORMAT_GR1616 } }, +#endif + +#ifdef DRM_FORMAT_P210 + { DRM_FORMAT_P210, VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, 2, { DRM_FORMAT_R16, DRM_FORMAT_GR1616 } }, +#endif + + { DRM_FORMAT_YUV420, VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, 3, { DRM_FORMAT_R8, DRM_FORMAT_R8, DRM_FORMAT_R8 } }, + { DRM_FORMAT_YUV422, VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM, 3, { DRM_FORMAT_R8, DRM_FORMAT_R8, DRM_FORMAT_R8 } }, + { DRM_FORMAT_YUV444, VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM, 3, { DRM_FORMAT_R8, DRM_FORMAT_R8, DRM_FORMAT_R8 } }, + + // All these DRM_FORMATs were added in the same libdrm commit. +#ifdef DRM_FORMAT_S010 + { DRM_FORMAT_S010, VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM, 3, { DRM_FORMAT_R16, DRM_FORMAT_R16, DRM_FORMAT_R16 } }, + { DRM_FORMAT_S012, VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM, 3, { DRM_FORMAT_R16, DRM_FORMAT_R16, DRM_FORMAT_R16 } }, + { DRM_FORMAT_S016, VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM, 3, { DRM_FORMAT_R16, DRM_FORMAT_R16, DRM_FORMAT_R16 } }, + { DRM_FORMAT_S210, VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, 3, { DRM_FORMAT_R16, DRM_FORMAT_R16, DRM_FORMAT_R16 } }, + { DRM_FORMAT_S212, VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, 3, { DRM_FORMAT_R16, DRM_FORMAT_R16, DRM_FORMAT_R16 } }, + { DRM_FORMAT_S216, VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, 3, { DRM_FORMAT_R16, DRM_FORMAT_R16, DRM_FORMAT_R16 } }, + { DRM_FORMAT_S410, VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, 3, { DRM_FORMAT_R16, DRM_FORMAT_R16, DRM_FORMAT_R16 } }, + { DRM_FORMAT_S412, VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, 3, { DRM_FORMAT_R16, DRM_FORMAT_R16, DRM_FORMAT_R16 } }, + { DRM_FORMAT_S416, VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, 3, { DRM_FORMAT_R16, DRM_FORMAT_R16, DRM_FORMAT_R16 } }, #endif }; @@ -3318,6 +3414,51 @@ static inline VkFormat drm_to_vulkan_fmt return VK_FORMAT_UNDEFINED; } +static inline int decompose_drm_frame_desc(AVDRMFrameDescriptor *dst, + const AVDRMFrameDescriptor *src) +{ + int map_idx = -1; + int p = 0; + + if (!src || !dst) + return AVERROR(EINVAL); + + for (int i = 0; i < FF_ARRAY_ELEMS(vulkan_drm_format_map); i++) { + if (vulkan_drm_format_map[i].drm_fourcc == src->layers[0].format) { + map_idx = i; + break; + } + } + if (map_idx == -1) + return AVERROR(ENOSYS); + if (vulkan_drm_format_map[map_idx].nb_planes == 1 && + vulkan_drm_format_map[map_idx].fallbacks[0] == src->layers[0].format) { + return AVERROR(ENOSYS); + } + + dst->nb_objects = src->nb_objects; + memcpy(dst->objects, src->objects, sizeof(src->objects)); + + for (int i = 0; i < src->nb_layers; i++) { + for (int j = 0; j < src->layers[i].nb_planes; j++) { + if (p >= vulkan_drm_format_map[map_idx].nb_planes) + goto end; + + dst->layers[p].format = vulkan_drm_format_map[map_idx].fallbacks[p]; + dst->layers[p].nb_planes = 1; + dst->layers[p].planes[0] = src->layers[i].planes[j]; + p++; + } + } + +end: + dst->nb_layers = p; + return 0; +} + +static VkImageAspectFlags plane_index_to_aspect(int plane); +static VkImageAspectFlags plane_index_to_aspect_linear(int plane, int nb_images, int nb_planes); + static int vulkan_map_from_drm_frame_desc(AVHWFramesContext *hwfc, AVVkFrame **frame, const AVFrame *src, int flags) { @@ -3329,10 +3470,15 @@ static int vulkan_map_from_drm_frame_des VulkanDevicePriv *p = ctx->hwctx; AVVulkanDeviceContext *hwctx = &p->p; FFVulkanFunctions *vk = &p->vkctx.vkfn; - const AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)src->data[0]; + AVDRMFrameDescriptor desc_decomposed = { 0 }; + AVDRMFrameDescriptor *desc_src = (AVDRMFrameDescriptor *)src->data[0]; + AVDRMFrameDescriptor *desc = desc_src; VkBindImageMemoryInfo bind_info[AV_DRM_MAX_PLANES]; VkBindImagePlaneMemoryInfo plane_info[AV_DRM_MAX_PLANES]; + const int has_modifiers = !!(p->vkctx.extensions & FF_VK_EXT_DRM_MODIFIER_FLAGS); + int retry = 0; +start: for (int i = 0; i < desc->nb_layers; i++) { if (drm_to_vulkan_fmt(desc->layers[i].format) == VK_FORMAT_UNDEFINED) { av_log(ctx, AV_LOG_ERROR, "Unsupported DMABUF layer format %#08x!\n", @@ -3341,13 +3487,21 @@ static int vulkan_map_from_drm_frame_des } } + if (!has_modifiers && + desc->objects[0].format_modifier != DRM_FORMAT_MOD_LINEAR && + desc->objects[0].format_modifier != DRM_FORMAT_MOD_INVALID) { + av_log(ctx, AV_LOG_ERROR, "Cannot map non-linear tiling image from DRM!\n"); + err = AVERROR_EXTERNAL; + goto fail; + } + if (!(f = av_vk_frame_alloc())) { av_log(ctx, AV_LOG_ERROR, "Unable to allocate memory for AVVkFrame!\n"); err = AVERROR(ENOMEM); goto fail; } - f->tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT; + f->tiling = has_modifiers ? VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT : VK_IMAGE_TILING_LINEAR; for (int i = 0; i < desc->nb_layers; i++) { const int planes = desc->layers[i].nb_planes; @@ -3385,7 +3539,7 @@ static int vulkan_map_from_drm_frame_des .mipLevels = 1, .arrayLayers = 1, .flags = 0x0, - .tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT, + .tiling = f->tiling, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, /* specs say so */ .usage = 0x0, /* filled in below */ .samples = VK_SAMPLE_COUNT_1_BIT, @@ -3426,7 +3580,7 @@ static int vulkan_map_from_drm_frame_des fmt_props = (VkPhysicalDeviceImageFormatInfo2) { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, - .pNext = &props_ext, + .pNext = has_modifiers ? &props_ext : NULL, .format = create_info.format, .type = create_info.imageType, .tiling = create_info.tiling, @@ -3438,6 +3592,13 @@ static int vulkan_map_from_drm_frame_des ret = vk->GetPhysicalDeviceImageFormatProperties2(hwctx->phys_dev, &fmt_props, &props_ret); if (ret != VK_SUCCESS) { + /* Decompose the AVDRMFrameDescriptor and retry */ + if (!retry && !decompose_drm_frame_desc(&desc_decomposed, desc_src)) { + retry++; + desc = &desc_decomposed; + vulkan_frame_free(hwfc, f); + goto start; + } av_log(ctx, AV_LOG_ERROR, "Cannot map DRM frame to Vulkan: %s\n", ff_vk_ret2str(ret)); err = AVERROR_EXTERNAL; @@ -3549,9 +3710,12 @@ static int vulkan_map_from_drm_frame_des for (int i = 0; i < desc->nb_layers; i++) { const int planes = desc->layers[i].nb_planes; for (int j = 0; j < planes; j++) { - VkImageAspectFlagBits aspect = j == 0 ? VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT : - j == 1 ? VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT : - VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT; + VkImageAspectFlagBits aspect; + + if (has_modifiers) + aspect = plane_index_to_aspect(j); + else + aspect = plane_index_to_aspect_linear(j, desc->nb_layers, planes); plane_info[bind_counts].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO; plane_info[bind_counts].pNext = NULL; @@ -3592,15 +3756,20 @@ static int vulkan_map_from_drm_frame_syn const AVDRMFrameDescriptor *desc, int flags) { int err; +#ifdef DMA_BUF_IOCTL_EXPORT_SYNC_FILE VkResult ret; +#endif AVHWDeviceContext *ctx = hwfc->device_ctx; VulkanDevicePriv *p = ctx->hwctx; VulkanFramesPriv *fp = hwfc->hwctx; AVVulkanDeviceContext *hwctx = &p->p; +#ifdef DMA_BUF_IOCTL_EXPORT_SYNC_FILE FFVulkanFunctions *vk = &p->vkctx.vkfn; +#endif #ifdef DMA_BUF_IOCTL_EXPORT_SYNC_FILE - if (p->vkctx.extensions & FF_VK_EXT_EXTERNAL_FD_SEM) { + if ((p->vkctx.extensions & FF_VK_EXT_EXTERNAL_FD_SEM) && + !p->drm_export_sync_file_ret) { VkCommandBuffer cmd_buf; FFVkExecContext *exec; VkImageMemoryBarrier2 img_bar[AV_NUM_DATA_POINTERS]; @@ -3618,18 +3787,19 @@ static int vulkan_map_from_drm_frame_syn }; VkImportSemaphoreFdInfoKHR import_info; struct dma_buf_export_sync_file implicit_fd_info = { - .flags = DMA_BUF_SYNC_READ, + .flags = DMA_BUF_SYNC_RW, .fd = -1, }; - if (ioctl(desc->objects[i].fd, DMA_BUF_IOCTL_EXPORT_SYNC_FILE, - &implicit_fd_info)) { + if (p->drm_export_sync_file_ret = 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)); + av_log(hwctx, AV_LOG_WARNING, "Failed to retrieve implicit DRM sync file: %s, " + "disabling for future frames\n", av_err2str(err)); for (; i >= 0; i--) vk->DestroySemaphore(hwctx->act_dev, drm_sync_sem[i], hwctx->alloc); - return err; + goto fallback; } ret = vk->CreateSemaphore(hwctx->act_dev, &sem_spawn, @@ -3638,6 +3808,7 @@ static int vulkan_map_from_drm_frame_syn av_log(hwctx, AV_LOG_ERROR, "Failed to create semaphore: %s\n", ff_vk_ret2str(ret)); err = AVERROR_EXTERNAL; + close(implicit_fd_info.fd); for (; i >= 0; i--) vk->DestroySemaphore(hwctx->act_dev, drm_sync_sem[i], hwctx->alloc); return err; @@ -3656,6 +3827,7 @@ static int vulkan_map_from_drm_frame_syn av_log(hwctx, AV_LOG_ERROR, "Failed to import semaphore: %s\n", ff_vk_ret2str(ret)); err = AVERROR_EXTERNAL; + close(implicit_fd_info.fd); for (; i >= 0; i--) vk->DestroySemaphore(hwctx->act_dev, drm_sync_sem[i], hwctx->alloc); return err; @@ -3672,13 +3844,13 @@ static int vulkan_map_from_drm_frame_syn drm_sync_sem, desc->nb_objects, VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, 1); if (err < 0) - return err; + goto fail; 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; + goto fail; ff_vk_frame_barrier(&p->vkctx, exec, dst, img_bar, &nb_img_bar, VK_PIPELINE_STAGE_2_NONE, @@ -3698,14 +3870,22 @@ static int vulkan_map_from_drm_frame_syn err = ff_vk_exec_submit(&p->vkctx, exec); if (err < 0) - return err; + goto fail; + + return 0; +fail: + ff_vk_exec_discard_deps(&p->vkctx, exec); + 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); +#ifdef DMA_BUF_IOCTL_EXPORT_SYNC_FILE +fallback: +#endif + av_log(hwctx, AV_LOG_DEBUG, "No support for synchronization when importing DMA-BUFs, " + "image may be corrupted.\n"); + err = prepare_frame(hwfc, &fp->compute_exec, + (AVVkFrame *)dst->data[0], PREP_MODE_EXTERNAL_IMPORT); if (err) return err; } @@ -3742,7 +3922,7 @@ static int vulkan_map_from_drm(AVHWFrame return 0; fail: - vulkan_frame_free(hwfc->device_ctx->hwctx, f); + vulkan_frame_free(hwfc, f); dst->data[0] = NULL; return err; } @@ -4112,16 +4292,10 @@ static int vulkan_map_to(AVHWFramesConte #if CONFIG_LIBDRM #if CONFIG_VAAPI case AV_PIX_FMT_VAAPI: - if (p->vkctx.extensions & FF_VK_EXT_DRM_MODIFIER_FLAGS) - return vulkan_map_from_vaapi(hwfc, dst, src, flags); - else - return AVERROR(ENOSYS); + return vulkan_map_from_vaapi(hwfc, dst, src, flags); #endif case AV_PIX_FMT_DRM_PRIME: - if (p->vkctx.extensions & FF_VK_EXT_DRM_MODIFIER_FLAGS) - return vulkan_map_from_drm(hwfc, dst, src, flags); - else - return AVERROR(ENOSYS); + return vulkan_map_from_drm(hwfc, dst, src, flags); #endif default: return AVERROR(ENOSYS); @@ -4137,10 +4311,51 @@ typedef struct VulkanDRMMapping { static void vulkan_unmap_to_drm(AVHWFramesContext *hwfc, HWMapDescriptor *hwmap) { AVDRMFrameDescriptor *drm_desc = hwmap->priv; +#ifdef DMA_BUF_IOCTL_EXPORT_SYNC_FILE + AVVkFrame *f = (AVVkFrame *)hwmap->source->data[0]; + AVHWDeviceContext *ctx = hwfc->device_ctx; + VulkanDevicePriv *p = ctx->hwctx; + AVVulkanDeviceContext *hwctx = &p->p; + FFVulkanFunctions *vk = &p->vkctx.vkfn; +#endif + + /* On unmap from DRM, import any fences the external consumer attached + * back into the original Vulkan frame's semaphore, so Vulkan waits on + * them before reusing the frame. We don't know if the consumer did reads + * or writes, so export both. */ +#ifdef DMA_BUF_IOCTL_EXPORT_SYNC_FILE + if ((p->vkctx.extensions & FF_VK_EXT_EXTERNAL_FD_SEM) && + !p->drm_export_sync_file_ret) { + for (int i = 0; i < drm_desc->nb_objects; i++) { + struct dma_buf_export_sync_file implicit_fd_info = { + .flags = DMA_BUF_SYNC_RW, + .fd = -1, + }; - /* on unmap from DRM, make sure to import sync objects so that we are sync'd with any work that was - * done on the buffer while exported. We don't know if who used the dmabuf did reads or writes, so protect against both */ - vulkan_map_from_drm_frame_sync(hwfc, hwmap->source, drm_desc, AV_HWFRAME_MAP_READ | AV_HWFRAME_MAP_WRITE); + if (drm_desc->objects[i].fd < 0) + break; + + if (p->drm_export_sync_file_ret = ioctl(drm_desc->objects[i].fd, + DMA_BUF_IOCTL_EXPORT_SYNC_FILE, + &implicit_fd_info)) { + av_log(hwfc, AV_LOG_WARNING, "DMA_BUF_IOCTL_EXPORT_SYNC_FILE failed: %s, " + "disabling for future frames\n", av_err2str(AVERROR(errno))); + break; + } + + VkImportSemaphoreFdInfoKHR import_info = { + .sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR, + .semaphore = f->sem[i], + .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + .flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT, + .fd = implicit_fd_info.fd, + }; + + if (vk->ImportSemaphoreFdKHR(hwctx->act_dev, &import_info) != VK_SUCCESS) + close(implicit_fd_info.fd); + } + } +#endif for (int i = 0; i < drm_desc->nb_objects; i++) close(drm_desc->objects[i].fd); @@ -4167,6 +4382,20 @@ static VkImageAspectFlags plane_index_to return VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT; } +static VkImageAspectFlags plane_index_to_aspect_linear(int plane, int nb_images, int nb_planes) +{ + if (nb_images == 1 && nb_planes > 1) { + if (plane == 0) return VK_IMAGE_ASPECT_PLANE_0_BIT; + if (plane == 1) return VK_IMAGE_ASPECT_PLANE_1_BIT; + if (plane == 2) return VK_IMAGE_ASPECT_PLANE_2_BIT; + + av_assert2(0 && "Invalid plane index"); + return VK_IMAGE_ASPECT_PLANE_0_BIT; + } + + return VK_IMAGE_ASPECT_COLOR_BIT; +} + #ifdef DMA_BUF_IOCTL_EXPORT_SYNC_FILE static int vulkan_drm_export_sync_fd(AVHWFramesContext *hwfc, AVVkFrame *f, VulkanFramesPriv *fp, int nb_sems) @@ -4245,14 +4474,22 @@ static int vulkan_map_to_drm(AVHWFramesC VulkanFramesPriv *fp = hwfc->hwctx; const int planes = av_pix_fmt_count_planes(hwfc->sw_format); const int nb_images = ff_vk_count_images(f); + const int is_modifier_tiling = (p->vkctx.extensions & FF_VK_EXT_DRM_MODIFIER_FLAGS) && + f->tiling == VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT; VkImageDrmFormatModifierPropertiesEXT drm_mod = { .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT, }; const int nb_sems = nb_images; int free_drm_desc_on_err = 1; int sync_fd = -1; + AVDRMFrameDescriptor *drm_desc = NULL; - AVDRMFrameDescriptor *drm_desc = av_mallocz(sizeof(*drm_desc)); + if (f->tiling == VK_IMAGE_TILING_OPTIMAL) { + av_log(hwfc, AV_LOG_ERROR, "Cannot map optimal tiling image to DRM!\n"); + return AVERROR(EINVAL); + } + + drm_desc = av_mallocz(sizeof(*drm_desc)); if (!drm_desc) return AVERROR(ENOMEM); @@ -4261,8 +4498,9 @@ static int vulkan_map_to_drm(AVHWFramesC goto end; #ifdef DMA_BUF_IOCTL_EXPORT_SYNC_FILE - if ((p->vkctx.extensions & FF_VK_EXT_EXTERNAL_FD_SEM) && - f->tiling == VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT && + // Known to be not working on AMD GPU with VAAPI, disable it for now + if (0 && (p->vkctx.extensions & FF_VK_EXT_EXTERNAL_FD_SEM) && + !p->drm_import_sync_file_ret && vk->GetSemaphoreFdKHR && vk->CreateSemaphore) { err = vulkan_drm_export_sync_fd(hwfc, f, fp, nb_sems); if (err < 0) @@ -4290,13 +4528,16 @@ static int vulkan_map_to_drm(AVHWFramesC /* It will be freed in ff_hwframe_map_create callback */ free_drm_desc_on_err = 0; - ret = vk->GetImageDrmFormatModifierPropertiesEXT(hwctx->act_dev, f->img[0], - &drm_mod); - if (ret != VK_SUCCESS) { - av_log(hwfc, AV_LOG_ERROR, "Failed to retrieve DRM format modifier!\n"); - err = AVERROR_EXTERNAL; - goto end; - } + if (is_modifier_tiling) { + ret = vk->GetImageDrmFormatModifierPropertiesEXT(hwctx->act_dev, f->img[0], + &drm_mod); + if (ret != VK_SUCCESS) { + av_log(hwfc, AV_LOG_ERROR, "Failed to retrieve DRM format modifier!\n"); + err = AVERROR_EXTERNAL; + goto end; + } + } else + drm_mod.drmFormatModifier = DRM_FORMAT_MOD_LINEAR; for (int i = 0; (i < planes) && (f->mem[i]); i++) { VkMemoryGetFdInfoKHR export_info = { @@ -4313,16 +4554,20 @@ static int vulkan_map_to_drm(AVHWFramesC goto end; } -#if HAVE_LINUX_DMA_BUF_H && defined(DMA_BUF_IOCTL_IMPORT_SYNC_FILE) - if (sync_fd >= 0) { +#ifdef DMA_BUF_IOCTL_IMPORT_SYNC_FILE + // Known to be not working on AMD GPU with VAAPI, disable it for now + if (0 && sync_fd >= 0 && !p->drm_import_sync_file_ret) { int dup_fd = dup(sync_fd); if (dup_fd >= 0) { struct dma_buf_import_sync_file import_info = { - .flags = DMA_BUF_SYNC_WRITE, + .flags = DMA_BUF_SYNC_RW, .fd = dup_fd, }; - if (ioctl(drm_desc->objects[i].fd, DMA_BUF_IOCTL_IMPORT_SYNC_FILE, &import_info) < 0) - av_log(hwfc, AV_LOG_WARNING, "DMA_BUF_IOCTL_IMPORT_SYNC_FILE failed: %s\n", av_err2str(AVERROR(errno))); + if (p->drm_import_sync_file_ret = ioctl(drm_desc->objects[i].fd, + DMA_BUF_IOCTL_IMPORT_SYNC_FILE, + &import_info)) + av_log(hwfc, AV_LOG_WARNING, "DMA_BUF_IOCTL_IMPORT_SYNC_FILE failed: %s, " + "disabling for future frames\n", av_err2str(AVERROR(errno))); close(dup_fd); } else { av_log(hwfc, AV_LOG_WARNING, "dup(sync_fd) failed: %s\n", av_err2str(AVERROR(errno))); @@ -4341,8 +4586,13 @@ static int vulkan_map_to_drm(AVHWFramesC VkFormat plane_vkfmt = av_vkfmt_from_pixfmt(hwfc->sw_format)[i]; drm_desc->layers[i].format = vulkan_fmt_to_drm(plane_vkfmt); - drm_desc->layers[i].nb_planes = fp->drm_format_modifier_properties[i].drmFormatModifierPlaneCount; + drm_desc->layers[i].nb_planes = is_modifier_tiling ? fp->drm_format_modifier_properties[i].drmFormatModifierPlaneCount : 1; + if (drm_desc->layers[i].format == DRM_FORMAT_INVALID) { + av_log(hwfc, AV_LOG_ERROR, "Cannot map to DRM layer, unsupported!\n"); + err = AVERROR_PATCHWELCOME; + goto end; + } if (drm_desc->layers[i].nb_planes > MAX_MEMORY_PLANES) { av_log(hwfc, AV_LOG_ERROR, "Too many memory planes for DRM format!\n"); err = AVERROR_EXTERNAL; @@ -4351,10 +4601,13 @@ static int vulkan_map_to_drm(AVHWFramesC for (int j = 0; j < drm_desc->layers[i].nb_planes; j++) { VkSubresourceLayout layout; - int aspect_plane = (nb_images == 1) ? i : j; - VkImageSubresource sub = { - .aspectMask = plane_index_to_aspect(aspect_plane), - }; + VkImageSubresource sub = { 0 }; + + if (is_modifier_tiling) { + int aspect_plane = (nb_images == 1) ? i : j; + sub.aspectMask = plane_index_to_aspect(aspect_plane); + } else + sub.aspectMask = plane_index_to_aspect_linear(i, nb_images, planes); drm_desc->layers[i].planes[j].object_index = FFMIN(i, drm_desc->nb_objects - 1); @@ -4362,34 +4615,24 @@ static int vulkan_map_to_drm(AVHWFramesC drm_desc->layers[i].planes[j].offset = layout.offset; drm_desc->layers[i].planes[j].pitch = layout.rowPitch; } - - if (drm_desc->layers[i].format == DRM_FORMAT_INVALID) { - av_log(hwfc, AV_LOG_ERROR, "Cannot map to DRM layer, unsupported!\n"); - err = AVERROR_PATCHWELCOME; - goto end; - } - - - if (f->tiling == VK_IMAGE_TILING_OPTIMAL) - continue; - } dst->width = src->width; dst->height = src->height; dst->data[0] = (uint8_t *)drm_desc; - dst->hw_frames_ctx = av_buffer_ref(src->hw_frames_ctx); if (sync_fd >= 0) close(sync_fd); - av_log(hwfc, AV_LOG_VERBOSE, "Mapped AVVkFrame to a DRM object!\n"); + av_log(hwfc, AV_LOG_DEBUG, "Mapped AVVkFrame to a DRM object!\n"); return 0; end: - for (int i = 0; i < drm_desc->nb_objects; i++) + for (int i = 0; i < drm_desc->nb_objects; i++) { close(drm_desc->objects[i].fd); + drm_desc->objects[i].fd = -1; + } if (free_drm_desc_on_err) av_free(drm_desc); if (sync_fd >= 0) @@ -4433,16 +4676,10 @@ static int vulkan_map_from(AVHWFramesCon switch (dst->format) { #if CONFIG_LIBDRM case AV_PIX_FMT_DRM_PRIME: - if (p->vkctx.extensions & FF_VK_EXT_DRM_MODIFIER_FLAGS) - return vulkan_map_to_drm(hwfc, dst, src, flags); - else - return AVERROR(ENOSYS); + return vulkan_map_to_drm(hwfc, dst, src, flags); #if CONFIG_VAAPI case AV_PIX_FMT_VAAPI: - if (p->vkctx.extensions & FF_VK_EXT_DRM_MODIFIER_FLAGS) - return vulkan_map_to_vaapi(hwfc, dst, src, flags); - else - return AVERROR(ENOSYS); + return vulkan_map_to_vaapi(hwfc, dst, src, flags); #endif #endif default: