/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, ¶ms); |
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, ¶ms); |
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 | } |