[vlc-devel] [PATCH 1/3] opengl: determine shader version dynamically

Romain Vimont rom1v at videolabs.io
Mon Apr 12 15:58:55 UTC 2021


In the future, we would like OpenGL and OpenGL ES2 versions of a filter
to be available without compiling it twice.

Co-authored-by: Alexandre Janniaux <ajanni at videolabs.io>
---
 modules/video_output/opengl/filter_draw.c |  58 +++++-----
 modules/video_output/opengl/filter_mock.c | 128 ++++++++++++++--------
 modules/video_output/opengl/renderer.c    | 112 ++++++++-----------
 3 files changed, 157 insertions(+), 141 deletions(-)

diff --git a/modules/video_output/opengl/filter_draw.c b/modules/video_output/opengl/filter_draw.c
index 60aeddc539..b921b3f0ee 100644
--- a/modules/video_output/opengl/filter_draw.c
+++ b/modules/video_output/opengl/filter_draw.c
@@ -97,16 +97,7 @@ vlc_gl_filter_draw_Open(struct vlc_gl_filter *filter,
 
     struct vlc_gl_sampler *sampler = vlc_gl_filter_GetSampler(filter);
 
-#ifdef USE_OPENGL_ES2
-# define SHADER_VERSION "#version 100\n"
-# define FRAGMENT_SHADER_PRECISION "precision highp float;\n"
-#else
-# define SHADER_VERSION "#version 120\n"
-# define FRAGMENT_SHADER_PRECISION
-#endif
-
-    static const char *const VERTEX_SHADER =
-        SHADER_VERSION
+    static const char *const VERTEX_SHADER_BODY =
         "attribute vec2 vertex_pos;\n"
         "varying vec2 tex_coords;\n"
         "void main() {\n"
@@ -115,8 +106,7 @@ vlc_gl_filter_draw_Open(struct vlc_gl_filter *filter,
         "                    (vertex_pos.y + 1.0) / 2.0);\n"
         "}\n";
 
-    static const char *const VERTEX_SHADER_VFLIP =
-        SHADER_VERSION
+    static const char *const VERTEX_SHADER_BODY_VFLIP =
         "attribute vec2 vertex_pos;\n"
         "varying vec2 tex_coords;\n"
         "void main() {\n"
@@ -125,11 +115,7 @@ vlc_gl_filter_draw_Open(struct vlc_gl_filter *filter,
         "                    (-vertex_pos.y + 1.0) / 2.0);\n"
         "}\n";
 
-    static const char *const FRAGMENT_SHADER_TEMPLATE =
-        SHADER_VERSION
-        "%s\n" /* extensions */
-        FRAGMENT_SHADER_PRECISION
-        "%s\n" /* vlc_texture definition */
+    static const char *const FRAGMENT_SHADER_BODY =
         "varying vec2 tex_coords;\n"
         "void main() {\n"
         "  gl_FragColor = vlc_texture(tex_coords);\n"
@@ -138,24 +124,40 @@ vlc_gl_filter_draw_Open(struct vlc_gl_filter *filter,
     const char *extensions = sampler->shader.extensions
                            ? sampler->shader.extensions : "";
 
-    char *fragment_shader;
-    int ret = asprintf(&fragment_shader, FRAGMENT_SHADER_TEMPLATE, extensions,
-                       sampler->shader.body);
-    if (ret < 0)
-        goto error;
-
     const opengl_vtable_t *vt = &filter->api->vt;
 
+    const char *shader_version;
+    const char *shader_precision;
+    if (filter->api->is_gles)
+    {
+        shader_version = "#version 100\n";
+        shader_precision = "precision highp float;\n";
+    }
+    else
+    {
+        shader_version = "#version 120\n";
+        shader_precision = "";
+    }
+
     config_ChainParse(filter, DRAW_CFG_PREFIX, filter_options, config);
     bool vflip = var_InheritBool(filter, DRAW_CFG_PREFIX "vflip");
 
-    const char *vertex_shader = vflip ? VERTEX_SHADER_VFLIP : VERTEX_SHADER;
+    const char *vertex_shader[] = {
+        shader_version,
+        vflip ? VERTEX_SHADER_BODY_VFLIP : VERTEX_SHADER_BODY,
+    };
+    const char *fragment_shader[] = {
+        shader_version,
+        extensions,
+        shader_precision,
+        sampler->shader.body,
+        FRAGMENT_SHADER_BODY,
+    };
+
     GLuint program_id =
         vlc_gl_BuildProgram(VLC_OBJECT(filter), vt,
-                            1, &vertex_shader,
-                            1, (const char **) &fragment_shader);
-
-    free(fragment_shader);
+                            ARRAY_SIZE(vertex_shader), vertex_shader,
+                            ARRAY_SIZE(fragment_shader), fragment_shader);
     if (!program_id)
         goto error;
 
diff --git a/modules/video_output/opengl/filter_mock.c b/modules/video_output/opengl/filter_mock.c
index 41851fe35d..e6252f36b6 100644
--- a/modules/video_output/opengl/filter_mock.c
+++ b/modules/video_output/opengl/filter_mock.c
@@ -73,14 +73,6 @@
 #include "gl_common.h"
 #include "gl_util.h"
 
-#ifdef USE_OPENGL_ES2
-# define SHADER_VERSION "#version 100\n"
-# define FRAGMENT_SHADER_PRECISION "precision mediump float;\n"
-#else
-# define SHADER_VERSION "#version 120\n"
-# define FRAGMENT_SHADER_PRECISION
-#endif
-
 #define MOCK_CFG_PREFIX "mock-"
 
 static const char *const filter_options[] = {
@@ -241,8 +233,7 @@ InitBlend(struct vlc_gl_filter *filter)
     struct sys *sys = filter->sys;
     const opengl_vtable_t *vt = &filter->api->vt;
 
-    static const char *const VERTEX_SHADER =
-        SHADER_VERSION
+    static const char *const VERTEX_SHADER_BODY =
         "attribute vec2 vertex_pos;\n"
         "attribute vec3 vertex_color;\n"
         "uniform mat4 rotation_matrix;\n"
@@ -252,18 +243,39 @@ InitBlend(struct vlc_gl_filter *filter)
         "  color = vertex_color;\n"
         "}\n";
 
-    static const char *const FRAGMENT_SHADER =
-        SHADER_VERSION
-        FRAGMENT_SHADER_PRECISION
+    static const char *const FRAGMENT_SHADER_BODY =
         "varying vec3 color;\n"
         "void main() {\n"
         "  gl_FragColor = vec4(color, 0.5);\n"
         "}\n";
 
+    const char *shader_version;
+    const char *shader_precision;
+    if (filter->api->is_gles)
+    {
+        shader_version = "#version 100\n";
+        shader_precision = "precision highp float;\n";
+    }
+    else
+    {
+        shader_version = "#version 120\n";
+        shader_precision = "";
+    }
+
+    const char *vertex_shader[] = {
+        shader_version,
+        VERTEX_SHADER_BODY,
+    };
+    const char *fragment_shader[] = {
+        shader_version,
+        shader_precision,
+        FRAGMENT_SHADER_BODY,
+    };
+
     GLuint program_id =
         vlc_gl_BuildProgram(VLC_OBJECT(filter), vt,
-                            1, (const char **) &VERTEX_SHADER,
-                            1, (const char **) &FRAGMENT_SHADER);
+                            ARRAY_SIZE(vertex_shader), vertex_shader,
+                            ARRAY_SIZE(fragment_shader), fragment_shader);
 
     if (!program_id)
         return VLC_EGENERIC;
@@ -316,8 +328,7 @@ InitMask(struct vlc_gl_filter *filter)
 
     struct vlc_gl_sampler *sampler = vlc_gl_filter_GetSampler(filter);
 
-    static const char *const VERTEX_SHADER =
-        SHADER_VERSION
+    static const char *const VERTEX_SHADER_BODY =
         "attribute vec2 vertex_pos;\n"
         "uniform mat4 rotation_matrix;\n"
         "varying vec2 tex_coords;\n"
@@ -328,11 +339,7 @@ InitMask(struct vlc_gl_filter *filter)
         "  gl_Position = pos\n;"
         "}\n";
 
-    static const char *const FRAGMENT_SHADER_TEMPLATE =
-        SHADER_VERSION
-        "%s\n" /* extensions */
-        FRAGMENT_SHADER_PRECISION
-        "%s\n" /* vlc_texture definition */
+    static const char *const FRAGMENT_SHADER_BODY =
         "varying vec2 tex_coords;\n"
         "void main() {\n"
         "  gl_FragColor = vlc_texture(tex_coords);\n"
@@ -341,17 +348,35 @@ InitMask(struct vlc_gl_filter *filter)
     const char *extensions = sampler->shader.extensions
                            ? sampler->shader.extensions : "";
 
-    char *fragment_shader;
-    int ret = asprintf(&fragment_shader, FRAGMENT_SHADER_TEMPLATE, extensions,
-                       sampler->shader.body);
-    if (ret < 0)
-        return VLC_EGENERIC;
+    const char *shader_version;
+    const char *shader_precision;
+    if (filter->api->is_gles)
+    {
+        shader_version = "#version 100\n";
+        shader_precision = "precision highp float;\n";
+    }
+    else
+    {
+        shader_version = "#version 120\n";
+        shader_precision = "";
+    }
+
+    const char *vertex_shader[] = {
+        shader_version,
+        VERTEX_SHADER_BODY,
+    };
+    const char *fragment_shader[] = {
+        shader_version,
+        extensions,
+        shader_precision,
+        sampler->shader.body,
+        FRAGMENT_SHADER_BODY,
+    };
 
     GLuint program_id =
         vlc_gl_BuildProgram(VLC_OBJECT(filter), vt,
-                            1, (const char **) &VERTEX_SHADER,
-                            1, (const char **) &fragment_shader);
-    free(fragment_shader);
+                            ARRAY_SIZE(vertex_shader), vertex_shader,
+                            ARRAY_SIZE(fragment_shader), fragment_shader);
     if (!program_id)
         return VLC_EGENERIC;
 
@@ -401,8 +426,7 @@ InitPlane(struct vlc_gl_filter *filter)
 
     struct vlc_gl_sampler *sampler = vlc_gl_filter_GetSampler(filter);
 
-    static const char *const VERTEX_SHADER =
-        SHADER_VERSION
+    static const char *const VERTEX_SHADER_BODY =
         "attribute vec2 vertex_pos;\n"
         "varying vec2 tex_coords;\n"
         "void main() {\n"
@@ -411,11 +435,7 @@ InitPlane(struct vlc_gl_filter *filter)
         "                    (vertex_pos.y + 1.0) / 2.0);\n"
         "}\n";
 
-    static const char *const FRAGMENT_SHADER_TEMPLATE =
-        SHADER_VERSION
-        "%s\n" /* extensions */
-        FRAGMENT_SHADER_PRECISION
-        "%s\n" /* vlc_texture definition */
+    static const char *const FRAGMENT_SHADER_BODY =
         "varying vec2 tex_coords;\n"
         "uniform int plane;\n"
         "void main() {\n"
@@ -426,17 +446,35 @@ InitPlane(struct vlc_gl_filter *filter)
     const char *extensions = sampler->shader.extensions
                            ? sampler->shader.extensions : "";
 
-    char *fragment_shader;
-    int ret = asprintf(&fragment_shader, FRAGMENT_SHADER_TEMPLATE, extensions,
-                       sampler->shader.body);
-    if (ret < 0)
-        return VLC_EGENERIC;
+    const char *shader_version;
+    const char *shader_precision;
+    if (filter->api->is_gles)
+    {
+        shader_version = "#version 100\n";
+        shader_precision = "precision highp float;\n";
+    }
+    else
+    {
+        shader_version = "#version 120\n";
+        shader_precision = "";
+    }
+
+    const char *vertex_shader[] = {
+        shader_version,
+        VERTEX_SHADER_BODY,
+    };
+    const char *fragment_shader[] = {
+        shader_version,
+        extensions,
+        shader_precision,
+        sampler->shader.body,
+        FRAGMENT_SHADER_BODY,
+    };
 
     GLuint program_id =
         vlc_gl_BuildProgram(VLC_OBJECT(filter), vt,
-                            1, (const char **) &VERTEX_SHADER,
-                            1, (const char **) &fragment_shader);
-    free(fragment_shader);
+                            ARRAY_SIZE(vertex_shader), vertex_shader,
+                            ARRAY_SIZE(fragment_shader), fragment_shader);
     if (!program_id)
         return VLC_EGENERIC;
 
diff --git a/modules/video_output/opengl/renderer.c b/modules/video_output/opengl/renderer.c
index a6d1b65508..773571e2fe 100644
--- a/modules/video_output/opengl/renderer.c
+++ b/modules/video_output/opengl/renderer.c
@@ -170,25 +170,14 @@ InitStereoMatrix(GLfloat matrix_out[static 3*3],
 #undef ROW
 }
 
-/* https://en.wikipedia.org/wiki/OpenGL_Shading_Language#Versions */
-#ifdef USE_OPENGL_ES2
-# define SHADER_VERSION "#version 100\n"
-  /* In OpenGL ES, the fragment language has no default precision qualifier for
-   * floating point types. */
-# define FRAGMENT_SHADER_PRECISION "precision highp float;\n"
-#else
-# define SHADER_VERSION "#version 120\n"
-# define FRAGMENT_SHADER_PRECISION
-#endif
-
-static char *
-BuildVertexShader(struct vlc_gl_filter *filter)
+static int
+opengl_link_program(struct vlc_gl_filter *filter)
 {
     struct vlc_gl_renderer *renderer = filter->sys;
+    struct vlc_gl_sampler *sampler = renderer->sampler;
+    const opengl_vtable_t *vt = renderer->vt;
 
-    /* Basic vertex shader */
-    static const char *template =
-        SHADER_VERSION
+    static const char *const VERTEX_SHADER_BODY =
         "attribute vec2 PicCoordsIn;\n"
         "varying vec2 PicCoords;\n"
         "attribute vec3 VertexPosition;\n"
@@ -200,29 +189,9 @@ BuildVertexShader(struct vlc_gl_filter *filter)
         " PicCoords = (StereoMatrix * vec3(PicCoordsIn, 1.0)).st;\n"
         " gl_Position = ProjectionMatrix * ZoomMatrix * ViewMatrix\n"
         "               * vec4(VertexPosition, 1.0);\n"
-        "}";
+        "}\n";
 
-    char *code = strdup(template);
-    if (!code)
-        return NULL;
-
-    if (renderer->dump_shaders)
-        msg_Dbg(filter, "\n=== Vertex shader for fourcc: %4.4s ===\n%s\n",
-                (const char *) &renderer->sampler->fmt.i_chroma, code);
-    return code;
-}
-
-static char *
-BuildFragmentShader(struct vlc_gl_filter *filter)
-{
-    struct vlc_gl_renderer *renderer = filter->sys;
-    struct vlc_gl_sampler *sampler = renderer->sampler;
-
-    static const char *template =
-        SHADER_VERSION
-        "%s" /* extensions */
-        FRAGMENT_SHADER_PRECISION
-        "%s" /* vlc_texture definition */
+    static const char *const FRAGMENT_SHADER_BODY =
         "varying vec2 PicCoords;\n"
         "void main() {\n"
         " gl_FragColor = vlc_texture(PicCoords);\n"
@@ -231,35 +200,44 @@ BuildFragmentShader(struct vlc_gl_filter *filter)
     const char *extensions = sampler->shader.extensions
                            ? sampler->shader.extensions : "";
 
-    char *code;
-    int ret = asprintf(&code, template, extensions, sampler->shader.body);
-    if (ret < 0)
-        return NULL;
+    const char *shader_version;
+    const char *shader_precision;
+    if (filter->api->is_gles)
+    {
+        shader_version = "#version 100\n";
+        shader_precision = "precision highp float;\n";
+    }
+    else
+    {
+        shader_version = "#version 120\n";
+        shader_precision = "";
+    }
+
+    const char *vertex_shader[] = {
+        shader_version,
+        VERTEX_SHADER_BODY,
+    };
+    const char *fragment_shader[] = {
+        shader_version,
+        extensions,
+        shader_precision,
+        sampler->shader.body,
+        FRAGMENT_SHADER_BODY,
+    };
 
     if (renderer->dump_shaders)
-        msg_Dbg(filter, "\n=== Fragment shader for fourcc: %4.4s, colorspace: %d ===\n%s\n",
-                        (const char *) &sampler->fmt.i_chroma,
-                        sampler->fmt.space, code);
-
-    return code;
-}
-
-static int
-opengl_link_program(struct vlc_gl_filter *filter)
-{
-    struct vlc_gl_renderer *renderer = filter->sys;
-    struct vlc_gl_sampler *sampler = renderer->sampler;
-    const opengl_vtable_t *vt = renderer->vt;
-
-    char *vertex_shader = BuildVertexShader(filter);
-    if (!vertex_shader)
-        return VLC_EGENERIC;
-
-    char *fragment_shader = BuildFragmentShader(filter);
-    if (!fragment_shader)
     {
-        free(vertex_shader);
-        return VLC_EGENERIC;
+        msg_Dbg(filter, "\n=== Vertex shader for fourcc: %4.4s ===\n",
+                (const char *) &renderer->sampler->fmt.i_chroma);
+        for (unsigned i = 0; i < ARRAY_SIZE(vertex_shader); ++i)
+            msg_Dbg(filter, "[%u] %s", i, vertex_shader[i]);
+
+        msg_Dbg(filter,
+                "\n=== Fragment shader for fourcc: %4.4s, colorspace: %d ===\n",
+                (const char *) &renderer->sampler->fmt.i_chroma,
+                sampler->fmt.space);
+        for (unsigned i = 0; i < ARRAY_SIZE(fragment_shader); ++i)
+            msg_Dbg(filter, "[%u] %s", i, fragment_shader[i]);
     }
 
     assert(sampler->ops &&
@@ -268,10 +246,8 @@ opengl_link_program(struct vlc_gl_filter *filter)
 
     GLuint program_id =
         vlc_gl_BuildProgram(VLC_OBJECT(filter), vt,
-                            1, (const char **) &vertex_shader,
-                            1, (const char **) &fragment_shader);
-    free(vertex_shader);
-    free(fragment_shader);
+                            ARRAY_SIZE(vertex_shader), vertex_shader,
+                            ARRAY_SIZE(fragment_shader), fragment_shader);
     if (!program_id)
         return VLC_EGENERIC;
 
-- 
2.31.0



More information about the vlc-devel mailing list