[vlc-devel] [PATCH 03/16] opengl: factorize program creation

Romain Vimont rom1v at videolabs.io
Tue Mar 17 17:26:36 CET 2020


Expose a single function to create an OpenGL program for a vertex shader
and a fragment shader.

This simplify the code, and handles the errors in the same way
everywhere.
---
 modules/video_output/Makefile.am              |   1 +
 .../video_output/opengl/fragment_shaders.c    |  35 ++--
 modules/video_output/opengl/gl_util.c         | 133 ++++++++++++++
 modules/video_output/opengl/gl_util.h         |  23 +++
 modules/video_output/opengl/internal.h        |   2 +-
 modules/video_output/opengl/renderer.c        |  83 ++-------
 modules/video_output/opengl/sub_renderer.c    | 169 ++++--------------
 7 files changed, 221 insertions(+), 225 deletions(-)
 create mode 100644 modules/video_output/opengl/gl_util.c

diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am
index cb53932315..cee3d447a9 100644
--- a/modules/video_output/Makefile.am
+++ b/modules/video_output/Makefile.am
@@ -5,6 +5,7 @@ EXTRA_DIST += video_output/README
 
 OPENGL_COMMONSOURCES = video_output/opengl/vout_helper.c \
 	video_output/opengl/gl_common.h \
+	video_output/opengl/gl_util.c \
 	video_output/opengl/gl_util.h \
 	video_output/opengl/interop.h \
 	video_output/opengl/vout_helper.h \
diff --git a/modules/video_output/opengl/fragment_shaders.c b/modules/video_output/opengl/fragment_shaders.c
index fc3391c32f..dfc4f51d1e 100644
--- a/modules/video_output/opengl/fragment_shaders.c
+++ b/modules/video_output/opengl/fragment_shaders.c
@@ -323,10 +323,9 @@ renderer_xyz12_prepare_shader(const struct vlc_gl_renderer *renderer,
     vt->Uniform1i(renderer->uloc.Texture[0], 0);
 }
 
-static GLuint
+static char *
 xyz12_shader_init(struct vlc_gl_renderer *renderer)
 {
-    const opengl_vtable_t *vt = renderer->vt;
     renderer->pf_fetch_locations = renderer_xyz12_fetch_locations;
     renderer->pf_prepare_shader = renderer_xyz12_prepare_shader;
 
@@ -365,13 +364,9 @@ xyz12_shader_init(struct vlc_gl_renderer *renderer)
     char *code;
     if (asprintf(&code, template, renderer->glsl_version,
                  renderer->glsl_precision_header) < 0)
-        return 0;
+        return NULL;
 
-    GLuint fragment_shader = vt->CreateShader(GL_FRAGMENT_SHADER);
-    vt->ShaderSource(fragment_shader, 1, (const char **) &code, NULL);
-    vt->CompileShader(fragment_shader);
-    free(code);
-    return fragment_shader;
+    return code;
 }
 
 static int
@@ -433,7 +428,7 @@ opengl_init_swizzle(const struct vlc_gl_interop *interop,
     return VLC_SUCCESS;
 }
 
-GLuint
+char *
 opengl_fragment_shader_init(struct vlc_gl_renderer *renderer, GLenum tex_target,
                             vlc_fourcc_t chroma, video_color_space_t yuv_space)
 {
@@ -446,7 +441,7 @@ opengl_fragment_shader_init(struct vlc_gl_renderer *renderer, GLenum tex_target,
 
     const vlc_chroma_description_t *desc = vlc_fourcc_GetChromaDescription(chroma);
     if (desc == NULL)
-        return 0;
+        return NULL;
 
     if (chroma == VLC_CODEC_XYZ12)
         return xyz12_shader_init(renderer);
@@ -455,10 +450,10 @@ opengl_fragment_shader_init(struct vlc_gl_renderer *renderer, GLenum tex_target,
     {
         ret = renderer_yuv_base_init(renderer, chroma, desc, yuv_space);
         if (ret != VLC_SUCCESS)
-            return 0;
+            return NULL;
         ret = opengl_init_swizzle(renderer->interop, swizzle_per_tex, chroma, desc);
         if (ret != VLC_SUCCESS)
-            return 0;
+            return NULL;
     }
 
     const char *sampler, *lookup, *coord_name;
@@ -485,7 +480,7 @@ opengl_fragment_shader_init(struct vlc_gl_renderer *renderer, GLenum tex_target,
 
     struct vlc_memstream ms;
     if (vlc_memstream_open(&ms) != 0)
-        return 0;
+        return NULL;
 
 #define ADD(x) vlc_memstream_puts(&ms, x)
 #define ADDF(x, ...) vlc_memstream_printf(&ms, x, ##__VA_ARGS__)
@@ -644,24 +639,14 @@ opengl_fragment_shader_init(struct vlc_gl_renderer *renderer, GLenum tex_target,
 #undef ADDF
 
     if (vlc_memstream_close(&ms) != 0)
-        return 0;
+        return NULL;
 
-    GLuint fragment_shader = vt->CreateShader(GL_FRAGMENT_SHADER);
-    if (fragment_shader == 0)
-    {
-        free(ms.ptr);
-        return 0;
-    }
-    GLint length = ms.length;
-    vt->ShaderSource(fragment_shader, 1, (const char **)&ms.ptr, &length);
-    vt->CompileShader(fragment_shader);
     if (renderer->b_dump_shaders)
         msg_Dbg(renderer->gl, "\n=== Fragment shader for fourcc: %4.4s, colorspace: %d ===\n%s\n",
                 (const char *)&chroma, yuv_space, ms.ptr);
-    free(ms.ptr);
 
     renderer->pf_fetch_locations = renderer_base_fetch_locations;
     renderer->pf_prepare_shader = renderer_base_prepare_shader;
 
-    return fragment_shader;
+    return ms.ptr;
 }
diff --git a/modules/video_output/opengl/gl_util.c b/modules/video_output/opengl/gl_util.c
new file mode 100644
index 0000000000..c47a272acf
--- /dev/null
+++ b/modules/video_output/opengl/gl_util.c
@@ -0,0 +1,133 @@
+/*****************************************************************************
+ * gl_util.c
+ *****************************************************************************
+ * Copyright (C) 2020 VLC authors and VideoLAN
+ * Copyright (C) 2020 Videolabs
+ *
+ * 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 "gl_util.h"
+
+static void
+LogShaderErrors(vlc_object_t *obj, const opengl_vtable_t *vt, GLuint id)
+{
+    GLint info_len;
+    vt->GetShaderiv(id, GL_INFO_LOG_LENGTH, &info_len);
+    if (info_len > 0)
+    {
+        char *info_log = malloc(info_len);
+        if (info_log)
+        {
+            GLsizei written;
+            vt->GetShaderInfoLog(id, info_len, &written, info_log);
+            msg_Err(obj, "shader: %s", info_log);
+            free(info_log);
+        }
+    }
+}
+
+static void
+LogProgramErrors(vlc_object_t *obj, const opengl_vtable_t *vt, GLuint id)
+{
+    GLint info_len;
+    vt->GetProgramiv(id, GL_INFO_LOG_LENGTH, &info_len);
+    if (info_len > 0)
+    {
+        char *info_log = malloc(info_len);
+        if (info_log)
+        {
+            GLsizei written;
+            vt->GetProgramInfoLog(id, info_len, &written, info_log);
+            msg_Err(obj, "program: %s", info_log);
+            free(info_log);
+        }
+    }
+}
+
+static GLuint
+CreateShader(vlc_object_t *obj, const opengl_vtable_t *vt, GLenum type,
+             GLsizei count, const GLchar **src)
+{
+    GLuint shader = vt->CreateShader(type);
+    if (!shader)
+        return 0;
+
+    vt->ShaderSource(shader, count, src, NULL);
+    vt->CompileShader(shader);
+
+    LogShaderErrors(obj, vt, shader);
+
+    GLint compiled;
+    vt->GetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+    if (!compiled)
+    {
+        msg_Err(obj, "Failed to compile shader");
+        vt->DeleteShader(shader);
+        return 0;
+    }
+
+    return shader;
+}
+
+
+GLuint
+vlc_gl_BuildProgram(vlc_object_t *obj, const opengl_vtable_t *vt,
+                    GLsizei vstring_count, const GLchar **vstrings,
+                    GLsizei fstring_count, const GLchar **fstrings)
+{
+    GLuint program = 0;
+
+    GLuint vertex_shader = CreateShader(obj, vt, GL_VERTEX_SHADER,
+                                        vstring_count, vstrings);
+    if (!vertex_shader)
+        return 0;
+
+    GLuint fragment_shader = CreateShader(obj, vt, GL_FRAGMENT_SHADER,
+                                          fstring_count, fstrings);
+    if (!fragment_shader)
+        goto finally_1;
+
+    program = vt->CreateProgram();
+    if (!program)
+        goto finally_2;
+
+    vt->AttachShader(program, vertex_shader);
+    vt->AttachShader(program, fragment_shader);
+
+    vt->LinkProgram(program);
+
+    LogProgramErrors(obj, vt, program);
+
+    GLint linked;
+    vt->GetProgramiv(program, GL_LINK_STATUS, &linked);
+    if (!linked)
+    {
+        msg_Err(obj, "Failed to link program");
+        vt->DeleteProgram(program);
+        program = 0;
+    }
+
+finally_2:
+    vt->DeleteShader(fragment_shader);
+finally_1:
+    vt->DeleteShader(vertex_shader);
+
+    return program;
+}
diff --git a/modules/video_output/opengl/gl_util.h b/modules/video_output/opengl/gl_util.h
index 21734aae21..051c034d3f 100644
--- a/modules/video_output/opengl/gl_util.h
+++ b/modules/video_output/opengl/gl_util.h
@@ -2,6 +2,7 @@
  * gl_util.h
  *****************************************************************************
  * Copyright (C) 2020 VLC authors and VideoLAN
+ * Copyright (C) 2020 Videolabs
  *
  * 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
@@ -26,6 +27,7 @@
 #endif
 
 #include <vlc_common.h>
+#include "gl_common.h"
 
 /** Return the smallest larger or equal power of 2 */
 static inline unsigned vlc_align_pot(unsigned x)
@@ -34,4 +36,25 @@ static inline unsigned vlc_align_pot(unsigned x)
     return ((align >> 1) == x) ? x : align;
 }
 
+/**
+ * Build an OpenGL program
+ *
+ * Both the fragment shader and fragment shader are passed as a list of
+ * strings, forming the shader source code once concatenated, like
+ * glShaderSource().
+ *
+ * \param obj a VLC object, used to log messages
+ * \param vt the OpenGL virtual table
+ * \param vstring_count the number of strings in vstrings
+ * \param vstrings a list of NUL-terminated strings containing the vertex
+ *                 shader source code
+ * \param fstring_count the number of strings in fstrings
+ * \param fstrings a list of NUL-terminated strings containing the fragment
+ *                 shader source code
+ */
+GLuint
+vlc_gl_BuildProgram(vlc_object_t *obj, const opengl_vtable_t *vt,
+                    GLsizei vstring_count, const GLchar **vstrings,
+                    GLsizei fstring_count, const GLchar **fstrings);
+
 #endif
diff --git a/modules/video_output/opengl/internal.h b/modules/video_output/opengl/internal.h
index e7143e1a05..f4ed495252 100644
--- a/modules/video_output/opengl/internal.h
+++ b/modules/video_output/opengl/internal.h
@@ -28,7 +28,7 @@ int
 opengl_interop_init_impl(struct vlc_gl_interop *interop, GLenum tex_target,
                          vlc_fourcc_t chroma, video_color_space_t yuv_space);
 
-GLuint
+char *
 opengl_fragment_shader_init(struct vlc_gl_renderer *rendeer,
                             GLenum, vlc_fourcc_t, video_color_space_t);
 int
diff --git a/modules/video_output/opengl/renderer.c b/modules/video_output/opengl/renderer.c
index 0969c99dc2..416530ffea 100644
--- a/modules/video_output/opengl/renderer.c
+++ b/modules/video_output/opengl/renderer.c
@@ -169,11 +169,9 @@ static void getOrientationTransformMatrix(video_orientation_t orientation,
     }
 }
 
-static GLuint BuildVertexShader(const struct vlc_gl_renderer *renderer,
-                                unsigned plane_count)
+static char *
+BuildVertexShader(const struct vlc_gl_renderer *renderer, unsigned plane_count)
 {
-    const opengl_vtable_t *vt = renderer->vt;
-
     /* Basic vertex shader */
     static const char *template =
         "#version %u\n"
@@ -205,16 +203,12 @@ static GLuint BuildVertexShader(const struct vlc_gl_renderer *renderer,
     char *code;
     if (asprintf(&code, template, renderer->glsl_version, coord1_header,
                  coord2_header, coord1_code, coord2_code) < 0)
-        return 0;
+        return NULL;
 
-    GLuint shader = vt->CreateShader(GL_VERTEX_SHADER);
-    vt->ShaderSource(shader, 1, (const char **) &code, NULL);
     if (renderer->b_dump_shaders)
         msg_Dbg(renderer->gl, "\n=== Vertex shader for fourcc: %4.4s ===\n%s\n",
                 (const char *) &renderer->interop->fmt.i_chroma, code);
-    vt->CompileShader(shader);
-    free(code);
-    return shader;
+    return code;
 }
 
 static int
@@ -223,16 +217,19 @@ opengl_link_program(struct vlc_gl_renderer *renderer)
     struct vlc_gl_interop *interop = renderer->interop;
     const opengl_vtable_t *vt = renderer->vt;
 
-    GLuint vertex_shader = BuildVertexShader(renderer, interop->tex_count);
+    char *vertex_shader = BuildVertexShader(renderer, interop->tex_count);
     if (!vertex_shader)
         return VLC_EGENERIC;
 
-    GLuint fragment_shader =
+    char *fragment_shader =
         opengl_fragment_shader_init(renderer, interop->tex_target,
                                     interop->sw_fmt.i_chroma,
                                     interop->sw_fmt.space);
     if (!fragment_shader)
+    {
+        free(vertex_shader);
         return VLC_EGENERIC;
+    }
 
     assert(interop->tex_target != 0 &&
            interop->tex_count > 0 &&
@@ -240,58 +237,14 @@ opengl_link_program(struct vlc_gl_renderer *renderer)
            renderer->pf_fetch_locations != NULL &&
            renderer->pf_prepare_shader != NULL);
 
-    GLuint shaders[] = { fragment_shader, vertex_shader };
-
-    /* Check shaders messages */
-    for (unsigned i = 0; i < 2; i++) {
-        int infoLength;
-        vt->GetShaderiv(shaders[i], GL_INFO_LOG_LENGTH, &infoLength);
-        if (infoLength <= 1)
-            continue;
-
-        char *infolog = malloc(infoLength);
-        if (infolog != NULL)
-        {
-            int charsWritten;
-            vt->GetShaderInfoLog(shaders[i], infoLength, &charsWritten,
-                                 infolog);
-            msg_Err(renderer->gl, "shader %u: %s", i, infolog);
-            free(infolog);
-        }
-    }
-
-    GLuint program_id = renderer->program_id = vt->CreateProgram();
-    vt->AttachShader(program_id, fragment_shader);
-    vt->AttachShader(program_id, vertex_shader);
-    vt->LinkProgram(program_id);
-
-    vt->DeleteShader(vertex_shader);
-    vt->DeleteShader(fragment_shader);
-
-    /* Check program messages */
-    int infoLength = 0;
-    vt->GetProgramiv(program_id, GL_INFO_LOG_LENGTH, &infoLength);
-    if (infoLength > 1)
-    {
-        char *infolog = malloc(infoLength);
-        if (infolog != NULL)
-        {
-            int charsWritten;
-            vt->GetProgramInfoLog(program_id, infoLength, &charsWritten,
-                                  infolog);
-            msg_Err(renderer->gl, "shader program: %s", infolog);
-            free(infolog);
-        }
-
-        /* If there is some message, better to check linking is ok */
-        GLint link_status = GL_TRUE;
-        vt->GetProgramiv(program_id, GL_LINK_STATUS, &link_status);
-        if (link_status == GL_FALSE)
-        {
-            msg_Err(renderer->gl, "Unable to use program");
-            goto error;
-        }
-    }
+    GLuint program_id =
+        vlc_gl_BuildProgram(VLC_OBJECT(renderer->gl), vt,
+                            1, (const char **) &vertex_shader,
+                            1, (const char **) &fragment_shader);
+    free(vertex_shader);
+    free(fragment_shader);
+    if (!program_id)
+        return VLC_EGENERIC;
 
     /* Fetch UniformLocations and AttribLocations */
 #define GET_LOC(type, x, str) do { \
@@ -332,6 +285,8 @@ opengl_link_program(struct vlc_gl_renderer *renderer)
         goto error;
     }
 
+    renderer->program_id = program_id;
+
     return VLC_SUCCESS;
 
 error:
diff --git a/modules/video_output/opengl/sub_renderer.c b/modules/video_output/opengl/sub_renderer.c
index a900f90473..e434f2ee30 100644
--- a/modules/video_output/opengl/sub_renderer.c
+++ b/modules/video_output/opengl/sub_renderer.c
@@ -82,140 +82,6 @@ struct vlc_gl_sub_renderer
     unsigned buffer_object_count;
 };
 
-static void
-LogShaderErrors(vlc_object_t *obj, const opengl_vtable_t *vt, GLuint id)
-{
-    GLint info_len;
-    vt->GetShaderiv(id, GL_INFO_LOG_LENGTH, &info_len);
-    if (info_len > 0)
-    {
-        char *info_log = malloc(info_len);
-        if (info_log)
-        {
-            GLsizei written;
-            vt->GetShaderInfoLog(id, info_len, &written, info_log);
-            msg_Err(obj, "shader: %s", info_log);
-            free(info_log);
-        }
-    }
-}
-
-static void
-LogProgramErrors(vlc_object_t *obj, const opengl_vtable_t *vt, GLuint id)
-{
-    GLint info_len;
-    vt->GetProgramiv(id, GL_INFO_LOG_LENGTH, &info_len);
-    if (info_len > 0)
-    {
-        char *info_log = malloc(info_len);
-        if (info_log)
-        {
-            GLsizei written;
-            vt->GetProgramInfoLog(id, info_len, &written, info_log);
-            msg_Err(obj, "program: %s", info_log);
-            free(info_log);
-        }
-    }
-}
-
-static GLuint
-CreateShader(vlc_object_t *obj, const opengl_vtable_t *vt, GLenum type,
-             const char *src)
-{
-    GLuint shader = vt->CreateShader(type);
-    if (!shader)
-        return 0;
-
-    vt->ShaderSource(shader, 1, &src, NULL);
-    vt->CompileShader(shader);
-
-    LogShaderErrors(obj, vt, shader);
-
-    GLint compiled;
-    vt->GetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
-    if (!compiled)
-    {
-        msg_Err(obj, "Failed to compile shader");
-        vt->DeleteShader(shader);
-        return 0;
-    }
-
-    return shader;
-}
-
-static GLuint
-CreateProgram(vlc_object_t *obj, const opengl_vtable_t *vt)
-{
-    static const char *const VERTEX_SHADER_SRC =
-#if defined(USE_OPENGL_ES2)
-        "#version 100\n"
-#else
-        "#version 120\n"
-#endif
-        "attribute vec2 vertex_pos;\n"
-        "attribute vec2 tex_coords_in;\n"
-        "varying vec2 tex_coords;\n"
-        "void main() {\n"
-        "  tex_coords = tex_coords_in;\n"
-        "  gl_Position = vec4(vertex_pos, 0.0, 1.0);\n"
-        "}\n";
-
-    static const char *const FRAGMENT_SHADER_SRC =
-#if defined(USE_OPENGL_ES2)
-        "#version 100\n"
-        "precision mediump float;\n"
-#else
-        "#version 120\n"
-#endif
-        "uniform sampler2D sampler;\n"
-        "uniform float alpha;\n"
-        "varying vec2 tex_coords;\n"
-        "void main() {\n"
-        "  vec4 color = texture2D(sampler, tex_coords);\n"
-        "  color.a *= alpha;\n"
-        "  gl_FragColor = color;\n"
-        "}\n";
-
-    GLuint program = 0;
-
-    GLuint vertex_shader = CreateShader(obj, vt, GL_VERTEX_SHADER,
-                                        VERTEX_SHADER_SRC);
-    if (!vertex_shader)
-        return 0;
-
-    GLuint fragment_shader = CreateShader(obj, vt, GL_FRAGMENT_SHADER,
-                                          FRAGMENT_SHADER_SRC);
-    if (!fragment_shader)
-        goto finally_1;
-
-    program = vt->CreateProgram();
-    if (!program)
-        goto finally_2;
-
-    vt->AttachShader(program, vertex_shader);
-    vt->AttachShader(program, fragment_shader);
-
-    vt->LinkProgram(program);
-
-    LogProgramErrors(obj, vt, program);
-
-    GLint linked;
-    vt->GetProgramiv(program, GL_LINK_STATUS, &linked);
-    if (!linked)
-    {
-        msg_Err(obj, "Failed to link program");
-        vt->DeleteProgram(program);
-        program = 0;
-    }
-
-finally_2:
-    vt->DeleteShader(fragment_shader);
-finally_1:
-    vt->DeleteShader(vertex_shader);
-
-    return program;
-}
-
 static int
 FetchLocations(struct vlc_gl_sub_renderer *sr)
 {
@@ -268,7 +134,40 @@ vlc_gl_sub_renderer_New(vlc_gl_t *gl, const opengl_vtable_t *vt,
     sr->region_count = 0;
     sr->regions = NULL;
 
-    sr->program_id = CreateProgram(VLC_OBJECT(sr->gl), vt);
+    static const char *const VERTEX_SHADER_SRC =
+#if defined(USE_OPENGL_ES2)
+        "#version 100\n"
+#else
+        "#version 120\n"
+#endif
+        "attribute vec2 vertex_pos;\n"
+        "attribute vec2 tex_coords_in;\n"
+        "varying vec2 tex_coords;\n"
+        "void main() {\n"
+        "  tex_coords = tex_coords_in;\n"
+        "  gl_Position = vec4(vertex_pos, 0.0, 1.0);\n"
+        "}\n";
+
+    static const char *const FRAGMENT_SHADER_SRC =
+#if defined(USE_OPENGL_ES2)
+        "#version 100\n"
+        "precision mediump float;\n"
+#else
+        "#version 120\n"
+#endif
+        "uniform sampler2D sampler;\n"
+        "uniform float alpha;\n"
+        "varying vec2 tex_coords;\n"
+        "void main() {\n"
+        "  vec4 color = texture2D(sampler, tex_coords);\n"
+        "  color.a *= alpha;\n"
+        "  gl_FragColor = color;\n"
+        "}\n";
+
+    sr->program_id =
+        vlc_gl_BuildProgram(VLC_OBJECT(sr->gl), vt,
+                            1, (const char **) &VERTEX_SHADER_SRC,
+                            1, (const char **) &FRAGMENT_SHADER_SRC);
     if (!sr->program_id)
         goto error_2;
 
-- 
2.25.1



More information about the vlc-devel mailing list