Coverage Report

Created: 2025-08-11 06:44

/src/mpv/video/out/vo_gpu.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Based on vo_gl.c by Reimar Doeffinger.
3
 *
4
 * This file is part of mpv.
5
 *
6
 * mpv is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
10
 *
11
 * mpv is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19
20
#include <stdio.h>
21
#include <stdlib.h>
22
#include <string.h>
23
#include <math.h>
24
#include <stdbool.h>
25
#include <assert.h>
26
27
#include <libavutil/common.h>
28
29
#include "mpv_talloc.h"
30
#include "common/common.h"
31
#include "misc/bstr.h"
32
#include "common/msg.h"
33
#include "options/m_config.h"
34
#include "vo.h"
35
#include "video/mp_image.h"
36
#include "sub/osd.h"
37
38
#include "gpu/context.h"
39
#include "gpu/hwdec.h"
40
#include "gpu/video.h"
41
42
struct gpu_priv {
43
    struct mp_log *log;
44
    struct ra_ctx *ctx;
45
46
    char *context_name;
47
    char *context_type;
48
    struct gl_video *renderer;
49
50
    int events;
51
};
52
static void resize(struct vo *vo)
53
0
{
54
0
    struct gpu_priv *p = vo->priv;
55
0
    struct ra_swapchain *sw = p->ctx->swapchain;
56
57
0
    MP_VERBOSE(vo, "Resize: %dx%d\n", vo->dwidth, vo->dheight);
58
59
0
    struct mp_rect src, dst;
60
0
    struct mp_osd_res osd;
61
0
    vo_get_src_dst_rects(vo, &src, &dst, &osd);
62
63
0
    gl_video_resize(p->renderer, &src, &dst, &osd);
64
65
0
    int fb_depth = sw->fns->color_depth ? sw->fns->color_depth(sw) : 0;
66
0
    if (fb_depth)
67
0
        MP_VERBOSE(p, "Reported display depth: %d\n", fb_depth);
68
0
    gl_video_set_fb_depth(p->renderer, fb_depth);
69
70
0
    vo->want_redraw = true;
71
0
}
72
73
static bool draw_frame(struct vo *vo, struct vo_frame *frame)
74
0
{
75
0
    struct gpu_priv *p = vo->priv;
76
0
    struct ra_swapchain *sw = p->ctx->swapchain;
77
78
0
    struct ra_fbo fbo;
79
0
    if (!sw->fns->start_frame(sw, &fbo))
80
0
        return VO_FALSE;
81
82
0
    gl_video_render_frame(p->renderer, frame, &fbo, RENDER_FRAME_DEF);
83
0
    if (!sw->fns->submit_frame(sw, frame)) {
84
0
        MP_ERR(vo, "Failed presenting frame!\n");
85
0
        return VO_FALSE;
86
0
    }
87
88
0
    struct mp_image_params *params = gl_video_get_target_params_ptr(p->renderer);
89
0
    mp_mutex_lock(&vo->params_mutex);
90
0
    vo->target_params = params;
91
0
    mp_mutex_unlock(&vo->params_mutex);
92
93
0
    return VO_TRUE;
94
0
}
95
96
static void flip_page(struct vo *vo)
97
0
{
98
0
    struct gpu_priv *p = vo->priv;
99
0
    struct ra_swapchain *sw = p->ctx->swapchain;
100
0
    sw->fns->swap_buffers(sw);
101
0
}
102
103
static void get_vsync(struct vo *vo, struct vo_vsync_info *info)
104
0
{
105
0
    struct gpu_priv *p = vo->priv;
106
0
    struct ra_swapchain *sw = p->ctx->swapchain;
107
0
    if (sw->fns->get_vsync)
108
0
        sw->fns->get_vsync(sw, info);
109
0
}
110
111
static int query_format(struct vo *vo, int format)
112
0
{
113
0
    struct gpu_priv *p = vo->priv;
114
0
    if (!gl_video_check_format(p->renderer, format))
115
0
        return 0;
116
0
    return 1;
117
0
}
118
119
static int reconfig(struct vo *vo, struct mp_image_params *params)
120
0
{
121
0
    struct gpu_priv *p = vo->priv;
122
123
0
    if (!p->ctx->fns->reconfig(p->ctx))
124
0
        return -1;
125
126
0
    resize(vo);
127
0
    gl_video_config(p->renderer, params);
128
129
0
    return 0;
130
0
}
131
132
static void request_hwdec_api(struct vo *vo, void *data)
133
0
{
134
0
    struct gpu_priv *p = vo->priv;
135
0
    gl_video_load_hwdecs_for_img_fmt(p->renderer, vo->hwdec_devs, data);
136
0
}
137
138
static void call_request_hwdec_api(void *ctx,
139
                                   struct hwdec_imgfmt_request *params)
140
0
{
141
    // Roundabout way to run hwdec loading on the VO thread.
142
    // Redirects to request_hwdec_api().
143
0
    vo_control(ctx, VOCTRL_LOAD_HWDEC_API, params);
144
0
}
145
146
static void get_and_update_icc_profile(struct gpu_priv *p)
147
0
{
148
0
    if (gl_video_icc_auto_enabled(p->renderer)) {
149
0
        MP_VERBOSE(p, "Querying ICC profile...\n");
150
0
        bstr icc = bstr0(NULL);
151
0
        int r = p->ctx->fns->control(p->ctx, &p->events, VOCTRL_GET_ICC_PROFILE, &icc);
152
153
0
        if (r != VO_NOTAVAIL) {
154
0
            if (r == VO_FALSE) {
155
0
                MP_WARN(p, "Could not retrieve an ICC profile.\n");
156
0
            } else if (r == VO_NOTIMPL) {
157
0
                MP_ERR(p, "icc-profile-auto not implemented on this platform.\n");
158
0
            }
159
160
0
            gl_video_set_icc_profile(p->renderer, icc);
161
0
        }
162
0
    }
163
0
}
164
165
static void get_and_update_ambient_lighting(struct gpu_priv *p)
166
0
{
167
0
    double lux;
168
0
    int r = p->ctx->fns->control(p->ctx, &p->events, VOCTRL_GET_AMBIENT_LUX, &lux);
169
0
    if (r == VO_TRUE) {
170
0
        gl_video_set_ambient_lux(p->renderer, lux);
171
0
    }
172
0
    if (r != VO_TRUE && gl_video_gamma_auto_enabled(p->renderer)) {
173
0
        MP_ERR(p, "gamma_auto option provided, but querying for ambient"
174
0
                  " lighting is not supported on this platform\n");
175
0
    }
176
0
}
177
178
static void update_ra_ctx_options(struct vo *vo, struct ra_ctx_opts *ctx_opts)
179
4.59k
{
180
4.59k
    struct gpu_priv *p = vo->priv;
181
4.59k
    struct gl_video_opts *gl_opts = mp_get_config_group(p->ctx, vo->global, &gl_video_conf);
182
4.59k
    ctx_opts->want_alpha = (gl_opts->background == BACKGROUND_COLOR &&
183
4.59k
                            gl_opts->background_color.a != 255) ||
184
4.59k
                            gl_opts->background == BACKGROUND_NONE;
185
4.59k
    talloc_free(gl_opts);
186
4.59k
}
187
188
static int control(struct vo *vo, uint32_t request, void *data)
189
0
{
190
0
    struct gpu_priv *p = vo->priv;
191
192
0
    switch (request) {
193
0
    case VOCTRL_SET_PANSCAN:
194
0
        resize(vo);
195
0
        return VO_TRUE;
196
0
    case VOCTRL_SCREENSHOT: {
197
0
        struct vo_frame *frame = vo_get_current_vo_frame(vo);
198
0
        if (frame)
199
0
            gl_video_screenshot(p->renderer, frame, data);
200
0
        talloc_free(frame);
201
0
        return true;
202
0
    }
203
0
    case VOCTRL_LOAD_HWDEC_API:
204
0
        request_hwdec_api(vo, data);
205
0
        return true;
206
0
    case VOCTRL_UPDATE_RENDER_OPTS: {
207
0
        struct ra_ctx_opts *ctx_opts = mp_get_config_group(vo, vo->global, &ra_ctx_conf);
208
0
        update_ra_ctx_options(vo, ctx_opts);
209
0
        gl_video_configure_queue(p->renderer, vo);
210
0
        get_and_update_icc_profile(p);
211
0
        if (p->ctx->fns->update_render_opts)
212
0
            p->ctx->fns->update_render_opts(p->ctx);
213
0
        vo->want_redraw = true;
214
0
        talloc_free(ctx_opts);
215
0
        return true;
216
0
    }
217
0
    case VOCTRL_RESET:
218
0
        gl_video_reset(p->renderer);
219
0
        return true;
220
0
    case VOCTRL_PAUSE:
221
0
        if (gl_video_showing_interpolated_frame(p->renderer))
222
0
            vo->want_redraw = true;
223
0
        return true;
224
0
    case VOCTRL_PERFORMANCE_DATA:
225
0
        gl_video_perfdata(p->renderer, (struct voctrl_performance_data *)data);
226
0
        return true;
227
0
    case VOCTRL_EXTERNAL_RESIZE:
228
0
        p->ctx->fns->reconfig(p->ctx);
229
0
        resize(vo);
230
0
        return true;
231
0
    }
232
233
0
    int events = 0;
234
0
    int r = p->ctx->fns->control(p->ctx, &events, request, data);
235
0
    if (events & VO_EVENT_ICC_PROFILE_CHANGED) {
236
0
        get_and_update_icc_profile(p);
237
0
        vo->want_redraw = true;
238
0
    }
239
0
    if (events & VO_EVENT_AMBIENT_LIGHTING_CHANGED) {
240
0
        get_and_update_ambient_lighting(p);
241
0
        vo->want_redraw = true;
242
0
    }
243
0
    events |= p->events;
244
0
    p->events = 0;
245
0
    if (events & VO_EVENT_RESIZE)
246
0
        resize(vo);
247
0
    if (events & VO_EVENT_EXPOSE)
248
0
        vo->want_redraw = true;
249
0
    vo_event(vo, events);
250
251
0
    return r;
252
0
}
253
254
static void wakeup(struct vo *vo)
255
0
{
256
0
    struct gpu_priv *p = vo->priv;
257
0
    if (p->ctx && p->ctx->fns->wakeup)
258
0
        p->ctx->fns->wakeup(p->ctx);
259
0
}
260
261
static void wait_events(struct vo *vo, int64_t until_time_ns)
262
0
{
263
0
    struct gpu_priv *p = vo->priv;
264
0
    if (p->ctx && p->ctx->fns->wait_events) {
265
0
        p->ctx->fns->wait_events(p->ctx, until_time_ns);
266
0
    } else {
267
0
        vo_wait_default(vo, until_time_ns);
268
0
    }
269
0
}
270
271
static struct mp_image *get_image(struct vo *vo, int imgfmt, int w, int h,
272
                                  int stride_align, int flags)
273
0
{
274
0
    struct gpu_priv *p = vo->priv;
275
276
0
    return gl_video_get_image(p->renderer, imgfmt, w, h, stride_align, flags);
277
0
}
278
279
static void uninit(struct vo *vo)
280
4.59k
{
281
4.59k
    struct gpu_priv *p = vo->priv;
282
283
4.59k
    gl_video_uninit(p->renderer);
284
4.59k
    mp_mutex_lock(&vo->params_mutex);
285
4.59k
    vo->target_params = NULL;
286
4.59k
    mp_mutex_unlock(&vo->params_mutex);
287
288
4.59k
    if (vo->hwdec_devs) {
289
0
        hwdec_devices_set_loader(vo->hwdec_devs, NULL, NULL);
290
0
        hwdec_devices_destroy(vo->hwdec_devs);
291
0
    }
292
4.59k
    ra_ctx_destroy(&p->ctx);
293
4.59k
}
294
295
static int preinit(struct vo *vo)
296
4.59k
{
297
4.59k
    struct gpu_priv *p = vo->priv;
298
4.59k
    p->log = vo->log;
299
300
4.59k
    struct ra_ctx_opts *ctx_opts = mp_get_config_group(vo, vo->global, &ra_ctx_conf);
301
4.59k
    update_ra_ctx_options(vo, ctx_opts);
302
4.59k
    p->ctx = ra_ctx_create(vo, *ctx_opts);
303
4.59k
    talloc_free(ctx_opts);
304
4.59k
    if (!p->ctx)
305
4.59k
        goto err_out;
306
0
    mp_assert(p->ctx->ra);
307
0
    mp_assert(p->ctx->swapchain);
308
309
0
    p->renderer = gl_video_init(p->ctx->ra, vo->log, vo->global);
310
0
    gl_video_set_osd_source(p->renderer, vo->osd);
311
0
    gl_video_configure_queue(p->renderer, vo);
312
313
0
    get_and_update_icc_profile(p);
314
315
0
    vo->hwdec_devs = hwdec_devices_create();
316
0
    hwdec_devices_set_loader(vo->hwdec_devs, call_request_hwdec_api, vo);
317
318
0
    gl_video_init_hwdecs(p->renderer, p->ctx, vo->hwdec_devs, false);
319
320
0
    return 0;
321
322
4.59k
err_out:
323
4.59k
    uninit(vo);
324
4.59k
    return -1;
325
0
}
326
327
const struct vo_driver video_out_gpu = {
328
    .description = "Shader-based GPU Renderer",
329
    .name = "gpu",
330
    .caps = VO_CAP_ROTATE90 | VO_CAP_VFLIP,
331
    .preinit = preinit,
332
    .query_format = query_format,
333
    .reconfig = reconfig,
334
    .control = control,
335
    .get_image = get_image,
336
    .draw_frame = draw_frame,
337
    .flip_page = flip_page,
338
    .get_vsync = get_vsync,
339
    .wait_events = wait_events,
340
    .wakeup = wakeup,
341
    .uninit = uninit,
342
    .priv_size = sizeof(struct gpu_priv),
343
};