diff options
Diffstat (limited to 'video/out/gpu/libmpv_gpu.c')
-rw-r--r-- | video/out/gpu/libmpv_gpu.c | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/video/out/gpu/libmpv_gpu.c b/video/out/gpu/libmpv_gpu.c new file mode 100644 index 0000000..aae1d18 --- /dev/null +++ b/video/out/gpu/libmpv_gpu.c @@ -0,0 +1,248 @@ +#include "config.h" +#include "hwdec.h" +#include "libmpv_gpu.h" +#include "libmpv/render_gl.h" +#include "video.h" +#include "video/out/libmpv.h" + +static const struct libmpv_gpu_context_fns *context_backends[] = { +#if HAVE_GL + &libmpv_gpu_context_gl, +#endif + NULL +}; + +struct priv { + struct libmpv_gpu_context *context; + + struct gl_video *renderer; +}; + +struct native_resource_entry { + const char *name; // ra_add_native_resource() internal name argument + size_t size; // size of struct pointed to (0 for no copy) +}; + +static const struct native_resource_entry native_resource_map[] = { + [MPV_RENDER_PARAM_X11_DISPLAY] = { + .name = "x11", + .size = 0, + }, + [MPV_RENDER_PARAM_WL_DISPLAY] = { + .name = "wl", + .size = 0, + }, + [MPV_RENDER_PARAM_DRM_DRAW_SURFACE_SIZE] = { + .name = "drm_draw_surface_size", + .size = sizeof (mpv_opengl_drm_draw_surface_size), + }, + [MPV_RENDER_PARAM_DRM_DISPLAY_V2] = { + .name = "drm_params_v2", + .size = sizeof (mpv_opengl_drm_params_v2), + }, +}; + +static int init(struct render_backend *ctx, mpv_render_param *params) +{ + ctx->priv = talloc_zero(NULL, struct priv); + struct priv *p = ctx->priv; + + char *api = get_mpv_render_param(params, MPV_RENDER_PARAM_API_TYPE, NULL); + if (!api) + return MPV_ERROR_INVALID_PARAMETER; + + for (int n = 0; context_backends[n]; n++) { + const struct libmpv_gpu_context_fns *backend = context_backends[n]; + if (strcmp(backend->api_name, api) == 0) { + p->context = talloc_zero(NULL, struct libmpv_gpu_context); + *p->context = (struct libmpv_gpu_context){ + .global = ctx->global, + .log = ctx->log, + .fns = backend, + }; + break; + } + } + + if (!p->context) + return MPV_ERROR_NOT_IMPLEMENTED; + + int err = p->context->fns->init(p->context, params); + if (err < 0) + return err; + + for (int n = 0; params && params[n].type; n++) { + if (params[n].type > 0 && + params[n].type < MP_ARRAY_SIZE(native_resource_map) && + native_resource_map[params[n].type].name) + { + const struct native_resource_entry *entry = + &native_resource_map[params[n].type]; + void *data = params[n].data; + if (entry->size) + data = talloc_memdup(p, data, entry->size); + ra_add_native_resource(p->context->ra_ctx->ra, entry->name, data); + } + } + + p->renderer = gl_video_init(p->context->ra_ctx->ra, ctx->log, ctx->global); + + ctx->hwdec_devs = hwdec_devices_create(); + gl_video_init_hwdecs(p->renderer, p->context->ra_ctx, ctx->hwdec_devs, true); + ctx->driver_caps = VO_CAP_ROTATE90; + return 0; +} + +static bool check_format(struct render_backend *ctx, int imgfmt) +{ + struct priv *p = ctx->priv; + + return gl_video_check_format(p->renderer, imgfmt); +} + +static int set_parameter(struct render_backend *ctx, mpv_render_param param) +{ + struct priv *p = ctx->priv; + + switch (param.type) { + case MPV_RENDER_PARAM_ICC_PROFILE: { + mpv_byte_array *data = param.data; + gl_video_set_icc_profile(p->renderer, (bstr){data->data, data->size}); + return 0; + } + case MPV_RENDER_PARAM_AMBIENT_LIGHT: { + int lux = *(int *)param.data; + gl_video_set_ambient_lux(p->renderer, lux); + return 0; + } + default: + return MPV_ERROR_NOT_IMPLEMENTED; + } +} + +static void reconfig(struct render_backend *ctx, struct mp_image_params *params) +{ + struct priv *p = ctx->priv; + + gl_video_config(p->renderer, params); +} + +static void reset(struct render_backend *ctx) +{ + struct priv *p = ctx->priv; + + gl_video_reset(p->renderer); +} + +static void update_external(struct render_backend *ctx, struct vo *vo) +{ + struct priv *p = ctx->priv; + + gl_video_set_osd_source(p->renderer, vo ? vo->osd : NULL); + if (vo) + gl_video_configure_queue(p->renderer, vo); +} + +static void resize(struct render_backend *ctx, struct mp_rect *src, + struct mp_rect *dst, struct mp_osd_res *osd) +{ + struct priv *p = ctx->priv; + + gl_video_resize(p->renderer, src, dst, osd); +} + +static int get_target_size(struct render_backend *ctx, mpv_render_param *params, + int *out_w, int *out_h) +{ + struct priv *p = ctx->priv; + + // Mapping the surface is cheap, better than adding new backend entrypoints. + struct ra_tex *tex; + int err = p->context->fns->wrap_fbo(p->context, params, &tex); + if (err < 0) + return err; + *out_w = tex->params.w; + *out_h = tex->params.h; + return 0; +} + +static int render(struct render_backend *ctx, mpv_render_param *params, + struct vo_frame *frame) +{ + struct priv *p = ctx->priv; + + // Mapping the surface is cheap, better than adding new backend entrypoints. + struct ra_tex *tex; + int err = p->context->fns->wrap_fbo(p->context, params, &tex); + if (err < 0) + return err; + + int depth = *(int *)get_mpv_render_param(params, MPV_RENDER_PARAM_DEPTH, + &(int){0}); + gl_video_set_fb_depth(p->renderer, depth); + + bool flip = *(int *)get_mpv_render_param(params, MPV_RENDER_PARAM_FLIP_Y, + &(int){0}); + + struct ra_fbo target = {.tex = tex, .flip = flip}; + gl_video_render_frame(p->renderer, frame, target, RENDER_FRAME_DEF); + p->context->fns->done_frame(p->context, frame->display_synced); + + return 0; +} + +static struct mp_image *get_image(struct render_backend *ctx, int imgfmt, + int w, int h, int stride_align, int flags) +{ + struct priv *p = ctx->priv; + + return gl_video_get_image(p->renderer, imgfmt, w, h, stride_align, flags); +} + +static void screenshot(struct render_backend *ctx, struct vo_frame *frame, + struct voctrl_screenshot *args) +{ + struct priv *p = ctx->priv; + + gl_video_screenshot(p->renderer, frame, args); +} + +static void perfdata(struct render_backend *ctx, + struct voctrl_performance_data *out) +{ + struct priv *p = ctx->priv; + + gl_video_perfdata(p->renderer, out); +} + +static void destroy(struct render_backend *ctx) +{ + struct priv *p = ctx->priv; + + if (p->renderer) + gl_video_uninit(p->renderer); + + hwdec_devices_destroy(ctx->hwdec_devs); + + if (p->context) { + p->context->fns->destroy(p->context); + talloc_free(p->context->priv); + talloc_free(p->context); + } +} + +const struct render_backend_fns render_backend_gpu = { + .init = init, + .check_format = check_format, + .set_parameter = set_parameter, + .reconfig = reconfig, + .reset = reset, + .update_external = update_external, + .resize = resize, + .get_target_size = get_target_size, + .render = render, + .get_image = get_image, + .screenshot = screenshot, + .perfdata = perfdata, + .destroy = destroy, +}; |