From d4fc44e711aee61b34edb654587d6380abe39b05 Mon Sep 17 00:00:00 2001 From: Niklas Haas Date: Sun, 27 Feb 2022 21:07:58 +0100 Subject: vo_gpu: move hwdec loading code to common helper So I can reuse it in vo_gpu_next without having to reinvent the wheel. In theory, a lot of the stuff could be made more private inside the hwdec code itself, but for the time being I don't care about refactoring this code, merely sharing it. --- video/out/gpu/hwdec.c | 179 +++++++++++++++++++++++++++++++++++++-------- video/out/gpu/hwdec.h | 28 ++++++- video/out/gpu/libmpv_gpu.c | 2 +- video/out/gpu/video.c | 133 ++++----------------------------- video/out/gpu/video.h | 2 +- video/out/vo_gpu.c | 2 +- 6 files changed, 193 insertions(+), 153 deletions(-) diff --git a/video/out/gpu/hwdec.c b/video/out/gpu/hwdec.c index dd2809f41e..b37407b064 100644 --- a/video/out/gpu/hwdec.c +++ b/video/out/gpu/hwdec.c @@ -105,36 +105,6 @@ struct ra_hwdec *ra_hwdec_load_driver(struct ra *ra, struct mp_log *log, return hwdec; } -int ra_hwdec_validate_opt(struct mp_log *log, const m_option_t *opt, - struct bstr name, const char **value) -{ - struct bstr param = bstr0(*value); - bool help = bstr_equals0(param, "help"); - if (help) - mp_info(log, "Available hwdecs:\n"); - for (int n = 0; ra_hwdec_drivers[n]; n++) { - const struct ra_hwdec_driver *drv = ra_hwdec_drivers[n]; - if (help) { - mp_info(log, " %s\n", drv->name); - } else if (bstr_equals0(param, drv->name)) { - return 1; - } - } - if (help) { - mp_info(log, " auto (behavior depends on context)\n" - " all (load all hwdecs)\n" - " no (do not load any and block loading on demand)\n"); - return M_OPT_EXIT; - } - if (!param.len) - return 1; // "" is treated specially - if (bstr_equals0(param, "all") || bstr_equals0(param, "auto") || - bstr_equals0(param, "no")) - return 1; - mp_fatal(log, "No hwdec backend named '%.*s' found!\n", BSTR_P(param)); - return M_OPT_INVALID; -} - void ra_hwdec_uninit(struct ra_hwdec *hwdec) { if (hwdec) @@ -201,3 +171,152 @@ int ra_hwdec_mapper_map(struct ra_hwdec_mapper *mapper, struct mp_image *img) } return 0; } + +int ra_hwdec_validate_opt(struct mp_log *log, const m_option_t *opt, + struct bstr name, const char **value) +{ + struct bstr param = bstr0(*value); + bool help = bstr_equals0(param, "help"); + if (help) + mp_info(log, "Available hwdecs:\n"); + for (int n = 0; ra_hwdec_drivers[n]; n++) { + const struct ra_hwdec_driver *drv = ra_hwdec_drivers[n]; + if (help) { + mp_info(log, " %s\n", drv->name); + } else if (bstr_equals0(param, drv->name)) { + return 1; + } + } + if (help) { + mp_info(log, " auto (behavior depends on context)\n" + " all (load all hwdecs)\n" + " no (do not load any and block loading on demand)\n"); + return M_OPT_EXIT; + } + if (!param.len) + return 1; // "" is treated specially + if (bstr_equals0(param, "all") || bstr_equals0(param, "auto") || + bstr_equals0(param, "no")) + return 1; + mp_fatal(log, "No hwdec backend named '%.*s' found!\n", BSTR_P(param)); + return M_OPT_INVALID; +} + +static void load_add_hwdec(struct ra_hwdec_ctx *ctx, struct mp_hwdec_devices *devs, + const struct ra_hwdec_driver *drv, bool is_auto) +{ + // Don't load duplicate hwdecs + for (int j = 0; j < ctx->num_hwdecs; j++) { + if (ctx->hwdecs[j]->driver == drv) + return; + } + + struct ra_hwdec *hwdec = + ra_hwdec_load_driver(ctx->ra, ctx->log, ctx->global, devs, drv, is_auto); + if (hwdec) + MP_TARRAY_APPEND(NULL, ctx->hwdecs, ctx->num_hwdecs, hwdec); +} + +static void load_hwdecs_all(struct ra_hwdec_ctx *ctx, struct mp_hwdec_devices *devs) +{ + if (!ctx->loading_done) { + for (int n = 0; ra_hwdec_drivers[n]; n++) + load_add_hwdec(ctx, devs, ra_hwdec_drivers[n], true); + ctx->loading_done = true; + } +} + +void ra_hwdec_ctx_init(struct ra_hwdec_ctx *ctx, struct mp_hwdec_devices *devs, + const char *type, bool load_all_by_default) +{ + assert(ctx->ra); + + /* + * 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. + */ + if (!type || !type[0] || strcmp(type, "auto") == 0) { + if (!load_all_by_default) + return; + type = "all"; + } + if (strcmp(type, "no") == 0) { + // do nothing, just block further loading + } else if (strcmp(type, "all") == 0) { + load_hwdecs_all(ctx, devs); + } else { + for (int n = 0; ra_hwdec_drivers[n]; n++) { + const struct ra_hwdec_driver *drv = ra_hwdec_drivers[n]; + if (strcmp(type, drv->name) == 0) { + load_add_hwdec(ctx, devs, drv, false); + break; + } + } + } + ctx->loading_done = true; +} + +void ra_hwdec_ctx_uninit(struct ra_hwdec_ctx *ctx) +{ + for (int n = 0; n < ctx->num_hwdecs; n++) + ra_hwdec_uninit(ctx->hwdecs[n]); + + talloc_free(ctx->hwdecs); + memset(ctx, 0, sizeof(*ctx)); +} + +void ra_hwdec_ctx_load_fmt(struct ra_hwdec_ctx *ctx, struct mp_hwdec_devices *devs, + int imgfmt) +{ + if (ctx->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(ctx, "Loading hwdec drivers for all formats\n"); + load_hwdecs_all(ctx, devs); + return; + } + + MP_VERBOSE(ctx, "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(ctx, devs, drv, false); + } +} + +struct ra_hwdec *ra_hwdec_get(struct ra_hwdec_ctx *ctx, int imgfmt) +{ + for (int n = 0; n < ctx->num_hwdecs; n++) { + if (ra_hwdec_test_format(ctx->hwdecs[n], imgfmt)) + return ctx->hwdecs[n]; + } + + return NULL; +} diff --git a/video/out/gpu/hwdec.h b/video/out/gpu/hwdec.h index 050a358c74..5150932743 100644 --- a/video/out/gpu/hwdec.h +++ b/video/out/gpu/hwdec.h @@ -5,6 +5,31 @@ #include "ra.h" #include "video/hwdec.h" +// Helper to organize/load hwdecs dynamically +struct ra_hwdec_ctx { + // Set these before calling `ra_hwdec_ctx_init` + struct mp_log *log; + struct mpv_global *global; + struct ra *ra; + + bool loading_done; + struct ra_hwdec **hwdecs; + int num_hwdecs; +}; + +int ra_hwdec_validate_opt(struct mp_log *log, const m_option_t *opt, + struct bstr name, const char **value); + +void ra_hwdec_ctx_init(struct ra_hwdec_ctx *ctx, struct mp_hwdec_devices *devs, + const char *opt, bool load_all_by_default); +void ra_hwdec_ctx_uninit(struct ra_hwdec_ctx *ctx); + +void ra_hwdec_ctx_load_fmt(struct ra_hwdec_ctx *ctx, struct mp_hwdec_devices *devs, + int imgfmt); + +// Gets the right `ra_hwdec` for a format, if any +struct ra_hwdec *ra_hwdec_get(struct ra_hwdec_ctx *ctx, int imgfmt); + struct ra_hwdec { const struct ra_hwdec_driver *driver; struct mp_log *log; @@ -108,9 +133,6 @@ struct ra_hwdec *ra_hwdec_load_driver(struct ra *ra, struct mp_log *log, const struct ra_hwdec_driver *drv, bool is_auto); -int ra_hwdec_validate_opt(struct mp_log *log, const m_option_t *opt, - struct bstr name, const char **value); - void ra_hwdec_uninit(struct ra_hwdec *hwdec); bool ra_hwdec_test_format(struct ra_hwdec *hwdec, int imgfmt); diff --git a/video/out/gpu/libmpv_gpu.c b/video/out/gpu/libmpv_gpu.c index 8b93dcadfd..0c43a71c20 100644 --- a/video/out/gpu/libmpv_gpu.c +++ b/video/out/gpu/libmpv_gpu.c @@ -88,7 +88,7 @@ static int init(struct render_backend *ctx, mpv_render_param *params) p->renderer = gl_video_init(p->context->ra, ctx->log, ctx->global); ctx->hwdec_devs = hwdec_devices_create(); - gl_video_load_hwdecs(p->renderer, ctx->hwdec_devs, true); + gl_video_init_hwdecs(p->renderer, ctx->hwdec_devs, true); ctx->driver_caps = VO_CAP_ROTATE90; return 0; } diff --git a/video/out/gpu/video.c b/video/out/gpu/video.c index e28a88de5b..e158d3bea9 100644 --- a/video/out/gpu/video.c +++ b/video/out/gpu/video.c @@ -281,10 +281,7 @@ struct gl_video { struct cached_file *files; int num_files; - bool hwdec_interop_loading_done; - struct ra_hwdec **hwdecs; - int num_hwdecs; - + struct ra_hwdec_ctx hwdec_ctx; struct ra_hwdec_mapper *hwdec_mapper; struct ra_hwdec *hwdec_overlay; bool hwdec_active; @@ -887,14 +884,7 @@ static void init_video(struct gl_video *p) { p->use_integer_conversion = false; - struct ra_hwdec *hwdec = NULL; - for (int n = 0; n < p->num_hwdecs; n++) { - if (ra_hwdec_test_format(p->hwdecs[n], p->image_params.imgfmt)) { - hwdec = p->hwdecs[n]; - break; - } - } - + struct ra_hwdec *hwdec = ra_hwdec_get(&p->hwdec_ctx, p->image_params.imgfmt); if (hwdec) { if (hwdec->driver->overlay_frame) { MP_WARN(p, "Using HW-overlay mode. No GL filtering is performed " @@ -3930,11 +3920,7 @@ void gl_video_uninit(struct gl_video *p) return; uninit_video(p); - - for (int n = 0; n < p->num_hwdecs; n++) - ra_hwdec_uninit(p->hwdecs[n]); - p->num_hwdecs = 0; - + ra_hwdec_ctx_uninit(&p->hwdec_ctx); gl_sc_destroy(p->sc); ra_tex_free(p->ra, &p->lut_3d_texture); @@ -3989,10 +3975,8 @@ bool gl_video_check_format(struct gl_video *p, int mp_format) if (ra_get_imgfmt_desc(p->ra, mp_format, &desc) && is_imgfmt_desc_supported(p, &desc)) return true; - for (int n = 0; n < p->num_hwdecs; n++) { - if (ra_hwdec_test_format(p->hwdecs[n], mp_format)) - return true; - } + if (ra_hwdec_get(&p->hwdec_ctx, mp_format)) + return true; return false; } @@ -4326,108 +4310,23 @@ struct mp_image *gl_video_get_image(struct gl_video *p, int imgfmt, int w, int h return res; } -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, +void gl_video_init_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) - return; - type = "all"; - } - if (strcmp(type, "no") == 0) { - // do nothing, just block further loading - } else if (strcmp(type, "all") == 0) { - 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]; - if (strcmp(type, drv->name) == 0) { - load_add_hwdec(p, devs, drv, false); - break; - } - } - } - p->hwdec_interop_loading_done = true; + assert(!p->hwdec_ctx.ra); + p->hwdec_ctx = (struct ra_hwdec_ctx) { + .log = p->log, + .global = p->global, + .ra = p->ra, + }; + + ra_hwdec_ctx_init(&p->hwdec_ctx, devs, p->opts.hwdec_interop, load_all_by_default); } 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) { - /* - * 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); - } + assert(p->hwdec_ctx.ra); + ra_hwdec_ctx_load_fmt(&p->hwdec_ctx, devs, imgfmt); } diff --git a/video/out/gpu/video.h b/video/out/gpu/video.h index 4c6422b1ab..6aeeca732f 100644 --- a/video/out/gpu/video.h +++ b/video/out/gpu/video.h @@ -218,7 +218,7 @@ void gl_video_reset(struct gl_video *p); 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, +void gl_video_init_hwdecs(struct gl_video *p, struct mp_hwdec_devices *devs, bool load_all_by_default); void gl_video_load_hwdecs_for_img_fmt(struct gl_video *p, struct mp_hwdec_devices *devs, int imgfmt); diff --git a/video/out/vo_gpu.c b/video/out/vo_gpu.c index 817d750c76..1f471815f1 100644 --- a/video/out/vo_gpu.c +++ b/video/out/vo_gpu.c @@ -312,7 +312,7 @@ static int preinit(struct vo *vo) vo->hwdec_devs = hwdec_devices_create(); hwdec_devices_set_loader(vo->hwdec_devs, call_request_hwdec_api, vo); - gl_video_load_hwdecs(p->renderer, vo->hwdec_devs, false); + gl_video_init_hwdecs(p->renderer, vo->hwdec_devs, false); return 0; -- cgit v1.2.3