diff options
Diffstat (limited to 'video/out/hwdec/hwdec_vaapi.c')
-rw-r--r-- | video/out/hwdec/hwdec_vaapi.c | 209 |
1 files changed, 175 insertions, 34 deletions
diff --git a/video/out/hwdec/hwdec_vaapi.c b/video/out/hwdec/hwdec_vaapi.c index 3ed9602400..34b6e52b3c 100644 --- a/video/out/hwdec/hwdec_vaapi.c +++ b/video/out/hwdec/hwdec_vaapi.c @@ -22,11 +22,12 @@ #include <libavutil/hwcontext.h> #include <libavutil/hwcontext_vaapi.h> +#include <va/va_drmcommon.h> #include "config.h" #include "video/out/gpu/hwdec.h" -#include "video/out/hwdec/hwdec_vaapi.h" +#include "video/out/hwdec/dmabuf_interop.h" #include "video/fmt-conversion.h" #include "video/mp_image_pool.h" #include "video/vaapi.h" @@ -51,6 +52,7 @@ static VADisplay *create_x11_va_display(struct ra *ra) static VADisplay *create_wayland_va_display(struct ra *ra) { struct wl_display *wl = ra_get_native_resource(ra, "wl"); + return wl ? vaGetDisplayWl(wl) : NULL; } #endif @@ -99,20 +101,36 @@ static VADisplay *create_native_va_display(struct ra *ra, struct mp_log *log) static void determine_working_formats(struct ra_hwdec *hw); +struct priv_owner { + struct mp_vaapi_ctx *ctx; + VADisplay *display; + int *formats; + bool probing_formats; // temporary during init + + struct dmabuf_interop dmabuf_interop; +}; + static void uninit(struct ra_hwdec *hw) { struct priv_owner *p = hw->priv; - if (p->ctx) + if (p->ctx) { hwdec_devices_remove(hw->devs, &p->ctx->hwctx); + if (p->ctx->hwctx.conversion_config) { + AVVAAPIHWConfig *hwconfig = p->ctx->hwctx.conversion_config; + vaDestroyConfig(p->ctx->display, hwconfig->config_id); + av_freep(&p->ctx->hwctx.conversion_config); + } + } va_destroy(p->ctx); } -const static vaapi_interop_init interop_inits[] = { -#if HAVE_VAAPI_EGL - vaapi_gl_init, +static const dmabuf_interop_init interop_inits[] = { +#if HAVE_DMABUF_INTEROP_GL + dmabuf_interop_gl_init, #endif -#if HAVE_VAAPI_VULKAN - vaapi_vk_init, + dmabuf_interop_pl_init, +#if HAVE_DMABUF_WAYLAND + dmabuf_interop_wl_init, #endif NULL }; @@ -120,19 +138,20 @@ const static vaapi_interop_init interop_inits[] = { static int init(struct ra_hwdec *hw) { struct priv_owner *p = hw->priv; + VAStatus vas; for (int i = 0; interop_inits[i]; i++) { - if (interop_inits[i](hw)) { + if (interop_inits[i](hw, &p->dmabuf_interop)) { break; } } - if (!p->interop_map || !p->interop_unmap) { + if (!p->dmabuf_interop.interop_map || !p->dmabuf_interop.interop_unmap) { MP_VERBOSE(hw, "VAAPI hwdec only works with OpenGL or Vulkan backends.\n"); return -1; } - p->display = create_native_va_display(hw->ra, hw->log); + p->display = create_native_va_display(hw->ra_ctx->ra, hw->log); if (!p->display) { MP_VERBOSE(hw, "Could not create a VA display.\n"); return -1; @@ -157,9 +176,23 @@ static int init(struct ra_hwdec *hw) return -1; } + VAConfigID config_id; + AVVAAPIHWConfig *hwconfig = NULL; + vas = vaCreateConfig(p->display, VAProfileNone, VAEntrypointVideoProc, NULL, + 0, &config_id); + if (vas == VA_STATUS_SUCCESS) { + hwconfig = av_hwdevice_hwconfig_alloc(p->ctx->av_device_ref); + hwconfig->config_id = config_id; + } + + // it's now safe to set the display resource + ra_add_native_resource(hw->ra_ctx->ra, "VADisplay", p->display); + p->ctx->hwctx.hw_imgfmt = IMGFMT_VAAPI; p->ctx->hwctx.supported_formats = p->formats; p->ctx->hwctx.driver_name = hw->driver->name; + p->ctx->hwctx.conversion_filter_name = "scale_vaapi"; + p->ctx->hwctx.conversion_config = hwconfig; hwdec_devices_add(hw->devs, &p->ctx->hwctx); return 0; } @@ -167,12 +200,12 @@ static int init(struct ra_hwdec *hw) static void mapper_unmap(struct ra_hwdec_mapper *mapper) { struct priv_owner *p_owner = mapper->owner->priv; - struct priv *p = mapper->priv; + struct dmabuf_interop_priv *p = mapper->priv; - p_owner->interop_unmap(mapper); + p_owner->dmabuf_interop.interop_unmap(mapper); if (p->surface_acquired) { - for (int n = 0; n < p->desc.num_objects; n++) + for (int n = 0; n < p->desc.nb_objects; n++) close(p->desc.objects[n].fd); p->surface_acquired = false; } @@ -181,8 +214,8 @@ static void mapper_unmap(struct ra_hwdec_mapper *mapper) static void mapper_uninit(struct ra_hwdec_mapper *mapper) { struct priv_owner *p_owner = mapper->owner->priv; - if (p_owner->interop_uninit) { - p_owner->interop_uninit(mapper); + if (p_owner->dmabuf_interop.interop_uninit) { + p_owner->dmabuf_interop.interop_uninit(mapper); } } @@ -199,7 +232,7 @@ static bool check_fmt(struct ra_hwdec_mapper *mapper, int fmt) static int mapper_init(struct ra_hwdec_mapper *mapper) { struct priv_owner *p_owner = mapper->owner->priv; - struct priv *p = mapper->priv; + struct dmabuf_interop_priv *p = mapper->priv; mapper->dst_params = mapper->src_params; mapper->dst_params.imgfmt = mapper->src_params.hw_subfmt; @@ -207,14 +240,15 @@ static int mapper_init(struct ra_hwdec_mapper *mapper) struct ra_imgfmt_desc desc = {0}; - if (!ra_get_imgfmt_desc(mapper->ra, mapper->dst_params.imgfmt, &desc)) - return -1; + if (mapper->ra->num_formats && + !ra_get_imgfmt_desc(mapper->ra, mapper->dst_params.imgfmt, &desc)) + return -1; p->num_planes = desc.num_planes; mp_image_set_params(&p->layout, &mapper->dst_params); - if (p_owner->interop_init) - if (!p_owner->interop_init(mapper, &desc)) + if (p_owner->dmabuf_interop.interop_init) + if (!p_owner->dmabuf_interop.interop_init(mapper, &desc)) return -1; if (!p_owner->probing_formats && !check_fmt(mapper, mapper->dst_params.imgfmt)) @@ -227,21 +261,31 @@ static int mapper_init(struct ra_hwdec_mapper *mapper) return 0; } +static void close_file_descriptors(const VADRMPRIMESurfaceDescriptor *desc) +{ + for (int i = 0; i < desc->num_objects; i++) + close(desc->objects[i].fd); +} + static int mapper_map(struct ra_hwdec_mapper *mapper) { struct priv_owner *p_owner = mapper->owner->priv; - struct priv *p = mapper->priv; + struct dmabuf_interop_priv *p = mapper->priv; VAStatus status; VADisplay *display = p_owner->display; + VADRMPRIMESurfaceDescriptor desc = {0}; + uint32_t flags = p_owner->dmabuf_interop.composed_layers ? + VA_EXPORT_SURFACE_COMPOSED_LAYERS : VA_EXPORT_SURFACE_SEPARATE_LAYERS; status = vaExportSurfaceHandle(display, va_surface_id(mapper->src), VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2, VA_EXPORT_SURFACE_READ_ONLY | - VA_EXPORT_SURFACE_SEPARATE_LAYERS, - &p->desc); + flags, + &desc); if (!CHECK_VA_STATUS_LEVEL(mapper, "vaExportSurfaceHandle()", p_owner->probing_formats ? MSGL_DEBUG : MSGL_ERR)) { + close_file_descriptors(&desc); goto err; } vaSyncSurface(display, va_surface_id(mapper->src)); @@ -249,10 +293,46 @@ static int mapper_map(struct ra_hwdec_mapper *mapper) CHECK_VA_STATUS(mapper, "vaSyncSurface()"); p->surface_acquired = true; - if (!p_owner->interop_map(mapper)) + // We use AVDRMFrameDescriptor to store the dmabuf so we need to copy the + // values over. + int num_returned_planes = 0; + p->desc.nb_layers = desc.num_layers; + p->desc.nb_objects = desc.num_objects; + for (int i = 0; i < desc.num_layers; i++) { + p->desc.layers[i].format = desc.layers[i].drm_format; + p->desc.layers[i].nb_planes = desc.layers[i].num_planes; + for (int j = 0; j < desc.layers[i].num_planes; j++) + { + p->desc.layers[i].planes[j].object_index = desc.layers[i].object_index[j]; + p->desc.layers[i].planes[j].offset = desc.layers[i].offset[j]; + p->desc.layers[i].planes[j].pitch = desc.layers[i].pitch[j]; + } + + num_returned_planes += desc.layers[i].num_planes; + } + for (int i = 0; i < desc.num_objects; i++) { + p->desc.objects[i].format_modifier = desc.objects[i].drm_format_modifier; + p->desc.objects[i].fd = desc.objects[i].fd; + p->desc.objects[i].size = desc.objects[i].size; + } + + // We can handle composed formats if the total number of planes is still + // equal the number of planes we expect. Complex formats with auxiliary + // planes cannot be supported. + if (p->num_planes != 0 && p->num_planes != num_returned_planes) { + mp_msg(mapper->log, p_owner->probing_formats ? MSGL_DEBUG : MSGL_ERR, + "Mapped surface with format '%s' has unexpected number of planes. " + "(%d layers and %d planes, but expected %d planes)\n", + mp_imgfmt_to_name(mapper->src->params.hw_subfmt), + desc.num_layers, num_returned_planes, p->num_planes); + goto err; + } + + if (!p_owner->dmabuf_interop.interop_map(mapper, &p_owner->dmabuf_interop, + p_owner->probing_formats)) goto err; - if (p->desc.fourcc == VA_FOURCC_YV12) + if (desc.fourcc == VA_FOURCC_YV12) MPSWAP(struct ra_tex*, mapper->tex[1], mapper->tex[2]); return 0; @@ -267,16 +347,20 @@ err: static bool try_format_map(struct ra_hwdec *hw, struct mp_image *surface) { - bool ok = false; struct ra_hwdec_mapper *mapper = ra_hwdec_mapper_create(hw, &surface->params); - if (mapper) - ok = ra_hwdec_mapper_map(mapper, surface) >= 0; + if (!mapper) { + MP_DBG(hw, "Failed to create mapper\n"); + return false; + } + + bool ok = ra_hwdec_mapper_map(mapper, surface) >= 0; ra_hwdec_mapper_free(&mapper); return ok; } static void try_format_pixfmt(struct ra_hwdec *hw, enum AVPixelFormat pixfmt) { + bool supported = false; struct priv_owner *p = hw->priv; int mp_fmt = pixfmt2imgfmt(pixfmt); @@ -312,10 +396,15 @@ static void try_format_pixfmt(struct ra_hwdec *hw, enum AVPixelFormat pixfmt) if (!s || !mp_image_params_valid(&s->params)) goto err; if (try_format_map(hw, s)) { + supported = true; MP_TARRAY_APPEND(p, p->formats, num_formats, mp_fmt); MP_TARRAY_APPEND(p, p->formats, num_formats, 0); // terminate it } err: + if (!supported) + MP_DBG(hw, "Unsupported format: %s\n", + mp_imgfmt_to_name(mp_fmt)); + talloc_free(s); av_frame_free(&frame); av_buffer_unref(&fref); @@ -324,16 +413,51 @@ err: static void try_format_config(struct ra_hwdec *hw, AVVAAPIHWConfig *hwconfig) { struct priv_owner *p = hw->priv; + enum AVPixelFormat *fmts = NULL; + AVHWFramesConstraints *fc = av_hwdevice_get_hwframe_constraints(p->ctx->av_device_ref, hwconfig); if (!fc) { MP_WARN(hw, "failed to retrieve libavutil frame constraints\n"); return; } - for (int n = 0; fc->valid_sw_formats && - fc->valid_sw_formats[n] != AV_PIX_FMT_NONE; n++) - try_format_pixfmt(hw, fc->valid_sw_formats[n]); + + /* + * We need a hwframe_ctx to be able to get the valid formats, but to + * initialise it, we need a format, so we get the first format from the + * hwconfig. We don't care about the other formats in the config because the + * transfer formats list will already include them. + */ + AVBufferRef *fref = NULL; + fref = av_hwframe_ctx_alloc(p->ctx->av_device_ref); + if (!fref) { + MP_WARN(hw, "failed to alloc libavutil frame context\n"); + goto err; + } + AVHWFramesContext *fctx = (void *)fref->data; + fctx->format = AV_PIX_FMT_VAAPI; + fctx->sw_format = fc->valid_sw_formats[0]; + fctx->width = 128; + fctx->height = 128; + if (av_hwframe_ctx_init(fref) < 0) { + MP_WARN(hw, "failed to init libavutil frame context\n"); + goto err; + } + + int ret = av_hwframe_transfer_get_formats(fref, AV_HWFRAME_TRANSFER_DIRECTION_TO, &fmts, 0); + if (ret) { + MP_WARN(hw, "failed to get libavutil frame context supported formats\n"); + goto err; + } + + for (int n = 0; fmts && + fmts[n] != AV_PIX_FMT_NONE; n++) + try_format_pixfmt(hw, fmts[n]); + +err: av_hwframe_constraints_free(&fc); + av_buffer_unref(&fref); + av_free(fmts); } static void determine_working_formats(struct ra_hwdec *hw) @@ -360,8 +484,18 @@ static void determine_working_formats(struct ra_hwdec *hw) if (!CHECK_VA_STATUS(hw, "vaQueryConfigProfiles()")) num_profiles = 0; + /* + * We need to find one declared format to bootstrap probing. So find a valid + * decoding profile and use its config. If try_format_config() finds any + * formats, they will be all the supported formats, and we don't need to + * look at any other profiles. + */ for (int n = 0; n < num_profiles; n++) { VAProfile profile = profiles[n]; + if (profile == VAProfileNone) { + // We don't use the None profile. + continue; + } int num_ep = 0; status = vaQueryConfigEntrypoints(p->display, profile, entrypoints, &num_ep); @@ -371,6 +505,10 @@ static void determine_working_formats(struct ra_hwdec *hw) continue; } for (int ep = 0; ep < num_ep; ep++) { + if (entrypoints[ep] != VAEntrypointVLD) { + // We are only interested in decoding entrypoints. + continue; + } VAConfigID config = VA_INVALID_ID; status = vaCreateConfig(p->display, profile, entrypoints[ep], NULL, 0, &config); @@ -384,6 +522,9 @@ static void determine_working_formats(struct ra_hwdec *hw) try_format_config(hw, hwconfig); vaDestroyConfig(p->display, config); + if (p->formats && p->formats[0]) { + goto done; + } } } @@ -400,14 +541,14 @@ done: MP_VERBOSE(hw, "Done probing surface formats.\n"); } -const struct ra_hwdec_driver ra_hwdec_vaegl = { - .name = "vaapi-egl", +const struct ra_hwdec_driver ra_hwdec_vaapi = { + .name = "vaapi", .priv_size = sizeof(struct priv_owner), .imgfmts = {IMGFMT_VAAPI, 0}, .init = init, .uninit = uninit, .mapper = &(const struct ra_hwdec_mapper_driver){ - .priv_size = sizeof(struct priv), + .priv_size = sizeof(struct dmabuf_interop_priv), .init = mapper_init, .uninit = mapper_uninit, .map = mapper_map, |