diff options
author | Philip Langdale <philipl@overt.org> | 2023-07-29 22:43:58 +0800 |
---|---|---|
committer | Philip Langdale <github.philipl@overt.org> | 2023-08-26 10:07:55 -0700 |
commit | 59478b0059f3af023eb3e9f9d3ac5fa537ce1caf (patch) | |
tree | 64ae195435add9a322da61d4c8e019bf333ff7d1 /filters/f_autoconvert.c | |
parent | 32be72623b1d43027f2d97c15fcf604d8c328e00 (diff) | |
download | mpv-59478b0059f3af023eb3e9f9d3ac5fa537ce1caf.tar.bz2 mpv-59478b0059f3af023eb3e9f9d3ac5fa537ce1caf.tar.xz |
hwtransfer: implement support for hw->hw format conversion
Historically, we have not attempted to support hw->hw format conversion
in the autoconvert logic. If a user needed to do these kinds of
conversions, they needed to manually insert the hw format's conversion
filter manually (eg: scale_vaapi).
This was usually fine because the general rule is that any format
supported by the hardware can be used as well as any other. ie: You
would only need to do conversion if you have a specific goal in mind.
However, we now have two situations where we can find ourselves with a
hardware format being produced by a decoder that cannot be accepted by
a VO via hwdec-interop:
* dmabuf-wayland can only accept formats that the Wayland compositor
accepts. In the case of GNOME, it can only accept a handful of RGB
formats.
* When decoding via VAAPI on Intel hardware, some of the more unusual
video encodings (4:2:2, 10bit 4:4:4) lead to packed frame formats
which gpu-next cannot handle, causing rendering to fail.
In both these cases (at least when using VAAPI with dmabuf-wayland), if
we could detect the failure case and insert a `scale_vaapi` filter, we
could get successful output automatically. For `hwdec=drm`, there is
currently no conversion filter, so dmabuf-wayland is still out of luck
there.
The basic approach to implementing this is to detect the case where we
are evaluating a hardware format where the VO can accept the hardware
format itself, but may not accept the underlying sw format. In the
current code, we bypass autoconvert as soon as we see the hardware
format is compatible.
My first observation was that we actually have logic in autoconvert to
detect when the input sw format is not in the list of allowed sw
formats passed into the autoconverter. Unfortunately, we never populate
this list, and the way you would expect to do that is vo-query-format
returning sw format information, which it does not. We could define an
extended vo-query-format-2, but we'd still need to implement the
probing logic to fill it in.
On the other hand, we already have the probing logic in the hwupload
filter - and most recently I used that logic to implement conversion
on upload. So if we could leverage that, we could both detect when
hw->hw conversion is required, and pick the best target format.
This exercise is then primarily one of detecting when we are in this
case and letting that code run in a useful way. The hwupload filter is
a bit awkward to work with today, and so I refactored a bunch of the
set up code to actually make it more encapsulated. Now, instead of the
caller instantiating it and then triggering the probe, we probe on
creation and instantiate the correct underlying filter (hwupload vs
conversion) automatically.
Diffstat (limited to 'filters/f_autoconvert.c')
-rw-r--r-- | filters/f_autoconvert.c | 48 |
1 files changed, 42 insertions, 6 deletions
diff --git a/filters/f_autoconvert.c b/filters/f_autoconvert.c index b2db4e7f9e..739a45b496 100644 --- a/filters/f_autoconvert.c +++ b/filters/f_autoconvert.c @@ -150,7 +150,12 @@ static bool build_image_converter(struct mp_autoconvert *c, struct mp_log *log, for (int n = 0; n < p->num_imgfmts; n++) { bool samefmt = img->params.imgfmt == p->imgfmts[n]; bool samesubffmt = img->params.hw_subfmt == p->subfmts[n]; - if (samefmt && (samesubffmt || !p->subfmts[n])) { + /* + * In practice, `p->subfmts` is not usually populated today, in which + * case we must actively probe formats below to establish if the VO can + * accept the subfmt being used by the hwdec. + */ + if (samefmt && samesubffmt) { if (p->imgparams_set) { if (!mp_image_params_equal(&p->imgparams, &img->params)) break; @@ -183,26 +188,57 @@ static bool build_image_converter(struct mp_autoconvert *c, struct mp_log *log, bool dst_all_hw = true; bool dst_have_sw = false; + bool has_src_hw_fmt = false; for (int n = 0; n < num_fmts; n++) { bool is_hw = IMGFMT_IS_HWACCEL(fmts[n]); dst_all_hw &= is_hw; dst_have_sw |= !is_hw; + has_src_hw_fmt |= is_hw && fmts[n] == imgpar.imgfmt; } // Source is hw, some targets are sw -> try to download. bool hw_to_sw = !imgfmt_is_sw && dst_have_sw; - if (dst_all_hw && num_fmts > 0) { + if (has_src_hw_fmt) { + int src_fmt = img->params.hw_subfmt; + /* + * If the source format is a hardware format, and our output supports + * that hardware format, we prioritize preserving the use of that + * hardware format. In most cases, the sub format will also be supported + * and no conversion will be required, but in some cases, the hwdec + * may be able to output formats that the VO cannot display, and + * hardware format conversion becomes necessary. + */ + struct mp_hwupload upload = mp_hwupload_create(conv, imgpar.imgfmt, + src_fmt, + true); + if (upload.successful_init) { + if (upload.f) { + mp_info(log, "Converting %s[%s] -> %s[%s]\n", + mp_imgfmt_to_name(imgpar.imgfmt), + mp_imgfmt_to_name(src_fmt), + mp_imgfmt_to_name(imgpar.imgfmt), + mp_imgfmt_to_name(upload.selected_sw_imgfmt)); + filters[2] = upload.f; + } + hw_to_sw = false; + need_sws = false; + } else { + mp_err(log, "Failed to create HW uploader for format %s\n", + mp_imgfmt_to_name(src_fmt)); + } + } else if (dst_all_hw && num_fmts > 0) { bool upload_created = false; int sw_fmt = imgfmt_is_sw ? img->imgfmt : img->params.hw_subfmt; for (int i = 0; i < num_fmts; i++) { // We can probably use this! Very lazy and very approximate. - struct mp_hwupload *upload = mp_hwupload_create(conv, fmts[i]); - if (upload) { + struct mp_hwupload upload = mp_hwupload_create(conv, fmts[i], + sw_fmt, false); + if (upload.successful_init) { mp_info(log, "HW-uploading to %s\n", mp_imgfmt_to_name(fmts[i])); - filters[2] = upload->f; - hwupload_fmt = mp_hwupload_find_upload_format(upload, sw_fmt); + filters[2] = upload.f; + hwupload_fmt = upload.selected_sw_imgfmt; fmts = &hwupload_fmt; num_fmts = hwupload_fmt ? 1 : 0; hw_to_sw = false; |