[vlc-commits] opengl: converter: add opengl_fragment_shader_init()
Thomas Guillem
git at videolan.org
Thu Feb 2 09:52:49 CET 2017
vlc | branch: master | Thomas Guillem <thomas at gllm.fr> | Fri Jan 27 14:34:11 2017 +0100| [4df4a4d1da96f391591d4bbc3baad648fc1680ca] | committer: Thomas Guillem
opengl: converter: add opengl_fragment_shader_init()
This function will compile a fragment shader generated from a chroma and a tex
target. This helper can be used by hw opengl tex converters that need a generic
fragment shader.
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=4df4a4d1da96f391591d4bbc3baad648fc1680ca
---
modules/video_output/opengl/converter_android.c | 2 +-
modules/video_output/opengl/converters.c | 624 +++++++++++++-----------
modules/video_output/opengl/internal.h | 41 +-
3 files changed, 392 insertions(+), 275 deletions(-)
diff --git a/modules/video_output/opengl/converter_android.c b/modules/video_output/opengl/converter_android.c
index d10f272..4abe40a 100644
--- a/modules/video_output/opengl/converter_android.c
+++ b/modules/video_output/opengl/converter_android.c
@@ -154,7 +154,7 @@ tc_anop_update(const opengl_tex_converter_t *tc, GLuint *textures,
}
static int
-tc_anop_fetch_locations(const opengl_tex_converter_t *tc, GLuint program)
+tc_anop_fetch_locations(opengl_tex_converter_t *tc, GLuint program)
{
struct priv *priv = tc->priv;
priv->uloc.uSTMatrix = tc->api->GetUniformLocation(program, "uSTMatrix");
diff --git a/modules/video_output/opengl/converters.c b/modules/video_output/opengl/converters.c
index 18529c3..f424453 100644
--- a/modules/video_output/opengl/converters.c
+++ b/modules/video_output/opengl/converters.c
@@ -26,7 +26,9 @@
#include <limits.h>
#include <stdlib.h>
+#include <vlc_common.h>
#include <vlc_memory.h>
+#include <vlc_memstream.h>
#include "internal.h"
#ifndef GL_RED
@@ -36,6 +38,10 @@
#define GL_R16 0
#endif
+#ifndef GL_LUMINANCE16
+#define GL_LUMINANCE16 0
+#endif
+
#ifndef GL_UNPACK_ROW_LENGTH
#define GL_UNPACK_ROW_LENGTH 0x0CF2
#define NEED_GL_EXT_unpack_subimage
@@ -54,18 +60,6 @@ struct picture_sys_t
struct priv
{
- GLint tex_internal;
- GLenum tex_format;
- GLenum tex_type;
-
- struct {
- GLint Texture0;
- GLint Texture1;
- GLint Texture2;
- GLint Coefficient;
- GLint FillColor;
- } uloc;
-
bool has_unpack_subimage;
void * texture_temp_buf;
size_t texture_temp_buf_size;
@@ -77,12 +71,6 @@ struct priv
#endif
};
-struct yuv_priv
-{
- struct priv priv;
- GLfloat local_value[16];
-};
-
#if !defined(USE_OPENGL_ES2)
static int GetTexFormatSize(int target, int tex_format, int tex_internal,
int tex_type)
@@ -112,6 +100,297 @@ static int GetTexFormatSize(int target, int tex_format, int tex_internal,
}
#endif
+static int
+tc_yuv_base_init(opengl_tex_converter_t *tc, GLenum tex_target,
+ vlc_fourcc_t chroma, video_color_space_t yuv_space,
+ bool *swap_uv, const char *swizzle_per_tex[])
+{
+ const vlc_chroma_description_t *desc = vlc_fourcc_GetChromaDescription(chroma);
+ if (desc == NULL)
+ return VLC_EGENERIC;
+
+ GLint oneplane_texfmt, oneplane16_texfmt;
+
+#if !defined(USE_OPENGL_ES2)
+ if (HasExtension(tc->glexts, "GL_ARB_texture_rg"))
+ {
+ oneplane_texfmt = GL_RED;
+ oneplane16_texfmt = GL_R16;
+ }
+ else
+#endif
+ {
+ oneplane_texfmt = GL_LUMINANCE;
+ oneplane16_texfmt = GL_LUMINANCE16;
+ }
+
+ float yuv_range_correction = 1.0;
+ if (desc->plane_count == 3)
+ {
+ GLint internal = 0;
+ if (desc->pixel_size == 1)
+ internal = oneplane_texfmt;
+#if !defined(USE_OPENGL_ES2)
+ else if (desc->pixel_size == 2)
+ {
+ if (oneplane16_texfmt == 0
+ || GetTexFormatSize(tex_target, oneplane_texfmt, oneplane16_texfmt,
+ GL_UNSIGNED_SHORT) != 16)
+ return VLC_EGENERIC;
+
+ internal = oneplane16_texfmt;
+ yuv_range_correction = (float)((1 << 16) - 1)
+ / ((1 << desc->pixel_bits) - 1);
+ }
+#endif
+ else
+ return VLC_EGENERIC;
+
+ assert(internal != 0);
+
+ for (unsigned i = 0; i < desc->plane_count; ++i )
+ {
+ tc->texs[i] = (struct opengl_tex_cfg) {
+ internal, oneplane_texfmt, GL_UNSIGNED_BYTE
+ };
+ }
+
+ if (oneplane_texfmt == GL_RED)
+ swizzle_per_tex[0] = swizzle_per_tex[1] = swizzle_per_tex[2] = "r";
+ }
+ else
+ return VLC_EGENERIC;
+
+ /* [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
+ */
+ static 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 ,
+ };
+ static 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 (yuv_space)
+ {
+ case COLOR_SPACE_BT601:
+ matrix = matrix_bt601_tv2full;
+ break;
+ default:
+ matrix = matrix_bt709_tv2full;
+ };
+
+ 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++)
+ tc->yuv_coefficients[i*4+j] = j < 3 ? correction * matrix[j*4+i] : 0.f;
+ }
+
+ tc->chroma = chroma;
+ tc->yuv_color = true;
+ tc->desc = desc;
+
+ *swap_uv = chroma == VLC_CODEC_YV12 || chroma == VLC_CODEC_YV9;
+ return VLC_SUCCESS;
+}
+
+static int
+tc_rgba_base_init(opengl_tex_converter_t *tc, GLenum tex_target,
+ vlc_fourcc_t chroma)
+{
+ (void) tex_target;
+
+ if (chroma != VLC_CODEC_RGBA && chroma != VLC_CODEC_RGB32)
+ return VLC_EGENERIC;
+
+ tc->chroma = VLC_CODEC_RGBA;
+ tc->desc = vlc_fourcc_GetChromaDescription(chroma);
+ assert(tc->desc != NULL);
+
+ tc->texs[0] = (struct opengl_tex_cfg) {
+ GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE
+ };
+ return VLC_SUCCESS;
+}
+
+static int
+tc_base_fetch_locations(opengl_tex_converter_t *tc, GLuint program)
+{
+ if (tc->yuv_color)
+ {
+ tc->uloc.Coefficients = tc->api->GetUniformLocation(program,
+ "Coefficients");
+ if (tc->uloc.Coefficients == -1)
+ return VLC_EGENERIC;
+ }
+
+ for (unsigned int i = 0; i < tc->desc->plane_count; ++i)
+ {
+ char name[sizeof("TextureX")];
+ snprintf(name, sizeof(name), "Texture%1u", i);
+ tc->uloc.Texture[i] = tc->api->GetUniformLocation(program, name);
+ if (tc->uloc.Texture[i] == -1)
+ return VLC_EGENERIC;
+ }
+
+ tc->uloc.FillColor = tc->api->GetUniformLocation(program, "FillColor");
+ if (tc->uloc.FillColor == -1)
+ return VLC_EGENERIC;
+ return VLC_SUCCESS;
+}
+
+static void
+tc_base_prepare_shader(const opengl_tex_converter_t *tc,
+ const GLsizei *tex_width, const GLsizei *tex_height,
+ float alpha)
+{
+ (void) tex_width; (void) tex_height;
+
+ if (tc->yuv_color)
+ tc->api->Uniform4fv(tc->uloc.Coefficients, 4, tc->yuv_coefficients);
+
+ for (unsigned i = 0; i < tc->desc->plane_count; ++i)
+ tc->api->Uniform1i(tc->uloc.Texture[i], i);
+
+ tc->api->Uniform4f(tc->uloc.FillColor, 1.0f, 1.0f, 1.0f, alpha);
+}
+
+GLuint
+opengl_fragment_shader_init(opengl_tex_converter_t *tc, GLenum tex_target,
+ vlc_fourcc_t chroma, video_color_space_t yuv_space)
+{
+ const char *swizzle_per_tex[PICTURE_PLANE_MAX] = { NULL, };
+ const bool is_yuv = vlc_fourcc_IsYUV(chroma);
+ bool yuv_swap_uv = false;
+ int ret;
+ if (is_yuv)
+ ret = tc_yuv_base_init(tc, tex_target, chroma, yuv_space,
+ &yuv_swap_uv, swizzle_per_tex);
+ else
+ ret = tc_rgba_base_init(tc, tex_target, chroma);
+
+ if (ret != VLC_SUCCESS)
+ return 0;
+
+ const char *sampler, *lookup, *coord_name;
+ switch (tex_target)
+ {
+ case GL_TEXTURE_2D:
+ sampler = "sampler2D";
+ lookup = "texture2D";
+ coord_name = "TexCoord";
+ break;
+ default:
+ vlc_assert_unreachable();
+ }
+
+ struct vlc_memstream ms;
+ if (vlc_memstream_open(&ms) != 0)
+ return 0;
+
+#define ADD(x) vlc_memstream_puts(&ms, x)
+#define ADDF(x, ...) vlc_memstream_printf(&ms, x, ##__VA_ARGS__)
+
+ ADD("#version " GLSL_VERSION "\n" PRECISION);
+
+ for (unsigned i = 0; i < tc->desc->plane_count; ++i)
+ ADDF("uniform %s Texture%u;"
+ "varying vec2 TexCoord%u;", sampler, i, i);
+
+ if (is_yuv)
+ ADD("uniform vec4 Coefficients[4];");
+
+ ADD("uniform vec4 FillColor;"
+ "void main(void) {"
+ "float val;vec4 colors;");
+
+ unsigned color_idx = 0;
+ for (unsigned i = 0; i < tc->desc->plane_count; ++i)
+ {
+ const char *swizzle = swizzle_per_tex[i];
+ if (swizzle)
+ {
+ size_t swizzle_count = strlen(swizzle);
+ ADDF("colors = %s(Texture%u, %s%u);", lookup, i, coord_name, i);
+ for (unsigned j = 0; j < swizzle_count; ++j)
+ {
+ ADDF("val = colors.%c;"
+ "vec4 color%u = vec4(val, val, val, 1);",
+ swizzle[j], color_idx);
+ color_idx++;
+ assert(color_idx <= PICTURE_PLANE_MAX);
+ }
+ }
+ else
+ {
+ ADDF("vec4 color%u = %s(Texture%u, %s%u);",
+ color_idx, lookup, i, coord_name, i);
+ color_idx++;
+ assert(color_idx <= PICTURE_PLANE_MAX);
+ }
+ }
+ unsigned color_count = color_idx;
+ assert(yuv_space == COLOR_SPACE_UNDEF || color_count == 3);
+
+ if (is_yuv)
+ ADD("vec4 result = (color0 * Coefficients[0]) + Coefficients[3];");
+ else
+ ADD("vec4 result = color0;");
+
+ for (unsigned i = 1; i < color_count; ++i)
+ {
+ unsigned color_idx;
+ if (yuv_swap_uv)
+ {
+ assert(color_count == 3);
+ color_idx = (i % 2) + 1;
+ }
+ else
+ color_idx = i;
+
+ if (is_yuv)
+ ADDF("result = (color%u * Coefficients[%u]) + result;", color_idx, i);
+ else
+ ADDF("result = color%u + result;", color_idx);
+ }
+
+ ADD("gl_FragColor = result * FillColor;"
+ "}");
+
+#undef ADD
+#undef ADDF
+
+ if (vlc_memstream_close(&ms) != 0)
+ return 0;
+
+ GLuint fragment_shader = tc->api->CreateShader(GL_FRAGMENT_SHADER);
+ if (fragment_shader == 0)
+ {
+ free(ms.ptr);
+ return 0;
+ }
+ GLint length = ms.length;
+ tc->api->ShaderSource(fragment_shader, 1, (const char **)&ms.ptr, &length);
+ tc->api->CompileShader(fragment_shader);
+ free(ms.ptr);
+
+ tc->tex_target = tex_target;
+
+ tc->pf_fetch_locations = tc_base_fetch_locations;
+ tc->pf_prepare_shader = tc_base_prepare_shader;
+
+ return fragment_shader;
+}
+
#ifdef VLCGL_HAS_PBO
static int
pbo_map(const opengl_tex_converter_t *tc, picture_t *pic)
@@ -209,7 +488,7 @@ pbo_common_update(const opengl_tex_converter_t *tc, const GLuint *textures,
pic->p[i].i_pitch / pic->p[i].i_pixel_pitch);
glTexSubImage2D(tc->tex_target, 0, 0, 0, tex_width[i], tex_height[i],
- priv->tex_format, priv->tex_type, NULL);
+ tc->texs[i].format, tc->texs[i].type, NULL);
}
bool hold;
@@ -349,22 +628,22 @@ tc_common_allocate_texture(const opengl_tex_converter_t *tc, GLuint texture,
unsigned tex_idx, const GLsizei tex_width,
const GLsizei tex_height)
{
- (void) texture; (void) tex_idx;
- struct priv *priv = tc->priv;
+ (void) texture;
- glTexImage2D(tc->tex_target, 0, priv->tex_internal, tex_width, tex_height,
- 0, priv->tex_format, priv->tex_type, NULL);
+ glTexImage2D(tc->tex_target, 0, tc->texs[tex_idx].internal,
+ tex_width, tex_height, 0, tc->texs[tex_idx].format,
+ tc->texs[tex_idx].type, NULL);
return VLC_SUCCESS;
}
static int
-upload_plane(const opengl_tex_converter_t *tc,
+upload_plane(const opengl_tex_converter_t *tc, unsigned tex_idx,
GLsizei width, GLsizei height,
unsigned pitch, unsigned pixel_pitch, const void *pixels)
{
struct priv *priv = tc->priv;
- int tex_target = tc->tex_target, tex_format = priv->tex_format,
- tex_type = priv->tex_type;
+ GLenum tex_format = tc->texs[tex_idx].format;
+ GLenum tex_type = tc->texs[tex_idx].type;
/* This unpack alignment is the default, but setting it just in case. */
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
@@ -398,12 +677,12 @@ upload_plane(const opengl_tex_converter_t *tc,
source += pitch;
destination += dst_pitch;
}
- glTexSubImage2D(tex_target, 0, 0, 0, width, height,
+ glTexSubImage2D(tc->tex_target, 0, 0, 0, width, height,
tex_format, tex_type, priv->texture_temp_buf);
}
else
{
- glTexSubImage2D(tex_target, 0, 0, 0, width, height,
+ glTexSubImage2D(tc->tex_target, 0, 0, 0, width, height,
tex_format, tex_type, pixels);
}
#undef ALIGN
@@ -411,7 +690,7 @@ upload_plane(const opengl_tex_converter_t *tc,
else
{
glPixelStorei(GL_UNPACK_ROW_LENGTH, pitch / pixel_pitch);
- glTexSubImage2D(tex_target, 0, 0, 0, width, height,
+ glTexSubImage2D(tc->tex_target, 0, 0, 0, width, height,
tex_format, tex_type, pixels);
}
return VLC_SUCCESS;
@@ -438,7 +717,7 @@ tc_common_update(const opengl_tex_converter_t *tc, GLuint *textures,
&pic->p[i].p_pixels[plane_offset[i]] :
pic->p[i].p_pixels;
- ret = upload_plane(tc, tex_width[i], tex_height[i],
+ ret = upload_plane(tc, i, tex_width[i], tex_height[i],
pic->p[i].i_pitch, pic->p[i].i_pixel_pitch, pixels);
}
return ret;
@@ -458,17 +737,12 @@ tc_common_release(const opengl_tex_converter_t *tc)
}
static int
-common_init(opengl_tex_converter_t *tc, size_t priv_size, vlc_fourcc_t chroma,
- GLint tex_internal, GLenum tex_format, GLenum tex_type)
+common_init(opengl_tex_converter_t *tc)
{
- struct priv *priv = tc->priv = calloc(1, priv_size);
+ struct priv *priv = tc->priv = calloc(1, sizeof(struct priv));
if (unlikely(priv == NULL))
return VLC_ENOMEM;
- tc->chroma = chroma;
- tc->desc = vlc_fourcc_GetChromaDescription(chroma);
- assert(tc->desc != NULL);
-
tc->pf_update = tc_common_update;
tc->pf_release = tc_common_release;
tc->pf_allocate_texture = tc_common_allocate_texture;
@@ -482,15 +756,10 @@ common_init(opengl_tex_converter_t *tc, size_t priv_size, vlc_fourcc_t chroma,
&& HasExtension(tc->glexts, "GL_ARB_buffer_storage");
if (supports_pbo)
tc->pf_get_pool = tc_common_get_pool;
- msg_Dbg(tc->parent, "PBO support for %4.4s (direct rendering): %s",
- (const char *) &chroma, supports_pbo ? "On" : "Off");
+ msg_Dbg(tc->parent, "PBO support (direct rendering): %s",
+ supports_pbo ? "On" : "Off");
#endif
- tc->tex_target = GL_TEXTURE_2D;
- priv->tex_internal = tex_internal;
- priv->tex_format = tex_format;
- priv->tex_type = tex_type;
-
#ifdef NEED_GL_EXT_unpack_subimage
priv->has_unpack_subimage = HasExtension(tc->glexts,
"GL_EXT_unpack_subimage");
@@ -501,262 +770,65 @@ common_init(opengl_tex_converter_t *tc, size_t priv_size, vlc_fourcc_t chroma,
return VLC_SUCCESS;
}
-static int
-tc_rgba_fetch_locations(const opengl_tex_converter_t *tc, GLuint program)
-{
- struct priv *priv = tc->priv;
- priv->uloc.Texture0 = tc->api->GetUniformLocation(program, "Texture0");
- priv->uloc.FillColor = tc->api->GetUniformLocation(program, "FillColor");
- return priv->uloc.Texture0 != -1 && priv->uloc.FillColor != -1 ?
- VLC_SUCCESS : VLC_EGENERIC;
-}
-
-static void
-tc_rgba_prepare_shader(const opengl_tex_converter_t *tc,
- const GLsizei *tex_width, const GLsizei *tex_height,
- float alpha)
-{
- (void) tex_width; (void) tex_height;
- struct priv *priv = tc->priv;
- tc->api->Uniform1i(priv->uloc.Texture0, 0);
- tc->api->Uniform4f(priv->uloc.FillColor, 1.0f, 1.0f, 1.0f, alpha);
-}
-
GLuint
opengl_tex_converter_rgba_init(const video_format_t *fmt,
opengl_tex_converter_t *tc)
{
- if (fmt->i_chroma != VLC_CODEC_RGBA && fmt->i_chroma != VLC_CODEC_RGB32)
- return 0;
-
- if (common_init(tc, sizeof(struct priv), VLC_CODEC_RGBA,
- GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE) != VLC_SUCCESS)
+ GLuint fragment_shader =
+ opengl_fragment_shader_init(tc, GL_TEXTURE_2D, fmt->i_chroma,
+ COLOR_SPACE_UNDEF);
+ if (fragment_shader == 0)
return 0;
- tc->pf_fetch_locations = tc_rgba_fetch_locations;
- tc->pf_prepare_shader = tc_rgba_prepare_shader;
-
-#if 0
- /* Simple shader for RGB */
- static const char *code =
- "#version " GLSL_VERSION "\n"
- PRECISION
- "uniform sampler2D Texture[3];"
- "varying vec2 TexCoord0;"
- "void main()"
- "{ "
- " gl_FragColor = texture2D(Texture[0], TexCoord0);"
- "}";
-#endif
-
- /* Simple shader for RGBA */
- static const char *code =
- "#version " GLSL_VERSION "\n"
- PRECISION
- "uniform sampler2D Texture0;"
- "uniform vec4 FillColor;"
- "varying vec2 TexCoord0;"
- "void main()"
- "{ "
- " gl_FragColor = texture2D(Texture0, TexCoord0) * FillColor;"
- "}";
-
- GLuint fragment_shader = tc->api->CreateShader(GL_FRAGMENT_SHADER);
- if (fragment_shader == 0)
+ if (common_init(tc) != VLC_SUCCESS)
{
- tc_common_release(tc);
+ tc->api->DeleteShader(fragment_shader);
return 0;
}
- tc->api->ShaderSource(fragment_shader, 1, &code, NULL);
- tc->api->CompileShader(fragment_shader);
- return fragment_shader;
-}
-
-static int
-tc_yuv_fetch_locations(const opengl_tex_converter_t *tc, GLuint program)
-{
- struct priv *priv = tc->priv;
- priv->uloc.Coefficient = tc->api->GetUniformLocation(program, "Coefficient");
- priv->uloc.Texture0 = tc->api->GetUniformLocation(program, "Texture0");
- priv->uloc.Texture1 = tc->api->GetUniformLocation(program, "Texture1");
- priv->uloc.Texture2 = tc->api->GetUniformLocation(program, "Texture2");
- return priv->uloc.Coefficient != -1 && priv->uloc.Texture0 != -1
- && priv->uloc.Texture1 != -1 && priv->uloc.Texture2 != -1 ?
- VLC_SUCCESS : VLC_EGENERIC;
-}
-static void
-tc_yuv_prepare_shader(const opengl_tex_converter_t *tc,
- const GLsizei *tex_width, const GLsizei *tex_height,
- float alpha)
-{
- (void) tex_width; (void) tex_height; (void) alpha;
- struct priv *priv = tc->priv;
- tc->api->Uniform4fv(priv->uloc.Coefficient, 4,
- ((struct yuv_priv *)priv)->local_value);
- tc->api->Uniform1i(priv->uloc.Texture0, 0);
- tc->api->Uniform1i(priv->uloc.Texture1, 1);
- tc->api->Uniform1i(priv->uloc.Texture2, 2);
+ return fragment_shader;
}
GLuint
opengl_tex_converter_yuv_init(const video_format_t *fmt,
opengl_tex_converter_t *tc)
{
- if (!vlc_fourcc_IsYUV(fmt->i_chroma))
- return 0;
-
- GLint max_texture_units = 0;
- glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_units);
-
- if (max_texture_units < 3)
- return 0;
-
-#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
-
- float yuv_range_correction = 1.0;
- const vlc_fourcc_t *list = vlc_fourcc_GetYUVFallback(fmt->i_chroma);
- while (*list)
+ GLuint fragment_shader = 0;
+ if (vlc_fourcc_IsYUV(fmt->i_chroma))
{
- const vlc_chroma_description_t *dsc =
- vlc_fourcc_GetChromaDescription(*list);
- if (dsc && dsc->plane_count == 3 && dsc->pixel_size == 1)
- {
- if (common_init(tc, sizeof(struct yuv_priv), *list,
- yuv_plane_texformat, yuv_plane_texformat,
- GL_UNSIGNED_BYTE) != VLC_SUCCESS)
- return 0;
+ GLint max_texture_units = 0;
+ glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_units);
+ if (max_texture_units < 3)
+ return 0;
- 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)
+ const vlc_fourcc_t *list = vlc_fourcc_GetYUVFallback(fmt->i_chroma);
+ while (*list && fragment_shader == 0)
{
- if (common_init(tc, sizeof(struct yuv_priv), *list,
- yuv_plane_texformat_16, yuv_plane_texformat,
- GL_UNSIGNED_SHORT) != VLC_SUCCESS)
- return 0;
-
- yuv_range_correction = (float)((1 << 16) - 1)
- / ((1 << dsc->pixel_bits) - 1);
- break;
-#endif
+ fragment_shader =
+ opengl_fragment_shader_init(tc, GL_TEXTURE_2D, *list,
+ fmt->space);
+ list++;
}
- list++;
+ if (fragment_shader == 0)
+ return 0;
}
- if (!*list)
- return 0;
-
- tc->pf_fetch_locations = tc_yuv_fetch_locations;
- tc->pf_prepare_shader = tc_yuv_prepare_shader;
-
- GLfloat *local_value = ((struct yuv_priv*) tc->priv)->local_value;
-
- /* [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
- */
- static 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 ,
- };
- static 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 vec2 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).x;"
- " float val1 = texture2D(Texture1, TexCoord1).x;"
- " float val2 = texture2D(Texture2, TexCoord2).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)
- {
- tc_common_release(tc);
+ else
return 0;
- }
- 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;
- }
-
- GLuint fragment_shader = tc->api->CreateShader(GL_FRAGMENT_SHADER);
- if (fragment_shader == 0)
+ if (common_init(tc) != VLC_SUCCESS)
{
- tc_common_release(tc);
- free(code);
+ tc->api->DeleteShader(fragment_shader);
return 0;
}
- tc->api->ShaderSource(fragment_shader, 1, (const char **)&code, NULL);
- tc->api->CompileShader(fragment_shader);
- free(code);
return fragment_shader;
}
static int
-tc_xyz12_fetch_locations(const opengl_tex_converter_t *tc, GLuint program)
+tc_xyz12_fetch_locations(opengl_tex_converter_t *tc, GLuint program)
{
- struct priv *priv = tc->priv;
- priv->uloc.Texture0 = tc->api->GetUniformLocation(program, "Texture0");
- return priv->uloc.Texture0 != -1 ? VLC_SUCCESS : VLC_EGENERIC;
+ tc->uloc.Texture[0] = tc->api->GetUniformLocation(program, "Texture0");
+ return tc->uloc.Texture[0] != -1 ? VLC_SUCCESS : VLC_EGENERIC;
}
static void
@@ -765,8 +837,7 @@ tc_xyz12_prepare_shader(const opengl_tex_converter_t *tc,
float alpha)
{
(void) tex_width; (void) tex_height; (void) alpha;
- struct priv *priv = tc->priv;
- tc->api->Uniform1i(priv->uloc.Texture0, 0);
+ tc->api->Uniform1i(tc->uloc.Texture[0], 0);
}
GLuint
@@ -776,13 +847,20 @@ opengl_tex_converter_xyz12_init(const video_format_t *fmt,
if (fmt->i_chroma != VLC_CODEC_XYZ12)
return 0;
- if (common_init(tc, sizeof(struct priv), VLC_CODEC_XYZ12,
- GL_RGB, GL_RGB, GL_UNSIGNED_SHORT) != VLC_SUCCESS)
- return 0;
+ tc->chroma = VLC_CODEC_XYZ12;
+ tc->desc = vlc_fourcc_GetChromaDescription(tc->chroma);
+ assert(tc->desc != NULL);
+ tc->tex_target = GL_TEXTURE_2D;
+ tc->texs[0] = (struct opengl_tex_cfg) {
+ GL_RGB, GL_RGB, GL_UNSIGNED_SHORT
+ };
tc->pf_fetch_locations = tc_xyz12_fetch_locations;
tc->pf_prepare_shader = tc_xyz12_prepare_shader;
+ if (common_init(tc) != VLC_SUCCESS)
+ return 0;
+
/* Shader for XYZ to RGB correction
* 3 steps :
* - XYZ gamma correction
diff --git a/modules/video_output/opengl/internal.h b/modules/video_output/opengl/internal.h
index a11d5c9..237322a 100644
--- a/modules/video_output/opengl/internal.h
+++ b/modules/video_output/opengl/internal.h
@@ -170,9 +170,28 @@ struct opengl_tex_converter_t
/* Description of the chroma, cannot be NULL */
const vlc_chroma_description_t *desc;
+
/* Texture mapping (usually: GL_TEXTURE_2D), cannot be 0 */
GLenum tex_target;
+ struct opengl_tex_cfg {
+ /* The following is used and filled by the opengl_fragment_shader_init
+ * function. */
+ GLint internal;
+ GLenum format;
+ GLenum type;
+ } texs[PICTURE_PLANE_MAX];
+
+ /* The following is used and filled by the opengl_fragment_shader_init
+ * function. */
+ struct {
+ GLint Texture[PICTURE_PLANE_MAX];
+ GLint Coefficients;
+ GLint FillColor;
+ } uloc;
+ bool yuv_color;
+ GLfloat yuv_coefficients[16];
+
/* Private context */
void *priv;
@@ -240,7 +259,7 @@ struct opengl_tex_converter_t
* \param program linked program that will be used by this tex converter
* \return VLC_SUCCESS or a VLC error
*/
- int (*pf_fetch_locations)(const opengl_tex_converter_t *fc, GLuint program);
+ int (*pf_fetch_locations)(opengl_tex_converter_t *fc, GLuint program);
/*
* Callback to prepare the fragment shader
@@ -266,6 +285,26 @@ struct opengl_tex_converter_t
void (*pf_release)(const opengl_tex_converter_t *fc);
};
+/*
+ * Generate a fragment shader
+ *
+ * This utility function can be used by hw opengl tex converters that need a
+ * generic fragment shader. It will compile a fragment shader generated from
+ * the chroma and the tex target. This will initialize all elements of the
+ * opengl_tex_converter_t struct except for priv, pf_allocate_texture,
+ * pf_get_pool, pf_update, and pf_release.
+ *
+ * \param tc OpenGL tex converter
+ * \param tex_target GL_TEXTURE_2D
+ * \param chroma chroma used to generate the fragment shader
+ * \param if not COLOR_SPACE_UNDEF, YUV planes will be converted to RGB
+ * according to the color space
+ * \return the compiled fragment shader or 0 in case of error
+ */
+GLuint
+opengl_fragment_shader_init(opengl_tex_converter_t *tc, GLenum tex_target,
+ vlc_fourcc_t chroma, video_color_space_t yuv_space);
+
extern GLuint
opengl_tex_converter_rgba_init(const video_format_t *,
opengl_tex_converter_t *);
More information about the vlc-commits
mailing list