[vlc-devel] [RFC PATCH 8/8] WIP: opengl: add converter_va

Thomas Guillem thomas at gllm.fr
Fri Feb 10 11:36:02 CET 2017


This converter will allow to render a va_surface directly to a GL surface.
It'll work with GLX and EGL (X11 or WAYLAND).

For now, it's working only with GLX, via va_glx.h API: performances are really
good (3% of CPU for 4K30fps).

I'm still working on the EGL part. For now vaGetEGLClientBufferFromSurface() is
returning the VA_STATUS_ERROR_UNIMPLEMENTED error, and I still don't know if
it's really not implemented or if I did something wrong.

I think there is an other way to render a va_surface via EGL without using
va_egl.h (via vaDeriveImage(), vaAcquireBufferHandle() and the
EXT_image_dma_buf_import extension).
---
 modules/video_output/Makefile.am           |  20 ++
 modules/video_output/opengl/converter_va.c | 291 +++++++++++++++++++++++++++++
 modules/video_output/opengl/internal.h     |   6 +
 modules/video_output/opengl/vout_helper.c  |   3 +
 4 files changed, 320 insertions(+)
 create mode 100644 modules/video_output/opengl/converter_va.c

diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am
index eb9dedf0af..0a4602f70d 100644
--- a/modules/video_output/Makefile.am
+++ b/modules/video_output/Makefile.am
@@ -10,6 +10,26 @@ OPENGL_DISPLAY_SRCS = video_output/opengl/vout_helper.c \
 if HAVE_ANDROID
 OPENGL_DISPLAY_SRCS += video_output/opengl/converter_android.c
 endif
+if HAVE_VAAPI
+OPENGL_DISPLAY_LIBS += $(LIBVA_LIBS)
+OPENGL_DISPLAY_SRCS += video_output/opengl/converter_va.c hw/va/vlc_va.c
+		       hw/va/vlc_va.h
+OPENGL_DISPLAY_CFLGS += -DVLCGL_CONV_VA
+if HAVE_VAAPI_EGL
+if HAVE_VAAPI_X11
+OPENGL_DISPLAY_LIBS += $(LIBVA_X11_LIBS) $(LIBVA_EGL_LIBS)
+OPENGL_DISPLAY_CFLGS += -DHAVE_VA_X11 -DHAVE_VA_EGL
+endif
+if HAVE_VAAPI_WL
+OPENGL_DISPLAY_LIBS += $(LIBVA_WL_LIBS) $(LIBVA_EGL_LIBS)
+OPENGL_DISPLAY_CFLGS += -DHAVE_VA_WL -DHAVE_VA_EGL
+endif
+endif
+if HAVE_VAAPI_GLX
+OPENGL_DISPLAY_LIBS += $(LIBVA_GLX_LIBS)
+OPENGL_DISPLAY_CFLGS += -DHAVE_VA_GLX
+endif
+endif
 
 if HAVE_DECKLINK
 libdecklinkoutput_plugin_la_SOURCES = video_output/decklink.cpp
diff --git a/modules/video_output/opengl/converter_va.c b/modules/video_output/opengl/converter_va.c
new file mode 100644
index 0000000000..e33b333543
--- /dev/null
+++ b/modules/video_output/opengl/converter_va.c
@@ -0,0 +1,291 @@
+/*****************************************************************************
+ * converter_va.c: OpenGL VA opaque converter
+ *****************************************************************************
+ * Copyright (C) 2017 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "internal.h"
+#include "../../hw/va/vlc_va.h"
+#include <vlc_vout_window.h>
+
+#include <assert.h>
+
+#ifdef HAVE_VA_EGL
+# include <va/va_egl.h>
+# include <EGL/egl.h>
+# include <EGL/eglext.h>
+#endif
+
+#ifdef HAVE_VA_X11
+# ifndef HAVE_VA_EGL
+# error X11 without EGL, check your Makefile
+# endif
+# include <va/va_x11.h>
+#endif
+
+#ifdef HAVE_VA_WL
+# ifndef HAVE_VA_EGL
+# error WL without EGL, check your Makefile
+# endif
+# include <va/va_wayland.h>
+#endif
+
+#ifdef HAVE_VA_GLX
+# include <va/va_glx.h>
+# include <GL/glxext.h>
+#endif
+
+struct priv
+{
+    VADisplay vadpy;
+    bool b_glx;
+    unsigned va_rt_format;
+#ifdef HAVE_VA_EGL
+    PFNEGLCREATEIMAGEKHRPROC    egl_create_image;
+    PFNEGLDESTROYIMAGEKHRPROC   egl_destroy_image;
+    PFNGLEGLIMAGETARGETTEXTURE2DOESPROC gl_image_target_texture_2DOES;
+    EGLDisplay egldpy;
+    EGLContext eglctx;
+#endif
+#ifdef HAVE_VA_GLX
+    void *gl_surface;
+#endif
+};
+
+#ifdef HAVE_VA_GLX
+static int
+tc_vaglx_allocate_textures(const opengl_tex_converter_t *tc, GLuint *textures,
+                           const GLsizei *tex_width, const GLsizei *tex_height)
+{
+    struct priv *priv = tc->priv;
+
+    glBindTexture(tc->tex_target, textures[0]);
+    glTexImage2D(tc->tex_target, 0, tc->texs[0].internal,
+                 tex_width[0], tex_height[0], 0, tc->texs[0].format,
+                 tc->texs[0].type, NULL);
+    VAStatus status = vaCreateSurfaceGLX(priv->vadpy, tc->tex_target,
+                                         textures[0], &priv->gl_surface);
+    fprintf(stderr, "status: %X\n", status);
+    assert(status == VA_STATUS_SUCCESS); /* TODO */
+    return status == VA_STATUS_SUCCESS ? VLC_SUCCESS : VLC_EGENERIC;
+}
+
+static int
+tc_vaglx_update(const opengl_tex_converter_t *tc, GLuint *textures,
+                const GLsizei *tex_width, const GLsizei *tex_height,
+                picture_t *pic, const size_t *plane_offset)
+{
+    (void) textures; (void) tex_width; (void) tex_height; (void) plane_offset;
+    struct priv *priv = tc->priv;
+
+    VAStatus status = vaCopySurfaceGLX(priv->vadpy, priv->gl_surface,
+                                       pic->p_sys->va_surface_id, 0);
+    return status == VA_STATUS_SUCCESS ? VLC_SUCCESS : VLC_EGENERIC;
+}
+#endif
+
+#ifdef HAVE_VA_EGL
+static int
+tc_vaegl_allocate_textures(const opengl_tex_converter_t *tc, GLuint *textures,
+                           const GLsizei *tex_width, const GLsizei *tex_height)
+{
+    glBindTexture(tc->tex_target, textures[0]);
+    glTexImage2D(tc->tex_target, 0, tc->texs[0].internal,
+                 tex_width[0], tex_height[0], 0, tc->texs[0].format,
+                 tc->texs[0].type, NULL);
+    return VLC_SUCCESS;
+}
+
+static int
+tc_vaegl_update(const opengl_tex_converter_t *tc, GLuint *textures,
+                const GLsizei *tex_width, const GLsizei *tex_height,
+                picture_t *pic, const size_t *plane_offset)
+{
+    (void) textures; (void) tex_width; (void) tex_height; (void) plane_offset;
+    struct priv *priv = tc->priv;
+
+    glBindTexture(tc->tex_target, textures[0]);
+
+    EGLClientBuffer eglbuffer;
+    VAStatus status =
+        vaGetEGLClientBufferFromSurface(priv->vadpy, pic->p_sys->va_surface_id,
+                                        &eglbuffer);
+    fprintf(stderr, "status: %X\n", status);
+    assert(status == VA_STATUS_SUCCESS); /* TODO */
+
+    EGLImageKHR egl_image =
+        priv->egl_create_image(priv->egldpy, priv->eglctx, EGL_GL_TEXTURE_2D_KHR,
+                               eglbuffer, NULL);
+    if (egl_image == NULL)
+        return VLC_EGENERIC;
+    priv->gl_image_target_texture_2DOES(GL_TEXTURE_2D, egl_image);
+    priv->egl_destroy_image(priv->egldpy, egl_image);
+#if 0
+    EGLImageKHR eglCreateImageKHR(
+                            EGLDisplay dpy,
+                            EGLContext ctx,
+                            EGLenum target,
+                            EGLClientBuffer buffer,
+                            const EGLint *attrib_list)
+
+    glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglImage); 
+    EGLBoolean eglDestroyImageKHR(
+                            EGLDisplay dpy,
+                            EGLImageKHR image)
+#endif
+    return VLC_SUCCESS;
+}
+#endif
+
+static picture_pool_t *
+tc_va_get_pool(const opengl_tex_converter_t *tc, const video_format_t *fmt,
+               unsigned requested_count, GLuint *textures)
+{
+    (void) textures;
+    struct priv *priv = tc->priv;
+
+    return vlc_va_PoolAlloc(VLC_OBJECT(tc->gl), priv->vadpy, requested_count,
+                            fmt, priv->va_rt_format);
+}
+
+static void
+tc_va_release(const opengl_tex_converter_t *tc)
+{
+    struct priv *priv = tc->priv;
+
+    vlc_va_Terminate(priv->vadpy);
+    free(tc->priv);
+}
+
+#ifdef HAVE_VA_EGL
+static int egl_init(opengl_tex_converter_t *tc, struct priv *priv,
+                    VADisplay vadpy)
+{
+    if (vadpy == NULL)
+        return -1;
+
+    if (!HasExtension(tc->glexts, "GL_OES_EGL_image"))
+        return -1;
+
+    if (vlc_gl_GetEglContexts(tc->gl, &priv->egldpy, &priv->eglctx) != 0)
+        return -1;
+
+    priv->vadpy = vadpy;
+    priv->egl_create_image = vlc_gl_GetProcAddress(tc->gl, "eglCreateImageKHR");
+    priv->egl_destroy_image = vlc_gl_GetProcAddress(tc->gl, "eglDestroyImageKHR");
+    priv->gl_image_target_texture_2DOES =
+        vlc_gl_GetProcAddress(tc->gl, "glEGLImageTargetTexture2DOES");
+    if (!priv->egl_create_image || !priv->egl_destroy_image
+     || !priv->gl_image_target_texture_2DOES)
+        return -1;
+
+    tc->pf_allocate_textures = tc_vaegl_allocate_textures;
+    tc->pf_update            = tc_vaegl_update;
+    return 0;
+}
+#endif
+
+GLuint
+opengl_tex_converter_va_init(const video_format_t *fmt,
+                             opengl_tex_converter_t *tc)
+{
+    fprintf(stderr, "opengl_tex_converter_va_init ? %4.4s\n", (const char *)&fmt->i_chroma);
+
+    unsigned va_fourcc, va_rt_format;
+    int status = vlc_va_VaFourcc(fmt->i_chroma, &va_fourcc, &va_rt_format);
+    if (status != VA_STATUS_SUCCESS)
+    {
+        fprintf(stderr, "NOC CHROMA FOR YOU\n");
+        return 0;
+    }
+
+    void *nativedpy = NULL;
+    if (vlc_gl_GetNativeDpy(tc->gl, &nativedpy) != 0)
+        return 0;
+
+    struct priv *priv = tc->priv = calloc(1, sizeof(struct priv));
+    if (unlikely(priv == NULL))
+        return 0;
+
+    switch (tc->gl->surface->type)
+    {
+#if defined(HAVE_VA_X11) || defined(HAVE_VA_GLX)
+        case VOUT_WINDOW_TYPE_XID:
+#ifdef HAVE_VA_GLX
+            if (glXGetCurrentDisplay() == nativedpy)
+            {
+                priv->vadpy = vaGetDisplayGLX(nativedpy);
+                if (priv->vadpy == NULL)
+                    goto error;
+                tc->pf_allocate_textures = tc_vaglx_allocate_textures;
+                tc->pf_update            = tc_vaglx_update;
+            }
+            else
+#endif
+            if (egl_init(tc, priv, vaGetDisplay(nativedpy)) != 0)
+                goto error;
+            break;
+#endif
+#ifdef HAVE_VA_WL
+        case VOUT_WINDOW_TYPE_WAYLAND:
+            if (egl_init(tc, priv, vaGetDisplayWl(nativedpy)) != 0)
+                goto error;
+            break;
+#endif
+        default:
+            goto error;
+    }
+    fprintf(stderr, "vadpy: %p\n", priv->vadpy);
+
+    status = vlc_va_Initialize(VLC_OBJECT(tc->gl), priv->vadpy);
+    assert(status == VA_STATUS_SUCCESS);
+    if (status != VA_STATUS_SUCCESS)
+        goto error;
+
+    priv->va_rt_format = va_rt_format;
+
+    tc->tex_target = GL_TEXTURE_2D;
+    GLuint fragment_shader =
+        opengl_fragment_shader_init(tc, tc->tex_target, VLC_CODEC_RGBA,
+                                    COLOR_SPACE_UNDEF);
+    if (fragment_shader == 0)
+    {
+        vlc_va_Terminate(priv->vadpy);
+        goto error;
+    }
+
+    tc->tex_count = 1;
+    tc->texs[0] = (struct opengl_tex_cfg) {
+        { 1, 1 }, { 1, 1 }, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE
+    };
+
+    tc->priv              = priv;
+    tc->chroma            = VLC_CODEC_VAAPI_OPAQUE;
+    tc->pf_get_pool       = tc_va_get_pool;
+    tc->pf_release        = tc_va_release;
+
+    fprintf(stderr, "opengl_tex_converter_va_init !\n");
+    return fragment_shader;
+
+error:
+    free(priv);
+    return 0;
+}
diff --git a/modules/video_output/opengl/internal.h b/modules/video_output/opengl/internal.h
index f2d5d04445..8d8060c5c9 100644
--- a/modules/video_output/opengl/internal.h
+++ b/modules/video_output/opengl/internal.h
@@ -335,4 +335,10 @@ opengl_tex_converter_cvpx_init(const video_format_t *fmt,
                                opengl_tex_converter_t *tc);
 #endif
 
+#ifdef VLCGL_CONV_VA
+GLuint
+opengl_tex_converter_va_init(const video_format_t *fmt,
+                             opengl_tex_converter_t *tc);
+#endif
+
 #endif /* include-guard */
diff --git a/modules/video_output/opengl/vout_helper.c b/modules/video_output/opengl/vout_helper.c
index a5c091cd94..384dad9620 100644
--- a/modules/video_output/opengl/vout_helper.c
+++ b/modules/video_output/opengl/vout_helper.c
@@ -50,6 +50,9 @@
 
 static opengl_tex_converter_init_cb opengl_tex_converter_init_cbs[] =
 {
+#ifdef VLCGL_CONV_VA
+    opengl_tex_converter_va_init,
+#endif
     opengl_tex_converter_generic_init,
 #ifdef __ANDROID__
     opengl_tex_converter_anop_init,
-- 
2.11.0



More information about the vlc-devel mailing list