Coverage Report

Created: 2026-01-26 07:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/video/out/opengl/context.c
Line
Count
Source
1
/*
2
 * This file is part of mpv.
3
 *
4
 * mpv is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2.1 of the License, or (at your option) any later version.
8
 *
9
 * mpv is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
 * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
16
 */
17
18
#include "options/m_config.h"
19
#include "context.h"
20
#include "ra_gl.h"
21
#include "utils.h"
22
23
// 0-terminated list of desktop GL versions a backend should try to
24
// initialize. Each entry is the minimum required version.
25
const int mpgl_min_required_gl_versions[] = {
26
    /*
27
     * Nvidia drivers will not provide the highest supported version
28
     * when 320 core is requested. Instead, it just returns 3.2. This
29
     * would be bad, as we actually want compute shaders that require
30
     * 4.2, so we have to request a sufficiently high version. We use
31
     * 440 to maximise driver compatibility as we don't need anything
32
     * from newer versions.
33
     */
34
    440,
35
    320,
36
    210,
37
    0
38
};
39
40
enum {
41
    FLUSH_NO = 0,
42
    FLUSH_YES,
43
    FLUSH_AUTO,
44
};
45
46
struct opengl_opts {
47
    bool use_glfinish;
48
    bool waitvsync;
49
    int vsync_pattern[2];
50
    int swapinterval;
51
    int early_flush;
52
    int gles_mode;
53
};
54
55
#define OPT_BASE_STRUCT struct opengl_opts
56
const struct m_sub_options opengl_conf = {
57
    .opts = (const struct m_option[]) {
58
        {"opengl-glfinish", OPT_BOOL(use_glfinish)},
59
        {"opengl-waitvsync", OPT_BOOL(waitvsync)},
60
        {"opengl-swapinterval", OPT_INT(swapinterval), .flags = UPDATE_VO},
61
        {"opengl-check-pattern-a", OPT_INT(vsync_pattern[0])},
62
        {"opengl-check-pattern-b", OPT_INT(vsync_pattern[1])},
63
        {"opengl-es", OPT_CHOICE(gles_mode,
64
            {"auto", GLES_AUTO}, {"yes", GLES_YES}, {"no", GLES_NO}),
65
            .flags = UPDATE_VO,
66
        },
67
        {"opengl-early-flush", OPT_CHOICE(early_flush,
68
            {"no", FLUSH_NO}, {"yes", FLUSH_YES}, {"auto", FLUSH_AUTO})},
69
        {0},
70
    },
71
    .defaults = &(const struct opengl_opts) {
72
        .swapinterval = 1,
73
    },
74
    .size = sizeof(struct opengl_opts),
75
};
76
77
struct priv {
78
    GL *gl;
79
    struct mp_log *log;
80
    struct ra_ctx_params params;
81
    struct opengl_opts *opts;
82
    GLuint main_fb;
83
    struct ra_tex *wrapped_fb; // corresponds to main_fb
84
    // for debugging:
85
    int frames_rendered;
86
    unsigned int prev_sgi_sync_count;
87
    // for gl_vsync_pattern
88
    int last_pattern;
89
    int matches, mismatches;
90
    // for swapchain_depth simulation
91
    GLsync *vsync_fences;
92
    int num_vsync_fences;
93
};
94
95
enum gles_mode ra_gl_ctx_get_glesmode(struct ra_ctx *ctx)
96
0
{
97
0
    void *tmp = talloc_new(NULL);
98
0
    struct opengl_opts *opts;
99
0
    enum gles_mode mode;
100
101
0
    opts = mp_get_config_group(tmp, ctx->global, &opengl_conf);
102
0
    mode = opts->gles_mode;
103
104
0
    talloc_free(tmp);
105
0
    return mode;
106
0
}
107
108
void ra_gl_ctx_uninit(struct ra_ctx *ctx)
109
0
{
110
0
    if (ctx->swapchain) {
111
0
        struct priv *p = ctx->swapchain->priv;
112
0
        if (ctx->ra && p->wrapped_fb)
113
0
            ra_tex_free(ctx->ra, &p->wrapped_fb);
114
0
        talloc_free(ctx->swapchain);
115
0
        ctx->swapchain = NULL;
116
0
    }
117
118
    // Clean up any potentially left-over debug callback
119
0
    if (ctx->ra)
120
0
        ra_gl_set_debug(ctx->ra, false);
121
122
0
    ra_free(&ctx->ra);
123
0
}
124
125
static const struct ra_swapchain_fns ra_gl_swapchain_fns;
126
127
bool ra_gl_ctx_init(struct ra_ctx *ctx, GL *gl, struct ra_ctx_params params)
128
0
{
129
0
    struct ra_swapchain *sw = ctx->swapchain = talloc_ptrtype(NULL, sw);
130
0
    *sw = (struct ra_swapchain) {
131
0
        .ctx = ctx,
132
0
        .fns = &ra_gl_swapchain_fns,
133
0
    };
134
135
0
    struct priv *p = sw->priv = talloc_ptrtype(sw, p);
136
0
    *p = (struct priv) {
137
0
        .gl     = gl,
138
0
        .log    = ctx->log,
139
0
        .params = params,
140
0
        .opts   = mp_get_config_group(p, ctx->global, &opengl_conf),
141
0
    };
142
143
0
    if (!gl->version && !gl->es)
144
0
        return false;
145
146
0
    if (gl->mpgl_caps & MPGL_CAP_SW) {
147
0
        MP_WARN(p, "Suspected software renderer or indirect context.\n");
148
0
        if (ctx->opts.probing && !ctx->opts.allow_sw)
149
0
            return false;
150
0
    }
151
152
0
    gl->debug_context = ctx->opts.debug;
153
154
0
    if (gl->SwapInterval) {
155
0
        gl->SwapInterval(p->opts->swapinterval);
156
0
    } else {
157
0
        MP_VERBOSE(p, "GL_*_swap_control extension missing.\n");
158
0
    }
159
160
0
    ctx->ra = ra_create_gl(p->gl, ctx->log);
161
0
    return !!ctx->ra;
162
0
}
163
164
void ra_gl_ctx_resize(struct ra_swapchain *sw, int w, int h, int fbo)
165
0
{
166
0
    struct priv *p = sw->priv;
167
0
    if (p->main_fb == fbo && p->wrapped_fb && p->wrapped_fb->params.w == w
168
0
        && p->wrapped_fb->params.h == h)
169
0
        return;
170
171
0
    if (p->wrapped_fb)
172
0
        ra_tex_free(sw->ctx->ra, &p->wrapped_fb);
173
174
0
    p->main_fb = fbo;
175
0
    p->wrapped_fb = ra_create_wrapped_fb(sw->ctx->ra, fbo, w, h);
176
0
}
177
178
int ra_gl_ctx_color_depth(struct ra_swapchain *sw)
179
0
{
180
0
    struct priv *p = sw->priv;
181
0
    GL *gl = p->gl;
182
183
0
    if (!p->wrapped_fb)
184
0
        return 0;
185
186
0
    if ((gl->es < 300 && !gl->version) || !(gl->mpgl_caps & MPGL_CAP_FB))
187
0
        return 0;
188
189
0
    gl->BindFramebuffer(GL_FRAMEBUFFER, p->main_fb);
190
191
0
    GLenum obj = gl->version ? GL_BACK_LEFT : GL_BACK;
192
0
    if (p->main_fb)
193
0
        obj = GL_COLOR_ATTACHMENT0;
194
195
0
    GLint depth_g = 0;
196
197
0
    gl->GetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, obj,
198
0
                            GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, &depth_g);
199
200
0
    gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
201
202
0
    return depth_g;
203
0
}
204
205
bool ra_gl_ctx_start_frame(struct ra_swapchain *sw, struct ra_fbo *out_fbo)
206
0
{
207
0
    struct priv *p = sw->priv;
208
209
0
    bool visible = true;
210
0
    if (p->params.check_visible)
211
0
        visible = p->params.check_visible(sw->ctx);
212
213
    // If out_fbo is NULL, this was called from vo_gpu_next. Bail out.
214
0
    if (!out_fbo || !visible)
215
0
        return visible;
216
217
0
    *out_fbo = (struct ra_fbo) {
218
0
         .tex = p->wrapped_fb,
219
0
         .flip = !p->gl->flipped, // OpenGL FBs are normally flipped
220
0
    };
221
0
    return true;
222
0
}
223
224
bool ra_gl_ctx_submit_frame(struct ra_swapchain *sw, const struct vo_frame *frame)
225
0
{
226
0
    struct priv *p = sw->priv;
227
0
    GL *gl = p->gl;
228
229
0
    if (p->opts->use_glfinish)
230
0
        gl->Finish();
231
232
0
    if (gl->FenceSync && p->params.swap_buffers) {
233
0
        GLsync fence = gl->FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
234
0
        if (fence)
235
0
            MP_TARRAY_APPEND(p, p->vsync_fences, p->num_vsync_fences, fence);
236
0
    }
237
238
0
    switch (p->opts->early_flush) {
239
0
    case FLUSH_AUTO:
240
0
        if (frame->display_synced)
241
0
            break;
242
0
        MP_FALLTHROUGH;
243
0
    case FLUSH_YES:
244
0
        gl->Flush();
245
0
    }
246
247
0
    return true;
248
0
}
249
250
static void check_pattern(struct priv *p, int item)
251
0
{
252
0
    int expected = p->opts->vsync_pattern[p->last_pattern];
253
0
    if (item == expected) {
254
0
        p->last_pattern++;
255
0
        if (p->last_pattern >= 2)
256
0
            p->last_pattern = 0;
257
0
        p->matches++;
258
0
    } else {
259
0
        p->mismatches++;
260
0
        MP_WARN(p, "wrong pattern, expected %d got %d (hit: %d, miss: %d)\n",
261
0
                expected, item, p->matches, p->mismatches);
262
0
    }
263
0
}
264
265
void ra_gl_ctx_swap_buffers(struct ra_swapchain *sw)
266
0
{
267
0
    struct priv *p = sw->priv;
268
0
    GL *gl = p->gl;
269
270
0
    p->params.swap_buffers(sw->ctx);
271
0
    p->frames_rendered++;
272
273
0
    if (p->frames_rendered > 5 && !sw->ctx->opts.debug)
274
0
        ra_gl_set_debug(sw->ctx->ra, false);
275
276
0
    if ((p->opts->waitvsync || p->opts->vsync_pattern[0])
277
0
        && gl->GetVideoSync)
278
0
    {
279
0
        unsigned int n1 = 0, n2 = 0;
280
0
        gl->GetVideoSync(&n1);
281
0
        if (p->opts->waitvsync)
282
0
            gl->WaitVideoSync(2, (n1 + 1) % 2, &n2);
283
0
        int step = n1 - p->prev_sgi_sync_count;
284
0
        p->prev_sgi_sync_count = n1;
285
0
        MP_DBG(p, "Flip counts: %u->%u, step=%d\n", n1, n2, step);
286
0
        if (p->opts->vsync_pattern[0])
287
0
            check_pattern(p, step);
288
0
    }
289
290
0
    while (p->num_vsync_fences >= sw->ctx->vo->opts->swapchain_depth) {
291
0
        gl->ClientWaitSync(p->vsync_fences[0], GL_SYNC_FLUSH_COMMANDS_BIT, 1e9);
292
0
        gl->DeleteSync(p->vsync_fences[0]);
293
0
        MP_TARRAY_REMOVE_AT(p->vsync_fences, p->num_vsync_fences, 0);
294
0
    }
295
0
}
296
297
static void ra_gl_ctx_get_vsync(struct ra_swapchain *sw,
298
                                struct vo_vsync_info *info)
299
0
{
300
0
    struct priv *p = sw->priv;
301
0
    if (p->params.get_vsync)
302
0
        p->params.get_vsync(sw->ctx, info);
303
0
}
304
305
static pl_color_space_t ra_gl_ctx_target_csp(struct ra_swapchain *sw)
306
0
{
307
0
    struct priv *p = sw->priv;
308
0
    if (p->params.preferred_csp)
309
0
        return p->params.preferred_csp(sw->ctx);
310
0
    return (pl_color_space_t){0};
311
0
}
312
313
static const struct ra_swapchain_fns ra_gl_swapchain_fns = {
314
    .color_depth   = ra_gl_ctx_color_depth,
315
    .target_csp    = ra_gl_ctx_target_csp,
316
    .start_frame   = ra_gl_ctx_start_frame,
317
    .submit_frame  = ra_gl_ctx_submit_frame,
318
    .swap_buffers  = ra_gl_ctx_swap_buffers,
319
    .get_vsync     = ra_gl_ctx_get_vsync,
320
};