[vlc-devel] [PATCH 02/10] opengl: merge fragment_shaders.c into sampler.c
Romain Vimont
rom1v at videolabs.io
Thu Jun 4 11:52:10 CEST 2020
The code in fragment_shaders.c is part of the implementation of the
sampler: it generates the fragment shader to expose a GLSL function
"vlc_texture(vec2 coords)".
---
modules/video_output/Makefile.am | 2 +-
.../video_output/opengl/fragment_shaders.c | 799 ------------------
modules/video_output/opengl/internal.h | 4 -
modules/video_output/opengl/sampler.c | 759 +++++++++++++++++
4 files changed, 760 insertions(+), 804 deletions(-)
delete mode 100644 modules/video_output/opengl/fragment_shaders.c
diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am
index aa1be4f57c52..36ad308081f3 100644
--- a/modules/video_output/Makefile.am
+++ b/modules/video_output/Makefile.am
@@ -11,7 +11,7 @@ OPENGL_COMMONSOURCES = video_output/opengl/vout_helper.c \
video_output/opengl/gl_util.h \
video_output/opengl/interop.h \
video_output/opengl/vout_helper.h \
- video_output/opengl/internal.h video_output/opengl/fragment_shaders.c \
+ video_output/opengl/internal.h \
video_output/opengl/interop.c video_output/opengl/interop_sw.c \
video_output/opengl/renderer.c \
video_output/opengl/renderer.h \
diff --git a/modules/video_output/opengl/fragment_shaders.c b/modules/video_output/opengl/fragment_shaders.c
deleted file mode 100644
index eab265dce7d6..000000000000
--- a/modules/video_output/opengl/fragment_shaders.c
+++ /dev/null
@@ -1,799 +0,0 @@
-/*****************************************************************************
- * fragment_shaders.c: OpenGL fragment shaders
- *****************************************************************************
- * Copyright (C) 2016,2017 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 <assert.h>
-#include <stdlib.h>
-
-#ifdef HAVE_LIBPLACEBO
-#include <libplacebo/shaders.h>
-#include <libplacebo/shaders/colorspace.h>
-#include "../placebo_utils.h"
-#endif
-
-#include <vlc_common.h>
-#include <vlc_memstream.h>
-
-#include "gl_util.h"
-#include "interop.h"
-#include "internal.h"
-#include "sampler.h"
-#include "vout_helper.h"
-
-static const float MATRIX_COLOR_RANGE_LIMITED[4*3] = {
- 255.0/219, 0, 0, -255.0/219 * 16.0/255,
- 0, 255.0/224, 0, -255.0/224 * 128.0/255,
- 0, 0, 255.0/224, -255.0/224 * 128.0/255,
-};
-
-static const float MATRIX_COLOR_RANGE_FULL[4*3] = {
- 1, 0, 0, 0,
- 0, 1, 0, -128.0/255,
- 0, 0, 1, -128.0/255,
-};
-
-/*
- * Construct the transformation matrix from the luma weight of the red and blue
- * component (the green component is deduced).
- */
-#define MATRIX_YUV_TO_RGB(KR, KB) \
- MATRIX_YUV_TO_RGB_(KR, (1-(KR)-(KB)), KB)
-
-/*
- * Construct the transformation matrix from the luma weight of the RGB
- * components.
- *
- * KR: luma weight of the red component
- * KG: luma weight of the green component
- * KB: luma weight of the blue component
- *
- * By definition, KR + KG + KB == 1.
- *
- * Ref: <https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion>
- * Ref: libplacebo: src/colorspace.c:luma_coeffs()
- * */
-#define MATRIX_YUV_TO_RGB_(KR, KG, KB) { \
- 1, 0, 2*(1.0-(KR)), \
- 1, -2*(1.0-(KB))*((KB)/(KG)), -2*(1.0-(KR))*((KR)/(KG)), \
- 1, 2*(1.0-(KB)), 0, \
-}
-
-static const float MATRIX_BT601[3*3] = MATRIX_YUV_TO_RGB(0.299, 0.114);
-static const float MATRIX_BT709[3*3] = MATRIX_YUV_TO_RGB(0.2126, 0.0722);
-static const float MATRIX_BT2020[3*3] = MATRIX_YUV_TO_RGB(0.2627, 0.0593);
-
-static void
-init_conv_matrix(float conv_matrix_out[],
- video_color_space_t color_space,
- video_color_range_t color_range)
-{
- const float *space_matrix;
- switch (color_space) {
- case COLOR_SPACE_BT601:
- space_matrix = MATRIX_BT601;
- break;
- case COLOR_SPACE_BT2020:
- space_matrix = MATRIX_BT2020;
- break;
- default:
- space_matrix = MATRIX_BT709;
- }
-
- /* Init the conversion matrix in column-major order (OpenGL expects
- * column-major order by default, and OpenGL ES does not support row-major
- * order at all). */
-
- const float *range_matrix = color_range == COLOR_RANGE_FULL
- ? MATRIX_COLOR_RANGE_FULL
- : MATRIX_COLOR_RANGE_LIMITED;
- /* Multiply the matrices on CPU once for all */
- for (int x = 0; x < 4; ++x)
- {
- for (int y = 0; y < 3; ++y)
- {
- /* Perform intermediate computation in double precision even if the
- * result is in single-precision, to avoid unnecessary errors. */
- double sum = 0;
- for (int k = 0; k < 3; ++k)
- sum += space_matrix[y * 3 + k] * range_matrix[k * 4 + x];
- /* Notice the reversed indices: x is now the row, y is the
- * column. */
- conv_matrix_out[x * 4 + y] = sum;
- }
- }
-
- /* Add a row to fill a 4x4 matrix (remember it's in column-major order).
- * (non-square matrices are not supported on old OpenGL ES versions) */
- conv_matrix_out[3] = 0;
- conv_matrix_out[7] = 0;
- conv_matrix_out[11] = 0;
- conv_matrix_out[15] = 1;
-}
-
-static int
-sampler_yuv_base_init(struct vlc_gl_sampler *sampler, vlc_fourcc_t chroma,
- const vlc_chroma_description_t *desc,
- video_color_space_t yuv_space)
-{
- /* The current implementation always converts from limited to full range. */
- const video_color_range_t range = COLOR_RANGE_LIMITED;
- float *matrix = sampler->conv_matrix;
- init_conv_matrix(matrix, yuv_space, range);
-
- if (desc->pixel_size == 2)
- {
- if (chroma != VLC_CODEC_P010 && chroma != VLC_CODEC_P016) {
- /* Do a bit shift if samples are stored on LSB. */
- float yuv_range_correction = (float)((1 << 16) - 1)
- / ((1 << desc->pixel_bits) - 1);
- /* We want to transform the input color (y, u, v, 1) to
- * (r*y, r*u, r*v, 1), where r = yuv_range_correction.
- *
- * This can be done by left-multiplying the color vector by a
- * matrix R:
- *
- * R
- * / r*y \ / r 0 0 0 \ / y \
- * | r*u | = | 0 r 0 0 | * | u |
- * | r*v | | 0 0 r 0 | | v |
- * \ 1 / \ 0 0 0 1 / \ 1 /
- *
- * Combine this transformation with the color conversion matrix:
- *
- * matrix := matrix * R
- *
- * This is equivalent to multipying the 3 first rows by r
- * (yuv_range_conversion).
- */
- for (int i = 0; i < 4*3; ++i)
- matrix[i] *= yuv_range_correction;
- }
- }
-
- sampler->yuv_color = true;
-
- /* Some formats require to swap the U and V components.
- *
- * This can be done by left-multiplying the color vector by a matrix S:
- *
- * S
- * / y \ / 1 0 0 0 \ / y \
- * | v | = | 0 0 1 0 | * | u |
- * | u | | 0 1 0 0 | | v |
- * \ 1 / \ 0 0 0 1 / \ 1 /
- *
- * Combine this transformation with the color conversion matrix:
- *
- * matrix := matrix * S
- *
- * This is equivalent to swap columns 1 and 2.
- */
- bool swap_uv = chroma == VLC_CODEC_YV12 || chroma == VLC_CODEC_YV9 ||
- chroma == VLC_CODEC_NV21;
- if (swap_uv)
- {
- /* Remember, the matrix in column-major order */
- float tmp[4];
- /* tmp <- column1 */
- memcpy(tmp, matrix + 4, sizeof(tmp));
- /* column1 <- column2 */
- memcpy(matrix + 4, matrix + 8, sizeof(tmp));
- /* column2 <- tmp */
- memcpy(matrix + 8, tmp, sizeof(tmp));
- }
- return VLC_SUCCESS;
-}
-
-static int
-sampler_base_fetch_locations(struct vlc_gl_sampler *sampler, GLuint program)
-{
- struct vlc_gl_interop *interop = sampler->interop;
- const opengl_vtable_t *vt = sampler->vt;
-
- if (sampler->yuv_color)
- {
- sampler->uloc.ConvMatrix = vt->GetUniformLocation(program,
- "ConvMatrix");
- if (sampler->uloc.ConvMatrix == -1)
- return VLC_EGENERIC;
- }
-
- sampler->uloc.TransformMatrix =
- vt->GetUniformLocation(program, "TransformMatrix");
- if (sampler->uloc.TransformMatrix == -1)
- return VLC_EGENERIC;
-
- sampler->uloc.OrientationMatrix =
- vt->GetUniformLocation(program, "OrientationMatrix");
- if (sampler->uloc.OrientationMatrix == -1)
- return VLC_EGENERIC;
-
- assert(interop->tex_count < 10); /* to guarantee variable names length */
- for (unsigned int i = 0; i < interop->tex_count; ++i)
- {
- char name[sizeof("TexCoordsMapX")];
-
- snprintf(name, sizeof(name), "Texture%1u", i);
- sampler->uloc.Texture[i] = vt->GetUniformLocation(program, name);
- if (sampler->uloc.Texture[i] == -1)
- return VLC_EGENERIC;
-
- snprintf(name, sizeof(name), "TexCoordsMap%1u", i);
- sampler->uloc.TexCoordsMap[i] = vt->GetUniformLocation(program, name);
- if (sampler->uloc.TexCoordsMap[i] == -1)
- return VLC_EGENERIC;
-
- if (interop->tex_target == GL_TEXTURE_RECTANGLE)
- {
- snprintf(name, sizeof(name), "TexSize%1u", i);
- sampler->uloc.TexSize[i] = vt->GetUniformLocation(program, name);
- if (sampler->uloc.TexSize[i] == -1)
- return VLC_EGENERIC;
- }
- }
-
-#ifdef HAVE_LIBPLACEBO
- const struct pl_shader_res *res = sampler->pl_sh_res;
- for (int i = 0; res && i < res->num_variables; i++) {
- struct pl_shader_var sv = res->variables[i];
- sampler->uloc.pl_vars[i] = vt->GetUniformLocation(program, sv.var.name);
- }
-#endif
-
- return VLC_SUCCESS;
-}
-
-static const GLfloat *
-GetTransformMatrix(const struct vlc_gl_interop *interop)
-{
- const GLfloat *tm = NULL;
- if (interop->ops && interop->ops->get_transform_matrix)
- tm = interop->ops->get_transform_matrix(interop);
- if (!tm)
- tm = MATRIX4_IDENTITY;
- return tm;
-}
-
-static void
-sampler_base_prepare_shader(const struct vlc_gl_sampler *sampler)
-{
- const struct vlc_gl_interop *interop = sampler->interop;
- const opengl_vtable_t *vt = sampler->vt;
-
- if (sampler->yuv_color)
- vt->UniformMatrix4fv(sampler->uloc.ConvMatrix, 1, GL_FALSE,
- sampler->conv_matrix);
-
- for (unsigned i = 0; i < interop->tex_count; ++i)
- {
- vt->Uniform1i(sampler->uloc.Texture[i], i);
-
- assert(sampler->textures[i] != 0);
- vt->ActiveTexture(GL_TEXTURE0 + i);
- vt->BindTexture(interop->tex_target, sampler->textures[i]);
-
- vt->UniformMatrix3fv(sampler->uloc.TexCoordsMap[i], 1, GL_FALSE,
- sampler->var.TexCoordsMap[i]);
- }
-
- const GLfloat *tm = GetTransformMatrix(interop);
- vt->UniformMatrix4fv(sampler->uloc.TransformMatrix, 1, GL_FALSE, tm);
-
- vt->UniformMatrix4fv(sampler->uloc.OrientationMatrix, 1, GL_FALSE,
- sampler->var.OrientationMatrix);
-
- if (interop->tex_target == GL_TEXTURE_RECTANGLE)
- {
- for (unsigned i = 0; i < interop->tex_count; ++i)
- vt->Uniform2f(sampler->uloc.TexSize[i], sampler->tex_width[i],
- sampler->tex_height[i]);
- }
-
-#ifdef HAVE_LIBPLACEBO
- const struct pl_shader_res *res = sampler->pl_sh_res;
- for (int i = 0; res && i < res->num_variables; i++) {
- GLint loc = sampler->uloc.pl_vars[i];
- if (loc == -1) // uniform optimized out
- continue;
-
- struct pl_shader_var sv = res->variables[i];
- struct pl_var var = sv.var;
- // libplacebo doesn't need anything else anyway
- if (var.type != PL_VAR_FLOAT)
- continue;
- if (var.dim_m > 1 && var.dim_m != var.dim_v)
- continue;
-
- const float *f = sv.data;
- switch (var.dim_m) {
- case 4: vt->UniformMatrix4fv(loc, 1, GL_FALSE, f); break;
- case 3: vt->UniformMatrix3fv(loc, 1, GL_FALSE, f); break;
- case 2: vt->UniformMatrix2fv(loc, 1, GL_FALSE, f); break;
-
- case 1:
- switch (var.dim_v) {
- case 1: vt->Uniform1f(loc, f[0]); break;
- case 2: vt->Uniform2f(loc, f[0], f[1]); break;
- case 3: vt->Uniform3f(loc, f[0], f[1], f[2]); break;
- case 4: vt->Uniform4f(loc, f[0], f[1], f[2], f[3]); break;
- }
- break;
- }
- }
-#endif
-}
-
-static int
-sampler_xyz12_fetch_locations(struct vlc_gl_sampler *sampler, GLuint program)
-{
- const opengl_vtable_t *vt = sampler->vt;
-
- sampler->uloc.Texture[0] = vt->GetUniformLocation(program, "Texture0");
- if (sampler->uloc.Texture[0] == -1)
- return VLC_EGENERIC;
-
- sampler->uloc.TransformMatrix =
- vt->GetUniformLocation(program, "TransformMatrix");
- if (sampler->uloc.TransformMatrix == -1)
- return VLC_EGENERIC;
-
- sampler->uloc.OrientationMatrix =
- vt->GetUniformLocation(program, "OrientationMatrix");
- if (sampler->uloc.OrientationMatrix == -1)
- return VLC_EGENERIC;
-
- sampler->uloc.TexCoordsMap[0] =
- vt->GetUniformLocation(program, "TexCoordsMap0");
- if (sampler->uloc.TexCoordsMap[0] == -1)
- return VLC_EGENERIC;
-
- return VLC_SUCCESS;
-}
-
-static void
-sampler_xyz12_prepare_shader(const struct vlc_gl_sampler *sampler)
-{
- const struct vlc_gl_interop *interop = sampler->interop;
- const opengl_vtable_t *vt = sampler->vt;
-
- vt->Uniform1i(sampler->uloc.Texture[0], 0);
-
- assert(sampler->textures[0] != 0);
- vt->ActiveTexture(GL_TEXTURE0);
- vt->BindTexture(interop->tex_target, sampler->textures[0]);
-
- vt->UniformMatrix3fv(sampler->uloc.TexCoordsMap[0], 1, GL_FALSE,
- sampler->var.TexCoordsMap[0]);
-
- const GLfloat *tm = GetTransformMatrix(interop);
- vt->UniformMatrix4fv(sampler->uloc.TransformMatrix, 1, GL_FALSE, tm);
-
- vt->UniformMatrix4fv(sampler->uloc.OrientationMatrix, 1, GL_FALSE,
- sampler->var.OrientationMatrix);
-}
-
-static int
-xyz12_shader_init(struct vlc_gl_sampler *sampler)
-{
- sampler->pf_fetch_locations = sampler_xyz12_fetch_locations;
- sampler->pf_prepare_shader = sampler_xyz12_prepare_shader;
-
- /* Shader for XYZ to RGB correction
- * 3 steps :
- * - XYZ gamma correction
- * - XYZ to RGB matrix conversion
- * - reverse RGB gamma correction
- */
- static const char *template =
- "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 "
- " );"
-
- "uniform mat4 TransformMatrix;\n"
- "uniform mat4 OrientationMatrix;\n"
- "uniform mat3 TexCoordsMap0;\n"
- "vec4 vlc_texture(vec2 pic_coords)\n"
- "{ "
- " vec4 v_in, v_out;"
- /* Homogeneous (oriented) coordinates */
- " vec3 pic_hcoords = vec3((TransformMatrix * OrientationMatrix * vec4(pic_coords, 0.0, 1.0)).st, 1.0);\n"
- " vec2 tex_coords = (TexCoordsMap0 * pic_hcoords).st;\n"
- " v_in = texture2D(Texture0, tex_coords);\n"
- " 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) ;"
- " return v_out;"
- "}\n";
-
- sampler->shader.body = strdup(template);
- if (!sampler->shader.body)
- return VLC_ENOMEM;
-
- return VLC_SUCCESS;
-}
-
-static int
-opengl_init_swizzle(const struct vlc_gl_interop *interop,
- const char *swizzle_per_tex[],
- vlc_fourcc_t chroma,
- const vlc_chroma_description_t *desc)
-{
- GLint oneplane_texfmt;
- if (vlc_gl_StrHasToken(interop->api->extensions, "GL_ARB_texture_rg"))
- oneplane_texfmt = GL_RED;
- else
- oneplane_texfmt = GL_LUMINANCE;
-
- if (desc->plane_count == 3)
- swizzle_per_tex[0] = swizzle_per_tex[1] = swizzle_per_tex[2] = "r";
- else if (desc->plane_count == 2)
- {
- if (oneplane_texfmt == GL_RED)
- {
- swizzle_per_tex[0] = "r";
- swizzle_per_tex[1] = "rg";
- }
- else
- {
- swizzle_per_tex[0] = "x";
- swizzle_per_tex[1] = "xa";
- }
- }
- else if (desc->plane_count == 1)
- {
- /*
- * Set swizzling in Y1 U V order
- * R G B A
- * U Y1 V Y2 => GRB
- * Y1 U Y2 V => RGA
- * V Y1 U Y2 => GBR
- * Y1 V Y2 U => RAG
- */
- switch (chroma)
- {
- case VLC_CODEC_UYVY:
- swizzle_per_tex[0] = "grb";
- break;
- case VLC_CODEC_YUYV:
- swizzle_per_tex[0] = "rga";
- break;
- case VLC_CODEC_VYUY:
- swizzle_per_tex[0] = "gbr";
- break;
- case VLC_CODEC_YVYU:
- swizzle_per_tex[0] = "rag";
- break;
- default:
- assert(!"missing chroma");
- return VLC_EGENERIC;
- }
- }
- return VLC_SUCCESS;
-}
-
-static void
-InitOrientationMatrix(GLfloat matrix[static 4*4],
- video_orientation_t orientation)
-{
- memcpy(matrix, MATRIX4_IDENTITY, sizeof(MATRIX4_IDENTITY));
-
- const int k_cos_pi = -1;
- const int k_cos_pi_2 = 0;
- const int k_cos_n_pi_2 = 0;
-
- const int k_sin_pi = 0;
- const int k_sin_pi_2 = 1;
- const int k_sin_n_pi_2 = -1;
-
- switch (orientation) {
-
- case ORIENT_ROTATED_90:
- matrix[0 * 4 + 0] = k_cos_pi_2;
- matrix[0 * 4 + 1] = -k_sin_pi_2;
- matrix[1 * 4 + 0] = k_sin_pi_2;
- matrix[1 * 4 + 1] = k_cos_pi_2;
- matrix[3 * 4 + 1] = 1;
- break;
- case ORIENT_ROTATED_180:
- matrix[0 * 4 + 0] = k_cos_pi;
- matrix[0 * 4 + 1] = -k_sin_pi;
- matrix[1 * 4 + 0] = k_sin_pi;
- matrix[1 * 4 + 1] = k_cos_pi;
- matrix[3 * 4 + 0] = 1;
- matrix[3 * 4 + 1] = 1;
- break;
- case ORIENT_ROTATED_270:
- matrix[0 * 4 + 0] = k_cos_n_pi_2;
- matrix[0 * 4 + 1] = -k_sin_n_pi_2;
- matrix[1 * 4 + 0] = k_sin_n_pi_2;
- matrix[1 * 4 + 1] = k_cos_n_pi_2;
- matrix[3 * 4 + 0] = 1;
- break;
- case ORIENT_HFLIPPED:
- matrix[0 * 4 + 0] = -1;
- matrix[3 * 4 + 0] = 1;
- break;
- case ORIENT_VFLIPPED:
- matrix[1 * 4 + 1] = -1;
- matrix[3 * 4 + 1] = 1;
- break;
- case ORIENT_TRANSPOSED:
- matrix[0 * 4 + 0] = 0;
- matrix[1 * 4 + 1] = 0;
- matrix[2 * 4 + 2] = -1;
- matrix[0 * 4 + 1] = 1;
- matrix[1 * 4 + 0] = 1;
- break;
- case ORIENT_ANTI_TRANSPOSED:
- matrix[0 * 4 + 0] = 0;
- matrix[1 * 4 + 1] = 0;
- matrix[2 * 4 + 2] = -1;
- matrix[0 * 4 + 1] = -1;
- matrix[1 * 4 + 0] = -1;
- matrix[3 * 4 + 0] = 1;
- matrix[3 * 4 + 1] = 1;
- break;
- default:
- break;
- }
-}
-
-int
-opengl_fragment_shader_init(struct vlc_gl_sampler *sampler, GLenum tex_target,
- vlc_fourcc_t chroma, video_color_space_t yuv_space,
- video_orientation_t orientation)
-{
- struct vlc_gl_interop *interop = sampler->interop;
-
- const char *swizzle_per_tex[PICTURE_PLANE_MAX] = { NULL, };
- const bool is_yuv = vlc_fourcc_IsYUV(chroma);
- int ret;
-
- const vlc_chroma_description_t *desc = vlc_fourcc_GetChromaDescription(chroma);
- if (desc == NULL)
- return VLC_EGENERIC;
-
- InitOrientationMatrix(sampler->var.OrientationMatrix, orientation);
-
- if (chroma == VLC_CODEC_XYZ12)
- return xyz12_shader_init(sampler);
-
- if (is_yuv)
- {
- ret = sampler_yuv_base_init(sampler, chroma, desc, yuv_space);
- if (ret != VLC_SUCCESS)
- return ret;
- ret = opengl_init_swizzle(interop, swizzle_per_tex, chroma, desc);
- if (ret != VLC_SUCCESS)
- return ret;
- }
-
- const char *glsl_sampler, *lookup;
- switch (tex_target)
- {
- case GL_TEXTURE_EXTERNAL_OES:
- glsl_sampler = "samplerExternalOES";
- lookup = "texture2D";
- break;
- case GL_TEXTURE_2D:
- glsl_sampler = "sampler2D";
- lookup = "texture2D";
- break;
- case GL_TEXTURE_RECTANGLE:
- glsl_sampler = "sampler2DRect";
- lookup = "texture2DRect";
- break;
- default:
- vlc_assert_unreachable();
- }
-
- struct vlc_memstream ms;
- if (vlc_memstream_open(&ms) != 0)
- return VLC_EGENERIC;
-
-#define ADD(x) vlc_memstream_puts(&ms, x)
-#define ADDF(x, ...) vlc_memstream_printf(&ms, x, ##__VA_ARGS__)
-
- ADD("uniform mat4 TransformMatrix;\n"
- "uniform mat4 OrientationMatrix;\n");
- for (unsigned i = 0; i < interop->tex_count; ++i)
- ADDF("uniform %s Texture%u;\n"
- "uniform mat3 TexCoordsMap%u;\n", glsl_sampler, i, i);
-
-#ifdef HAVE_LIBPLACEBO
- if (sampler->pl_sh) {
- struct pl_shader *sh = sampler->pl_sh;
- struct pl_color_map_params color_params = pl_color_map_default_params;
- color_params.intent = var_InheritInteger(sampler->gl, "rendering-intent");
- color_params.tone_mapping_algo = var_InheritInteger(sampler->gl, "tone-mapping");
- color_params.tone_mapping_param = var_InheritFloat(sampler->gl, "tone-mapping-param");
-# if PL_API_VER >= 10
- color_params.desaturation_strength = var_InheritFloat(sampler->gl, "desat-strength");
- color_params.desaturation_exponent = var_InheritFloat(sampler->gl, "desat-exponent");
- color_params.desaturation_base = var_InheritFloat(sampler->gl, "desat-base");
-# else
- color_params.tone_mapping_desaturate = var_InheritFloat(sampler->gl, "tone-mapping-desat");
-# endif
- color_params.gamut_warning = var_InheritBool(sampler->gl, "tone-mapping-warn");
-
- struct pl_color_space dst_space = pl_color_space_unknown;
- dst_space.primaries = var_InheritInteger(sampler->gl, "target-prim");
- dst_space.transfer = var_InheritInteger(sampler->gl, "target-trc");
-
- pl_shader_color_map(sh, &color_params,
- vlc_placebo_ColorSpace(&interop->fmt),
- dst_space, NULL, false);
-
- struct pl_shader_obj *dither_state = NULL;
- int method = var_InheritInteger(sampler->gl, "dither-algo");
- if (method >= 0) {
-
- unsigned out_bits = 0;
- int override = var_InheritInteger(sampler->gl, "dither-depth");
- if (override > 0)
- out_bits = override;
- else
- {
- GLint fb_depth = 0;
-#if !defined(USE_OPENGL_ES2)
- const opengl_vtable_t *vt = sampler->vt;
- /* fetch framebuffer depth (we are already bound to the default one). */
- if (vt->GetFramebufferAttachmentParameteriv != NULL)
- vt->GetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_BACK_LEFT,
- GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE,
- &fb_depth);
-#endif
- if (fb_depth <= 0)
- fb_depth = 8;
- out_bits = fb_depth;
- }
-
- pl_shader_dither(sh, out_bits, &dither_state, &(struct pl_dither_params) {
- .method = method,
- .lut_size = 4, // avoid too large values, since this gets embedded
- });
- }
-
- const struct pl_shader_res *res = sampler->pl_sh_res = pl_shader_finalize(sh);
- pl_shader_obj_destroy(&dither_state);
-
- FREENULL(sampler->uloc.pl_vars);
- sampler->uloc.pl_vars = calloc(res->num_variables, sizeof(GLint));
- for (int i = 0; i < res->num_variables; i++) {
- struct pl_shader_var sv = res->variables[i];
- const char *glsl_type_name = pl_var_glsl_type_name(sv.var);
- ADDF("uniform %s %s;\n", glsl_type_name, sv.var.name);
- }
-
- // We can't handle these yet, but nothing we use requires them, either
- assert(res->num_vertex_attribs == 0);
- assert(res->num_descriptors == 0);
-
- ADD(res->glsl);
- }
-#else
- if (interop->fmt.transfer == TRANSFER_FUNC_SMPTE_ST2084 ||
- interop->fmt.primaries == COLOR_PRIMARIES_BT2020)
- {
- // no warning for HLG because it's more or less backwards-compatible
- msg_Warn(sampler->gl, "VLC needs to be built with support for libplacebo "
- "in order to display wide gamut or HDR signals correctly.");
- }
-#endif
-
- if (tex_target == GL_TEXTURE_RECTANGLE)
- {
- for (unsigned i = 0; i < interop->tex_count; ++i)
- ADDF("uniform vec2 TexSize%u;\n", i);
- }
-
- if (is_yuv)
- ADD("uniform mat4 ConvMatrix;\n");
-
- ADD("vec4 vlc_texture(vec2 pic_coords) {\n"
- /* Homogeneous (oriented) coordinates */
- " vec3 pic_hcoords = vec3((TransformMatrix * OrientationMatrix * vec4(pic_coords, 0.0, 1.0)).st, 1.0);\n"
- " vec2 tex_coords;\n");
-
- unsigned color_count;
- if (is_yuv) {
- ADD(" vec4 texel;\n"
- " vec4 pixel = vec4(0.0, 0.0, 0.0, 1.0);\n");
- unsigned color_idx = 0;
- for (unsigned i = 0; i < interop->tex_count; ++i)
- {
- const char *swizzle = swizzle_per_tex[i];
- assert(swizzle);
- size_t swizzle_count = strlen(swizzle);
- ADDF(" tex_coords = (TexCoordsMap%u * pic_hcoords).st;\n", i);
- if (tex_target == GL_TEXTURE_RECTANGLE)
- {
- /* The coordinates are in texels values, not normalized */
- ADDF(" tex_coords = vec2(tex_coords.x * TexSize%u.x,\n"
- " tex_coords.y * TexSize%u.y);\n", i, i);
- }
- ADDF(" texel = %s(Texture%u, tex_coords);\n", lookup, i);
- for (unsigned j = 0; j < swizzle_count; ++j)
- {
- ADDF(" pixel[%u] = texel.%c;\n", color_idx, swizzle[j]);
- color_idx++;
- assert(color_idx <= PICTURE_PLANE_MAX);
- }
- }
- ADD(" vec4 result = ConvMatrix * pixel;\n");
- color_count = color_idx;
- }
- else
- {
- ADD(" tex_coords = (TexCoordsMap0 * pic_hcoords).st;\n");
- if (tex_target == GL_TEXTURE_RECTANGLE)
- ADD(" tex_coords *= TexSize0;\n");
-
- ADDF(" vec4 result = %s(Texture0, tex_coords);\n", lookup);
- color_count = 1;
- }
- assert(yuv_space == COLOR_SPACE_UNDEF || color_count == 3);
-
-#ifdef HAVE_LIBPLACEBO
- if (sampler->pl_sh_res) {
- const struct pl_shader_res *res = sampler->pl_sh_res;
- assert(res->input == PL_SHADER_SIG_COLOR);
- assert(res->output == PL_SHADER_SIG_COLOR);
- ADDF(" result = %s(result);\n", res->name);
- }
-#endif
-
- ADD(" return result;\n"
- "}\n");
-
-#undef ADD
-#undef ADDF
-
- if (vlc_memstream_close(&ms) != 0)
- return VLC_EGENERIC;
-
- if (tex_target == GL_TEXTURE_EXTERNAL_OES)
- {
- sampler->shader.extensions =
- strdup("#extension GL_OES_EGL_image_external : require\n");
- if (!sampler->shader.extensions)
- {
- free(ms.ptr);
- return VLC_EGENERIC;
- }
- }
- sampler->shader.body = ms.ptr;
-
- sampler->pf_fetch_locations = sampler_base_fetch_locations;
- sampler->pf_prepare_shader = sampler_base_prepare_shader;
-
- return VLC_SUCCESS;
-}
diff --git a/modules/video_output/opengl/internal.h b/modules/video_output/opengl/internal.h
index a8d8975f5516..b37e134d0c80 100644
--- a/modules/video_output/opengl/internal.h
+++ b/modules/video_output/opengl/internal.h
@@ -29,10 +29,6 @@ opengl_interop_init_impl(struct vlc_gl_interop *interop, GLenum tex_target,
vlc_fourcc_t chroma, video_color_space_t yuv_space);
int
-opengl_fragment_shader_init(struct vlc_gl_sampler *sampler,
- GLenum, vlc_fourcc_t, video_color_space_t,
- video_orientation_t);
-int
opengl_interop_generic_init(struct vlc_gl_interop *interop, bool);
void
diff --git a/modules/video_output/opengl/sampler.c b/modules/video_output/opengl/sampler.c
index ed8c97f462e2..22ddf67e4840 100644
--- a/modules/video_output/opengl/sampler.c
+++ b/modules/video_output/opengl/sampler.c
@@ -26,6 +26,7 @@
#include <vlc_common.h>
#include <vlc_es.h>
+#include <vlc_memstream.h>
#include <vlc_opengl.h>
#ifdef HAVE_LIBPLACEBO
@@ -40,6 +41,764 @@
#include "internal.h"
#include "interop.h"
+static const float MATRIX_COLOR_RANGE_LIMITED[4*3] = {
+ 255.0/219, 0, 0, -255.0/219 * 16.0/255,
+ 0, 255.0/224, 0, -255.0/224 * 128.0/255,
+ 0, 0, 255.0/224, -255.0/224 * 128.0/255,
+};
+
+static const float MATRIX_COLOR_RANGE_FULL[4*3] = {
+ 1, 0, 0, 0,
+ 0, 1, 0, -128.0/255,
+ 0, 0, 1, -128.0/255,
+};
+
+/*
+ * Construct the transformation matrix from the luma weight of the red and blue
+ * component (the green component is deduced).
+ */
+#define MATRIX_YUV_TO_RGB(KR, KB) \
+ MATRIX_YUV_TO_RGB_(KR, (1-(KR)-(KB)), KB)
+
+/*
+ * Construct the transformation matrix from the luma weight of the RGB
+ * components.
+ *
+ * KR: luma weight of the red component
+ * KG: luma weight of the green component
+ * KB: luma weight of the blue component
+ *
+ * By definition, KR + KG + KB == 1.
+ *
+ * Ref: <https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion>
+ * Ref: libplacebo: src/colorspace.c:luma_coeffs()
+ * */
+#define MATRIX_YUV_TO_RGB_(KR, KG, KB) { \
+ 1, 0, 2*(1.0-(KR)), \
+ 1, -2*(1.0-(KB))*((KB)/(KG)), -2*(1.0-(KR))*((KR)/(KG)), \
+ 1, 2*(1.0-(KB)), 0, \
+}
+
+static const float MATRIX_BT601[3*3] = MATRIX_YUV_TO_RGB(0.299, 0.114);
+static const float MATRIX_BT709[3*3] = MATRIX_YUV_TO_RGB(0.2126, 0.0722);
+static const float MATRIX_BT2020[3*3] = MATRIX_YUV_TO_RGB(0.2627, 0.0593);
+
+static void
+init_conv_matrix(float conv_matrix_out[],
+ video_color_space_t color_space,
+ video_color_range_t color_range)
+{
+ const float *space_matrix;
+ switch (color_space) {
+ case COLOR_SPACE_BT601:
+ space_matrix = MATRIX_BT601;
+ break;
+ case COLOR_SPACE_BT2020:
+ space_matrix = MATRIX_BT2020;
+ break;
+ default:
+ space_matrix = MATRIX_BT709;
+ }
+
+ /* Init the conversion matrix in column-major order (OpenGL expects
+ * column-major order by default, and OpenGL ES does not support row-major
+ * order at all). */
+
+ const float *range_matrix = color_range == COLOR_RANGE_FULL
+ ? MATRIX_COLOR_RANGE_FULL
+ : MATRIX_COLOR_RANGE_LIMITED;
+ /* Multiply the matrices on CPU once for all */
+ for (int x = 0; x < 4; ++x)
+ {
+ for (int y = 0; y < 3; ++y)
+ {
+ /* Perform intermediate computation in double precision even if the
+ * result is in single-precision, to avoid unnecessary errors. */
+ double sum = 0;
+ for (int k = 0; k < 3; ++k)
+ sum += space_matrix[y * 3 + k] * range_matrix[k * 4 + x];
+ /* Notice the reversed indices: x is now the row, y is the
+ * column. */
+ conv_matrix_out[x * 4 + y] = sum;
+ }
+ }
+
+ /* Add a row to fill a 4x4 matrix (remember it's in column-major order).
+ * (non-square matrices are not supported on old OpenGL ES versions) */
+ conv_matrix_out[3] = 0;
+ conv_matrix_out[7] = 0;
+ conv_matrix_out[11] = 0;
+ conv_matrix_out[15] = 1;
+}
+
+static int
+sampler_yuv_base_init(struct vlc_gl_sampler *sampler, vlc_fourcc_t chroma,
+ const vlc_chroma_description_t *desc,
+ video_color_space_t yuv_space)
+{
+ /* The current implementation always converts from limited to full range. */
+ const video_color_range_t range = COLOR_RANGE_LIMITED;
+ float *matrix = sampler->conv_matrix;
+ init_conv_matrix(matrix, yuv_space, range);
+
+ if (desc->pixel_size == 2)
+ {
+ if (chroma != VLC_CODEC_P010 && chroma != VLC_CODEC_P016) {
+ /* Do a bit shift if samples are stored on LSB. */
+ float yuv_range_correction = (float)((1 << 16) - 1)
+ / ((1 << desc->pixel_bits) - 1);
+ /* We want to transform the input color (y, u, v, 1) to
+ * (r*y, r*u, r*v, 1), where r = yuv_range_correction.
+ *
+ * This can be done by left-multiplying the color vector by a
+ * matrix R:
+ *
+ * R
+ * / r*y \ / r 0 0 0 \ / y \
+ * | r*u | = | 0 r 0 0 | * | u |
+ * | r*v | | 0 0 r 0 | | v |
+ * \ 1 / \ 0 0 0 1 / \ 1 /
+ *
+ * Combine this transformation with the color conversion matrix:
+ *
+ * matrix := matrix * R
+ *
+ * This is equivalent to multipying the 3 first rows by r
+ * (yuv_range_conversion).
+ */
+ for (int i = 0; i < 4*3; ++i)
+ matrix[i] *= yuv_range_correction;
+ }
+ }
+
+ sampler->yuv_color = true;
+
+ /* Some formats require to swap the U and V components.
+ *
+ * This can be done by left-multiplying the color vector by a matrix S:
+ *
+ * S
+ * / y \ / 1 0 0 0 \ / y \
+ * | v | = | 0 0 1 0 | * | u |
+ * | u | | 0 1 0 0 | | v |
+ * \ 1 / \ 0 0 0 1 / \ 1 /
+ *
+ * Combine this transformation with the color conversion matrix:
+ *
+ * matrix := matrix * S
+ *
+ * This is equivalent to swap columns 1 and 2.
+ */
+ bool swap_uv = chroma == VLC_CODEC_YV12 || chroma == VLC_CODEC_YV9 ||
+ chroma == VLC_CODEC_NV21;
+ if (swap_uv)
+ {
+ /* Remember, the matrix in column-major order */
+ float tmp[4];
+ /* tmp <- column1 */
+ memcpy(tmp, matrix + 4, sizeof(tmp));
+ /* column1 <- column2 */
+ memcpy(matrix + 4, matrix + 8, sizeof(tmp));
+ /* column2 <- tmp */
+ memcpy(matrix + 8, tmp, sizeof(tmp));
+ }
+ return VLC_SUCCESS;
+}
+
+static int
+sampler_base_fetch_locations(struct vlc_gl_sampler *sampler, GLuint program)
+{
+ struct vlc_gl_interop *interop = sampler->interop;
+ const opengl_vtable_t *vt = sampler->vt;
+
+ if (sampler->yuv_color)
+ {
+ sampler->uloc.ConvMatrix = vt->GetUniformLocation(program,
+ "ConvMatrix");
+ if (sampler->uloc.ConvMatrix == -1)
+ return VLC_EGENERIC;
+ }
+
+ sampler->uloc.TransformMatrix =
+ vt->GetUniformLocation(program, "TransformMatrix");
+ if (sampler->uloc.TransformMatrix == -1)
+ return VLC_EGENERIC;
+
+ sampler->uloc.OrientationMatrix =
+ vt->GetUniformLocation(program, "OrientationMatrix");
+ if (sampler->uloc.OrientationMatrix == -1)
+ return VLC_EGENERIC;
+
+ assert(interop->tex_count < 10); /* to guarantee variable names length */
+ for (unsigned int i = 0; i < interop->tex_count; ++i)
+ {
+ char name[sizeof("TexCoordsMapX")];
+
+ snprintf(name, sizeof(name), "Texture%1u", i);
+ sampler->uloc.Texture[i] = vt->GetUniformLocation(program, name);
+ if (sampler->uloc.Texture[i] == -1)
+ return VLC_EGENERIC;
+
+ snprintf(name, sizeof(name), "TexCoordsMap%1u", i);
+ sampler->uloc.TexCoordsMap[i] = vt->GetUniformLocation(program, name);
+ if (sampler->uloc.TexCoordsMap[i] == -1)
+ return VLC_EGENERIC;
+
+ if (interop->tex_target == GL_TEXTURE_RECTANGLE)
+ {
+ snprintf(name, sizeof(name), "TexSize%1u", i);
+ sampler->uloc.TexSize[i] = vt->GetUniformLocation(program, name);
+ if (sampler->uloc.TexSize[i] == -1)
+ return VLC_EGENERIC;
+ }
+ }
+
+#ifdef HAVE_LIBPLACEBO
+ const struct pl_shader_res *res = sampler->pl_sh_res;
+ for (int i = 0; res && i < res->num_variables; i++) {
+ struct pl_shader_var sv = res->variables[i];
+ sampler->uloc.pl_vars[i] = vt->GetUniformLocation(program, sv.var.name);
+ }
+#endif
+
+ return VLC_SUCCESS;
+}
+
+static const GLfloat *
+GetTransformMatrix(const struct vlc_gl_interop *interop)
+{
+ const GLfloat *tm = NULL;
+ if (interop->ops && interop->ops->get_transform_matrix)
+ tm = interop->ops->get_transform_matrix(interop);
+ if (!tm)
+ tm = MATRIX4_IDENTITY;
+ return tm;
+}
+
+static void
+sampler_base_prepare_shader(const struct vlc_gl_sampler *sampler)
+{
+ const struct vlc_gl_interop *interop = sampler->interop;
+ const opengl_vtable_t *vt = sampler->vt;
+
+ if (sampler->yuv_color)
+ vt->UniformMatrix4fv(sampler->uloc.ConvMatrix, 1, GL_FALSE,
+ sampler->conv_matrix);
+
+ for (unsigned i = 0; i < interop->tex_count; ++i)
+ {
+ vt->Uniform1i(sampler->uloc.Texture[i], i);
+
+ assert(sampler->textures[i] != 0);
+ vt->ActiveTexture(GL_TEXTURE0 + i);
+ vt->BindTexture(interop->tex_target, sampler->textures[i]);
+
+ vt->UniformMatrix3fv(sampler->uloc.TexCoordsMap[i], 1, GL_FALSE,
+ sampler->var.TexCoordsMap[i]);
+ }
+
+ const GLfloat *tm = GetTransformMatrix(interop);
+ vt->UniformMatrix4fv(sampler->uloc.TransformMatrix, 1, GL_FALSE, tm);
+
+ vt->UniformMatrix4fv(sampler->uloc.OrientationMatrix, 1, GL_FALSE,
+ sampler->var.OrientationMatrix);
+
+ if (interop->tex_target == GL_TEXTURE_RECTANGLE)
+ {
+ for (unsigned i = 0; i < interop->tex_count; ++i)
+ vt->Uniform2f(sampler->uloc.TexSize[i], sampler->tex_width[i],
+ sampler->tex_height[i]);
+ }
+
+#ifdef HAVE_LIBPLACEBO
+ const struct pl_shader_res *res = sampler->pl_sh_res;
+ for (int i = 0; res && i < res->num_variables; i++) {
+ GLint loc = sampler->uloc.pl_vars[i];
+ if (loc == -1) // uniform optimized out
+ continue;
+
+ struct pl_shader_var sv = res->variables[i];
+ struct pl_var var = sv.var;
+ // libplacebo doesn't need anything else anyway
+ if (var.type != PL_VAR_FLOAT)
+ continue;
+ if (var.dim_m > 1 && var.dim_m != var.dim_v)
+ continue;
+
+ const float *f = sv.data;
+ switch (var.dim_m) {
+ case 4: vt->UniformMatrix4fv(loc, 1, GL_FALSE, f); break;
+ case 3: vt->UniformMatrix3fv(loc, 1, GL_FALSE, f); break;
+ case 2: vt->UniformMatrix2fv(loc, 1, GL_FALSE, f); break;
+
+ case 1:
+ switch (var.dim_v) {
+ case 1: vt->Uniform1f(loc, f[0]); break;
+ case 2: vt->Uniform2f(loc, f[0], f[1]); break;
+ case 3: vt->Uniform3f(loc, f[0], f[1], f[2]); break;
+ case 4: vt->Uniform4f(loc, f[0], f[1], f[2], f[3]); break;
+ }
+ break;
+ }
+ }
+#endif
+}
+
+static int
+sampler_xyz12_fetch_locations(struct vlc_gl_sampler *sampler, GLuint program)
+{
+ const opengl_vtable_t *vt = sampler->vt;
+
+ sampler->uloc.Texture[0] = vt->GetUniformLocation(program, "Texture0");
+ if (sampler->uloc.Texture[0] == -1)
+ return VLC_EGENERIC;
+
+ sampler->uloc.TransformMatrix =
+ vt->GetUniformLocation(program, "TransformMatrix");
+ if (sampler->uloc.TransformMatrix == -1)
+ return VLC_EGENERIC;
+
+ sampler->uloc.OrientationMatrix =
+ vt->GetUniformLocation(program, "OrientationMatrix");
+ if (sampler->uloc.OrientationMatrix == -1)
+ return VLC_EGENERIC;
+
+ sampler->uloc.TexCoordsMap[0] =
+ vt->GetUniformLocation(program, "TexCoordsMap0");
+ if (sampler->uloc.TexCoordsMap[0] == -1)
+ return VLC_EGENERIC;
+
+ return VLC_SUCCESS;
+}
+
+static void
+sampler_xyz12_prepare_shader(const struct vlc_gl_sampler *sampler)
+{
+ const struct vlc_gl_interop *interop = sampler->interop;
+ const opengl_vtable_t *vt = sampler->vt;
+
+ vt->Uniform1i(sampler->uloc.Texture[0], 0);
+
+ assert(sampler->textures[0] != 0);
+ vt->ActiveTexture(GL_TEXTURE0);
+ vt->BindTexture(interop->tex_target, sampler->textures[0]);
+
+ vt->UniformMatrix3fv(sampler->uloc.TexCoordsMap[0], 1, GL_FALSE,
+ sampler->var.TexCoordsMap[0]);
+
+ const GLfloat *tm = GetTransformMatrix(interop);
+ vt->UniformMatrix4fv(sampler->uloc.TransformMatrix, 1, GL_FALSE, tm);
+
+ vt->UniformMatrix4fv(sampler->uloc.OrientationMatrix, 1, GL_FALSE,
+ sampler->var.OrientationMatrix);
+}
+
+static int
+xyz12_shader_init(struct vlc_gl_sampler *sampler)
+{
+ sampler->pf_fetch_locations = sampler_xyz12_fetch_locations;
+ sampler->pf_prepare_shader = sampler_xyz12_prepare_shader;
+
+ /* Shader for XYZ to RGB correction
+ * 3 steps :
+ * - XYZ gamma correction
+ * - XYZ to RGB matrix conversion
+ * - reverse RGB gamma correction
+ */
+ static const char *template =
+ "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 "
+ " );"
+
+ "uniform mat4 TransformMatrix;\n"
+ "uniform mat4 OrientationMatrix;\n"
+ "uniform mat3 TexCoordsMap0;\n"
+ "vec4 vlc_texture(vec2 pic_coords)\n"
+ "{ "
+ " vec4 v_in, v_out;"
+ /* Homogeneous (oriented) coordinates */
+ " vec3 pic_hcoords = vec3((TransformMatrix * OrientationMatrix * vec4(pic_coords, 0.0, 1.0)).st, 1.0);\n"
+ " vec2 tex_coords = (TexCoordsMap0 * pic_hcoords).st;\n"
+ " v_in = texture2D(Texture0, tex_coords);\n"
+ " 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) ;"
+ " return v_out;"
+ "}\n";
+
+ sampler->shader.body = strdup(template);
+ if (!sampler->shader.body)
+ return VLC_ENOMEM;
+
+ return VLC_SUCCESS;
+}
+
+static int
+opengl_init_swizzle(const struct vlc_gl_interop *interop,
+ const char *swizzle_per_tex[],
+ vlc_fourcc_t chroma,
+ const vlc_chroma_description_t *desc)
+{
+ GLint oneplane_texfmt;
+ if (vlc_gl_StrHasToken(interop->api->extensions, "GL_ARB_texture_rg"))
+ oneplane_texfmt = GL_RED;
+ else
+ oneplane_texfmt = GL_LUMINANCE;
+
+ if (desc->plane_count == 3)
+ swizzle_per_tex[0] = swizzle_per_tex[1] = swizzle_per_tex[2] = "r";
+ else if (desc->plane_count == 2)
+ {
+ if (oneplane_texfmt == GL_RED)
+ {
+ swizzle_per_tex[0] = "r";
+ swizzle_per_tex[1] = "rg";
+ }
+ else
+ {
+ swizzle_per_tex[0] = "x";
+ swizzle_per_tex[1] = "xa";
+ }
+ }
+ else if (desc->plane_count == 1)
+ {
+ /*
+ * Set swizzling in Y1 U V order
+ * R G B A
+ * U Y1 V Y2 => GRB
+ * Y1 U Y2 V => RGA
+ * V Y1 U Y2 => GBR
+ * Y1 V Y2 U => RAG
+ */
+ switch (chroma)
+ {
+ case VLC_CODEC_UYVY:
+ swizzle_per_tex[0] = "grb";
+ break;
+ case VLC_CODEC_YUYV:
+ swizzle_per_tex[0] = "rga";
+ break;
+ case VLC_CODEC_VYUY:
+ swizzle_per_tex[0] = "gbr";
+ break;
+ case VLC_CODEC_YVYU:
+ swizzle_per_tex[0] = "rag";
+ break;
+ default:
+ assert(!"missing chroma");
+ return VLC_EGENERIC;
+ }
+ }
+ return VLC_SUCCESS;
+}
+
+static void
+InitOrientationMatrix(GLfloat matrix[static 4*4],
+ video_orientation_t orientation)
+{
+ memcpy(matrix, MATRIX4_IDENTITY, sizeof(MATRIX4_IDENTITY));
+
+ const int k_cos_pi = -1;
+ const int k_cos_pi_2 = 0;
+ const int k_cos_n_pi_2 = 0;
+
+ const int k_sin_pi = 0;
+ const int k_sin_pi_2 = 1;
+ const int k_sin_n_pi_2 = -1;
+
+ switch (orientation) {
+
+ case ORIENT_ROTATED_90:
+ matrix[0 * 4 + 0] = k_cos_pi_2;
+ matrix[0 * 4 + 1] = -k_sin_pi_2;
+ matrix[1 * 4 + 0] = k_sin_pi_2;
+ matrix[1 * 4 + 1] = k_cos_pi_2;
+ matrix[3 * 4 + 1] = 1;
+ break;
+ case ORIENT_ROTATED_180:
+ matrix[0 * 4 + 0] = k_cos_pi;
+ matrix[0 * 4 + 1] = -k_sin_pi;
+ matrix[1 * 4 + 0] = k_sin_pi;
+ matrix[1 * 4 + 1] = k_cos_pi;
+ matrix[3 * 4 + 0] = 1;
+ matrix[3 * 4 + 1] = 1;
+ break;
+ case ORIENT_ROTATED_270:
+ matrix[0 * 4 + 0] = k_cos_n_pi_2;
+ matrix[0 * 4 + 1] = -k_sin_n_pi_2;
+ matrix[1 * 4 + 0] = k_sin_n_pi_2;
+ matrix[1 * 4 + 1] = k_cos_n_pi_2;
+ matrix[3 * 4 + 0] = 1;
+ break;
+ case ORIENT_HFLIPPED:
+ matrix[0 * 4 + 0] = -1;
+ matrix[3 * 4 + 0] = 1;
+ break;
+ case ORIENT_VFLIPPED:
+ matrix[1 * 4 + 1] = -1;
+ matrix[3 * 4 + 1] = 1;
+ break;
+ case ORIENT_TRANSPOSED:
+ matrix[0 * 4 + 0] = 0;
+ matrix[1 * 4 + 1] = 0;
+ matrix[2 * 4 + 2] = -1;
+ matrix[0 * 4 + 1] = 1;
+ matrix[1 * 4 + 0] = 1;
+ break;
+ case ORIENT_ANTI_TRANSPOSED:
+ matrix[0 * 4 + 0] = 0;
+ matrix[1 * 4 + 1] = 0;
+ matrix[2 * 4 + 2] = -1;
+ matrix[0 * 4 + 1] = -1;
+ matrix[1 * 4 + 0] = -1;
+ matrix[3 * 4 + 0] = 1;
+ matrix[3 * 4 + 1] = 1;
+ break;
+ default:
+ break;
+ }
+}
+
+static int
+opengl_fragment_shader_init(struct vlc_gl_sampler *sampler, GLenum tex_target,
+ vlc_fourcc_t chroma, video_color_space_t yuv_space,
+ video_orientation_t orientation)
+{
+ struct vlc_gl_interop *interop = sampler->interop;
+
+ const char *swizzle_per_tex[PICTURE_PLANE_MAX] = { NULL, };
+ const bool is_yuv = vlc_fourcc_IsYUV(chroma);
+ int ret;
+
+ const vlc_chroma_description_t *desc = vlc_fourcc_GetChromaDescription(chroma);
+ if (desc == NULL)
+ return VLC_EGENERIC;
+
+ InitOrientationMatrix(sampler->var.OrientationMatrix, orientation);
+
+ if (chroma == VLC_CODEC_XYZ12)
+ return xyz12_shader_init(sampler);
+
+ if (is_yuv)
+ {
+ ret = sampler_yuv_base_init(sampler, chroma, desc, yuv_space);
+ if (ret != VLC_SUCCESS)
+ return ret;
+ ret = opengl_init_swizzle(interop, swizzle_per_tex, chroma, desc);
+ if (ret != VLC_SUCCESS)
+ return ret;
+ }
+
+ const char *glsl_sampler, *lookup;
+ switch (tex_target)
+ {
+ case GL_TEXTURE_EXTERNAL_OES:
+ glsl_sampler = "samplerExternalOES";
+ lookup = "texture2D";
+ break;
+ case GL_TEXTURE_2D:
+ glsl_sampler = "sampler2D";
+ lookup = "texture2D";
+ break;
+ case GL_TEXTURE_RECTANGLE:
+ glsl_sampler = "sampler2DRect";
+ lookup = "texture2DRect";
+ break;
+ default:
+ vlc_assert_unreachable();
+ }
+
+ struct vlc_memstream ms;
+ if (vlc_memstream_open(&ms) != 0)
+ return VLC_EGENERIC;
+
+#define ADD(x) vlc_memstream_puts(&ms, x)
+#define ADDF(x, ...) vlc_memstream_printf(&ms, x, ##__VA_ARGS__)
+
+ ADD("uniform mat4 TransformMatrix;\n"
+ "uniform mat4 OrientationMatrix;\n");
+ for (unsigned i = 0; i < interop->tex_count; ++i)
+ ADDF("uniform %s Texture%u;\n"
+ "uniform mat3 TexCoordsMap%u;\n", glsl_sampler, i, i);
+
+#ifdef HAVE_LIBPLACEBO
+ if (sampler->pl_sh) {
+ struct pl_shader *sh = sampler->pl_sh;
+ struct pl_color_map_params color_params = pl_color_map_default_params;
+ color_params.intent = var_InheritInteger(sampler->gl, "rendering-intent");
+ color_params.tone_mapping_algo = var_InheritInteger(sampler->gl, "tone-mapping");
+ color_params.tone_mapping_param = var_InheritFloat(sampler->gl, "tone-mapping-param");
+# if PL_API_VER >= 10
+ color_params.desaturation_strength = var_InheritFloat(sampler->gl, "desat-strength");
+ color_params.desaturation_exponent = var_InheritFloat(sampler->gl, "desat-exponent");
+ color_params.desaturation_base = var_InheritFloat(sampler->gl, "desat-base");
+# else
+ color_params.tone_mapping_desaturate = var_InheritFloat(sampler->gl, "tone-mapping-desat");
+# endif
+ color_params.gamut_warning = var_InheritBool(sampler->gl, "tone-mapping-warn");
+
+ struct pl_color_space dst_space = pl_color_space_unknown;
+ dst_space.primaries = var_InheritInteger(sampler->gl, "target-prim");
+ dst_space.transfer = var_InheritInteger(sampler->gl, "target-trc");
+
+ pl_shader_color_map(sh, &color_params,
+ vlc_placebo_ColorSpace(&interop->fmt),
+ dst_space, NULL, false);
+
+ struct pl_shader_obj *dither_state = NULL;
+ int method = var_InheritInteger(sampler->gl, "dither-algo");
+ if (method >= 0) {
+
+ unsigned out_bits = 0;
+ int override = var_InheritInteger(sampler->gl, "dither-depth");
+ if (override > 0)
+ out_bits = override;
+ else
+ {
+ GLint fb_depth = 0;
+#if !defined(USE_OPENGL_ES2)
+ const opengl_vtable_t *vt = sampler->vt;
+ /* fetch framebuffer depth (we are already bound to the default one). */
+ if (vt->GetFramebufferAttachmentParameteriv != NULL)
+ vt->GetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_BACK_LEFT,
+ GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE,
+ &fb_depth);
+#endif
+ if (fb_depth <= 0)
+ fb_depth = 8;
+ out_bits = fb_depth;
+ }
+
+ pl_shader_dither(sh, out_bits, &dither_state, &(struct pl_dither_params) {
+ .method = method,
+ .lut_size = 4, // avoid too large values, since this gets embedded
+ });
+ }
+
+ const struct pl_shader_res *res = sampler->pl_sh_res = pl_shader_finalize(sh);
+ pl_shader_obj_destroy(&dither_state);
+
+ FREENULL(sampler->uloc.pl_vars);
+ sampler->uloc.pl_vars = calloc(res->num_variables, sizeof(GLint));
+ for (int i = 0; i < res->num_variables; i++) {
+ struct pl_shader_var sv = res->variables[i];
+ const char *glsl_type_name = pl_var_glsl_type_name(sv.var);
+ ADDF("uniform %s %s;\n", glsl_type_name, sv.var.name);
+ }
+
+ // We can't handle these yet, but nothing we use requires them, either
+ assert(res->num_vertex_attribs == 0);
+ assert(res->num_descriptors == 0);
+
+ ADD(res->glsl);
+ }
+#else
+ if (interop->fmt.transfer == TRANSFER_FUNC_SMPTE_ST2084 ||
+ interop->fmt.primaries == COLOR_PRIMARIES_BT2020)
+ {
+ // no warning for HLG because it's more or less backwards-compatible
+ msg_Warn(sampler->gl, "VLC needs to be built with support for libplacebo "
+ "in order to display wide gamut or HDR signals correctly.");
+ }
+#endif
+
+ if (tex_target == GL_TEXTURE_RECTANGLE)
+ {
+ for (unsigned i = 0; i < interop->tex_count; ++i)
+ ADDF("uniform vec2 TexSize%u;\n", i);
+ }
+
+ if (is_yuv)
+ ADD("uniform mat4 ConvMatrix;\n");
+
+ ADD("vec4 vlc_texture(vec2 pic_coords) {\n"
+ /* Homogeneous (oriented) coordinates */
+ " vec3 pic_hcoords = vec3((TransformMatrix * OrientationMatrix * vec4(pic_coords, 0.0, 1.0)).st, 1.0);\n"
+ " vec2 tex_coords;\n");
+
+ unsigned color_count;
+ if (is_yuv) {
+ ADD(" vec4 texel;\n"
+ " vec4 pixel = vec4(0.0, 0.0, 0.0, 1.0);\n");
+ unsigned color_idx = 0;
+ for (unsigned i = 0; i < interop->tex_count; ++i)
+ {
+ const char *swizzle = swizzle_per_tex[i];
+ assert(swizzle);
+ size_t swizzle_count = strlen(swizzle);
+ ADDF(" tex_coords = (TexCoordsMap%u * pic_hcoords).st;\n", i);
+ if (tex_target == GL_TEXTURE_RECTANGLE)
+ {
+ /* The coordinates are in texels values, not normalized */
+ ADDF(" tex_coords = vec2(tex_coords.x * TexSize%u.x,\n"
+ " tex_coords.y * TexSize%u.y);\n", i, i);
+ }
+ ADDF(" texel = %s(Texture%u, tex_coords);\n", lookup, i);
+ for (unsigned j = 0; j < swizzle_count; ++j)
+ {
+ ADDF(" pixel[%u] = texel.%c;\n", color_idx, swizzle[j]);
+ color_idx++;
+ assert(color_idx <= PICTURE_PLANE_MAX);
+ }
+ }
+ ADD(" vec4 result = ConvMatrix * pixel;\n");
+ color_count = color_idx;
+ }
+ else
+ {
+ ADD(" tex_coords = (TexCoordsMap0 * pic_hcoords).st;\n");
+ if (tex_target == GL_TEXTURE_RECTANGLE)
+ ADD(" tex_coords *= TexSize0;\n");
+
+ ADDF(" vec4 result = %s(Texture0, tex_coords);\n", lookup);
+ color_count = 1;
+ }
+ assert(yuv_space == COLOR_SPACE_UNDEF || color_count == 3);
+
+#ifdef HAVE_LIBPLACEBO
+ if (sampler->pl_sh_res) {
+ const struct pl_shader_res *res = sampler->pl_sh_res;
+ assert(res->input == PL_SHADER_SIG_COLOR);
+ assert(res->output == PL_SHADER_SIG_COLOR);
+ ADDF(" result = %s(result);\n", res->name);
+ }
+#endif
+
+ ADD(" return result;\n"
+ "}\n");
+
+#undef ADD
+#undef ADDF
+
+ if (vlc_memstream_close(&ms) != 0)
+ return VLC_EGENERIC;
+
+ if (tex_target == GL_TEXTURE_EXTERNAL_OES)
+ {
+ sampler->shader.extensions =
+ strdup("#extension GL_OES_EGL_image_external : require\n");
+ if (!sampler->shader.extensions)
+ {
+ free(ms.ptr);
+ return VLC_EGENERIC;
+ }
+ }
+ sampler->shader.body = ms.ptr;
+
+ sampler->pf_fetch_locations = sampler_base_fetch_locations;
+ sampler->pf_prepare_shader = sampler_base_prepare_shader;
+
+ return VLC_SUCCESS;
+}
+
struct vlc_gl_sampler *
vlc_gl_sampler_New(struct vlc_gl_interop *interop)
{
--
2.27.0
More information about the vlc-devel
mailing list