/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 | | }; |