[vlc-devel] [PATCH 13/13] vout/opengl: move YUV/XYZ12 fragment shader configuration

Thomas Guillem thomas at gllm.fr
Mon Dec 12 17:03:54 CET 2016


---
 modules/video_output/Makefile.am             |   3 +-
 modules/video_output/opengl/common_formats.c | 318 ++++++++++++++++++++
 modules/video_output/opengl/internal.h       |   8 +
 modules/video_output/opengl/vout_helper.c    | 419 ++++++++-------------------
 4 files changed, 452 insertions(+), 296 deletions(-)
 create mode 100644 modules/video_output/opengl/common_formats.c

diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am
index 95bda31..ebebc6a 100644
--- a/modules/video_output/Makefile.am
+++ b/modules/video_output/Makefile.am
@@ -3,7 +3,8 @@ vout_LTLIBRARIES =
 
 EXTRA_DIST += video_output/README
 OPENGL_DISPLAY_SOURCES = video_output/opengl/vout_helper.c \
-	video_output/opengl/vout_helper.h video_output/opengl/internal.h
+	video_output/opengl/vout_helper.h video_output/opengl/internal.h \
+	video_output/opengl/common_formats.c
 
 if HAVE_DECKLINK
 libdecklinkoutput_plugin_la_SOURCES = video_output/decklink.cpp
diff --git a/modules/video_output/opengl/common_formats.c b/modules/video_output/opengl/common_formats.c
new file mode 100644
index 0000000..2d46648
--- /dev/null
+++ b/modules/video_output/opengl/common_formats.c
@@ -0,0 +1,318 @@
+/*****************************************************************************
+ * common_formats.c: OpenGL common formats configuration
+ *****************************************************************************
+ * Copyright (C) 2016 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"
+
+#ifndef GL_RED
+#define GL_RED 0
+#endif
+#ifndef GL_R16
+#define GL_R16 0
+#endif
+
+#if !defined(USE_OPENGL_ES2)
+static int GetTexFormatSize(int target, int tex_format, int tex_internal,
+                            int tex_type)
+{
+    GLint tex_param_size;
+    switch (tex_format)
+    {
+        case GL_RED:
+            tex_param_size = GL_TEXTURE_RED_SIZE;
+            break;
+        case GL_LUMINANCE:
+            tex_param_size = GL_TEXTURE_LUMINANCE_SIZE;
+            break;
+        default:
+            return -1;
+    }
+    GLuint texture;
+
+    glGenTextures(1, &texture);
+    glBindTexture(target, texture);
+    glTexImage2D(target, 0, tex_internal, 64, 64, 0, tex_format, tex_type, NULL);
+    GLint size = 0;
+    glGetTexLevelParameteriv(target, 0, tex_param_size, &size);
+
+    glDeleteTextures(1, &texture);
+    return size;
+}
+#endif
+
+static int
+BuildYUVFragmentShader(const vout_display_opengl_api_t *api, GLuint *shader,
+                       GLfloat *local_value, const video_format_t *fmt,
+                       float yuv_range_correction)
+
+{
+    /* [R/G/B][Y U V O] from TV range to full range
+     * XXX we could also do hue/brightness/constrast/gamma
+     * by simply changing the coefficients
+     */
+    const float matrix_bt601_tv2full[12] = {
+        1.164383561643836,  0.0000,             1.596026785714286, -0.874202217873451 ,
+        1.164383561643836, -0.391762290094914, -0.812967647237771,  0.531667823499146 ,
+        1.164383561643836,  2.017232142857142,  0.0000,            -1.085630789302022 ,
+    };
+    const float matrix_bt709_tv2full[12] = {
+        1.164383561643836,  0.0000,             1.792741071428571, -0.972945075016308 ,
+        1.164383561643836, -0.21324861427373,  -0.532909328559444,  0.301482665475862 ,
+        1.164383561643836,  2.112401785714286,  0.0000,            -1.133402217873451 ,
+    };
+    const float *matrix;
+    switch( fmt->space )
+    {
+        case COLOR_SPACE_BT601:
+            matrix = matrix_bt601_tv2full;
+            break;
+        default:
+            matrix = matrix_bt709_tv2full;
+    };
+
+    /* Basic linear YUV -> RGB conversion using bilinear interpolation */
+    static const char *template_glsl_yuv =
+        "#version " GLSL_VERSION "\n"
+        PRECISION
+        "uniform sampler2D Texture0;"
+        "uniform sampler2D Texture1;"
+        "uniform sampler2D Texture2;"
+        "uniform vec4      Coefficient[4];"
+        "varying vec4      TexCoord0,TexCoord1,TexCoord2;"
+
+        "void main(void) {"
+        " vec4 x,y,z,result;"
+
+        /* The texture format can be GL_RED: vec4(R,0,0,1) or GL_LUMINANCE:
+         * vec4(L,L,L,1). The following transform a vec4(x, y, z, w) into a
+         * vec4(x, x, x, 1) (we may want to use texture swizzling starting
+         * OpenGL 3.3). */
+        " float val0 = texture2D(Texture0, TexCoord0.st).x;"
+        " float val1 = texture2D(Texture1, TexCoord1.st).x;"
+        " float val2 = texture2D(Texture2, TexCoord2.st).x;"
+        " x  = vec4(val0, val0, val0, 1);"
+        " %c = vec4(val1, val1, val1, 1);"
+        " %c = vec4(val2, val2, val2, 1);"
+
+        " result = x * Coefficient[0] + Coefficient[3];"
+        " result = (y * Coefficient[1]) + result;"
+        " result = (z * Coefficient[2]) + result;"
+        " gl_FragColor = result;"
+        "}";
+    bool swap_uv = fmt->i_chroma == VLC_CODEC_YV12 ||
+                   fmt->i_chroma == VLC_CODEC_YV9;
+
+    char *code;
+    if (asprintf(&code, template_glsl_yuv,
+                 swap_uv ? 'z' : 'y',
+                 swap_uv ? 'y' : 'z') < 0)
+        return VLC_ENOMEM;
+
+    for (int i = 0; i < 4; i++) {
+        float correction = i < 3 ? yuv_range_correction : 1.f;
+        /* We place coefficient values for coefficient[4] in one array from matrix values.
+           Notice that we fill values from top down instead of left to right.*/
+        for (int j = 0; j < 4; j++)
+            local_value[i*4+j] = j < 3 ? correction * matrix[j*4+i] : 0.f;
+    }
+
+    *shader = api->CreateShader(GL_FRAGMENT_SHADER);
+    if (*shader == 0)
+    {
+        free(code);
+        return VLC_EGENERIC;
+    }
+    api->ShaderSource(*shader, 1, (const char **)&code, NULL);
+    api->CompileShader(*shader);
+
+    free(code);
+    return VLC_SUCCESS;
+}
+
+static void
+fc_yuv_prepare_shader(const vout_display_opengl_api_t *api,
+                      const opengl_fmt_cfg_t *fc, GLuint program)
+{
+    api->Uniform4fv(api->GetUniformLocation(program, "Coefficient"), 4,
+                    fc->priv);
+    api->Uniform1i(api->GetUniformLocation(program, "Texture0"), 0);
+    api->Uniform1i(api->GetUniformLocation(program, "Texture1"), 1);
+    api->Uniform1i(api->GetUniformLocation(program, "Texture2"), 2);
+}
+
+static void
+fc_yuv_release(const vout_display_opengl_api_t *api,
+               const opengl_fmt_cfg_t *fc)
+{
+    api->DeleteShader(fc->fragment_shader);
+    free(fc->priv);
+}
+
+int
+opengl_fmt_cfg_yuv_init(const vout_display_opengl_api_t *api,
+                        const video_format_t *fmt, opengl_fmt_cfg_t *fc)
+{
+    if (!vlc_fourcc_IsYUV(fmt->i_chroma))
+        return VLC_EGENERIC;
+
+    GLint max_texture_units = 0;
+    glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_units);
+
+    if (max_texture_units < 3)
+        return VLC_EGENERIC;
+
+#if !defined(USE_OPENGL_ES2)
+    const unsigned char *ogl_version = glGetString(GL_VERSION);
+    const bool oglv3 = strverscmp((const char *)ogl_version, "3.0") >= 0;
+    const int yuv_plane_texformat = oglv3 ? GL_RED : GL_LUMINANCE;
+    const int yuv_plane_texformat_16 = oglv3 ? GL_R16 : GL_LUMINANCE16;
+#else
+    const int yuv_plane_texformat = GL_LUMINANCE;
+#endif
+
+    fc->pf_prepare_shader = fc_yuv_prepare_shader;
+    fc->pf_release = fc_yuv_release;
+
+    float yuv_range_correction = 1.0;
+    const vlc_fourcc_t *list = vlc_fourcc_GetYUVFallback(fmt->i_chroma);
+    while (*list) {
+        const vlc_chroma_description_t *dsc =
+            vlc_fourcc_GetChromaDescription(*list);
+        if (dsc && dsc->plane_count == 3 && dsc->pixel_size == 1) {
+            fc->chroma = *list;
+            fc->tex_format   = yuv_plane_texformat;
+            fc->tex_internal = yuv_plane_texformat;
+            fc->tex_type     = GL_UNSIGNED_BYTE;
+            yuv_range_correction = 1.0;
+            break;
+#if !defined(USE_OPENGL_ES2)
+        } else if (dsc && dsc->plane_count == 3 && dsc->pixel_size == 2 &&
+                   GetTexFormatSize(GL_TEXTURE_2D,
+                                    yuv_plane_texformat,
+                                    yuv_plane_texformat_16,
+                                    GL_UNSIGNED_SHORT) == 16)
+        {
+            fc->chroma = *list;
+            fc->tex_format   = yuv_plane_texformat;
+            fc->tex_internal = yuv_plane_texformat_16;
+            fc->tex_type     = GL_UNSIGNED_SHORT;
+            yuv_range_correction = (float)((1 << 16) - 1)
+                                 / ((1 << dsc->pixel_bits) - 1);
+            break;
+#endif
+        }
+        list++;
+    }
+    if (!*list)
+        return VLC_EGENERIC;
+
+    fc->tex_target = GL_TEXTURE_2D;
+    fc->priv = malloc(16 * sizeof(GLfloat));
+    if (!fc->priv)
+        return VLC_ENOMEM;
+
+    int ret = BuildYUVFragmentShader(api, &fc->fragment_shader, fc->priv,
+                                     fmt, yuv_range_correction);
+    if (ret != VLC_SUCCESS)
+    {
+        free(fc->priv);
+        return ret;
+    }
+
+    return VLC_SUCCESS;
+}
+
+static int
+BuildXYZFragmentShader(const vout_display_opengl_api_t *api, GLuint *shader)
+{
+    /* Shader for XYZ to RGB correction
+     * 3 steps :
+     *  - XYZ gamma correction
+     *  - XYZ to RGB matrix conversion
+     *  - reverse RGB gamma correction
+     */
+    static const char *code =
+        "#version " GLSL_VERSION "\n"
+        PRECISION
+        "uniform sampler2D Texture0;"
+        "uniform vec4 xyz_gamma = vec4(2.6);"
+        "uniform vec4 rgb_gamma = vec4(1.0/2.2);"
+        // WARN: matrix Is filled column by column (not row !)
+        "uniform mat4 matrix_xyz_rgb = mat4("
+        "    3.240454 , -0.9692660, 0.0556434, 0.0,"
+        "   -1.5371385,  1.8760108, -0.2040259, 0.0,"
+        "    -0.4985314, 0.0415560, 1.0572252,  0.0,"
+        "    0.0,      0.0,         0.0,        1.0 "
+        " );"
+
+        "varying vec4 TexCoord0;"
+        "void main()"
+        "{ "
+        " vec4 v_in, v_out;"
+        " v_in  = texture2D(Texture0, TexCoord0.st);"
+        " v_in = pow(v_in, xyz_gamma);"
+        " v_out = matrix_xyz_rgb * v_in ;"
+        " v_out = pow(v_out, rgb_gamma) ;"
+        " v_out = clamp(v_out, 0.0, 1.0) ;"
+        " gl_FragColor = v_out;"
+        "}";
+    *shader = api->CreateShader(GL_FRAGMENT_SHADER);
+    if (*shader == 0)
+        return VLC_EGENERIC;
+    api->ShaderSource(*shader, 1, &code, NULL);
+    api->CompileShader(*shader);
+    return VLC_SUCCESS;
+}
+
+static void
+fc_xyz12_prepare_shader(const vout_display_opengl_api_t *api,
+                        const opengl_fmt_cfg_t *fc, GLuint program)
+{
+    (void) fc;
+    api->Uniform1i(api->GetUniformLocation(program, "Texture0"), 0);
+}
+
+static void
+fc_xyz12_release(const vout_display_opengl_api_t *api,
+                 const opengl_fmt_cfg_t *fc)
+{
+    api->DeleteShader(fc->fragment_shader);
+}
+
+int
+opengl_fmt_cfg_xyz12_init(const vout_display_opengl_api_t *api,
+                          const video_format_t *fmt, opengl_fmt_cfg_t *fc)
+{
+    if (fmt->i_chroma != VLC_CODEC_XYZ12)
+        return VLC_EGENERIC;
+
+    fc->pf_prepare_shader = fc_xyz12_prepare_shader;
+    fc->pf_release = fc_xyz12_release;
+
+    fc->chroma = VLC_CODEC_XYZ12;
+    fc->tex_target   = GL_TEXTURE_2D;
+    fc->tex_format   = GL_RGB;
+    fc->tex_internal = GL_RGB;
+    fc->tex_type     = GL_UNSIGNED_SHORT;
+    return BuildXYZFragmentShader(api, &fc->fragment_shader);
+}
diff --git a/modules/video_output/opengl/internal.h b/modules/video_output/opengl/internal.h
index 42a7e96..27a551b 100644
--- a/modules/video_output/opengl/internal.h
+++ b/modules/video_output/opengl/internal.h
@@ -177,4 +177,12 @@ struct opengl_fmt_cfg_t
     void (*pf_release)(const vout_display_opengl_api_t *api,
                        const opengl_fmt_cfg_t *fc);
 };
+
+extern int
+opengl_fmt_cfg_yuv_init(const vout_display_opengl_api_t *api,
+                        const video_format_t *, opengl_fmt_cfg_t *);
+extern int
+opengl_fmt_cfg_xyz12_init(const vout_display_opengl_api_t *api,
+                          const video_format_t *, opengl_fmt_cfg_t *);
+
 #endif /* include-guard */
diff --git a/modules/video_output/opengl/vout_helper.c b/modules/video_output/opengl/vout_helper.c
index b12db93..f79b5f8 100644
--- a/modules/video_output/opengl/vout_helper.c
+++ b/modules/video_output/opengl/vout_helper.c
@@ -46,19 +46,16 @@
 # define GL_CLAMP_TO_EDGE 0x812F
 #endif
 
-#ifndef GL_RED
-#define GL_RED 0
-#endif
-#ifndef GL_R16
-#define GL_R16 0
-#endif
-
 #define SPHERE_RADIUS 1.f
 
+static opengl_fmt_cfg_init_cb opengl_fmt_cfg_init_cbs[] =
+{
+    opengl_fmt_cfg_yuv_init,
+    opengl_fmt_cfg_xyz12_init
+};
+
 typedef struct {
     GLuint   texture;
-    unsigned format;
-    unsigned type;
     unsigned width;
     unsigned height;
 
@@ -81,11 +78,6 @@ struct vout_display_opengl_t {
     video_format_t fmt;
     const vlc_chroma_description_t *chroma;
 
-    int        tex_target;
-    int        tex_format;
-    int        tex_internal;
-    int        tex_type;
-
     int        tex_width[PICTURE_PLANE_MAX];
     int        tex_height[PICTURE_PLANE_MAX];
 
@@ -99,10 +91,8 @@ struct vout_display_opengl_t {
 
     /* One YUV program and/or one RGBA program (for subpics) */
     GLuint     program[2];
-    /* One YUV fragment shader and/or one RGBA fragment shader and
-     * one vertex shader */
-    GLint      shader[3];
-    GLfloat    local_value[16];
+    opengl_fmt_cfg_t fmt_cfg[2];
+    GLuint     vertex_shader;
 
     /* Index of main picture program */
     unsigned   program_idx;
@@ -143,37 +133,8 @@ static inline int GetAlignedSize(unsigned size)
     return ((align >> 1) == size) ? size : align;
 }
 
-#if !defined(USE_OPENGL_ES2)
-static int GetTexFormatSize(int target, int tex_format, int tex_internal,
-                            int tex_type)
-{
-    GLint tex_param_size;
-    switch (tex_format)
-    {
-        case GL_RED:
-            tex_param_size = GL_TEXTURE_RED_SIZE;
-            break;
-        case GL_LUMINANCE:
-            tex_param_size = GL_TEXTURE_LUMINANCE_SIZE;
-            break;
-        default:
-            return -1;
-    }
-    GLuint texture;
-
-    glGenTextures(1, &texture);
-    glBindTexture(target, texture);
-    glTexImage2D(target, 0, tex_internal, 64, 64, 0, tex_format, tex_type, NULL);
-    GLint size = 0;
-    glGetTexLevelParameteriv(target, 0, tex_param_size, &size);
-
-    glDeleteTextures(1, &texture);
-    return size;
-}
-#endif
-
 static void BuildVertexShader(vout_display_opengl_t *vgl,
-                              GLint *shader)
+                              GLuint *shader)
 {
     /* Basic vertex shader */
     const char *vertexShader =
@@ -200,93 +161,9 @@ static void BuildVertexShader(vout_display_opengl_t *vgl,
     vgl->api.CompileShader(*shader);
 }
 
-static void BuildYUVFragmentShader(vout_display_opengl_t *vgl,
-                                   GLint *shader,
-                                   GLfloat *local_value,
-                                   const video_format_t *fmt,
-                                   float yuv_range_correction)
-
-{
-    /* [R/G/B][Y U V O] from TV range to full range
-     * XXX we could also do hue/brightness/constrast/gamma
-     * by simply changing the coefficients
-     */
-    const float matrix_bt601_tv2full[12] = {
-        1.164383561643836,  0.0000,             1.596026785714286, -0.874202217873451 ,
-        1.164383561643836, -0.391762290094914, -0.812967647237771,  0.531667823499146 ,
-        1.164383561643836,  2.017232142857142,  0.0000,            -1.085630789302022 ,
-    };
-    const float matrix_bt709_tv2full[12] = {
-        1.164383561643836,  0.0000,             1.792741071428571, -0.972945075016308 ,
-        1.164383561643836, -0.21324861427373,  -0.532909328559444,  0.301482665475862 ,
-        1.164383561643836,  2.112401785714286,  0.0000,            -1.133402217873451 ,
-    };
-    const float *matrix;
-    switch( fmt->space )
-    {
-        case COLOR_SPACE_BT601:
-            matrix = matrix_bt601_tv2full;
-            break;
-        default:
-            matrix = matrix_bt709_tv2full;
-    };
-
-    /* Basic linear YUV -> RGB conversion using bilinear interpolation */
-    const char *template_glsl_yuv =
-        "#version " GLSL_VERSION "\n"
-        PRECISION
-        "uniform sampler2D Texture0;"
-        "uniform sampler2D Texture1;"
-        "uniform sampler2D Texture2;"
-        "uniform vec4      Coefficient[4];"
-        "varying vec4      TexCoord0,TexCoord1,TexCoord2;"
-
-        "void main(void) {"
-        " vec4 x,y,z,result;"
-
-        /* The texture format can be GL_RED: vec4(R,0,0,1) or GL_LUMINANCE:
-         * vec4(L,L,L,1). The following transform a vec4(x, y, z, w) into a
-         * vec4(x, x, x, 1) (we may want to use texture swizzling starting
-         * OpenGL 3.3). */
-        " float val0 = texture2D(Texture0, TexCoord0.st).x;"
-        " float val1 = texture2D(Texture1, TexCoord1.st).x;"
-        " float val2 = texture2D(Texture2, TexCoord2.st).x;"
-        " x  = vec4(val0, val0, val0, 1);"
-        " %c = vec4(val1, val1, val1, 1);"
-        " %c = vec4(val2, val2, val2, 1);"
-
-        " result = x * Coefficient[0] + Coefficient[3];"
-        " result = (y * Coefficient[1]) + result;"
-        " result = (z * Coefficient[2]) + result;"
-        " gl_FragColor = result;"
-        "}";
-    bool swap_uv = fmt->i_chroma == VLC_CODEC_YV12 ||
-                   fmt->i_chroma == VLC_CODEC_YV9;
-
-    char *code;
-    if (asprintf(&code, template_glsl_yuv,
-                 swap_uv ? 'z' : 'y',
-                 swap_uv ? 'y' : 'z') < 0)
-        return;
-
-    for (int i = 0; i < 4; i++) {
-        float correction = i < 3 ? yuv_range_correction : 1.f;
-        /* We place coefficient values for coefficient[4] in one array from matrix values.
-           Notice that we fill values from top down instead of left to right.*/
-        for (int j = 0; j < 4; j++)
-            local_value[i*4+j] = j < 3 ? correction * matrix[j*4+i] : 0.f;
-    }
-
-    *shader = vgl->api.CreateShader(GL_FRAGMENT_SHADER);
-    vgl->api.ShaderSource(*shader, 1, (const char **)&code, NULL);
-    vgl->api.CompileShader(*shader);
-
-    free(code);
-}
-
 #if 0
 static void BuildRGBFragmentShader(vout_display_opengl_t *vgl,
-                                   GLint *shader)
+                                   GLuint *shader)
 {
     // Simple shader for RGB
     const char *code =
@@ -304,8 +181,8 @@ static void BuildRGBFragmentShader(vout_display_opengl_t *vgl,
 }
 #endif
 
-static void BuildRGBAFragmentShader(vout_display_opengl_t *vgl,
-                                   GLint *shader)
+static int BuildRGBAFragmentShader(const vout_display_opengl_api_t *api,
+                                   GLuint *shader)
 {
     // Simple shader for RGBA
     const char *code =
@@ -318,48 +195,46 @@ static void BuildRGBAFragmentShader(vout_display_opengl_t *vgl,
         "{ "
         "  gl_FragColor = texture2D(Texture, TexCoord0.st) * FillColor;"
         "}";
-    *shader = vgl->api.CreateShader(GL_FRAGMENT_SHADER);
-    vgl->api.ShaderSource(*shader, 1, &code, NULL);
-    vgl->api.CompileShader(*shader);
+    *shader = api->CreateShader(GL_FRAGMENT_SHADER);
+    if (*shader == 0)
+        return VLC_EGENERIC;
+    api->ShaderSource(*shader, 1, &code, NULL);
+    api->CompileShader(*shader);
+    return VLC_SUCCESS;
 }
 
-static void BuildXYZFragmentShader(vout_display_opengl_t *vgl,
-                                   GLint *shader)
+static void
+fc_rgba_prepare_shader(const vout_display_opengl_api_t *api,
+                       const opengl_fmt_cfg_t *fc, GLuint program)
 {
-    /* Shader for XYZ to RGB correction
-     * 3 steps :
-     *  - XYZ gamma correction
-     *  - XYZ to RGB matrix conversion
-     *  - reverse RGB gamma correction
-     */
-      const char *code =
-        "#version " GLSL_VERSION "\n"
-        PRECISION
-        "uniform sampler2D Texture0;"
-        "uniform vec4 xyz_gamma = vec4(2.6);"
-        "uniform vec4 rgb_gamma = vec4(1.0/2.2);"
-        // WARN: matrix Is filled column by column (not row !)
-        "uniform mat4 matrix_xyz_rgb = mat4("
-        "    3.240454 , -0.9692660, 0.0556434, 0.0,"
-        "   -1.5371385,  1.8760108, -0.2040259, 0.0,"
-        "    -0.4985314, 0.0415560, 1.0572252,  0.0,"
-        "    0.0,      0.0,         0.0,        1.0 "
-        " );"
+    (void) fc;
+    api->Uniform1i(api->GetUniformLocation(program, "Texture0"), 0);
+    api->Uniform4f(api->GetUniformLocation(program, "FillColor"),
+                   1.0f, 1.0f, 1.0f, 1.0f);
+}
 
-        "varying vec4 TexCoord0;"
-        "void main()"
-        "{ "
-        " vec4 v_in, v_out;"
-        " v_in  = texture2D(Texture0, TexCoord0.st);"
-        " v_in = pow(v_in, xyz_gamma);"
-        " v_out = matrix_xyz_rgb * v_in ;"
-        " v_out = pow(v_out, rgb_gamma) ;"
-        " v_out = clamp(v_out, 0.0, 1.0) ;"
-        " gl_FragColor = v_out;"
-        "}";
-    *shader = vgl->api.CreateShader(GL_FRAGMENT_SHADER);
-    vgl->api.ShaderSource(*shader, 1, &code, NULL);
-    vgl->api.CompileShader(*shader);
+static void
+fc_rgba_release(const vout_display_opengl_api_t *api,
+                const opengl_fmt_cfg_t *fc)
+{
+    api->DeleteShader(fc->fragment_shader);
+}
+
+static int
+opengl_fmt_cfg_rgba_init(const vout_display_opengl_api_t *api,
+                         const video_format_t *fmt, opengl_fmt_cfg_t *fc)
+{
+    (void) fmt;
+    fc->pf_prepare_shader = fc_rgba_prepare_shader;
+    fc->pf_release = fc_rgba_release;
+
+    fc->chroma = VLC_CODEC_RGBA;
+    fc->tex_target   = GL_TEXTURE_2D;
+    fc->tex_format   = GL_RGBA;
+    fc->tex_internal = GL_RGBA;
+    fc->tex_type     = GL_UNSIGNED_BYTE;
+
+    return BuildRGBAFragmentShader(api, &fc->fragment_shader);
 }
 
 vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
@@ -387,12 +262,8 @@ vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
 #if !defined(USE_OPENGL_ES2)
     const unsigned char *ogl_version = glGetString(GL_VERSION);
     bool supports_shaders = strverscmp((const char *)ogl_version, "2.0") >= 0;
-    const bool oglv3 = strverscmp((const char *)ogl_version, "3.0") >= 0;
-    const int yuv_plane_texformat = oglv3 ? GL_RED : GL_LUMINANCE;
-    const int yuv_plane_texformat_16 = oglv3 ? GL_R16 : GL_LUMINANCE16;
 #else
     bool supports_shaders = true;
-    const int yuv_plane_texformat = GL_LUMINANCE;
 #endif
 
     vout_display_opengl_api_t *api = &vgl->api;
@@ -488,9 +359,6 @@ vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
     vgl->supports_npot = true;
 #endif
 
-    GLint max_texture_units = 0;
-    glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_units);
-
     /* Initialize with default chroma */
     vgl->fmt = *fmt;
     vgl->fmt.i_chroma = VLC_CODEC_RGB32;
@@ -503,56 +371,34 @@ vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
     vgl->fmt.i_gmask  = 0x0000ff00;
     vgl->fmt.i_bmask  = 0x00ff0000;
 #   endif
-    vgl->tex_target   = GL_TEXTURE_2D;
-    vgl->tex_format   = GL_RGBA;
-    vgl->tex_internal = GL_RGBA;
-    vgl->tex_type     = GL_UNSIGNED_BYTE;
-    /* Use YUV if possible and needed */
-    bool need_fs_yuv = false;
-    bool need_fs_xyz = false;
-    float yuv_range_correction = 1.0;
-
-    if (max_texture_units >= 3 && vlc_fourcc_IsYUV(fmt->i_chroma)) {
-        const vlc_fourcc_t *list = vlc_fourcc_GetYUVFallback(fmt->i_chroma);
-        while (*list) {
-            const vlc_chroma_description_t *dsc = vlc_fourcc_GetChromaDescription(*list);
-            if (dsc && dsc->plane_count == 3 && dsc->pixel_size == 1) {
-                need_fs_yuv       = true;
-                vgl->fmt          = *fmt;
-                vgl->fmt.i_chroma = *list;
-                vgl->tex_format   = yuv_plane_texformat;
-                vgl->tex_internal = yuv_plane_texformat;
-                vgl->tex_type     = GL_UNSIGNED_BYTE;
-                yuv_range_correction = 1.0;
-                break;
-#if !defined(USE_OPENGL_ES2)
-            } else if (dsc && dsc->plane_count == 3 && dsc->pixel_size == 2 &&
-                       GetTexFormatSize(vgl->tex_target,
-                                        yuv_plane_texformat,
-                                        yuv_plane_texformat_16,
-                                        GL_UNSIGNED_SHORT) == 16) {
-                need_fs_yuv       = true;
-                vgl->fmt          = *fmt;
-                vgl->fmt.i_chroma = *list;
-                vgl->tex_format   = yuv_plane_texformat;
-                vgl->tex_internal = yuv_plane_texformat_16;
-                vgl->tex_type     = GL_UNSIGNED_SHORT;
-                yuv_range_correction = (float)((1 << 16) - 1) / ((1 << dsc->pixel_bits) - 1);
-                break;
-#endif
-            }
-            list++;
-        }
+    opengl_fmt_cfg_t fmt_cfg;
+    opengl_fmt_cfg_t rgba_fmt_cfg;
+
+    /* RGBA is needed for subpictures or for non YUV pictures */
+    if (opengl_fmt_cfg_rgba_init(&vgl->api, fmt, &rgba_fmt_cfg) != VLC_SUCCESS)
+    {
+        msg_Err(gl, "RGBA shader failed");
+        vlc_gl_Unlock(vgl->gl);
+        free(vgl);
+        return NULL;
     }
 
-    if (fmt->i_chroma == VLC_CODEC_XYZ12) {
-        need_fs_xyz       = true;
-        vgl->fmt          = *fmt;
-        vgl->fmt.i_chroma = VLC_CODEC_XYZ12;
-        vgl->tex_format   = GL_RGB;
-        vgl->tex_internal = GL_RGB;
-        vgl->tex_type     = GL_UNSIGNED_SHORT;
+    for (size_t i = 0; i < ARRAY_SIZE(opengl_fmt_cfg_init_cbs); ++i)
+    {
+        memset(&fmt_cfg, 0, sizeof(fmt_cfg));
+        int ret = opengl_fmt_cfg_init_cbs[i](&vgl->api, fmt, &fmt_cfg);
+        if (ret == VLC_SUCCESS)
+        {
+            assert(fmt_cfg.chroma != 0 && fmt_cfg.tex_target != 0 &&
+                   fmt_cfg.fragment_shader != 0 &&
+                   fmt_cfg.pf_prepare_shader != NULL &&
+                   fmt_cfg.pf_release != NULL);
+            vgl->fmt = *fmt;
+            vgl->fmt.i_chroma = fmt_cfg.chroma;
+            break;
+        }
     }
+
     vgl->chroma = vlc_fourcc_GetChromaDescription(vgl->fmt.i_chroma);
     assert(vgl->chroma != NULL);
     vgl->use_multitexture = vgl->chroma->plane_count > 1;
@@ -573,35 +419,25 @@ vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
     /* Build program if needed */
     vgl->program[0] =
     vgl->program[1] = 0;
-    vgl->shader[0] =
-    vgl->shader[1] =
-    vgl->shader[2] = -1;
+    vgl->vertex_shader = 0;
+    GLuint shaders[3] = { 0, 0, 0 };
     unsigned nb_shaders = 0;
-    int vertex_shader_idx = -1, fragment_shader_idx = -1,
-        rgba_fragment_shader_idx = -1;
 
-    if (need_fs_xyz)
-    {
-        fragment_shader_idx = nb_shaders++;
-        BuildXYZFragmentShader(vgl, &vgl->shader[fragment_shader_idx]);
-    }
-    else if (need_fs_yuv)
-    {
-        fragment_shader_idx = nb_shaders++;
-        BuildYUVFragmentShader(vgl, &vgl->shader[fragment_shader_idx],
-                               vgl->local_value, fmt, yuv_range_correction);
-    }
+    if (fmt_cfg.fragment_shader != 0)
+        shaders[nb_shaders++] = fmt_cfg.fragment_shader;
 
-    rgba_fragment_shader_idx = nb_shaders++;
-    BuildRGBAFragmentShader(vgl, &vgl->shader[rgba_fragment_shader_idx]);
+    shaders[nb_shaders++] = rgba_fmt_cfg.fragment_shader;
 
-    vertex_shader_idx = nb_shaders++;
-    BuildVertexShader(vgl, &vgl->shader[vertex_shader_idx]);
+    BuildVertexShader(vgl, &vgl->vertex_shader);
+    shaders[nb_shaders++] = vgl->vertex_shader;
+
+    /* One/two fragment shader and one vertex shader */
+    assert(shaders[0] != 0 && shaders[1] != 0);
 
     /* Check shaders messages */
     for (unsigned j = 0; j < nb_shaders; j++) {
         int infoLength;
-        vgl->api.GetShaderiv(vgl->shader[j], GL_INFO_LOG_LENGTH, &infoLength);
+        vgl->api.GetShaderiv(shaders[j], GL_INFO_LOG_LENGTH, &infoLength);
         if (infoLength <= 1)
             continue;
 
@@ -609,34 +445,35 @@ vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
         if (infolog != NULL)
         {
             int charsWritten;
-            vgl->api.GetShaderInfoLog(vgl->shader[j], infoLength, &charsWritten,
+            vgl->api.GetShaderInfoLog(shaders[j], infoLength, &charsWritten,
                                       infolog);
             msg_Err(gl, "shader %d: %s", j, infolog);
             free(infolog);
         }
     }
-    assert(vertex_shader_idx != -1 && rgba_fragment_shader_idx != -1);
 
     unsigned nb_programs = 0;
     GLuint program;
     int program_idx = -1, rgba_program_idx = -1;
 
     /* YUV/XYZ & Vertex shaders */
-    if (fragment_shader_idx != -1)
+    if (fmt_cfg.fragment_shader != 0)
     {
         program_idx = nb_programs++;
 
+        vgl->fmt_cfg[program_idx] = fmt_cfg;
         program = vgl->program[program_idx] = vgl->api.CreateProgram();
-        vgl->api.AttachShader(program, vgl->shader[fragment_shader_idx]);
-        vgl->api.AttachShader(program, vgl->shader[vertex_shader_idx]);
+        vgl->api.AttachShader(program, fmt_cfg.fragment_shader);
+        vgl->api.AttachShader(program, vgl->vertex_shader);
         vgl->api.LinkProgram(program);
     }
 
     /* RGB & Vertex shaders */
     rgba_program_idx = nb_programs++;
+    vgl->fmt_cfg[rgba_program_idx] = rgba_fmt_cfg;
     program = vgl->program[rgba_program_idx] = vgl->api.CreateProgram();
-    vgl->api.AttachShader(program, vgl->shader[rgba_fragment_shader_idx]);
-    vgl->api.AttachShader(program, vgl->shader[vertex_shader_idx]);
+    vgl->api.AttachShader(program, rgba_fmt_cfg.fragment_shader);
+    vgl->api.AttachShader(program, vgl->vertex_shader);
     vgl->api.LinkProgram(program);
 
     vgl->program_idx = program_idx != -1 ? program_idx : rgba_program_idx;
@@ -732,9 +569,12 @@ void vout_display_opengl_Delete(vout_display_opengl_t *vgl)
         free(vgl->region);
 
         for (int i = 0; i < 2 && vgl->program[i] != 0; i++)
+        {
             vgl->api.DeleteProgram(vgl->program[i]);
-        for (int i = 0; i < 3 && vgl->shader[i] != 0; i++)
-            vgl->api.DeleteShader(vgl->shader[i]);
+            opengl_fmt_cfg_t *fc = &vgl->fmt_cfg[i];
+            fc->pf_release(&vgl->api, fc);
+        }
+        vgl->api.DeleteShader(vgl->vertex_shader);
         vgl->api.DeleteBuffers(1, &vgl->vertex_buffer_object);
         vgl->api.DeleteBuffers(1, &vgl->index_buffer_object);
         vgl->api.DeleteBuffers(vgl->chroma->plane_count, vgl->texture_buffer_object);
@@ -844,6 +684,8 @@ picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl, unsigned
     if (vlc_gl_Lock(vgl->gl))
         return vgl->pool;
 
+    opengl_fmt_cfg_t *fc = &vgl->fmt_cfg[vgl->program_idx];
+
     for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
         glGenTextures(vgl->chroma->plane_count, vgl->texture[i]);
         for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
@@ -851,23 +693,23 @@ picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl, unsigned
                 glActiveTexture(GL_TEXTURE0 + j);
                 glClientActiveTexture(GL_TEXTURE0 + j);
             }
-            glBindTexture(vgl->tex_target, vgl->texture[i][j]);
+            glBindTexture(fc->tex_target, vgl->texture[i][j]);
 
 #if !defined(USE_OPENGL_ES2)
             /* Set the texture parameters */
-            glTexParameterf(vgl->tex_target, GL_TEXTURE_PRIORITY, 1.0);
+            glTexParameterf(fc->tex_target, GL_TEXTURE_PRIORITY, 1.0);
             glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
 #endif
 
-            glTexParameteri(vgl->tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-            glTexParameteri(vgl->tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-            glTexParameteri(vgl->tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-            glTexParameteri(vgl->tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+            glTexParameteri(fc->tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+            glTexParameteri(fc->tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+            glTexParameteri(fc->tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+            glTexParameteri(fc->tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
             /* Call glTexImage2D only once, and use glTexSubImage2D later */
-            glTexImage2D(vgl->tex_target, 0,
-                         vgl->tex_internal, vgl->tex_width[j], vgl->tex_height[j],
-                         0, vgl->tex_format, vgl->tex_type, NULL);
+            glTexImage2D(fc->tex_target, 0,
+                         fc->tex_internal, vgl->tex_width[j], vgl->tex_height[j],
+                         0, fc->tex_format, fc->tex_type, NULL);
         }
     }
 
@@ -887,8 +729,11 @@ static void Upload(vout_display_opengl_t *vgl, int in_width, int in_height,
                    int w_num, int w_den, int h_num, int h_den,
                    int pitch, int pixel_pitch,
                    int full_upload, const uint8_t *pixels,
-                   int tex_target, int tex_format, int tex_type)
+                   const opengl_fmt_cfg_t *fc)
 {
+    int tex_target = fc->tex_target;
+    int tex_format = fc->tex_format;
+    int tex_type = fc->tex_type;
     int width       =       in_width * w_num / w_den;
     int full_width  =  in_full_width * w_num / w_den;
     int height      =      in_height * h_num / h_den;
@@ -951,6 +796,7 @@ int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
 {
     if (vlc_gl_Lock(vgl->gl))
         return VLC_EGENERIC;
+    opengl_fmt_cfg_t *fc = &vgl->fmt_cfg[vgl->program_idx];
 
     /* Update the texture */
     for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
@@ -958,12 +804,12 @@ int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
             glActiveTexture(GL_TEXTURE0 + j);
             glClientActiveTexture(GL_TEXTURE0 + j);
         }
-        glBindTexture(vgl->tex_target, vgl->texture[0][j]);
+        glBindTexture(fc->tex_target, vgl->texture[0][j]);
 
         Upload(vgl, picture->format.i_visible_width, vgl->fmt.i_visible_height,
                vgl->fmt.i_width, vgl->fmt.i_height,
                vgl->chroma->p[j].w.num, vgl->chroma->p[j].w.den, vgl->chroma->p[j].h.num, vgl->chroma->p[j].h.den,
-               picture->p[j].i_pitch, picture->p[j].i_pixel_pitch, 0, picture->p[j].p_pixels, vgl->tex_target, vgl->tex_format, vgl->tex_type);
+               picture->p[j].i_pitch, picture->p[j].i_pixel_pitch, 0, picture->p[j].p_pixels, fc);
     }
 
     int         last_count = vgl->region_count;
@@ -972,6 +818,7 @@ int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
     vgl->region_count = 0;
     vgl->region       = NULL;
 
+    fc = &vgl->fmt_cfg[vgl->program_sub_idx];
     if (subpicture) {
 
         int count = 0;
@@ -989,8 +836,6 @@ int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
         for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next, i++) {
             gl_region_t *glr = &vgl->region[i];
 
-            glr->format = GL_RGBA;
-            glr->type   = GL_UNSIGNED_BYTE;
             glr->width  = r->fmt.i_visible_width;
             glr->height = r->fmt.i_visible_height;
             if (!vgl->supports_npot) {
@@ -1014,9 +859,7 @@ int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
             for (int j = 0; j < last_count; j++) {
                 if (last[j].texture &&
                     last[j].width  == glr->width &&
-                    last[j].height == glr->height &&
-                    last[j].format == glr->format &&
-                    last[j].type   == glr->type) {
+                    last[j].height == glr->height) {
                     glr->texture = last[j].texture;
                     memset(&last[j], 0, sizeof(last[j]));
                     break;
@@ -1030,7 +873,7 @@ int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
                 glBindTexture(GL_TEXTURE_2D, glr->texture);
                 Upload(vgl, r->fmt.i_visible_width, r->fmt.i_visible_height, glr->width, glr->height, 1, 1, 1, 1,
                        r->p_picture->p->i_pitch, r->p_picture->p->i_pixel_pitch, 0,
-                       &r->p_picture->p->p_pixels[pixels_offset], GL_TEXTURE_2D, glr->format, glr->type);
+                       &r->p_picture->p->p_pixels[pixels_offset], fc);
             } else {
                 /* Could not recycle a previous texture, generate a new one. */
                 glGenTextures(1, &glr->texture);
@@ -1045,7 +888,7 @@ int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
                 Upload(vgl, r->fmt.i_visible_width, r->fmt.i_visible_height, glr->width, glr->height, 1, 1, 1, 1,
                        r->p_picture->p->i_pitch, r->p_picture->p->i_pixel_pitch, 1,
-                       &r->p_picture->p->p_pixels[pixels_offset], GL_TEXTURE_2D, glr->format, glr->type);
+                       &r->p_picture->p->p_pixels[pixels_offset], fc);
             }
         }
     }
@@ -1487,24 +1330,9 @@ static void DrawWithShaders(vout_display_opengl_t *vgl,
                             unsigned int program_idx)
 {
     GLuint program = vgl->program[program_idx];
+    opengl_fmt_cfg_t *fc = &vgl->fmt_cfg[program_idx];
     vgl->api.UseProgram(program);
-    if (vlc_fourcc_IsYUV(vgl->fmt.i_chroma)
-     || vgl->fmt.i_chroma == VLC_CODEC_XYZ12) { /* FIXME: ugly */
-        if (vgl->chroma->plane_count == 3) {
-            vgl->api.Uniform4fv(vgl->api.GetUniformLocation(program,
-                            "Coefficient"), 4, vgl->local_value);
-            vgl->api.Uniform1i(vgl->api.GetUniformLocation(program, "Texture0"), 0);
-            vgl->api.Uniform1i(vgl->api.GetUniformLocation(program, "Texture1"), 1);
-            vgl->api.Uniform1i(vgl->api.GetUniformLocation(program, "Texture2"), 2);
-        }
-        else if (vgl->chroma->plane_count == 1) {
-            vgl->api.Uniform1i(vgl->api.GetUniformLocation(program, "Texture0"), 0);
-        }
-    } else {
-        vgl->api.Uniform1i(vgl->api.GetUniformLocation(program, "Texture0"), 0);
-        vgl->api.Uniform4f(vgl->api.GetUniformLocation(program, "FillColor"),
-                       1.0f, 1.0f, 1.0f, 1.0f);
-    }
+    fc->pf_prepare_shader(&vgl->api, fc, program);
 
     GLfloat *vertexCoord, *textureCoord;
     GLushort *indices;
@@ -1569,7 +1397,7 @@ static void DrawWithShaders(vout_display_opengl_t *vgl,
     for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
         glActiveTexture(GL_TEXTURE0+j);
         glClientActiveTexture(GL_TEXTURE0+j);
-        glBindTexture(vgl->tex_target, vgl->texture[0][j]);
+        glBindTexture(fc->tex_target, vgl->texture[0][j]);
 
         vgl->api.BindBuffer(GL_ARRAY_BUFFER, vgl->texture_buffer_object[j]);
         vgl->api.BufferData(GL_ARRAY_BUFFER, nbVertices * 2 * sizeof(GLfloat),
@@ -1619,6 +1447,7 @@ int vout_display_opengl_Display(vout_display_opengl_t *vgl,
     if (vlc_gl_Lock(vgl->gl))
         return VLC_EGENERIC;
 
+    opengl_fmt_cfg_t *fc = &vgl->fmt_cfg[vgl->program_idx];
     /* Why drawing here and not in Render()? Because this way, the
        OpenGL providers can call vout_display_opengl_Display to force redraw.i
        Currently, the OS X provider uses it to get a smooth window resizing */
@@ -1634,7 +1463,7 @@ int vout_display_opengl_Display(vout_display_opengl_t *vgl,
            GL_TEXTURE_RECTANGLE_EXT */
         float scale_w, scale_h;
 
-        if (vgl->tex_target == GL_TEXTURE_2D) {
+        if (fc->tex_target == GL_TEXTURE_2D) {
             scale_w = (float)vgl->chroma->p[j].w.num / vgl->chroma->p[j].w.den / vgl->tex_width[j];
             scale_h = (float)vgl->chroma->p[j].h.num / vgl->chroma->p[j].h.den / vgl->tex_height[j];
 
-- 
2.10.2




More information about the vlc-devel mailing list