[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