Coverage Report

Created: 2026-11-11 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/video/out/gpu/libmpv_gpu.c
Line
Count
Source
1
#include "config.h"
2
#include "hwdec.h"
3
#include "libmpv_gpu.h"
4
#include "mpv/render_gl.h"
5
#include "video.h"
6
#include "video/out/libmpv.h"
7
8
static const struct libmpv_gpu_context_fns *context_backends[] = {
9
#if HAVE_D3D11
10
    &libmpv_gpu_context_d3d11,
11
#endif
12
#if HAVE_GL
13
    &libmpv_gpu_context_gl,
14
#endif
15
    NULL
16
};
17
18
struct priv {
19
    struct libmpv_gpu_context *context;
20
21
    struct gl_video *renderer;
22
};
23
24
struct native_resource_entry {
25
    const char *name;   // ra_add_native_resource() internal name argument
26
    size_t size;        // size of struct pointed to (0 for no copy)
27
};
28
29
static const struct native_resource_entry native_resource_map[] = {
30
    [MPV_RENDER_PARAM_X11_DISPLAY] = {
31
        .name = "x11",
32
        .size = 0,
33
    },
34
    [MPV_RENDER_PARAM_WL_DISPLAY] = {
35
        .name = "wl",
36
        .size = 0,
37
    },
38
    [MPV_RENDER_PARAM_DRM_DRAW_SURFACE_SIZE] = {
39
        .name = "drm_draw_surface_size",
40
        .size = sizeof (mpv_opengl_drm_draw_surface_size),
41
    },
42
    [MPV_RENDER_PARAM_DRM_DISPLAY_V2] = {
43
        .name = "drm_params_v2",
44
        .size = sizeof (mpv_opengl_drm_params_v2),
45
    },
46
};
47
48
static int init(struct render_backend *ctx, mpv_render_param *params)
49
0
{
50
0
    ctx->priv = talloc_zero(NULL, struct priv);
51
0
    struct priv *p = ctx->priv;
52
53
0
    char *api = get_mpv_render_param(params, MPV_RENDER_PARAM_API_TYPE, NULL);
54
0
    if (!api)
55
0
        return MPV_ERROR_INVALID_PARAMETER;
56
57
0
    for (int n = 0; context_backends[n]; n++) {
58
0
        const struct libmpv_gpu_context_fns *backend = context_backends[n];
59
0
        if (strcmp(backend->api_name, api) == 0) {
60
0
            p->context = talloc_zero(NULL, struct libmpv_gpu_context);
61
0
            *p->context = (struct libmpv_gpu_context){
62
0
                .global = ctx->global,
63
0
                .log = ctx->log,
64
0
                .fns = backend,
65
0
            };
66
0
            break;
67
0
        }
68
0
    }
69
70
0
    if (!p->context)
71
0
        return MPV_ERROR_NOT_IMPLEMENTED;
72
73
0
    int err = p->context->fns->init(p->context, params);
74
0
    if (err < 0)
75
0
        return err;
76
77
0
    for (int n = 0; params && params[n].type; n++) {
78
0
        if (params[n].type > 0 &&
79
0
            params[n].type < MP_ARRAY_SIZE(native_resource_map) &&
80
0
            native_resource_map[params[n].type].name)
81
0
        {
82
0
            const struct native_resource_entry *entry =
83
0
                &native_resource_map[params[n].type];
84
0
            void *data = params[n].data;
85
0
            if (entry->size)
86
0
                data = talloc_memdup(p, data, entry->size);
87
0
            ra_add_native_resource(p->context->ra_ctx->ra, entry->name, data);
88
0
        }
89
0
    }
90
91
0
    p->renderer = gl_video_init(p->context->ra_ctx->ra, ctx->log, ctx->global);
92
93
0
    ctx->hwdec_devs = hwdec_devices_create();
94
0
    gl_video_init_hwdecs(p->renderer, p->context->ra_ctx, ctx->hwdec_devs, true);
95
0
    ctx->driver_caps = VO_CAP_ROTATE90 | VO_CAP_VFLIP;
96
0
    return 0;
97
0
}
98
99
static bool check_format(struct render_backend *ctx, int imgfmt)
100
0
{
101
0
    struct priv *p = ctx->priv;
102
103
0
    return gl_video_check_format(p->renderer, imgfmt);
104
0
}
105
106
static int set_parameter(struct render_backend *ctx, mpv_render_param param)
107
0
{
108
0
    struct priv *p = ctx->priv;
109
110
0
    switch (param.type) {
111
0
    case MPV_RENDER_PARAM_ICC_PROFILE: {
112
0
        mpv_byte_array *data = param.data;
113
0
        gl_video_set_icc_profile(p->renderer, bstrdup(NULL, (bstr){data->data, data->size}));
114
0
        return 0;
115
0
    }
116
0
    case MPV_RENDER_PARAM_AMBIENT_LIGHT: {
117
0
        MP_WARN(ctx, "MPV_RENDER_PARAM_AMBIENT_LIGHT is deprecated and might be "
118
0
                     "removed in the future (replacement: gamma-auto.lua)\n");
119
0
        int lux = *(int *)param.data;
120
0
        gl_video_set_ambient_lux(p->renderer, (double)lux);
121
0
        return 0;
122
0
    }
123
0
    default:
124
0
        return MPV_ERROR_NOT_IMPLEMENTED;
125
0
    }
126
0
}
127
128
static void reconfig(struct render_backend *ctx, struct mp_image_params *params)
129
0
{
130
0
    struct priv *p = ctx->priv;
131
132
0
    gl_video_config(p->renderer, params);
133
0
}
134
135
static void reset(struct render_backend *ctx)
136
0
{
137
0
    struct priv *p = ctx->priv;
138
139
0
    gl_video_reset(p->renderer);
140
0
}
141
142
static void update_external(struct render_backend *ctx, struct vo *vo)
143
0
{
144
0
    struct priv *p = ctx->priv;
145
146
0
    gl_video_set_osd_source(p->renderer, vo ? vo->osd : NULL);
147
0
    if (vo)
148
0
        gl_video_configure_queue(p->renderer, vo);
149
0
}
150
151
static void resize(struct render_backend *ctx, struct mp_rect *src,
152
                   struct mp_rect *dst, struct mp_osd_res *osd)
153
0
{
154
0
    struct priv *p = ctx->priv;
155
156
0
    gl_video_resize(p->renderer, src, dst, osd);
157
0
}
158
159
static int get_target_size(struct render_backend *ctx, mpv_render_param *params,
160
                           int *out_w, int *out_h)
161
0
{
162
0
    struct priv *p = ctx->priv;
163
164
    // Mapping the surface is cheap, better than adding new backend entrypoints.
165
0
    struct ra_tex *tex;
166
0
    int err = p->context->fns->wrap_fbo(p->context, params, &tex);
167
0
    if (err < 0)
168
0
        return err;
169
0
    *out_w = tex->params.w;
170
0
    *out_h = tex->params.h;
171
0
    return 0;
172
0
}
173
174
static int render(struct render_backend *ctx, mpv_render_param *params,
175
                  struct vo_frame *frame)
176
0
{
177
0
    struct priv *p = ctx->priv;
178
179
    // Mapping the surface is cheap, better than adding new backend entrypoints.
180
0
    struct ra_tex *tex;
181
0
    int err = p->context->fns->wrap_fbo(p->context, params, &tex);
182
0
    if (err < 0)
183
0
        return err;
184
185
0
    int depth = *(int *)get_mpv_render_param(params, MPV_RENDER_PARAM_DEPTH,
186
0
                                             &(int){0});
187
0
    if (!depth)
188
0
        depth = tex->params.format->component_depth[0];
189
0
    gl_video_set_fb_depth(p->renderer, depth);
190
191
0
    bool flip = *(int *)get_mpv_render_param(params, MPV_RENDER_PARAM_FLIP_Y,
192
0
                                             &(int){0});
193
194
0
    struct ra_fbo target = {.tex = tex, .flip = flip};
195
0
    gl_video_render_frame(p->renderer, frame, &target, RENDER_FRAME_DEF);
196
0
    p->context->fns->done_frame(p->context, frame->display_synced);
197
198
0
    return 0;
199
0
}
200
201
static struct mp_image *get_image(struct render_backend *ctx, int imgfmt,
202
                                  int w, int h, int stride_align, int flags)
203
0
{
204
0
    struct priv *p = ctx->priv;
205
206
0
    return gl_video_get_image(p->renderer, imgfmt, w, h, stride_align, flags);
207
0
}
208
209
static void screenshot(struct render_backend *ctx, struct vo_frame *frame,
210
                       struct voctrl_screenshot *args)
211
0
{
212
0
    struct priv *p = ctx->priv;
213
214
0
    gl_video_screenshot(p->renderer, frame, args);
215
0
}
216
217
static void perfdata(struct render_backend *ctx,
218
                     struct voctrl_performance_data *out)
219
0
{
220
0
    struct priv *p = ctx->priv;
221
222
0
    gl_video_perfdata(p->renderer, out);
223
0
}
224
225
static void destroy(struct render_backend *ctx)
226
0
{
227
0
    struct priv *p = ctx->priv;
228
229
0
    if (p->renderer)
230
0
        gl_video_uninit(p->renderer);
231
232
0
    hwdec_devices_destroy(ctx->hwdec_devs);
233
234
0
    if (p->context) {
235
0
        p->context->fns->destroy(p->context);
236
0
        talloc_free(p->context->priv);
237
0
        talloc_free(p->context);
238
0
    }
239
0
}
240
241
const struct render_backend_fns render_backend_gpu = {
242
    .init = init,
243
    .check_format = check_format,
244
    .set_parameter = set_parameter,
245
    .reconfig = reconfig,
246
    .reset = reset,
247
    .update_external = update_external,
248
    .resize = resize,
249
    .get_target_size = get_target_size,
250
    .render = render,
251
    .get_image = get_image,
252
    .screenshot = screenshot,
253
    .perfdata = perfdata,
254
    .destroy = destroy,
255
};