summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilip Langdale <philipl@overt.org>2022-02-06 14:18:59 -0800
committerPhilip Langdale <github.philipl@overt.org>2022-02-17 20:02:32 -0800
commit5186651f304fb49867f2542d2eb149f8191cf0de (patch)
treec04acfdbf374bb4142c0c564a73aecd98082c977
parentbc9805c71a67b2717c79533731608eff679cded1 (diff)
downloadmpv-5186651f304fb49867f2542d2eb149f8191cf0de.tar.bz2
mpv-5186651f304fb49867f2542d2eb149f8191cf0de.tar.xz
vo_gpu: hwdec: load hwdec interops on-demand by default
Historically, we have treated hwdec interop loading as a completely separate step from loading the hwdecs themselves. Some hwdecs need an interop, and some don't, and users generally configure the exact hwdec they want, so interops that aren't relevant for that hwdec shouldn't be loaded to save time and avoid warning/error spam. The basic approach here is to recognise that interops are tied to hwdecs by imgfmt. The hwdec outputs some format, and an interop is needed to get that format to the vo without read back. So, when we try to load an hwdec, instead of just blindly loading all interops as we do today, let's pass the imgfmt in and only load interops that work for that format. If more than one interop is available for the format, the existing logic (whatever it is) will continue to be used to pick one. We also have one callsite in filters where we seem to pre-emptively load all the interops. It's probably possible to trace down a specific format but for now I'm just letting it keep loading all of them; it's no worse than before. You may notice there is no documentation update - and that's because the current docs say that when the interop mode is `auto`, the interop is loaded on demand. So reality now reflects the docs. How nice.
-rw-r--r--filters/filter.c18
-rw-r--r--video/decode/vd_lavc.c6
-rw-r--r--video/filter/vf_d3d11vpp.c2
-rw-r--r--video/hwdec.c8
-rw-r--r--video/hwdec.h9
-rw-r--r--video/out/gpu/video.c75
-rw-r--r--video/out/gpu/video.h3
-rw-r--r--video/out/vo_gpu.c11
8 files changed, 108 insertions, 24 deletions
diff --git a/filters/filter.c b/filters/filter.c
index 350047f975..7720eb9fd3 100644
--- a/filters/filter.c
+++ b/filters/filter.c
@@ -1,12 +1,15 @@
#include <math.h>
#include <pthread.h>
+#include <libavutil/hwcontext.h>
+
#include "common/common.h"
#include "common/global.h"
#include "common/msg.h"
#include "osdep/atomic.h"
#include "osdep/timer.h"
#include "video/hwdec.h"
+#include "video/img_format.h"
#include "filter.h"
#include "filter_internal.h"
@@ -688,7 +691,20 @@ struct AVBufferRef *mp_filter_load_hwdec_device(struct mp_filter *f, int avtype)
if (!info || !info->hwdec_devs)
return NULL;
- hwdec_devices_request_all(info->hwdec_devs);
+ int imgfmt = IMGFMT_NONE;
+ switch (avtype) {
+ case AV_HWDEVICE_TYPE_VAAPI:
+ imgfmt = IMGFMT_VAAPI;
+ break;
+ case AV_HWDEVICE_TYPE_VDPAU:
+ imgfmt = IMGFMT_VDPAU;
+ break;
+ default:
+ MP_WARN(f,
+ "Unrecognised HW Device type requested. Loading all devices\n");
+ }
+
+ hwdec_devices_request_for_img_fmt(info->hwdec_devs, imgfmt);
return hwdec_devices_get_lavc(info->hwdec_devs, avtype);
}
diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c
index 0e3afa1957..69769e3178 100644
--- a/video/decode/vd_lavc.c
+++ b/video/decode/vd_lavc.c
@@ -431,7 +431,8 @@ static AVBufferRef *hwdec_create_dev(struct mp_filter *vd,
return ref;
}
} else if (ctx->hwdec_devs) {
- hwdec_devices_request_all(ctx->hwdec_devs);
+ hwdec_devices_request_for_img_fmt(ctx->hwdec_devs,
+ pixfmt2imgfmt(hwdec->pix_fmt));
return hwdec_devices_get_lavc(ctx->hwdec_devs, hwdec->lavc_device);
}
@@ -510,7 +511,8 @@ static void select_and_set_hwdec(struct mp_filter *vd)
// Most likely METHOD_INTERNAL, which often use delay-loaded
// VO support as well.
if (ctx->hwdec_devs)
- hwdec_devices_request_all(ctx->hwdec_devs);
+ hwdec_devices_request_for_img_fmt(
+ ctx->hwdec_devs, pixfmt2imgfmt(hwdec->pix_fmt));
}
ctx->use_hwdec = true;
diff --git a/video/filter/vf_d3d11vpp.c b/video/filter/vf_d3d11vpp.c
index e3aa90c05e..6c4e840b5c 100644
--- a/video/filter/vf_d3d11vpp.c
+++ b/video/filter/vf_d3d11vpp.c
@@ -428,7 +428,7 @@ static struct mp_filter *vf_d3d11vpp_create(struct mp_filter *parent,
if (!info || !info->hwdec_devs)
goto fail;
- hwdec_devices_request_all(info->hwdec_devs);
+ hwdec_devices_request_for_img_fmt(info->hwdec_devs, IMGFMT_D3D11);
struct mp_hwdec_ctx *hwctx =
hwdec_devices_get_by_lavc(info->hwdec_devs, AV_HWDEVICE_TYPE_D3D11VA);
diff --git a/video/hwdec.c b/video/hwdec.c
index 97b984db18..596a418865 100644
--- a/video/hwdec.c
+++ b/video/hwdec.c
@@ -13,7 +13,7 @@ struct mp_hwdec_devices {
struct mp_hwdec_ctx **hwctxs;
int num_hwctxs;
- void (*load_api)(void *ctx);
+ void (*load_api)(void *ctx, int imgfmt);
void *load_api_ctx;
};
@@ -95,16 +95,16 @@ void hwdec_devices_remove(struct mp_hwdec_devices *devs, struct mp_hwdec_ctx *ct
}
void hwdec_devices_set_loader(struct mp_hwdec_devices *devs,
- void (*load_api)(void *ctx), void *load_api_ctx)
+ void (*load_api)(void *ctx, int imgfmt), void *load_api_ctx)
{
devs->load_api = load_api;
devs->load_api_ctx = load_api_ctx;
}
-void hwdec_devices_request_all(struct mp_hwdec_devices *devs)
+void hwdec_devices_request_for_img_fmt(struct mp_hwdec_devices *devs, int imgfmt)
{
if (devs->load_api && !hwdec_devices_get_first(devs))
- devs->load_api(devs->load_api_ctx);
+ devs->load_api(devs->load_api_ctx, imgfmt);
}
char *hwdec_devices_get_names(struct mp_hwdec_devices *devs)
diff --git a/video/hwdec.h b/video/hwdec.h
index b2ca8ca894..a57010d33f 100644
--- a/video/hwdec.h
+++ b/video/hwdec.h
@@ -55,11 +55,12 @@ void hwdec_devices_remove(struct mp_hwdec_devices *devs, struct mp_hwdec_ctx *ct
// If used at all, this must be set/unset during initialization/uninitialization,
// as concurrent use with hwdec_devices_request() is a race condition.
void hwdec_devices_set_loader(struct mp_hwdec_devices *devs,
- void (*load_api)(void *ctx), void *load_api_ctx);
+ void (*load_api)(void *ctx, int imgfmt), void *load_api_ctx);
-// Cause VO to lazily load all devices, and will block until this is done (even
-// if not available).
-void hwdec_devices_request_all(struct mp_hwdec_devices *devs);
+// Cause VO to lazily load all devices for a specified img format, and will
+// block until this is done (even if not available). Pass IMGFMT_NONE to load
+// all available devices.
+void hwdec_devices_request_for_img_fmt(struct mp_hwdec_devices *devs, int imgfmt);
// Return "," concatenated list (for introspection/debugging). Use talloc_free().
char *hwdec_devices_get_names(struct mp_hwdec_devices *devs);
diff --git a/video/out/gpu/video.c b/video/out/gpu/video.c
index 449728ccb5..e28a88de5b 100644
--- a/video/out/gpu/video.c
+++ b/video/out/gpu/video.c
@@ -4329,15 +4329,49 @@ struct mp_image *gl_video_get_image(struct gl_video *p, int imgfmt, int w, int h
static void load_add_hwdec(struct gl_video *p, struct mp_hwdec_devices *devs,
const struct ra_hwdec_driver *drv, bool is_auto)
{
+ bool needs_loading = true;
+ for (int j = 0; j < p->num_hwdecs; j++) {
+ const struct ra_hwdec *hwdec = p->hwdecs[j];
+ if (hwdec->driver == drv) {
+ needs_loading = false;
+ break;
+ }
+ }
+ if (!needs_loading) {
+ return;
+ }
+
struct ra_hwdec *hwdec =
ra_hwdec_load_driver(p->ra, p->log, p->global, devs, drv, is_auto);
if (hwdec)
MP_TARRAY_APPEND(p, p->hwdecs, p->num_hwdecs, hwdec);
}
+static void load_hwdecs_all(struct gl_video *p, struct mp_hwdec_devices *devs)
+{
+ if (!p->hwdec_interop_loading_done) {
+ for (int n = 0; ra_hwdec_drivers[n]; n++)
+ load_add_hwdec(p, devs, ra_hwdec_drivers[n], true);
+ p->hwdec_interop_loading_done = true;
+ }
+}
+
void gl_video_load_hwdecs(struct gl_video *p, struct mp_hwdec_devices *devs,
bool load_all_by_default)
{
+ /*
+ * By default, or if the option value is "auto", we will not pre-emptively
+ * load any interops, and instead allow them to be loaded on-demand.
+ *
+ * If the option value is "no", then no interops will be loaded now, and
+ * no interops will be loaded, even if requested later.
+ *
+ * If the option value is "all", then all interops will be loaded now, and
+ * obviously no interops will need to be loaded later.
+ *
+ * Finally, if a specific interop is requested, it will be loaded now, and
+ * no other interop will be loaded, even if requested later.
+ */
char *type = p->opts.hwdec_interop;
if (!type || !type[0] || strcmp(type, "auto") == 0) {
if (!load_all_by_default)
@@ -4347,7 +4381,7 @@ void gl_video_load_hwdecs(struct gl_video *p, struct mp_hwdec_devices *devs,
if (strcmp(type, "no") == 0) {
// do nothing, just block further loading
} else if (strcmp(type, "all") == 0) {
- gl_video_load_hwdecs_all(p, devs);
+ load_hwdecs_all(p, devs);
} else {
for (int n = 0; ra_hwdec_drivers[n]; n++) {
const struct ra_hwdec_driver *drv = ra_hwdec_drivers[n];
@@ -4360,11 +4394,40 @@ void gl_video_load_hwdecs(struct gl_video *p, struct mp_hwdec_devices *devs,
p->hwdec_interop_loading_done = true;
}
-void gl_video_load_hwdecs_all(struct gl_video *p, struct mp_hwdec_devices *devs)
+void gl_video_load_hwdecs_for_img_fmt(struct gl_video *p,
+ struct mp_hwdec_devices *devs,
+ int imgfmt)
{
- if (!p->hwdec_interop_loading_done) {
- for (int n = 0; ra_hwdec_drivers[n]; n++)
- load_add_hwdec(p, devs, ra_hwdec_drivers[n], true);
- p->hwdec_interop_loading_done = true;
+ if (p->hwdec_interop_loading_done) {
+ /*
+ * If we previously marked interop loading as done (for reasons
+ * discussed above), then do not load any other interops regardless
+ * of imgfmt.
+ */
+ return;
+ }
+
+ if (imgfmt == IMGFMT_NONE) {
+ MP_VERBOSE(p, "Loading hwdec drivers for all formats\n");
+ load_hwdecs_all(p, devs);
+ return;
+ }
+
+ MP_VERBOSE(p, "Loading hwdec drivers for format: '%s'\n",
+ mp_imgfmt_to_name(imgfmt));
+ for (int i = 0; ra_hwdec_drivers[i]; i++) {
+ bool matched_fmt = false;
+ const struct ra_hwdec_driver *drv = ra_hwdec_drivers[i];
+ for (int j = 0; drv->imgfmts[j]; j++) {
+ if (imgfmt == drv->imgfmts[j]) {
+ matched_fmt = true;
+ break;
+ }
+ }
+ if (!matched_fmt) {
+ continue;
+ }
+
+ load_add_hwdec(p, devs, drv, false);
}
}
diff --git a/video/out/gpu/video.h b/video/out/gpu/video.h
index 0bb180e514..4c6422b1ab 100644
--- a/video/out/gpu/video.h
+++ b/video/out/gpu/video.h
@@ -220,7 +220,8 @@ bool gl_video_showing_interpolated_frame(struct gl_video *p);
struct mp_hwdec_devices;
void gl_video_load_hwdecs(struct gl_video *p, struct mp_hwdec_devices *devs,
bool load_all_by_default);
-void gl_video_load_hwdecs_all(struct gl_video *p, struct mp_hwdec_devices *devs);
+void gl_video_load_hwdecs_for_img_fmt(struct gl_video *p, struct mp_hwdec_devices *devs,
+ int imgfmt);
struct vo;
void gl_video_configure_queue(struct gl_video *p, struct vo *vo);
diff --git a/video/out/vo_gpu.c b/video/out/vo_gpu.c
index 9e83b380b8..817d750c76 100644
--- a/video/out/vo_gpu.c
+++ b/video/out/vo_gpu.c
@@ -126,18 +126,19 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
return 0;
}
-static void request_hwdec_api(struct vo *vo)
+static void request_hwdec_api(struct vo *vo, void *data)
{
struct gpu_priv *p = vo->priv;
+ int imgfmt = (intptr_t)data;
- gl_video_load_hwdecs_all(p->renderer, vo->hwdec_devs);
+ gl_video_load_hwdecs_for_img_fmt(p->renderer, vo->hwdec_devs, imgfmt);
}
-static void call_request_hwdec_api(void *ctx)
+static void call_request_hwdec_api(void *ctx, int imgfmt)
{
// Roundabout way to run hwdec loading on the VO thread.
// Redirects to request_hwdec_api().
- vo_control(ctx, VOCTRL_LOAD_HWDEC_API, NULL);
+ vo_control(ctx, VOCTRL_LOAD_HWDEC_API, (void *)(intptr_t)imgfmt);
}
static void get_and_update_icc_profile(struct gpu_priv *p)
@@ -200,7 +201,7 @@ static int control(struct vo *vo, uint32_t request, void *data)
return true;
}
case VOCTRL_LOAD_HWDEC_API:
- request_hwdec_api(vo);
+ request_hwdec_api(vo, data);
return true;
case VOCTRL_UPDATE_RENDER_OPTS: {
update_ra_ctx_options(vo);