hwdec/vaapi: additionally probe hwupload format conversions

This is probably fringe, but I encountered it on my machine. The
hwupload filter happily does conversions all on the GPU side if
possible. Makes sense. However, it turns out that there is a difference
between e.g.yuv420p -> vaapi[bgr0] and a vaapi[yuv420p] -> vaapi[bgr0].
The latter worked without any issues on my hardware, but the first
example fails. To remedy this, we need to also do hardware uploads
during probing against formats and track what actually works (i.e.
vaSurfaceExportHandle). This commit only implements it in vaapi since it
is the only known place where this edge case is relevant. But other
hardware decoding drivers could easily add support later if needed.
This commit is contained in:
Dudemanguy 2024-09-09 15:56:45 -05:00
parent 7a80330be3
commit 6797f54378
2 changed files with 53 additions and 0 deletions

View File

@ -20,6 +20,10 @@ struct mp_hwdec_ctx {
// HW format used by the hwdec
int hw_imgfmt;
// List of support software formats when doing hwuploads.
// If NULL, all possible hwuploads are assumed to be supported.
const int *supported_hwupload_formats;
// The name of this hwdec's matching conversion filter if available.
// This will be used for hardware conversion of frame formats.
// NULL otherwise.

View File

@ -106,6 +106,8 @@ struct priv_owner {
struct mp_vaapi_ctx *ctx;
VADisplay *display;
int *formats;
int *hwupload_formats;
int num_hwupload_formats;
bool probing_formats; // temporary during init
struct dmabuf_interop dmabuf_interop;
@ -191,6 +193,7 @@ static int init(struct ra_hwdec *hw)
p->ctx->hwctx.hw_imgfmt = IMGFMT_VAAPI;
p->ctx->hwctx.supported_formats = p->formats;
p->ctx->hwctx.supported_hwupload_formats = p->hwupload_formats;
p->ctx->hwctx.driver_name = hw->driver->name;
p->ctx->hwctx.conversion_filter_name = "scale_vaapi";
p->ctx->hwctx.conversion_config = hwconfig;
@ -411,6 +414,47 @@ err:
av_buffer_unref(&fref);
}
static void try_format_upload(struct ra_hwdec *hw, enum AVPixelFormat pixfmt)
{
int mp_fmt = pixfmt2imgfmt(pixfmt);
if (!mp_fmt || IMGFMT_IS_HWACCEL(mp_fmt))
return;
// Arbitrarily use the first format we have for the hw_subfmt.
struct priv_owner *p = hw->priv;
if (!p->formats || !p->formats[0])
return;
struct mp_image *src = mp_image_alloc(mp_fmt, 2, 2);
if (!src)
return;
AVBufferRef *hw_pool = av_hwframe_ctx_alloc(p->ctx->av_device_ref);
mp_update_av_hw_frames_pool(&hw_pool, p->ctx->av_device_ref, IMGFMT_VAAPI,
p->formats[0], src->w, src->h, false);
struct mp_image *dst = mp_av_pool_image_hw_upload(hw_pool, src);
VADisplay *display = p->display;
VADRMPRIMESurfaceDescriptor desc = {0};
VASurfaceID id = va_surface_id(dst);
uint32_t flags = p->dmabuf_interop.composed_layers ?
VA_EXPORT_SURFACE_COMPOSED_LAYERS : VA_EXPORT_SURFACE_SEPARATE_LAYERS;
VAStatus status = vaExportSurfaceHandle(display, id, VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
flags | VA_EXPORT_SURFACE_READ_ONLY, &desc);
if (status == VA_STATUS_SUCCESS)
MP_TARRAY_APPEND(p, p->hwupload_formats, p->num_hwupload_formats, mp_fmt);
close_file_descriptors(&desc);
av_buffer_unref(&hw_pool);
mp_image_unrefp(&dst);
mp_image_unrefp(&src);
return;
}
static void try_format_config(struct ra_hwdec *hw, AVVAAPIHWConfig *hwconfig)
{
struct priv_owner *p = hw->priv;
@ -455,6 +499,11 @@ static void try_format_config(struct ra_hwdec *hw, AVVAAPIHWConfig *hwconfig)
fmts[n] != AV_PIX_FMT_NONE; n++)
try_format_pixfmt(hw, fmts[n]);
for (int n = 0; fmts &&
fmts[n] != AV_PIX_FMT_NONE; n++)
try_format_upload(hw, fmts[n]);
MP_TARRAY_APPEND(p, p->hwupload_formats, p->num_hwupload_formats, 0); // sanity check
err:
av_hwframe_constraints_free(&fc);
av_buffer_unref(&fref);