[FFmpeg-devel] [PATCH 1/6] lavu: add a Vulkan hwcontext

Mark Thompson sw at jkqxz.net
Sun Sep 2 23:39:34 EEST 2018


(Finally got around to this again, apologies for the delay.  The Vulkan-only parts all look pretty much fine to me (modulo one transfer case below), but mapping seems to still have some issues.)

On 21/06/18 17:55, Rostislav Pehlivanov wrote:
> This commit adds a Vulkan hwcontext, currently capable of mapping DRM and
> VAAPI frames but additional functionality can be added later to support
> importing of D3D11 surfaces as well as exporting to various other APIs.

Have you tried on any non-desktop platforms?  With the V4L2 DRM PRIME stuff we should be able to import here (and Rockchip would already work if they would just compile the Mali binary with Vulkan enabled...).

> This context requires the newest stable version of the Vulkan API.
> 
> It makes use of every part of the Vulkan spec in order to ensure fastest
> possible uploading, downloading and mapping of frames.
> 
> To be useful for non-RGB images an implementation with the YUV images
> extension is needed. All current implementations support that with the
> exception of AMD, though support is coming soon for Mesa.

Is there any ETA on when that will be?

These formats still don't seem to be getting much use - I wonder whether it would be sensible to admit the possibility of multiple-image cases in the format definition (so NV12 as R8 + R8G8, in the same way the OpenCL does).  That doesn't need to have any hwcontext implementation now, but there might need to be some checks in the filters to distinguish the two different representations of multiplanar image types (though they all actually operate on the separate planes, so hopefully that can be some minor amount of common code).

> Signed-off-by: Rostislav Pehlivanov <atomnuker at gmail.com>
> ---
>  configure                      |   10 +
>  doc/APIchanges                 |    4 +
>  libavutil/Makefile             |    3 +
>  libavutil/hwcontext.c          |    4 +
>  libavutil/hwcontext.h          |    1 +
>  libavutil/hwcontext_internal.h |    1 +
>  libavutil/hwcontext_vulkan.c   | 2225 ++++++++++++++++++++++++++++++++
>  libavutil/hwcontext_vulkan.h   |  133 ++
>  libavutil/pixdesc.c            |    4 +
>  libavutil/pixfmt.h             |    4 +
>  libavutil/version.h            |    4 +-
>  11 files changed, 2391 insertions(+), 2 deletions(-)
>  create mode 100644 libavutil/hwcontext_vulkan.c
>  create mode 100644 libavutil/hwcontext_vulkan.h
> 
> ...
> diff --git a/doc/APIchanges b/doc/APIchanges
> index efe15ba4e0..b2684eb442 100644
> --- a/doc/APIchanges
> +++ b/doc/APIchanges
> @@ -15,6 +15,10 @@ libavutil:     2017-10-21
>  
>  API changes, most recent first:
>  
> +2018-04-xx - xxxxxxxxxx - lavu 56.19.100 - hwcontext.h
> +  Add AV_PIX_FMT_VULKAN

Is in pixfmt.h.

> +  Add AV_HWDEVICE_TYPE_VULKAN and implementation.
> +
>  2018-05-xx - xxxxxxxxxx - lavf 58.15.100 - avformat.h
>    Add pmt_version field to AVProgram
>  
> ...
> diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
> new file mode 100644
> index 0000000000..91d06712d4
> --- /dev/null
> +++ b/libavutil/hwcontext_vulkan.c
> ...
> +
> +static int vulkan_device_create(AVHWDeviceContext *ctx, const char *device,
> +                                AVDictionary *opts, int flags)
> +{
> +    VulkanDeviceSelection dev_select = { 0 };
> +    if (device && device[0]) {
> +        char *end = NULL;
> +        dev_select.index = strtol(device, &end, 10);
> +        if (end == device) {
> +            dev_select.index = 0;
> +            dev_select.name  = device;
> +        }
> +    }
> +
> +    return vulkan_device_create_internal(ctx, &dev_select, opts, flags);
> +}
> +
> +static int vulkan_device_derive(AVHWDeviceContext *ctx,
> +                                AVHWDeviceContext *src_ctx, int flags)
> +{
> +    VulkanDeviceSelection dev_select = { 0 };
> +
> +    switch(src_ctx->type) {
> +#if CONFIG_LIBDRM
> +#if CONFIG_VAAPI
> +    case AV_HWDEVICE_TYPE_VAAPI: {
> +        AVVAAPIDeviceContext *src_hwctx = src_ctx->hwctx;
> +        const char *vendor = vaQueryVendorString(src_hwctx->display);
> +        if (!vendor) {
> +            av_log(ctx, AV_LOG_ERROR, "Unable to get device info from vaapi!\n");
> +            return AVERROR_EXTERNAL;
> +        }
> +
> +        if (strstr(vendor, "Intel"))
> +            dev_select.vendor_id = 0x8086;
> +        if (strstr(vendor, "AMD"))
> +            dev_select.vendor_id = 0x1002;
> +
> +        return vulkan_device_create_internal(ctx, &dev_select, NULL, flags);
> +    }
> +#endif
> +    case AV_HWDEVICE_TYPE_DRM: {
> +        AVDRMDeviceContext *src_hwctx = src_ctx->hwctx;
> +
> +        drmDevice *drm_dev_info;
> +        int err = drmGetDevice(src_hwctx->fd, &drm_dev_info);
> +        if (err) {
> +            av_log(ctx, AV_LOG_ERROR, "Unable to get device info from drm fd!\n");
> +            return AVERROR_EXTERNAL;
> +        }
> +
> +        if (drm_dev_info->bustype == DRM_BUS_PCI)
> +            dev_select.pci_device = drm_dev_info->deviceinfo.pci->device_id;
> +
> +        drmFreeDevice(&drm_dev_info);
> +
> +        return vulkan_device_create_internal(ctx, &dev_select, NULL, flags);
> +    }
> +#endif

Both of these look like they will pick a random device if they can't get any mapping information?  I think it would probably be better for derivation to fail in that case.

(I guess I feel the same way about the picking a random device on create with no arguments, but that doesn't have the same possibility of blowing up in a weird way later.)

> +    default:
> +        return AVERROR(ENOSYS);
> +    }
> +}
> +
> ...
> +
> +static int create_frame(AVHWFramesContext *hwfc, AVVkFrame **frame,
> +                        VkImageTiling tiling, VkImageUsageFlagBits usage,
> +                        int disjoint, void *create_pnext, void *alloc_pnext,
> +                        size_t alloc_pnext_stride)
> +{
> +    int err;
> +    VkResult ret;
> +    AVHWDeviceContext *ctx = hwfc->device_ctx;
> +    enum AVPixelFormat format = hwfc->sw_format;
> +    VkFormat img_fmt = av_vkfmt_from_pixfmt(format);
> +    const int planes = av_pix_fmt_count_planes(format);
> +
> +    /* Allocated */
> +    AVVkFrame *f = NULL;
> +
> +    /* Contexts */
> +    AVVulkanDeviceContext *hwctx = ctx->hwctx;
> +    VulkanDevicePriv *p = ctx->internal->priv;
> +
> +    /* Image properties */
> +    VkFormat possible_fmts[2];
> +    VkImageFormatListCreateInfoKHR img_fmt_list = {
> +        .sType           = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR,
> +        .pNext           = create_pnext,
> +        .pViewFormats    = possible_fmts,
> +        .viewFormatCount = 1,
> +    };
> +    VkImageCreateInfo image_create_info = {
> +        .sType         = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
> +        .pNext         = create_pnext,
> +        .imageType     = VK_IMAGE_TYPE_2D,
> +        .format        = img_fmt,
> +        .extent.width  = hwfc->width,
> +        .extent.height = hwfc->height,
> +        .extent.depth  = 1,
> +        .mipLevels     = 1,
> +        .arrayLayers   = 1,
> +        .flags         = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT |
> +                         VK_IMAGE_CREATE_EXTENDED_USAGE_BIT |
> +                         (disjoint ? VK_IMAGE_CREATE_DISJOINT_BIT : 0),
> +        .tiling        = tiling,
> +        .initialLayout = tiling == VK_IMAGE_TILING_LINEAR ?
> +                                   VK_IMAGE_LAYOUT_PREINITIALIZED :
> +                                   VK_IMAGE_LAYOUT_UNDEFINED,
> +        .usage         = usage,
> +        .sharingMode   = VK_SHARING_MODE_EXCLUSIVE,
> +        .samples       = VK_SAMPLE_COUNT_1_BIT,
> +    };
> +
> +    if (img_fmt == VK_FORMAT_UNDEFINED) {
> +        av_log(ctx, AV_LOG_ERROR, "Unsupported image format!\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    f = av_mallocz(sizeof(*f));
> +    if (!f) {
> +        av_log(ctx, AV_LOG_ERROR, "Unable to allocate memory for AVVkFrame!\n");
> +        err = AVERROR(ENOMEM);
> +        goto fail;
> +    }
> +
> +    /* Needed */
> +    f->flags     = 0;
> +    f->mem_count = disjoint ? planes : 1;
> +    f->tiling    = image_create_info.tiling;
> +    f->layout    = image_create_info.initialLayout;
> +    f->access    = 0;
> +
> +    possible_fmts[0] = image_create_info.format;
> +    /* Mark the formats that a VkImageView can be made of if supported */
> +    if ((planes > 1) && (p->extensions & EXT_IMAGE_FORMAT_LIST)) {
> +        const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(format);
> +        switch (desc->comp[0].depth) {
> +        case  8: possible_fmts[1] =           VK_FORMAT_R8_UNORM; break;
> +        case 10: possible_fmts[1] = VK_FORMAT_R10X6_UNORM_PACK16; break;
> +        case 12: possible_fmts[1] = VK_FORMAT_R12X4_UNORM_PACK16; break;
> +        case 16: possible_fmts[1] =          VK_FORMAT_R16_UNORM; break;
> +        }

Does this want to have the R8G8 and similar cases as well?  (Those views are created in lavfi.)

> +        img_fmt_list.viewFormatCount++;
> +        image_create_info.pNext = &img_fmt_list;
> +    }
> +
> +    /* Create the image */
> +    ret = vkCreateImage(hwctx->act_dev, &image_create_info,
> +                        hwctx->alloc, &f->img);
> +    if (ret != VK_SUCCESS) {
> +        av_log(ctx, AV_LOG_ERROR, "Image creation failure: %s\n",
> +               vk_ret2str(ret));
> +        err = AVERROR(EINVAL);
> +        goto fail;
> +    }
> +
> +    if ((err = alloc_bind_mem(ctx, f, alloc_pnext, alloc_pnext_stride)))
> +        goto fail;
> +
> +    *frame = f;
> +    return 0;
> +
> +fail:
> +    vulkan_frame_free(hwctx, (uint8_t *)f);
> +    return err;
> +}
> +
> ...
> +
> +static int vulkan_map_frame(AVHWFramesContext *hwfc, AVFrame *dst,
> +                            const AVFrame *src, int flags)
> +{
> +    VkResult ret;
> +    int err, mapped_mem_count = 0;
> +    AVVkFrame *f = (AVVkFrame *)src->data[0];
> +    AVVulkanDeviceContext *hwctx = hwfc->device_ctx->hwctx;
> +    const int planes = av_pix_fmt_count_planes(hwfc->sw_format);
> +
> +    VulkanMapping *map = av_mallocz(sizeof(VulkanMapping));
> +    if (!map)
> +        return AVERROR(EINVAL);
> +
> +    if (src->format != AV_PIX_FMT_VULKAN) {
> +        av_log(hwfc, AV_LOG_ERROR, "Cannot map from pixel format %s!\n",
> +               av_get_pix_fmt_name(src->format));
> +        err = AVERROR(EINVAL);
> +        goto fail;
> +    }
> +
> +    if (!(f->flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) ||
> +        !(f->tiling == VK_IMAGE_TILING_LINEAR)) {

Is this a hard constraint?  On Intel at least you can map tiled memory as linear through the MMU.

(Mainly for my curiosity - even if it isn't required it may well not be worth handling the other case.)

> +        av_log(hwfc, AV_LOG_ERROR, "Unable to map frame, not host visible "
> +               "and linear!\n");
> +        err = AVERROR(EINVAL);
> +        goto fail;
> +    }
> +
> +    dst->width  = src->width;
> +    dst->height = src->height;
> +
> +    for (int i = 0; i < f->mem_count; i++) {
> +        ret = vkMapMemory(hwctx->act_dev, f->mem[i], 0,
> +                          VK_WHOLE_SIZE, 0, (void **)&dst->data[i]);
> +        if (ret != VK_SUCCESS) {
> +            av_log(hwfc, AV_LOG_ERROR, "Failed to map image memory: %s\n",
> +                vk_ret2str(ret));
> +            err = AVERROR_EXTERNAL;
> +            goto fail;
> +        }
> +        mapped_mem_count++;
> +    }
> +
> +    /* For non disjoint memory duplicate them */
> +    if (f->mem_count == 1)
> +        for (int i = 1; i < planes; i++)
> +            dst->data[i] = dst->data[0];
> +
> +    /* Check if the memory contents matter */
> +    if (((flags & AV_HWFRAME_MAP_READ) || !(flags & AV_HWFRAME_MAP_OVERWRITE)) &&
> +        !(f->flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
> +        VkMappedMemoryRange map_mem_ranges[AV_NUM_DATA_POINTERS] = { { 0 } };
> +        for (int i = 0; i < f->mem_count; i++) {
> +            map_mem_ranges[i].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
> +            map_mem_ranges[i].size = VK_WHOLE_SIZE;
> +            map_mem_ranges[i].memory = f->mem[i];
> +        }
> +
> +        ret = vkInvalidateMappedMemoryRanges(hwctx->act_dev, f->mem_count,
> +                                             map_mem_ranges);
> +        if (ret != VK_SUCCESS) {
> +            av_log(hwfc, AV_LOG_ERROR, "Failed to invalidate memory: %s\n",
> +                   vk_ret2str(ret));
> +            err = AVERROR_EXTERNAL;
> +            goto fail;
> +        }
> +    }
> +
> +    for (int i = 0; i < planes; i++) {
> +        VkImageSubresource sub = {
> +            .aspectMask = planes < 2 ? VK_IMAGE_ASPECT_COLOR_BIT :
> +                              i == 0 ? VK_IMAGE_ASPECT_PLANE_0_BIT :
> +                              i == 1 ? VK_IMAGE_ASPECT_PLANE_1_BIT :
> +                                       VK_IMAGE_ASPECT_PLANE_2_BIT,
> +        };
> +        VkSubresourceLayout layout;
> +        vkGetImageSubresourceLayout(hwctx->act_dev, f->img, &sub, &layout);
> +        dst->data[i]    += layout.offset;
> +        dst->linesize[i] = layout.rowPitch;
> +    }
> +
> +    map->frame = f;
> +    map->flags = flags;
> +
> +    err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src,
> +                                &vulkan_unmap_frame, map);
> +    if (err < 0)
> +        goto fail;
> +
> +    return 0;
> +
> +fail:
> +    for (int i = 0; i < mapped_mem_count; i++)
> +        vkUnmapMemory(hwctx->act_dev, f->mem[i]);
> +
> +    av_free(map);
> +    return err;
> +}
> +
> ...
> +
> +static int vulkan_map_from_drm_frame_desc(AVHWFramesContext *hwfc, AVVkFrame **f,
> +                                          AVDRMFrameDescriptor *desc)
> +{
> +    int err = 0;
> +
> +    /* Destination frame */
> +#if HAVE_VULKAN_DRM_MOD
> +    uint64_t modifier_buf[AV_NUM_DATA_POINTERS];
> +    VkImageDrmFormatModifierListCreateInfoEXT drm_mod = {
> +        .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT,
> +    };
> +#endif
> +    VkExternalMemoryImageCreateInfo ext_info = {
> +        .sType       = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
> +#if HAVE_VULKAN_DRM_MOD
> +        .pNext       = &drm_mod,
> +#endif
> +        .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
> +    };
> +    VkImportMemoryFdInfoKHR import_desc[AV_NUM_DATA_POINTERS];
> +
> +    if ((desc->nb_objects > 1) &&
> +        (desc->nb_objects != av_pix_fmt_count_planes(hwfc->sw_format))) {
> +        av_log(hwfc, AV_LOG_ERROR, "Number of DRM objects doesn't match "
> +               "plane count!\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    for (int i = 0; i < desc->nb_objects; i++) {
> +        import_desc[i].sType      = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR;
> +        import_desc[i].pNext      = NULL;
> +        import_desc[i].handleType = ext_info.handleTypes;
> +        import_desc[i].fd         = desc->objects[i].fd;
> +#if HAVE_VULKAN_DRM_MOD
> +        modifier_buf[i]           = desc->objects[i].format_modifier;
> +        if (modifier_buf[i] == DRM_FORMAT_MOD_INVALID) {
> +            av_log(hwfc, AV_LOG_ERROR, "DRM format modifier is invalid!\n");
> +            return AVERROR(EINVAL);
> +        }
> +#endif
> +    }
> +#if HAVE_VULKAN_DRM_MOD
> +    drm_mod.pDrmFormatModifiers    = modifier_buf;
> +    drm_mod.drmFormatModifierCount = desc->nb_objects;
> +#endif
> +
> +    err = create_frame(hwfc, f,
> +#if HAVE_VULKAN_DRM_MOD
> +                       VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT,
> +#else
> +                       desc->objects[0].format_modifier == DRM_FORMAT_MOD_LINEAR ?
> +                       VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL,

Is that expected to do anything sensible?  If you don't expect it to always work then it might be better to reject this case, or at least warn if the format modifier isn't linear.

> +#endif
> +                       DEFAULT_USAGE_FLAGS, desc->nb_objects > 1, &ext_info,
> +                       import_desc, sizeof(*import_desc));
> +    if (err < 0)
> +        return err;
> +
> +    return 0;
> +}
> +
> ...
> +
> +static void vulkan_unmap_to_drm(AVHWFramesContext *hwfc, HWMapDescriptor *hwmap)
> +{
> +    AVDRMFrameDescriptor *drm_desc = hwmap->priv;
> +
> +    for (int i = 0; i < drm_desc->nb_objects; i++)
> +        close(drm_desc->objects[i].fd);
> +
> +    av_free(drm_desc);
> +}
> +
> +const uint32_t drm_format_map[AV_PIX_FMT_NB] = {
> +    [AV_PIX_FMT_NV12]    = DRM_FORMAT_NV12,
> +    [AV_PIX_FMT_YUV420P] = DRM_FORMAT_YVU420,

I think this should be YUV420, not YVU420?

(Or if it is YVU then we might have confusion somewhere in the mapping.)

> +};
> +
> +static int vulkan_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst,
> +                             const AVFrame *src, int flags)
> +{
> +    int err = 0;
> +    VkResult ret;
> +    AVVkFrame *f = (AVVkFrame *)src->data[0];
> +    AVVulkanDeviceContext *hwctx = hwfc->device_ctx->hwctx;
> +    VK_LOAD_PFN(hwctx->inst, vkGetMemoryFdKHR);
> +#if HAVE_VULKAN_DRM_MOD
> +    VkImageDrmFormatModifierPropertiesEXT drm_mod = {
> +        .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT,
> +    };
> +#endif
> +
> +    AVDRMFrameDescriptor *drm_desc = av_mallocz(sizeof(*drm_desc));
> +    if (!drm_desc)
> +        return AVERROR(ENOMEM);
> +
> +    err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src, &vulkan_unmap_to_drm, drm_desc);
> +    if (err < 0)
> +        goto end;
> +
> +#if HAVE_VULKAN_DRM_MOD
> +    ret = vkGetImageDrmFormatModifierPropertiesEXT(hwctx->act_dev, f->img,
> +                                                   &drm_mod);
> +    if (ret != VK_SUCCESS) {
> +        av_log(hwfc, AV_LOG_ERROR, "Failed to retrieve DRM format modifier!\n");
> +        err = AVERROR_EXTERNAL;
> +        goto end;
> +    }
> +#endif
> +
> +    drm_desc->nb_objects = f->mem_count;
> +    for (int i = 0; i < drm_desc->nb_objects; i++) {
> +        VkMemoryGetFdInfoKHR export_info = {
> +            .sType      = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
> +            .memory     = f->mem[i],
> +            .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
> +        };
> +
> +        ret = pfn_vkGetMemoryFdKHR(hwctx->act_dev, &export_info,
> +                                   &drm_desc->objects[i].fd);
> +        if (ret != VK_SUCCESS) {
> +            av_log(hwfc, AV_LOG_ERROR, "Unable to export the image as a FD!\n");
> +            err = AVERROR_EXTERNAL;
> +            goto end;
> +        }
> +
> +        drm_desc->objects[i].size = lseek(drm_desc->objects[i].fd, 0, SEEK_END);
> +#if HAVE_VULKAN_DRM_MOD
> +        drm_desc->objects[i].format_modifier = drm_mod.drmFormatModifier;
> +#endif
> +    }
> +
> +    drm_desc->nb_layers = 1;
> +    for (int i = 0; i < drm_desc->nb_layers; i++) {
> +        drm_desc->layers[i].format    = drm_format_map[hwfc->sw_format];
> +        drm_desc->layers[i].nb_planes = av_pix_fmt_count_planes(hwfc->sw_format);
> +
> +        if (!drm_desc->layers[i].format) {
> +            av_log(hwfc, AV_LOG_ERROR, "Cannot map to DRM layer, unsupported!\n");
> +            err = AVERROR_PATCHWELCOME;
> +            goto end;
> +        }
> +
> +        for (int j = 0; j < drm_desc->layers[i].nb_planes; j++) {
> +            const int disjoint = drm_desc->nb_objects;
> +            const int nb_planes = drm_desc->layers[i].nb_planes;
> +            VkSubresourceLayout layout;
> +            VkImageSubresource sub = {
> +                .aspectMask = nb_planes < 2 ? VK_IMAGE_ASPECT_COLOR_BIT :
> +                                     i == 0 ? VK_IMAGE_ASPECT_PLANE_0_BIT :
> +                                     i == 1 ? VK_IMAGE_ASPECT_PLANE_1_BIT :
> +                                              VK_IMAGE_ASPECT_PLANE_2_BIT,
> +            };
> +            if (f->tiling != VK_IMAGE_TILING_LINEAR)
> +                continue;

This looks wrong.  Pitch and offset are still required for tiled images.

> +            vkGetImageSubresourceLayout(hwctx->act_dev, f->img, &sub, &layout);
> +            drm_desc->layers[i].planes[j].object_index = disjoint ? j : 0;
> +            drm_desc->layers[i].planes[j].offset       = layout.offset;
> +            drm_desc->layers[i].planes[j].pitch        = layout.rowPitch;

Having removed the tiling check above, exporting an NV12 image from ANV (current git Mesa on Coffee Lake) seems to give me offset == 0 for both planes, which is wrong (while the pitch is correct).

What is your setup for testing these cases?  If it's a bug in Mesa then that should be reported.

> +        }
> +    }
> +
> +    dst->width   = src->width;
> +    dst->height  = src->height;
> +    dst->data[0] = (uint8_t*)drm_desc;
> +
> +    av_log(hwfc, AV_LOG_DEBUG, "Mapped AVVkFrame to a DRM object!\n");
> +
> +    return 0;
> +
> +end:
> +    av_free(drm_desc);
> +    return err;
> +}
> +
> ...
> +
> +static int vulkan_transfer_data_to(AVHWFramesContext *hwfc, AVFrame *dst,
> +                                   const AVFrame *src)
> +{
> +    int err = 0;
> +    AVFrame *map = NULL;
> +    ImageBuffer buf[3] = { { 0 } };
> +    AVVkFrame *f = (AVVkFrame *)dst->data[0];
> +    AVHWDeviceContext *dev_ctx = hwfc->device_ctx;
> +    VulkanDevicePriv *p = dev_ctx->internal->priv;
> +    const int planes = av_pix_fmt_count_planes(src->format);
> +    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(src->format);
> +    int map_host = p->extensions & EXT_EXTERNAL_HOST_MEMORY;
> +
> +    if ((src->format != AV_PIX_FMT_NONE && !av_vkfmt_from_pixfmt(src->format))) {
> +        av_log(hwfc, AV_LOG_ERROR, "Unsupported source pixel format!\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    if (src->width > hwfc->width || src->height > hwfc->height)
> +        return AVERROR(EINVAL);
> +
> +    /* Path one - image is host visible and linear */
> +    if (f->tiling == VK_IMAGE_TILING_LINEAR &&
> +        f->flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
> +        map = av_frame_alloc();
> +        if (!map)
> +            return AVERROR(ENOMEM);
> +        map->format = src->format;
> +
> +        err = vulkan_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE);
> +        if (err)
> +            goto end;
> +
> +        err = av_frame_copy(map, src);
> +        goto end;
> +    }
> +
> +    /* Path three - we can import _host_ memory and bind it to a buffer */
> +    for (int i = 0; i < planes; i++) {
> +        int h = dst->height;
> +        int p_height = i > 0 ? AV_CEIL_RSHIFT(h, desc->log2_chroma_h) : h;
> +        VkImportMemoryHostPointerInfoEXT import_desc = {
> +            .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT,
> +            .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT,
> +            .pHostPointer = dst->data[i],

I think this should be src, not dst.  This path looks untested, because dst->data definitely isn't valid for this purpose?

> +        };
> +        VkMemoryAllocateInfo import_mem_info = {
> +            .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
> +            .pNext = &import_desc,
> +            .allocationSize = p_height * dst->linesize[i],

Also here.

> +        };
> +        err = create_buf(dev_ctx, &buf[i], import_mem_info.allocationSize,
> +                         VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
> +                         VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, NULL,
> +                         map_host ? &import_mem_info : NULL);
> +        if (err)
> +            goto end;
> +    }
> +
> +    /* Path two - we can't import host memory so we have to do 2 copies */
> +    if (!map_host) {
> +        uint8_t *mem[3];
> +        if ((err = map_buffers(dev_ctx, buf, mem, planes, 0)))
> +            goto end;
> +
> +        for (int i = 0; i < planes; i++) {
> +            int h = src->height;
> +            int p_height = i > 0 ? AV_CEIL_RSHIFT(h, desc->log2_chroma_h) : h;
> +            memcpy(mem[i], src->data[i], p_height*src->linesize[i]);
> +        }
> +
> +        if ((err = unmap_buffers(dev_ctx, buf, planes, 1)))
> +            goto end;
> +    }
> +
> +    /* Copy buffer to image */
> +    transfer_image_buf(dev_ctx, f, buf, src->linesize,
> +                       src->width, src->height, src->format, 0);
> +
> +end:
> +    av_frame_free(&map);
> +    for (int i = 0; i < planes; i++)
> +        free_buf(dev_ctx, &buf[i]);
> +
> +    return err;
> +}
> +

Thanks,

- Mark


More information about the ffmpeg-devel mailing list