/src/mpv/video/out/gpu/hwdec.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 <stddef.h> |
19 | | #include <string.h> |
20 | | |
21 | | #include "config.h" |
22 | | |
23 | | #include "common/common.h" |
24 | | #include "common/msg.h" |
25 | | #include "options/m_config.h" |
26 | | #include "hwdec.h" |
27 | | |
28 | | extern const struct ra_hwdec_driver ra_hwdec_vaapi; |
29 | | extern const struct ra_hwdec_driver ra_hwdec_videotoolbox; |
30 | | extern const struct ra_hwdec_driver ra_hwdec_vdpau; |
31 | | extern const struct ra_hwdec_driver ra_hwdec_dxva2egl; |
32 | | extern const struct ra_hwdec_driver ra_hwdec_d3d11egl; |
33 | | extern const struct ra_hwdec_driver ra_hwdec_dxva2gldx; |
34 | | extern const struct ra_hwdec_driver ra_hwdec_d3d11va; |
35 | | extern const struct ra_hwdec_driver ra_hwdec_dxva2dxgi; |
36 | | extern const struct ra_hwdec_driver ra_hwdec_cuda; |
37 | | extern const struct ra_hwdec_driver ra_hwdec_drmprime; |
38 | | extern const struct ra_hwdec_driver ra_hwdec_drmprime_overlay; |
39 | | extern const struct ra_hwdec_driver ra_hwdec_aimagereader; |
40 | | extern const struct ra_hwdec_driver ra_hwdec_vulkan; |
41 | | |
42 | | const struct ra_hwdec_driver *const ra_hwdec_drivers[] = { |
43 | | #if HAVE_D3D_HWACCEL |
44 | | #if HAVE_D3D11 |
45 | | &ra_hwdec_d3d11va, |
46 | | #if HAVE_D3D9_HWACCEL |
47 | | &ra_hwdec_dxva2dxgi, |
48 | | #endif |
49 | | #endif |
50 | | #if HAVE_EGL_ANGLE |
51 | | &ra_hwdec_d3d11egl, |
52 | | #if HAVE_D3D9_HWACCEL |
53 | | &ra_hwdec_dxva2egl, |
54 | | #endif |
55 | | #endif |
56 | | #endif |
57 | | #if HAVE_VAAPI |
58 | | &ra_hwdec_vaapi, |
59 | | #endif |
60 | | #if HAVE_VIDEOTOOLBOX_GL || HAVE_IOS_GL || HAVE_VIDEOTOOLBOX_PL |
61 | | &ra_hwdec_videotoolbox, |
62 | | #endif |
63 | | #if HAVE_GL_DXINTEROP_D3D9 |
64 | | &ra_hwdec_dxva2gldx, |
65 | | #endif |
66 | | #if HAVE_CUDA_INTEROP |
67 | | &ra_hwdec_cuda, |
68 | | #endif |
69 | | #if HAVE_VDPAU_GL_X11 |
70 | | &ra_hwdec_vdpau, |
71 | | #endif |
72 | | #if HAVE_DRM |
73 | | &ra_hwdec_drmprime, |
74 | | &ra_hwdec_drmprime_overlay, |
75 | | #endif |
76 | | #if HAVE_ANDROID_MEDIA_NDK |
77 | | &ra_hwdec_aimagereader, |
78 | | #endif |
79 | | #if HAVE_VULKAN |
80 | | &ra_hwdec_vulkan, |
81 | | #endif |
82 | | |
83 | | NULL |
84 | | }; |
85 | | |
86 | | struct ra_hwdec *ra_hwdec_load_driver(struct ra_ctx *ra_ctx, |
87 | | struct mp_log *log, |
88 | | struct mpv_global *global, |
89 | | struct mp_hwdec_devices *devs, |
90 | | const struct ra_hwdec_driver *drv, |
91 | | bool is_auto) |
92 | 0 | { |
93 | 0 | struct ra_hwdec *hwdec = talloc(NULL, struct ra_hwdec); |
94 | 0 | *hwdec = (struct ra_hwdec) { |
95 | 0 | .driver = drv, |
96 | 0 | .log = mp_log_new(hwdec, log, drv->name), |
97 | 0 | .global = global, |
98 | 0 | .ra_ctx = ra_ctx, |
99 | 0 | .devs = devs, |
100 | 0 | .probing = is_auto, |
101 | 0 | .priv = talloc_zero_size(hwdec, drv->priv_size), |
102 | 0 | }; |
103 | 0 | mp_verbose(log, "Loading hwdec driver '%s'\n", drv->name); |
104 | 0 | if (hwdec->driver->init(hwdec) < 0) { |
105 | 0 | ra_hwdec_uninit(hwdec); |
106 | 0 | mp_verbose(log, "Loading failed.\n"); |
107 | 0 | return NULL; |
108 | 0 | } |
109 | 0 | return hwdec; |
110 | 0 | } |
111 | | |
112 | | void ra_hwdec_uninit(struct ra_hwdec *hwdec) |
113 | 0 | { |
114 | 0 | if (hwdec) |
115 | 0 | hwdec->driver->uninit(hwdec); |
116 | 0 | talloc_free(hwdec); |
117 | 0 | } |
118 | | |
119 | | bool ra_hwdec_test_format(struct ra_hwdec *hwdec, int imgfmt) |
120 | 0 | { |
121 | 0 | for (int n = 0; hwdec->driver->imgfmts[n]; n++) { |
122 | 0 | if (hwdec->driver->imgfmts[n] == imgfmt) |
123 | 0 | return true; |
124 | 0 | } |
125 | 0 | return false; |
126 | 0 | } |
127 | | |
128 | | struct ra_hwdec_mapper *ra_hwdec_mapper_create(struct ra_hwdec *hwdec, |
129 | | const struct mp_image_params *params) |
130 | 0 | { |
131 | 0 | mp_assert(ra_hwdec_test_format(hwdec, params->imgfmt)); |
132 | | |
133 | 0 | struct ra_hwdec_mapper *mapper = talloc_ptrtype(NULL, mapper); |
134 | 0 | *mapper = (struct ra_hwdec_mapper){ |
135 | 0 | .owner = hwdec, |
136 | 0 | .driver = hwdec->driver->mapper, |
137 | 0 | .log = hwdec->log, |
138 | 0 | .ra = hwdec->ra_ctx->ra, |
139 | 0 | .priv = talloc_zero_size(mapper, hwdec->driver->mapper->priv_size), |
140 | 0 | .src_params = *params, |
141 | 0 | .dst_params = *params, |
142 | 0 | }; |
143 | 0 | if (mapper->driver->init(mapper) < 0) |
144 | 0 | ra_hwdec_mapper_free(&mapper); |
145 | 0 | return mapper; |
146 | 0 | } |
147 | | |
148 | | void ra_hwdec_mapper_free(struct ra_hwdec_mapper **mapper) |
149 | 0 | { |
150 | 0 | struct ra_hwdec_mapper *p = *mapper; |
151 | 0 | if (p) { |
152 | 0 | ra_hwdec_mapper_unmap(p); |
153 | 0 | p->driver->uninit(p); |
154 | 0 | talloc_free(p); |
155 | 0 | } |
156 | 0 | *mapper = NULL; |
157 | 0 | } |
158 | | |
159 | | void ra_hwdec_mapper_unmap(struct ra_hwdec_mapper *mapper) |
160 | 0 | { |
161 | 0 | if (mapper->driver->unmap) |
162 | 0 | mapper->driver->unmap(mapper); |
163 | | |
164 | | // Clean up after the image if the mapper didn't already |
165 | 0 | mp_image_unrefp(&mapper->src); |
166 | 0 | } |
167 | | |
168 | | int ra_hwdec_mapper_map(struct ra_hwdec_mapper *mapper, struct mp_image *img) |
169 | 0 | { |
170 | 0 | ra_hwdec_mapper_unmap(mapper); |
171 | 0 | mp_image_setrefp(&mapper->src, img); |
172 | 0 | if (mapper->driver->map(mapper) < 0) { |
173 | 0 | ra_hwdec_mapper_unmap(mapper); |
174 | 0 | return -1; |
175 | 0 | } |
176 | 0 | return 0; |
177 | 0 | } |
178 | | |
179 | | static int ra_hwdec_validate_opt_full(struct mp_log *log, bool include_modes, |
180 | | const m_option_t *opt, |
181 | | struct bstr name, const char **value) |
182 | 2.59k | { |
183 | 2.59k | struct bstr param = bstr0(*value); |
184 | 2.59k | bool help = bstr_equals0(param, "help"); |
185 | 2.59k | if (help) |
186 | 73 | mp_info(log, "Available hwdecs:\n"); |
187 | 2.59k | for (int n = 0; ra_hwdec_drivers[n]; n++) { |
188 | 0 | const struct ra_hwdec_driver *drv = ra_hwdec_drivers[n]; |
189 | 0 | if (help) { |
190 | 0 | mp_info(log, " %s\n", drv->name); |
191 | 0 | } else if (bstr_equals0(param, drv->name)) { |
192 | 0 | return 1; |
193 | 0 | } |
194 | 0 | } |
195 | 2.59k | if (help) { |
196 | 73 | if (include_modes) { |
197 | 36 | mp_info(log, " auto (behavior depends on context)\n" |
198 | 36 | " all (load all hwdecs)\n" |
199 | 36 | " no (do not load any and block loading on demand)\n"); |
200 | 36 | } |
201 | 73 | return M_OPT_EXIT; |
202 | 73 | } |
203 | 2.52k | if (!param.len) |
204 | 1.29k | return 1; // "" is treated specially |
205 | 1.23k | if (include_modes && |
206 | 1.12k | (bstr_equals0(param, "all") || bstr_equals0(param, "auto") || |
207 | 582 | bstr_equals0(param, "no"))) |
208 | 881 | return 1; |
209 | 354 | mp_fatal(log, "No hwdec backend named '%.*s' found!\n", BSTR_P(param)); |
210 | 354 | return M_OPT_INVALID; |
211 | 1.23k | } |
212 | | |
213 | | int ra_hwdec_validate_opt(struct mp_log *log, const m_option_t *opt, |
214 | | struct bstr name, const char **value) |
215 | 2.03k | { |
216 | 2.03k | return ra_hwdec_validate_opt_full(log, true, opt, name, value); |
217 | 2.03k | } |
218 | | |
219 | | int ra_hwdec_validate_drivers_only_opt(struct mp_log *log, |
220 | | const m_option_t *opt, |
221 | | struct bstr name, const char **value) |
222 | 569 | { |
223 | 569 | return ra_hwdec_validate_opt_full(log, false, opt, name, value); |
224 | 569 | } |
225 | | |
226 | | static void load_add_hwdec(struct ra_hwdec_ctx *ctx, struct mp_hwdec_devices *devs, |
227 | | const struct ra_hwdec_driver *drv, bool is_auto) |
228 | 0 | { |
229 | | // Don't load duplicate hwdecs |
230 | 0 | for (int j = 0; j < ctx->num_hwdecs; j++) { |
231 | 0 | if (ctx->hwdecs[j]->driver == drv) |
232 | 0 | return; |
233 | 0 | } |
234 | | |
235 | 0 | struct ra_hwdec *hwdec = |
236 | 0 | ra_hwdec_load_driver(ctx->ra_ctx, ctx->log, ctx->global, devs, drv, is_auto); |
237 | 0 | if (hwdec) |
238 | 0 | MP_TARRAY_APPEND(NULL, ctx->hwdecs, ctx->num_hwdecs, hwdec); |
239 | 0 | } |
240 | | |
241 | | static void load_hwdecs_all(struct ra_hwdec_ctx *ctx, struct mp_hwdec_devices *devs) |
242 | 0 | { |
243 | 0 | if (!ctx->loading_done) { |
244 | 0 | for (int n = 0; ra_hwdec_drivers[n]; n++) |
245 | 0 | load_add_hwdec(ctx, devs, ra_hwdec_drivers[n], true); |
246 | 0 | ctx->loading_done = true; |
247 | 0 | } |
248 | 0 | } |
249 | | |
250 | | void ra_hwdec_ctx_init(struct ra_hwdec_ctx *ctx, struct mp_hwdec_devices *devs, |
251 | | const char *type, bool load_all_by_default) |
252 | 0 | { |
253 | 0 | mp_assert(ctx->ra_ctx); |
254 | | |
255 | | /* |
256 | | * By default, or if the option value is "auto", we will not pre-emptively |
257 | | * load any interops, and instead allow them to be loaded on-demand. |
258 | | * |
259 | | * If the option value is "no", then no interops will be loaded now, and |
260 | | * no interops will be loaded, even if requested later. |
261 | | * |
262 | | * If the option value is "all", then all interops will be loaded now, and |
263 | | * obviously no interops will need to be loaded later. |
264 | | * |
265 | | * Finally, if a specific interop is requested, it will be loaded now, and |
266 | | * other interops can be loaded, if requested later. |
267 | | */ |
268 | 0 | if (!type || !type[0] || strcmp(type, "auto") == 0) { |
269 | 0 | if (!load_all_by_default) |
270 | 0 | return; |
271 | 0 | type = "all"; |
272 | 0 | } |
273 | 0 | if (strcmp(type, "no") == 0) { |
274 | | // do nothing, just block further loading |
275 | 0 | } else if (strcmp(type, "all") == 0) { |
276 | 0 | load_hwdecs_all(ctx, devs); |
277 | 0 | } else { |
278 | 0 | for (int n = 0; ra_hwdec_drivers[n]; n++) { |
279 | 0 | const struct ra_hwdec_driver *drv = ra_hwdec_drivers[n]; |
280 | 0 | if (strcmp(type, drv->name) == 0) { |
281 | 0 | load_add_hwdec(ctx, devs, drv, false); |
282 | 0 | break; |
283 | 0 | } |
284 | 0 | } |
285 | 0 | } |
286 | 0 | ctx->loading_done = true; |
287 | 0 | } |
288 | | |
289 | | void ra_hwdec_ctx_uninit(struct ra_hwdec_ctx *ctx) |
290 | 0 | { |
291 | 0 | for (int n = 0; n < ctx->num_hwdecs; n++) |
292 | 0 | ra_hwdec_uninit(ctx->hwdecs[n]); |
293 | |
|
294 | 0 | talloc_free(ctx->hwdecs); |
295 | 0 | memset(ctx, 0, sizeof(*ctx)); |
296 | 0 | } |
297 | | |
298 | | void ra_hwdec_ctx_load_fmt(struct ra_hwdec_ctx *ctx, struct mp_hwdec_devices *devs, |
299 | | struct hwdec_imgfmt_request *params) |
300 | 0 | { |
301 | 0 | int imgfmt = params->imgfmt; |
302 | 0 | if (ctx->loading_done) { |
303 | | /* |
304 | | * If we previously marked interop loading as done (for reasons |
305 | | * discussed above), then do not load any other interops regardless |
306 | | * of imgfmt. |
307 | | */ |
308 | 0 | return; |
309 | 0 | } |
310 | | |
311 | 0 | if (imgfmt == IMGFMT_NONE) { |
312 | 0 | MP_VERBOSE(ctx, "Loading hwdec drivers for all formats\n"); |
313 | 0 | load_hwdecs_all(ctx, devs); |
314 | 0 | return; |
315 | 0 | } |
316 | | |
317 | 0 | MP_VERBOSE(ctx, "Loading hwdec drivers for format: '%s'\n", |
318 | 0 | mp_imgfmt_to_name(imgfmt)); |
319 | 0 | for (int i = 0; ra_hwdec_drivers[i]; i++) { |
320 | 0 | bool matched_fmt = false; |
321 | 0 | const struct ra_hwdec_driver *drv = ra_hwdec_drivers[i]; |
322 | 0 | for (int j = 0; drv->imgfmts[j]; j++) { |
323 | 0 | if (imgfmt == drv->imgfmts[j]) { |
324 | 0 | matched_fmt = true; |
325 | 0 | break; |
326 | 0 | } |
327 | 0 | } |
328 | 0 | if (!matched_fmt) { |
329 | 0 | continue; |
330 | 0 | } |
331 | | |
332 | 0 | load_add_hwdec(ctx, devs, drv, params->probing); |
333 | 0 | } |
334 | 0 | } |
335 | | |
336 | | struct ra_hwdec *ra_hwdec_get(struct ra_hwdec_ctx *ctx, int imgfmt) |
337 | 0 | { |
338 | 0 | for (int n = 0; n < ctx->num_hwdecs; n++) { |
339 | 0 | if (ra_hwdec_test_format(ctx->hwdecs[n], imgfmt)) |
340 | 0 | return ctx->hwdecs[n]; |
341 | 0 | } |
342 | | |
343 | 0 | return NULL; |
344 | 0 | } |
345 | | |
346 | | int ra_hwdec_driver_get_imgfmt_for_name(const char *name) |
347 | 0 | { |
348 | 0 | for (int i = 0; ra_hwdec_drivers[i]; i++) { |
349 | 0 | if (!strcmp(ra_hwdec_drivers[i]->name, name)) { |
350 | 0 | return ra_hwdec_drivers[i]->imgfmts[0]; |
351 | 0 | } |
352 | 0 | } |
353 | 0 | return IMGFMT_NONE; |
354 | 0 | } |
355 | | |
356 | | enum AVHWDeviceType ra_hwdec_driver_get_device_type_for_name(const char *name) |
357 | 0 | { |
358 | 0 | for (int i = 0; ra_hwdec_drivers[i]; i++) { |
359 | 0 | if (!strcmp(ra_hwdec_drivers[i]->name, name)) { |
360 | 0 | return ra_hwdec_drivers[i]->device_type; |
361 | 0 | } |
362 | 0 | } |
363 | 0 | return AV_HWDEVICE_TYPE_NONE; |
364 | 0 | } |