/src/mpv/video/out/gpu/libmpv_gpu.c
Line | Count | Source |
1 | | #include "config.h" |
2 | | #include "hwdec.h" |
3 | | #include "libmpv_gpu.h" |
4 | | #include "mpv/render_gl.h" |
5 | | #include "video.h" |
6 | | #include "video/out/libmpv.h" |
7 | | |
8 | | static const struct libmpv_gpu_context_fns *context_backends[] = { |
9 | | #if HAVE_D3D11 |
10 | | &libmpv_gpu_context_d3d11, |
11 | | #endif |
12 | | #if HAVE_GL |
13 | | &libmpv_gpu_context_gl, |
14 | | #endif |
15 | | NULL |
16 | | }; |
17 | | |
18 | | struct priv { |
19 | | struct libmpv_gpu_context *context; |
20 | | |
21 | | struct gl_video *renderer; |
22 | | }; |
23 | | |
24 | | struct native_resource_entry { |
25 | | const char *name; // ra_add_native_resource() internal name argument |
26 | | size_t size; // size of struct pointed to (0 for no copy) |
27 | | }; |
28 | | |
29 | | static const struct native_resource_entry native_resource_map[] = { |
30 | | [MPV_RENDER_PARAM_X11_DISPLAY] = { |
31 | | .name = "x11", |
32 | | .size = 0, |
33 | | }, |
34 | | [MPV_RENDER_PARAM_WL_DISPLAY] = { |
35 | | .name = "wl", |
36 | | .size = 0, |
37 | | }, |
38 | | [MPV_RENDER_PARAM_DRM_DRAW_SURFACE_SIZE] = { |
39 | | .name = "drm_draw_surface_size", |
40 | | .size = sizeof (mpv_opengl_drm_draw_surface_size), |
41 | | }, |
42 | | [MPV_RENDER_PARAM_DRM_DISPLAY_V2] = { |
43 | | .name = "drm_params_v2", |
44 | | .size = sizeof (mpv_opengl_drm_params_v2), |
45 | | }, |
46 | | }; |
47 | | |
48 | | static int init(struct render_backend *ctx, mpv_render_param *params) |
49 | 0 | { |
50 | 0 | ctx->priv = talloc_zero(NULL, struct priv); |
51 | 0 | struct priv *p = ctx->priv; |
52 | |
|
53 | 0 | char *api = get_mpv_render_param(params, MPV_RENDER_PARAM_API_TYPE, NULL); |
54 | 0 | if (!api) |
55 | 0 | return MPV_ERROR_INVALID_PARAMETER; |
56 | | |
57 | 0 | for (int n = 0; context_backends[n]; n++) { |
58 | 0 | const struct libmpv_gpu_context_fns *backend = context_backends[n]; |
59 | 0 | if (strcmp(backend->api_name, api) == 0) { |
60 | 0 | p->context = talloc_zero(NULL, struct libmpv_gpu_context); |
61 | 0 | *p->context = (struct libmpv_gpu_context){ |
62 | 0 | .global = ctx->global, |
63 | 0 | .log = ctx->log, |
64 | 0 | .fns = backend, |
65 | 0 | }; |
66 | 0 | break; |
67 | 0 | } |
68 | 0 | } |
69 | |
|
70 | 0 | if (!p->context) |
71 | 0 | return MPV_ERROR_NOT_IMPLEMENTED; |
72 | | |
73 | 0 | int err = p->context->fns->init(p->context, params); |
74 | 0 | if (err < 0) |
75 | 0 | return err; |
76 | | |
77 | 0 | for (int n = 0; params && params[n].type; n++) { |
78 | 0 | if (params[n].type > 0 && |
79 | 0 | params[n].type < MP_ARRAY_SIZE(native_resource_map) && |
80 | 0 | native_resource_map[params[n].type].name) |
81 | 0 | { |
82 | 0 | const struct native_resource_entry *entry = |
83 | 0 | &native_resource_map[params[n].type]; |
84 | 0 | void *data = params[n].data; |
85 | 0 | if (entry->size) |
86 | 0 | data = talloc_memdup(p, data, entry->size); |
87 | 0 | ra_add_native_resource(p->context->ra_ctx->ra, entry->name, data); |
88 | 0 | } |
89 | 0 | } |
90 | |
|
91 | 0 | p->renderer = gl_video_init(p->context->ra_ctx->ra, ctx->log, ctx->global); |
92 | |
|
93 | 0 | ctx->hwdec_devs = hwdec_devices_create(); |
94 | 0 | gl_video_init_hwdecs(p->renderer, p->context->ra_ctx, ctx->hwdec_devs, true); |
95 | 0 | ctx->driver_caps = VO_CAP_ROTATE90 | VO_CAP_VFLIP; |
96 | 0 | return 0; |
97 | 0 | } |
98 | | |
99 | | static bool check_format(struct render_backend *ctx, int imgfmt) |
100 | 0 | { |
101 | 0 | struct priv *p = ctx->priv; |
102 | |
|
103 | 0 | return gl_video_check_format(p->renderer, imgfmt); |
104 | 0 | } |
105 | | |
106 | | static int set_parameter(struct render_backend *ctx, mpv_render_param param) |
107 | 0 | { |
108 | 0 | struct priv *p = ctx->priv; |
109 | |
|
110 | 0 | switch (param.type) { |
111 | 0 | case MPV_RENDER_PARAM_ICC_PROFILE: { |
112 | 0 | mpv_byte_array *data = param.data; |
113 | 0 | gl_video_set_icc_profile(p->renderer, bstrdup(NULL, (bstr){data->data, data->size})); |
114 | 0 | return 0; |
115 | 0 | } |
116 | 0 | case MPV_RENDER_PARAM_AMBIENT_LIGHT: { |
117 | 0 | MP_WARN(ctx, "MPV_RENDER_PARAM_AMBIENT_LIGHT is deprecated and might be " |
118 | 0 | "removed in the future (replacement: gamma-auto.lua)\n"); |
119 | 0 | int lux = *(int *)param.data; |
120 | 0 | gl_video_set_ambient_lux(p->renderer, (double)lux); |
121 | 0 | return 0; |
122 | 0 | } |
123 | 0 | default: |
124 | 0 | return MPV_ERROR_NOT_IMPLEMENTED; |
125 | 0 | } |
126 | 0 | } |
127 | | |
128 | | static void reconfig(struct render_backend *ctx, struct mp_image_params *params) |
129 | 0 | { |
130 | 0 | struct priv *p = ctx->priv; |
131 | |
|
132 | 0 | gl_video_config(p->renderer, params); |
133 | 0 | } |
134 | | |
135 | | static void reset(struct render_backend *ctx) |
136 | 0 | { |
137 | 0 | struct priv *p = ctx->priv; |
138 | |
|
139 | 0 | gl_video_reset(p->renderer); |
140 | 0 | } |
141 | | |
142 | | static void update_external(struct render_backend *ctx, struct vo *vo) |
143 | 0 | { |
144 | 0 | struct priv *p = ctx->priv; |
145 | |
|
146 | 0 | gl_video_set_osd_source(p->renderer, vo ? vo->osd : NULL); |
147 | 0 | if (vo) |
148 | 0 | gl_video_configure_queue(p->renderer, vo); |
149 | 0 | } |
150 | | |
151 | | static void resize(struct render_backend *ctx, struct mp_rect *src, |
152 | | struct mp_rect *dst, struct mp_osd_res *osd) |
153 | 0 | { |
154 | 0 | struct priv *p = ctx->priv; |
155 | |
|
156 | 0 | gl_video_resize(p->renderer, src, dst, osd); |
157 | 0 | } |
158 | | |
159 | | static int get_target_size(struct render_backend *ctx, mpv_render_param *params, |
160 | | int *out_w, int *out_h) |
161 | 0 | { |
162 | 0 | struct priv *p = ctx->priv; |
163 | | |
164 | | // Mapping the surface is cheap, better than adding new backend entrypoints. |
165 | 0 | struct ra_tex *tex; |
166 | 0 | int err = p->context->fns->wrap_fbo(p->context, params, &tex); |
167 | 0 | if (err < 0) |
168 | 0 | return err; |
169 | 0 | *out_w = tex->params.w; |
170 | 0 | *out_h = tex->params.h; |
171 | 0 | return 0; |
172 | 0 | } |
173 | | |
174 | | static int render(struct render_backend *ctx, mpv_render_param *params, |
175 | | struct vo_frame *frame) |
176 | 0 | { |
177 | 0 | struct priv *p = ctx->priv; |
178 | | |
179 | | // Mapping the surface is cheap, better than adding new backend entrypoints. |
180 | 0 | struct ra_tex *tex; |
181 | 0 | int err = p->context->fns->wrap_fbo(p->context, params, &tex); |
182 | 0 | if (err < 0) |
183 | 0 | return err; |
184 | | |
185 | 0 | int depth = *(int *)get_mpv_render_param(params, MPV_RENDER_PARAM_DEPTH, |
186 | 0 | &(int){0}); |
187 | 0 | if (!depth) |
188 | 0 | depth = tex->params.format->component_depth[0]; |
189 | 0 | gl_video_set_fb_depth(p->renderer, depth); |
190 | |
|
191 | 0 | bool flip = *(int *)get_mpv_render_param(params, MPV_RENDER_PARAM_FLIP_Y, |
192 | 0 | &(int){0}); |
193 | |
|
194 | 0 | struct ra_fbo target = {.tex = tex, .flip = flip}; |
195 | 0 | gl_video_render_frame(p->renderer, frame, &target, RENDER_FRAME_DEF); |
196 | 0 | p->context->fns->done_frame(p->context, frame->display_synced); |
197 | |
|
198 | 0 | return 0; |
199 | 0 | } |
200 | | |
201 | | static struct mp_image *get_image(struct render_backend *ctx, int imgfmt, |
202 | | int w, int h, int stride_align, int flags) |
203 | 0 | { |
204 | 0 | struct priv *p = ctx->priv; |
205 | |
|
206 | 0 | return gl_video_get_image(p->renderer, imgfmt, w, h, stride_align, flags); |
207 | 0 | } |
208 | | |
209 | | static void screenshot(struct render_backend *ctx, struct vo_frame *frame, |
210 | | struct voctrl_screenshot *args) |
211 | 0 | { |
212 | 0 | struct priv *p = ctx->priv; |
213 | |
|
214 | 0 | gl_video_screenshot(p->renderer, frame, args); |
215 | 0 | } |
216 | | |
217 | | static void perfdata(struct render_backend *ctx, |
218 | | struct voctrl_performance_data *out) |
219 | 0 | { |
220 | 0 | struct priv *p = ctx->priv; |
221 | |
|
222 | 0 | gl_video_perfdata(p->renderer, out); |
223 | 0 | } |
224 | | |
225 | | static void destroy(struct render_backend *ctx) |
226 | 0 | { |
227 | 0 | struct priv *p = ctx->priv; |
228 | |
|
229 | 0 | if (p->renderer) |
230 | 0 | gl_video_uninit(p->renderer); |
231 | |
|
232 | 0 | hwdec_devices_destroy(ctx->hwdec_devs); |
233 | |
|
234 | 0 | if (p->context) { |
235 | 0 | p->context->fns->destroy(p->context); |
236 | 0 | talloc_free(p->context->priv); |
237 | 0 | talloc_free(p->context); |
238 | 0 | } |
239 | 0 | } |
240 | | |
241 | | const struct render_backend_fns render_backend_gpu = { |
242 | | .init = init, |
243 | | .check_format = check_format, |
244 | | .set_parameter = set_parameter, |
245 | | .reconfig = reconfig, |
246 | | .reset = reset, |
247 | | .update_external = update_external, |
248 | | .resize = resize, |
249 | | .get_target_size = get_target_size, |
250 | | .render = render, |
251 | | .get_image = get_image, |
252 | | .screenshot = screenshot, |
253 | | .perfdata = perfdata, |
254 | | .destroy = destroy, |
255 | | }; |