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.
This commit is contained in:
Niklas Haas 2022-02-27 21:07:58 +01:00 committed by Niklas Haas
parent bb434a60ed
commit d4fc44e711
6 changed files with 193 additions and 153 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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;