f_hwtransfer: mp_image_pool: support HW -> HW mapping

Certain combinations of hardware formats require the use of hwmap to
transfer frames between the formats, rather than hwupload, which will
fail if attempted.

To keep the usage of vf_format for HW -> HW transfers as intuitive as
possible, we should detect these cases and do the map operation instead
of uploading.

For now, the relevant cases are moving between VAAPI and Vulkan, and
VAAPI and DRM Prime, in both directions. I have introduced the IMGFMT
entry for Vulkan here so that I can put in the complete mapping table.
It's actually not useless, as you can map to Vulkan, use a Vulkan
filter and then map back to VAAPI for display output.
This commit is contained in:
Philip Langdale 2022-04-03 20:12:50 -07:00 committed by Philip Langdale
parent 7b84e6fa89
commit f221666ade
7 changed files with 80 additions and 4 deletions

View File

@ -35,6 +35,10 @@ struct priv {
int *fmt_upload_index;
int *fmt_upload_num;
// List of source formats that require hwmap instead of hwupload.
int *map_fmts;
int num_map_fmts;
struct mp_hwupload public;
};
@ -58,6 +62,25 @@ static const struct ffmpeg_and_other_bugs shitlist[] = {
{0}
};
struct hwmap_pairs {
int first_fmt;
int second_fmt;
};
// We cannot discover which pairs of hardware formats need to use hwmap to
// convert between the formats, so we need a lookup table.
static const struct hwmap_pairs hwmap_pairs[] = {
{
.first_fmt = IMGFMT_VAAPI,
.second_fmt = IMGFMT_VULKAN,
},
{
.first_fmt = IMGFMT_DRMPRIME,
.second_fmt = IMGFMT_VAAPI,
},
{0}
};
static bool select_format(struct priv *p, int input_fmt,
int *out_sw_fmt, int *out_upload_fmt)
{
@ -183,7 +206,19 @@ static void process(struct mp_filter *f)
goto error;
}
struct mp_image *dst = mp_av_pool_image_hw_upload(p->hw_pool, src);
struct mp_image *dst;
bool map_images = false;
for (int n = 0; n < p->num_map_fmts; n++) {
if (src->imgfmt == p->map_fmts[n]) {
map_images = true;
break;
}
}
if (map_images)
dst = mp_av_pool_image_hw_map(p->hw_pool, src);
else
dst = mp_av_pool_image_hw_upload(p->hw_pool, src);
if (!dst)
goto error;
@ -296,6 +331,16 @@ static bool probe_formats(struct mp_hwupload *u, int hw_imgfmt)
}
}
for (int n = 0; hwmap_pairs[n].first_fmt; n++) {
if (hwmap_pairs[n].first_fmt == hw_imgfmt) {
MP_TARRAY_APPEND(p, p->map_fmts, p->num_map_fmts,
hwmap_pairs[n].second_fmt);
} else if (hwmap_pairs[n].second_fmt == hw_imgfmt) {
MP_TARRAY_APPEND(p, p->map_fmts, p->num_map_fmts,
hwmap_pairs[n].first_fmt);
}
}
for (int n = 0; cstr->valid_sw_formats &&
cstr->valid_sw_formats[n] != AV_PIX_FMT_NONE; n++)
{

View File

@ -561,8 +561,8 @@ static void init_graph(struct lavfi *c)
hwdec_ctx = hwdec_devices_get_first(info->hwdec_devs);
}
if (hwdec_ctx && hwdec_ctx->av_device_ref) {
MP_VERBOSE(c, "Configuring hwdec_interop=%s for filters\n",
hwdec_ctx->driver_name);
MP_VERBOSE(c, "Configuring hwdec_interop=%s for filter graph: %s\n",
hwdec_ctx->driver_name, c->graph_string);
for (int n = 0; n < c->graph->nb_filters; n++) {
AVFilterContext *filter = c->graph->filters[n];
filter->hw_device_ctx =

View File

@ -1239,7 +1239,7 @@ videotoolbox: ctype=unknown
planes=0, chroma=0:0 align=1:1
{}
AVD: name=videotoolbox_vld chroma=0:0 flags=0x8 [hw]
vulkan: [GENERIC] ctype=unknown
vulkan: ctype=unknown
Basic desc: [le][be][hw]
planes=0, chroma=0:0 align=1:1
{}

View File

@ -67,6 +67,7 @@ static const struct {
{IMGFMT_CUDA, AV_PIX_FMT_CUDA},
{IMGFMT_P010, AV_PIX_FMT_P010},
{IMGFMT_DRMPRIME, AV_PIX_FMT_DRM_PRIME},
{IMGFMT_VULKAN, AV_PIX_FMT_VULKAN},
{0, AV_PIX_FMT_NONE}
};

View File

@ -319,6 +319,7 @@ enum mp_imgfmt {
IMGFMT_VDPAU_OUTPUT, // VdpOutputSurface
IMGFMT_VAAPI,
IMGFMT_VIDEOTOOLBOX, // CVPixelBufferRef
IMGFMT_VULKAN, // VKImage
// Generic pass-through of AV_PIX_FMT_*. Used for formats which don't have
// a corresponding IMGFMT_ value.

View File

@ -426,3 +426,30 @@ struct mp_image *mp_av_pool_image_hw_upload(struct AVBufferRef *hw_frames_ctx,
mp_image_copy_attributes(dst, src);
return dst;
}
struct mp_image *mp_av_pool_image_hw_map(struct AVBufferRef *hw_frames_ctx,
struct mp_image *src)
{
AVFrame *dst_frame = av_frame_alloc();
if (!dst_frame)
return NULL;
dst_frame->format = ((AVHWFramesContext*)hw_frames_ctx->data)->format;
dst_frame->hw_frames_ctx = av_buffer_ref(hw_frames_ctx);
AVFrame *src_frame = mp_image_to_av_frame(src);
if (av_hwframe_map(dst_frame, src_frame, 0) < 0) {
av_frame_free(&src_frame);
av_frame_free(&dst_frame);
return NULL;
}
av_frame_free(&src_frame);
struct mp_image *dst = mp_image_from_av_frame(dst_frame);
av_frame_free(&dst_frame);
if (!dst)
return NULL;
mp_image_copy_attributes(dst, src);
return dst;
}

View File

@ -41,4 +41,6 @@ bool mp_update_av_hw_frames_pool(struct AVBufferRef **hw_frames_ctx,
struct mp_image *mp_av_pool_image_hw_upload(struct AVBufferRef *hw_frames_ctx,
struct mp_image *src);
struct mp_image *mp_av_pool_image_hw_map(struct AVBufferRef *hw_frames_ctx,
struct mp_image *src);
#endif