Coverage Report

Created: 2025-06-12 07:21

/src/mpv/video/out/gpu/osd.c
Line
Count
Source (jump to first uncovered line)
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 <stdlib.h>
19
#include <assert.h>
20
#include <limits.h>
21
22
#include "common/common.h"
23
#include "common/msg.h"
24
#include "video/csputils.h"
25
#include "video/mp_image.h"
26
#include "osd.h"
27
28
0
#define GLSL(x) gl_sc_add(sc, #x "\n");
29
30
// glBlendFuncSeparate() arguments
31
static const int blend_factors[SUBBITMAP_COUNT][4] = {
32
    [SUBBITMAP_LIBASS] = {RA_BLEND_SRC_ALPHA, RA_BLEND_ONE_MINUS_SRC_ALPHA,
33
                          RA_BLEND_ONE,       RA_BLEND_ONE_MINUS_SRC_ALPHA},
34
    [SUBBITMAP_BGRA] =   {RA_BLEND_ONE,       RA_BLEND_ONE_MINUS_SRC_ALPHA,
35
                          RA_BLEND_ONE,       RA_BLEND_ONE_MINUS_SRC_ALPHA},
36
};
37
38
struct vertex {
39
    float position[2];
40
    float texcoord[2];
41
    uint8_t ass_color[4];
42
};
43
44
static const struct ra_renderpass_input vertex_vao[] = {
45
    {"position",  RA_VARTYPE_FLOAT,      2, 1, offsetof(struct vertex, position)},
46
    {"texcoord" , RA_VARTYPE_FLOAT,      2, 1, offsetof(struct vertex, texcoord)},
47
    {"ass_color", RA_VARTYPE_BYTE_UNORM, 4, 1, offsetof(struct vertex, ass_color)},
48
};
49
50
struct mpgl_osd_part {
51
    enum sub_bitmap_format format;
52
    int change_id;
53
    struct ra_tex *texture;
54
    int w, h;
55
    int num_subparts;
56
    int prev_num_subparts;
57
    struct sub_bitmap *subparts;
58
    int num_vertices;
59
    struct vertex *vertices;
60
};
61
62
struct mpgl_osd {
63
    struct mp_log *log;
64
    struct osd_state *osd;
65
    struct ra *ra;
66
    struct mpgl_osd_part *parts[MAX_OSD_PARTS];
67
    const struct ra_format *fmt_table[SUBBITMAP_COUNT];
68
    bool formats[SUBBITMAP_COUNT];
69
    bool change_flag; // for reporting to API user only
70
    // temporary
71
    int stereo_mode;
72
    struct mp_osd_res osd_res;
73
    void *scratch;
74
};
75
76
struct mpgl_osd *mpgl_osd_init(struct ra *ra, struct mp_log *log,
77
                               struct osd_state *osd)
78
0
{
79
0
    struct mpgl_osd *ctx = talloc_ptrtype(NULL, ctx);
80
0
    *ctx = (struct mpgl_osd) {
81
0
        .log = log,
82
0
        .osd = osd,
83
0
        .ra = ra,
84
0
        .change_flag = true,
85
0
        .scratch = talloc_zero_size(ctx, 1),
86
0
    };
87
88
0
    ctx->fmt_table[SUBBITMAP_LIBASS] = ra_find_unorm_format(ra, 1, 1);
89
0
    ctx->fmt_table[SUBBITMAP_BGRA]   = ra_find_unorm_format(ra, 1, 4);
90
91
0
    for (int n = 0; n < MAX_OSD_PARTS; n++)
92
0
        ctx->parts[n] = talloc_zero(ctx, struct mpgl_osd_part);
93
94
0
    for (int n = 0; n < SUBBITMAP_COUNT; n++)
95
0
        ctx->formats[n] = !!ctx->fmt_table[n];
96
97
0
    return ctx;
98
0
}
99
100
void mpgl_osd_destroy(struct mpgl_osd *ctx)
101
0
{
102
0
    if (!ctx)
103
0
        return;
104
105
0
    for (int n = 0; n < MAX_OSD_PARTS; n++) {
106
0
        struct mpgl_osd_part *p = ctx->parts[n];
107
0
        ra_tex_free(ctx->ra, &p->texture);
108
0
    }
109
0
    talloc_free(ctx);
110
0
}
111
112
static int next_pow2(int v)
113
0
{
114
0
    for (int x = 0; x < 30; x++) {
115
0
        if ((1 << x) >= v)
116
0
            return 1 << x;
117
0
    }
118
0
    return INT_MAX;
119
0
}
120
121
static bool upload_osd(struct mpgl_osd *ctx, struct mpgl_osd_part *osd,
122
                       struct sub_bitmaps *imgs)
123
0
{
124
0
    struct ra *ra = ctx->ra;
125
0
    bool ok = false;
126
127
0
    mp_assert(imgs->packed);
128
129
0
    int req_w = next_pow2(imgs->packed_w);
130
0
    int req_h = next_pow2(imgs->packed_h);
131
132
0
    const struct ra_format *fmt = ctx->fmt_table[imgs->format];
133
0
    mp_assert(fmt);
134
135
0
    if (!osd->texture || req_w > osd->w || req_h > osd->h ||
136
0
        osd->format != imgs->format)
137
0
    {
138
0
        ra_tex_free(ra, &osd->texture);
139
140
0
        osd->format = imgs->format;
141
0
        osd->w = MPMAX(32, req_w);
142
0
        osd->h = MPMAX(32, req_h);
143
144
0
        MP_VERBOSE(ctx, "Reallocating OSD texture to %dx%d.\n", osd->w, osd->h);
145
146
0
        if (osd->w > ra->max_texture_wh || osd->h > ra->max_texture_wh) {
147
0
            MP_ERR(ctx, "OSD bitmaps do not fit on a surface with the maximum "
148
0
                   "supported size %dx%d.\n", ra->max_texture_wh,
149
0
                   ra->max_texture_wh);
150
0
            goto done;
151
0
        }
152
153
0
        struct ra_tex_params params = {
154
0
            .dimensions = 2,
155
0
            .w = osd->w,
156
0
            .h = osd->h,
157
0
            .d = 1,
158
0
            .format = fmt,
159
0
            .render_src = true,
160
0
            .src_linear = true,
161
0
            .host_mutable = true,
162
0
        };
163
0
        osd->texture = ra_tex_create(ra, &params);
164
0
        if (!osd->texture)
165
0
            goto done;
166
0
    }
167
168
0
    struct ra_tex_upload_params params = {
169
0
        .tex = osd->texture,
170
0
        .src = imgs->packed->planes[0],
171
0
        .invalidate = true,
172
0
        .rc = &(struct mp_rect){0, 0, imgs->packed_w, imgs->packed_h},
173
0
        .stride = imgs->packed->stride[0],
174
0
    };
175
176
0
    ok = ra->fns->tex_upload(ra, &params);
177
178
0
done:
179
0
    return ok;
180
0
}
181
182
static void gen_osd_cb(void *pctx, struct sub_bitmaps *imgs)
183
0
{
184
0
    struct mpgl_osd *ctx = pctx;
185
186
0
    if (imgs->num_parts == 0 || !ctx->formats[imgs->format])
187
0
        return;
188
189
0
    struct mpgl_osd_part *osd = ctx->parts[imgs->render_index];
190
191
0
    bool ok = true;
192
0
    if (imgs->change_id != osd->change_id) {
193
0
        if (!upload_osd(ctx, osd, imgs))
194
0
            ok = false;
195
196
0
        osd->change_id = imgs->change_id;
197
0
        ctx->change_flag = true;
198
0
    }
199
0
    osd->num_subparts = ok ? imgs->num_parts : 0;
200
201
0
    MP_TARRAY_GROW(osd, osd->subparts, osd->num_subparts);
202
0
    memcpy(osd->subparts, imgs->parts,
203
0
           osd->num_subparts * sizeof(osd->subparts[0]));
204
0
}
205
206
bool mpgl_osd_draw_prepare(struct mpgl_osd *ctx, int index,
207
                           struct gl_shader_cache *sc)
208
0
{
209
0
    mp_assert(index >= 0 && index < MAX_OSD_PARTS);
210
0
    struct mpgl_osd_part *part = ctx->parts[index];
211
212
0
    enum sub_bitmap_format fmt = part->format;
213
0
    if (!fmt || !part->num_subparts || !part->texture)
214
0
        return false;
215
216
0
    gl_sc_uniform_texture(sc, "osdtex", part->texture);
217
0
    switch (fmt) {
218
0
    case SUBBITMAP_BGRA: {
219
0
        GLSL(color = texture(osdtex, texcoord).bgra;)
220
0
        break;
221
0
    }
222
0
    case SUBBITMAP_LIBASS: {
223
0
        GLSL(color =
224
0
            vec4(ass_color.rgb, ass_color.a * texture(osdtex, texcoord).r);)
225
0
        break;
226
0
    }
227
0
    default:
228
0
        MP_ASSERT_UNREACHABLE();
229
0
    }
230
231
0
    return true;
232
0
}
233
234
static void write_quad(struct vertex *va, struct gl_transform t,
235
                       float x0, float y0, float x1, float y1,
236
                       float tx0, float ty0, float tx1, float ty1,
237
                       float tex_w, float tex_h, const uint8_t color[4])
238
0
{
239
0
    gl_transform_vec(t, &x0, &y0);
240
0
    gl_transform_vec(t, &x1, &y1);
241
242
0
#define COLOR_INIT {color[0], color[1], color[2], color[3]}
243
0
    va[0] = (struct vertex){ {x0, y0}, {tx0 / tex_w, ty0 / tex_h}, COLOR_INIT };
244
0
    va[1] = (struct vertex){ {x0, y1}, {tx0 / tex_w, ty1 / tex_h}, COLOR_INIT };
245
0
    va[2] = (struct vertex){ {x1, y0}, {tx1 / tex_w, ty0 / tex_h}, COLOR_INIT };
246
0
    va[3] = (struct vertex){ {x1, y1}, {tx1 / tex_w, ty1 / tex_h}, COLOR_INIT };
247
0
    va[4] = va[2];
248
0
    va[5] = va[1];
249
0
#undef COLOR_INIT
250
0
}
251
252
static void generate_verts(struct mpgl_osd_part *part, struct gl_transform t)
253
0
{
254
0
    MP_TARRAY_GROW(part, part->vertices,
255
0
                   part->num_vertices + part->num_subparts * 6);
256
257
0
    for (int n = 0; n < part->num_subparts; n++) {
258
0
        struct sub_bitmap *b = &part->subparts[n];
259
0
        struct vertex *va = &part->vertices[part->num_vertices];
260
261
        // NOTE: the blend color is used with SUBBITMAP_LIBASS only, so it
262
        //       doesn't matter that we upload garbage for the other formats
263
0
        uint32_t c = b->libass.color;
264
0
        uint8_t color[4] = { c >> 24, (c >> 16) & 0xff,
265
0
                            (c >> 8) & 0xff, 255 - (c & 0xff) };
266
267
0
        write_quad(va, t,
268
0
                   b->x, b->y, b->x + b->dw, b->y + b->dh,
269
0
                   b->src_x, b->src_y, b->src_x + b->w, b->src_y + b->h,
270
0
                   part->w, part->h, color);
271
272
0
        part->num_vertices += 6;
273
0
    }
274
0
}
275
276
// number of screen divisions per axis (x=0, y=1) for the current 3D mode
277
static void get_3d_side_by_side(int stereo_mode, int div[2])
278
0
{
279
0
    div[0] = div[1] = 1;
280
0
    switch (stereo_mode) {
281
0
    case MP_STEREO3D_SBS2L:
282
0
    case MP_STEREO3D_SBS2R: div[0] = 2; break;
283
0
    case MP_STEREO3D_AB2R:
284
0
    case MP_STEREO3D_AB2L:  div[1] = 2; break;
285
0
    }
286
0
}
287
288
void mpgl_osd_draw_finish(struct mpgl_osd *ctx, int index,
289
                          struct gl_shader_cache *sc, const struct ra_fbo *fbo)
290
0
{
291
0
    struct mpgl_osd_part *part = ctx->parts[index];
292
293
0
    int div[2];
294
0
    get_3d_side_by_side(ctx->stereo_mode, div);
295
296
0
    part->num_vertices = 0;
297
298
0
    for (int x = 0; x < div[0]; x++) {
299
0
        for (int y = 0; y < div[1]; y++) {
300
0
            struct gl_transform t;
301
0
            gl_transform_ortho_fbo(&t, fbo);
302
303
0
            float a_x = ctx->osd_res.w * x;
304
0
            float a_y = ctx->osd_res.h * y;
305
0
            t.t[0] += a_x * t.m[0][0] + a_y * t.m[1][0];
306
0
            t.t[1] += a_x * t.m[0][1] + a_y * t.m[1][1];
307
308
0
            generate_verts(part, t);
309
0
        }
310
0
    }
311
312
0
    const int *factors = &blend_factors[part->format][0];
313
0
    gl_sc_blend(sc, factors[0], factors[1], factors[2], factors[3]);
314
315
0
    gl_sc_dispatch_draw(sc, fbo->tex, false, vertex_vao, MP_ARRAY_SIZE(vertex_vao),
316
0
                        sizeof(struct vertex), part->vertices, part->num_vertices);
317
0
}
318
319
static void set_res(struct mpgl_osd *ctx, struct mp_osd_res res, int stereo_mode)
320
0
{
321
0
    int div[2];
322
0
    get_3d_side_by_side(stereo_mode, div);
323
324
0
    res.w /= div[0];
325
0
    res.h /= div[1];
326
0
    ctx->osd_res = res;
327
0
}
328
329
void mpgl_osd_generate(struct mpgl_osd *ctx, struct mp_osd_res res, double pts,
330
                       int stereo_mode, int draw_flags)
331
0
{
332
0
    for (int n = 0; n < MAX_OSD_PARTS; n++)
333
0
        ctx->parts[n]->num_subparts = 0;
334
335
0
    set_res(ctx, res, stereo_mode);
336
337
0
    osd_draw(ctx->osd, ctx->osd_res, pts, draw_flags, ctx->formats, gen_osd_cb, ctx);
338
0
    ctx->stereo_mode = stereo_mode;
339
340
    // Parts going away does not necessarily result in gen_osd_cb() being called
341
    // (not even with num_parts==0), so check this separately.
342
0
    for (int n = 0; n < MAX_OSD_PARTS; n++) {
343
0
        struct mpgl_osd_part *part = ctx->parts[n];
344
0
        if (part->num_subparts !=  part->prev_num_subparts)
345
0
            ctx->change_flag = true;
346
0
        part->prev_num_subparts = part->num_subparts;
347
0
    }
348
0
}
349
350
// See osd_resize() for remarks. This function is an optional optimization too.
351
void mpgl_osd_resize(struct mpgl_osd *ctx, struct mp_osd_res res, int stereo_mode)
352
0
{
353
0
    set_res(ctx, res, stereo_mode);
354
0
    osd_resize(ctx->osd, ctx->osd_res);
355
0
}
356
357
bool mpgl_osd_check_change(struct mpgl_osd *ctx, struct mp_osd_res *res,
358
                           double pts)
359
0
{
360
0
    ctx->change_flag = false;
361
0
    mpgl_osd_generate(ctx, *res, pts, 0, 0);
362
0
    return ctx->change_flag;
363
0
}