opengl: vaapi: specify texture scale for NV12
[vlc.git] / modules / video_output / opengl / converter_vaapi.c
blob10ee7b170ab7f0b6537a5954e6575e753f23de71
1 /*****************************************************************************
2 * converter_vaapi.c: OpenGL VAAPI opaque converter
3 *****************************************************************************
4 * Copyright (C) 2017 VLC authors and VideoLAN
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program 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.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
25 #include "converter.h"
26 #include "../../hw/vaapi/vlc_vaapi.h"
27 #include <vlc_vout_window.h>
29 #include <assert.h>
31 #include <EGL/egl.h>
32 #include <EGL/eglext.h>
33 #include <va/va_drmcommon.h>
35 #ifdef HAVE_VA_WL
36 # include <va/va_wayland.h>
37 #endif
39 #ifdef HAVE_VA_X11
40 # include <va/va_x11.h>
41 # include <vlc_xlib.h>
42 #endif
44 #ifdef HAVE_VA_DRM
45 # include <va/va_drm.h>
46 # include <vlc_fs.h>
47 # include <fcntl.h>
48 #endif
50 #if defined(USE_OPENGL_ES2)
51 # include <GLES2/gl2ext.h>
52 #endif
54 struct priv
56 struct vlc_vaapi_instance *vainst;
57 VADisplay vadpy;
58 VASurfaceID *va_surface_ids;
59 PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
61 unsigned fourcc;
62 EGLint drm_fourccs[3];
64 struct {
65 picture_t * pic;
66 VAImage va_image;
67 VABufferInfo va_buffer_info;
68 void * egl_images[3];
69 } last;
72 static EGLImageKHR
73 vaegl_image_create(const opengl_tex_converter_t *tc, EGLint w, EGLint h,
74 EGLint fourcc, EGLint fd, EGLint offset, EGLint pitch)
76 EGLint attribs[] = {
77 EGL_WIDTH, w,
78 EGL_HEIGHT, h,
79 EGL_LINUX_DRM_FOURCC_EXT, fourcc,
80 EGL_DMA_BUF_PLANE0_FD_EXT, fd,
81 EGL_DMA_BUF_PLANE0_OFFSET_EXT, offset,
82 EGL_DMA_BUF_PLANE0_PITCH_EXT, pitch,
83 EGL_NONE
86 return tc->gl->egl.createImageKHR(tc->gl, EGL_LINUX_DMA_BUF_EXT, NULL,
87 attribs);
90 static void
91 vaegl_image_destroy(const opengl_tex_converter_t *tc, EGLImageKHR image)
93 tc->gl->egl.destroyImageKHR(tc->gl, image);
96 static void
97 vaegl_release_last_pic(const opengl_tex_converter_t *tc, struct priv *priv)
99 vlc_object_t *o = VLC_OBJECT(tc->gl);
101 for (unsigned i = 0; i < priv->last.va_image.num_planes; ++i)
102 vaegl_image_destroy(tc, priv->last.egl_images[i]);
104 vlc_vaapi_ReleaseBufferHandle(o, priv->vadpy, priv->last.va_image.buf);
106 vlc_vaapi_DestroyImage(o, priv->vadpy, priv->last.va_image.image_id);
108 picture_Release(priv->last.pic);
111 static int
112 vaegl_init_fourcc(const opengl_tex_converter_t *tc, struct priv *priv,
113 unsigned va_fourcc)
115 (void) tc;
116 switch (va_fourcc)
118 case VA_FOURCC_NV12:
119 priv->drm_fourccs[0] = VLC_FOURCC('R', '8', ' ', ' ');
120 priv->drm_fourccs[1] = VLC_FOURCC('G', 'R', '8', '8');
121 break;
122 case VA_FOURCC_P010:
123 priv->drm_fourccs[0] = VLC_FOURCC('R', '1', '6', ' ');
124 priv->drm_fourccs[1] = VLC_FOURCC('G', 'R', '3', '2');
125 break;
126 #if 0
127 /* TODO: the following fourcc are not handled for now */
128 case VA_FOURCC_RGBA:
129 priv->drm_fourccs[0] = VLC_FOURCC('G', 'R', '3', '2');
130 break;
131 case VA_FOURCC_BGRA:
132 priv->drm_fourccs[0] = VLC_FOURCC('G', 'R', '3', '2');
133 break;
134 case VA_FOURCC_YV12:
135 priv->drm_fourccs[0] = VLC_FOURCC('R', '8', ' ', ' ');
136 priv->drm_fourccs[1] = VLC_FOURCC('R', '8', ' ', ' ');
137 priv->drm_fourccs[2] = VLC_FOURCC('R', '8', ' ', ' ');
138 break;
139 case VA_FOURCC_422H:
140 priv->drm_fourccs[0] = VLC_FOURCC('R', '8', ' ', ' ');
141 priv->drm_fourccs[1] = VLC_FOURCC('R', '8', ' ', ' ');
142 priv->drm_fourccs[2] = VLC_FOURCC('R', '8', ' ', ' ');
143 break;
144 case VA_FOURCC_UYVY:
145 priv->drm_fourccs[0] = VLC_FOURCC('R', '1', '6', ' ');
146 break;
147 case VA_FOURCC_444P:
148 priv->drm_fourccs[0] = VLC_FOURCC('R', '1', '6', ' ');
149 priv->drm_fourccs[1] = VLC_FOURCC('R', '1', '6', ' ');
150 priv->drm_fourccs[2] = VLC_FOURCC('R', '1', '6', ' ');
151 break;
152 #endif
153 default: return VLC_EGENERIC;
155 priv->fourcc = va_fourcc;
156 return VLC_SUCCESS;
159 static int
160 tc_vaegl_update(const opengl_tex_converter_t *tc, GLuint *textures,
161 const GLsizei *tex_width, const GLsizei *tex_height,
162 picture_t *pic, const size_t *plane_offset)
164 (void) plane_offset;
165 struct priv *priv = tc->priv;
166 vlc_object_t *o = VLC_OBJECT(tc->gl);
167 VAImage va_image;
168 VABufferInfo va_buffer_info;
169 EGLImageKHR egl_images[3] = { };
170 bool release_image = false, release_buffer_info = false;
172 if (pic == priv->last.pic)
174 va_image = priv->last.va_image;
175 va_buffer_info = priv->last.va_buffer_info;
176 for (unsigned i = 0; i < priv->last.va_image.num_planes; ++i)
177 egl_images[i] = priv->last.egl_images[i];
179 else
181 if (vlc_vaapi_DeriveImage(o, priv->vadpy, vlc_vaapi_PicGetSurface(pic),
182 &va_image))
183 goto error;
184 release_image = true;
186 assert(va_image.format.fourcc == priv->fourcc);
188 va_buffer_info = (VABufferInfo) {
189 .mem_type = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME
191 if (vlc_vaapi_AcquireBufferHandle(o, priv->vadpy, va_image.buf,
192 &va_buffer_info))
193 goto error;
194 release_buffer_info = true;
197 for (unsigned i = 0; i < va_image.num_planes; ++i)
199 egl_images[i] =
200 vaegl_image_create(tc, tex_width[i], tex_height[i],
201 priv->drm_fourccs[i], va_buffer_info.handle,
202 va_image.offsets[i], va_image.pitches[i]);
203 if (egl_images[i] == NULL)
204 goto error;
206 tc->vt->BindTexture(tc->tex_target, textures[i]);
208 priv->glEGLImageTargetTexture2DOES(tc->tex_target, egl_images[i]);
211 if (pic != priv->last.pic)
213 if (priv->last.pic != NULL)
214 vaegl_release_last_pic(tc, priv);
215 priv->last.pic = picture_Hold(pic);
216 priv->last.va_image = va_image;
217 priv->last.va_buffer_info = va_buffer_info;
218 for (unsigned i = 0; i < va_image.num_planes; ++i)
219 priv->last.egl_images[i] = egl_images[i];
222 return VLC_SUCCESS;
224 error:
225 if (release_image)
227 if (release_buffer_info)
228 vlc_vaapi_ReleaseBufferHandle(o, priv->vadpy, va_image.buf);
230 for (unsigned i = 0; i < 3 && egl_images[i] != NULL; ++i)
231 vaegl_image_destroy(tc, egl_images[i]);
233 vlc_vaapi_DestroyImage(o, priv->vadpy, va_image.image_id);
235 return VLC_EGENERIC;
238 static picture_pool_t *
239 tc_vaegl_get_pool(const opengl_tex_converter_t *tc, unsigned requested_count)
241 vlc_object_t *o = VLC_OBJECT(tc->gl);
242 struct priv *priv = tc->priv;
244 picture_pool_t *pool =
245 vlc_vaapi_PoolNew(VLC_OBJECT(tc->gl), priv->vainst, priv->vadpy,
246 requested_count, &priv->va_surface_ids, &tc->fmt,
247 true);
248 if (!pool)
249 return NULL;
251 /* Check if a surface from the pool can be derived and displayed via dmabuf
252 * */
253 bool success = false;
254 VAImage va_image = { .image_id = VA_INVALID_ID };
255 if (vlc_vaapi_DeriveImage(o, priv->vadpy, priv->va_surface_ids[0],
256 &va_image))
257 goto error;
259 assert(va_image.format.fourcc == priv->fourcc);
261 VABufferInfo va_buffer_info = (VABufferInfo) {
262 .mem_type = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME
264 if (vlc_vaapi_AcquireBufferHandle(o ,priv->vadpy, va_image.buf,
265 &va_buffer_info))
266 goto error;
268 for (unsigned i = 0; i < va_image.num_planes; ++i)
270 EGLint w = (va_image.width * tc->texs[i].w.num) / tc->texs[i].w.den;
271 EGLint h = (va_image.height * tc->texs[i].h.num) / tc->texs[i].h.den;
272 EGLImageKHR egl_image =
273 vaegl_image_create(tc, w, h, priv->drm_fourccs[i], va_buffer_info.handle,
274 va_image.offsets[i], va_image.pitches[i]);
275 if (egl_image == NULL)
277 msg_Warn(o, "Can't create Image KHR: kernel too old ?");
278 goto error;
280 vaegl_image_destroy(tc, egl_image);
283 success = true;
284 error:
285 if (va_image.image_id != VA_INVALID_ID)
287 if (va_image.buf != VA_INVALID_ID)
288 vlc_vaapi_ReleaseBufferHandle(o, priv->vadpy, va_image.buf);
289 vlc_vaapi_DestroyImage(o, priv->vadpy, va_image.image_id);
291 if (!success)
293 picture_pool_Release(pool);
294 pool = NULL;
296 return pool;
299 static void
300 Close(vlc_object_t *obj)
302 opengl_tex_converter_t *tc = (void *)obj;
303 struct priv *priv = tc->priv;
305 if (priv->last.pic != NULL)
306 vaegl_release_last_pic(tc, priv);
308 vlc_vaapi_ReleaseInstance(priv->vainst);
310 free(tc->priv);
313 static int
314 tc_va_check_interop_blacklist(opengl_tex_converter_t *tc, VADisplay *vadpy)
316 const char *vendor = vaQueryVendorString(vadpy);
317 if (vendor == NULL)
318 return VLC_SUCCESS;
320 #define BL_SIZE_MAX 19
321 static const char blacklist_prefix[][BL_SIZE_MAX] = {
322 /* XXX: case insensitive and alphabetical order */
323 "mesa gallium vaapi",
326 char vendor_prefix[BL_SIZE_MAX];
327 strncpy(vendor_prefix, vendor, BL_SIZE_MAX);
328 vendor_prefix[BL_SIZE_MAX - 1] = '\0';
330 const char *found = bsearch(vendor_prefix, blacklist_prefix,
331 ARRAY_SIZE(blacklist_prefix),
332 BL_SIZE_MAX, (void *) strcasecmp);
333 if (found != NULL)
335 msg_Warn(tc->gl, "The '%s' driver is blacklisted: no interop", found);
336 return VLC_EGENERIC;
339 return VLC_SUCCESS;
342 static int
343 tc_vaegl_init(opengl_tex_converter_t *tc, VADisplay *vadpy,
344 VANativeDisplay native,
345 vlc_vaapi_native_destroy_cb native_destroy_cb)
347 int ret = VLC_EGENERIC;
348 struct priv *priv = NULL;
350 if (vadpy == NULL)
351 goto error;
353 ret = VLC_ENOMEM;
354 priv = tc->priv = calloc(1, sizeof(struct priv));
355 if (unlikely(tc->priv == NULL))
356 goto error;
358 ret = VLC_EGENERIC;
359 priv->vadpy = vadpy;
360 priv->fourcc = 0;
362 int va_fourcc;
363 int vlc_sw_chroma;
364 switch (tc->fmt.i_chroma)
366 case VLC_CODEC_VAAPI_420:
367 va_fourcc = VA_FOURCC_NV12;
368 vlc_sw_chroma = VLC_CODEC_NV12;
369 break;
370 case VLC_CODEC_VAAPI_420_10BPP:
371 va_fourcc = VA_FOURCC_P010;
372 vlc_sw_chroma = VLC_CODEC_P010;
373 break;
374 default:
375 vlc_assert_unreachable();
378 if (vaegl_init_fourcc(tc, priv, va_fourcc))
379 goto error;
381 priv->glEGLImageTargetTexture2DOES =
382 vlc_gl_GetProcAddress(tc->gl, "glEGLImageTargetTexture2DOES");
383 if (priv->glEGLImageTargetTexture2DOES == NULL)
384 goto error;
386 tc->pf_update = tc_vaegl_update;
387 tc->pf_get_pool = tc_vaegl_get_pool;
389 priv->vainst = vlc_vaapi_InitializeInstance(VLC_OBJECT(tc->gl), priv->vadpy,
390 native, native_destroy_cb);
391 if (priv->vainst == NULL)
393 /* Already released by vlc_vaapi_InitializeInstance */
394 vadpy = NULL;
395 native_destroy_cb = NULL;
396 goto error;
399 if (tc_va_check_interop_blacklist(tc, priv->vadpy))
400 goto error;
402 tc->fshader = opengl_fragment_shader_init(tc, GL_TEXTURE_2D, vlc_sw_chroma,
403 tc->fmt.space);
404 if (tc->fshader == 0)
405 goto error;
407 /* Fix the UV plane texture scale factor for GR */
408 if (vlc_sw_chroma == VLC_CODEC_NV12 || vlc_sw_chroma == VLC_CODEC_P010)
409 tc->texs[1].h = (vlc_rational_t) { 1, 2 };
411 return VLC_SUCCESS;
413 error:
414 if (priv && priv->vainst)
415 vlc_vaapi_ReleaseInstance(priv->vainst);
416 else
418 if (vadpy != NULL)
419 vaTerminate(vadpy);
420 if (native != NULL && native_destroy_cb != NULL)
421 native_destroy_cb(native);
423 free(priv);
424 return ret;
427 #ifdef HAVE_VA_X11
428 static void
429 x11_native_destroy_cb(VANativeDisplay native)
431 XCloseDisplay(native);
433 #endif
434 #ifdef HAVE_VA_DRM
435 static void
436 drm_native_destroy_cb(VANativeDisplay native)
438 vlc_close((intptr_t) native);
440 #endif
442 static int
443 Open(vlc_object_t *obj)
445 opengl_tex_converter_t *tc = (void *) obj;
447 if (!vlc_vaapi_IsChromaOpaque(tc->fmt.i_chroma)
448 || tc->gl->ext != VLC_GL_EXT_EGL
449 || tc->gl->egl.createImageKHR == NULL
450 || tc->gl->egl.destroyImageKHR == NULL)
451 return VLC_EGENERIC;
453 if (!HasExtension(tc->glexts, "GL_OES_EGL_image"))
454 return VLC_EGENERIC;
456 const char *eglexts = tc->gl->egl.queryString(tc->gl, EGL_EXTENSIONS);
457 if (eglexts == NULL || !HasExtension(eglexts, "EGL_EXT_image_dma_buf_import"))
458 return VLC_EGENERIC;
460 int ret = VLC_EGENERIC;
461 #if defined (HAVE_VA_X11)
462 if (tc->gl->surface->type == VOUT_WINDOW_TYPE_XID)
464 if (!vlc_xlib_init(VLC_OBJECT(tc->gl)))
465 return VLC_EGENERIC;
466 Display *x11dpy = XOpenDisplay(tc->gl->surface->display.x11);
467 if (x11dpy == NULL)
468 return VLC_EGENERIC;
470 ret = tc_vaegl_init(tc, vaGetDisplay(x11dpy), x11dpy,
471 x11_native_destroy_cb);
473 #elif defined(HAVE_VA_WL)
474 if (tc->gl->surface->type == VOUT_WINDOW_TYPE_WAYLAND)
475 ret = tc_vaegl_init(tc, vaGetDisplayWl(tc->gl->surface->display.wl),
476 NULL, NULL);
477 #elif defined (HAVE_VA_DRM)
478 static const char *const drm_device_paths[] = {
479 "/dev/dri/renderD128",
480 "/dev/dri/card0"
483 for (size_t i = 0; i < ARRAY_SIZE(drm_device_paths); i++)
485 int drm_fd = vlc_open(drm_device_paths[i], O_RDWR);
486 if (drm_fd == -1)
487 continue;
489 VADisplay dpy = vaGetDisplayDRM(drm_fd);
490 if (dpy)
492 ret = tc_vaegl_init(tc, dpy, (VANativeDisplay) (intptr_t) drm_fd,
493 drm_native_destroy_cb);
494 if (ret == VLC_SUCCESS)
495 break;
497 else
498 vlc_close(drm_fd);
500 #endif
502 return ret;
505 #if defined (HAVE_VA_X11)
506 # define PRIORITY 2
507 # define SHORTCUT "vaapi_x11"
508 # define DESCRIPTION_SUFFIX "X11"
509 #elif defined(HAVE_VA_WL)
510 # define PRIORITY 2
511 # define SHORTCUT "vaapi_wl"
512 # define DESCRIPTION_SUFFIX "Wayland"
513 #elif defined (HAVE_VA_DRM)
514 # define PRIORITY 1
515 # define SHORTCUT "vaapi_drm"
516 # define DESCRIPTION_SUFFIX "DRM"
517 #endif
519 vlc_module_begin ()
520 set_description("VA-API OpenGL surface converter for " DESCRIPTION_SUFFIX)
521 set_capability("glconv", PRIORITY)
522 set_callbacks(Open, Close)
523 set_category(CAT_VIDEO)
524 set_subcategory(SUBCAT_VIDEO_VOUT)
525 add_shortcut("vaapi", SHORTCUT)
526 vlc_module_end ()