From e3d0c11923c8b64716f41d28b9ed24402da68ea5 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 28 Jan 2020 14:42:10 +0100 Subject: [PATCH] opengl: factorize program creation Expose a single function to create an OpenGL program for a vertex shader and a fragment shader. This simplify the code, and handles the errors in the same way everywhere. Signed-off-by: Jean-Baptiste Kempf --- modules/video_output/Makefile.am | 1 + modules/video_output/opengl/fragment_shaders.c | 35 ++--- modules/video_output/opengl/gl_util.c | 133 +++++++++++++++++++ modules/video_output/opengl/gl_util.h | 23 ++++ modules/video_output/opengl/internal.h | 2 +- modules/video_output/opengl/renderer.c | 83 +++--------- modules/video_output/opengl/sub_renderer.c | 169 +++++-------------------- 7 files changed, 221 insertions(+), 225 deletions(-) create mode 100644 modules/video_output/opengl/gl_util.c diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am index cb53932315..cee3d447a9 100644 --- a/modules/video_output/Makefile.am +++ b/modules/video_output/Makefile.am @@ -5,6 +5,7 @@ EXTRA_DIST += video_output/README OPENGL_COMMONSOURCES = video_output/opengl/vout_helper.c \ video_output/opengl/gl_common.h \ + video_output/opengl/gl_util.c \ video_output/opengl/gl_util.h \ video_output/opengl/interop.h \ video_output/opengl/vout_helper.h \ diff --git a/modules/video_output/opengl/fragment_shaders.c b/modules/video_output/opengl/fragment_shaders.c index fc3391c32f..dfc4f51d1e 100644 --- a/modules/video_output/opengl/fragment_shaders.c +++ b/modules/video_output/opengl/fragment_shaders.c @@ -323,10 +323,9 @@ renderer_xyz12_prepare_shader(const struct vlc_gl_renderer *renderer, vt->Uniform1i(renderer->uloc.Texture[0], 0); } -static GLuint +static char * xyz12_shader_init(struct vlc_gl_renderer *renderer) { - const opengl_vtable_t *vt = renderer->vt; renderer->pf_fetch_locations = renderer_xyz12_fetch_locations; renderer->pf_prepare_shader = renderer_xyz12_prepare_shader; @@ -365,13 +364,9 @@ xyz12_shader_init(struct vlc_gl_renderer *renderer) char *code; if (asprintf(&code, template, renderer->glsl_version, renderer->glsl_precision_header) < 0) - return 0; + return NULL; - GLuint fragment_shader = vt->CreateShader(GL_FRAGMENT_SHADER); - vt->ShaderSource(fragment_shader, 1, (const char **) &code, NULL); - vt->CompileShader(fragment_shader); - free(code); - return fragment_shader; + return code; } static int @@ -433,7 +428,7 @@ opengl_init_swizzle(const struct vlc_gl_interop *interop, return VLC_SUCCESS; } -GLuint +char * opengl_fragment_shader_init(struct vlc_gl_renderer *renderer, GLenum tex_target, vlc_fourcc_t chroma, video_color_space_t yuv_space) { @@ -446,7 +441,7 @@ opengl_fragment_shader_init(struct vlc_gl_renderer *renderer, GLenum tex_target, const vlc_chroma_description_t *desc = vlc_fourcc_GetChromaDescription(chroma); if (desc == NULL) - return 0; + return NULL; if (chroma == VLC_CODEC_XYZ12) return xyz12_shader_init(renderer); @@ -455,10 +450,10 @@ opengl_fragment_shader_init(struct vlc_gl_renderer *renderer, GLenum tex_target, { ret = renderer_yuv_base_init(renderer, chroma, desc, yuv_space); if (ret != VLC_SUCCESS) - return 0; + return NULL; ret = opengl_init_swizzle(renderer->interop, swizzle_per_tex, chroma, desc); if (ret != VLC_SUCCESS) - return 0; + return NULL; } const char *sampler, *lookup, *coord_name; @@ -485,7 +480,7 @@ opengl_fragment_shader_init(struct vlc_gl_renderer *renderer, GLenum tex_target, struct vlc_memstream ms; if (vlc_memstream_open(&ms) != 0) - return 0; + return NULL; #define ADD(x) vlc_memstream_puts(&ms, x) #define ADDF(x, ...) vlc_memstream_printf(&ms, x, ##__VA_ARGS__) @@ -644,24 +639,14 @@ opengl_fragment_shader_init(struct vlc_gl_renderer *renderer, GLenum tex_target, #undef ADDF if (vlc_memstream_close(&ms) != 0) - return 0; + return NULL; - GLuint fragment_shader = vt->CreateShader(GL_FRAGMENT_SHADER); - if (fragment_shader == 0) - { - free(ms.ptr); - return 0; - } - GLint length = ms.length; - vt->ShaderSource(fragment_shader, 1, (const char **)&ms.ptr, &length); - vt->CompileShader(fragment_shader); if (renderer->b_dump_shaders) msg_Dbg(renderer->gl, "\n=== Fragment shader for fourcc: %4.4s, colorspace: %d ===\n%s\n", (const char *)&chroma, yuv_space, ms.ptr); - free(ms.ptr); renderer->pf_fetch_locations = renderer_base_fetch_locations; renderer->pf_prepare_shader = renderer_base_prepare_shader; - return fragment_shader; + return ms.ptr; } diff --git a/modules/video_output/opengl/gl_util.c b/modules/video_output/opengl/gl_util.c new file mode 100644 index 0000000000..c47a272acf --- /dev/null +++ b/modules/video_output/opengl/gl_util.c @@ -0,0 +1,133 @@ +/***************************************************************************** + * gl_util.c + ***************************************************************************** + * Copyright (C) 2020 VLC authors and VideoLAN + * Copyright (C) 2020 Videolabs + * + * 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 "gl_util.h" + +static void +LogShaderErrors(vlc_object_t *obj, const opengl_vtable_t *vt, GLuint id) +{ + GLint info_len; + vt->GetShaderiv(id, GL_INFO_LOG_LENGTH, &info_len); + if (info_len > 0) + { + char *info_log = malloc(info_len); + if (info_log) + { + GLsizei written; + vt->GetShaderInfoLog(id, info_len, &written, info_log); + msg_Err(obj, "shader: %s", info_log); + free(info_log); + } + } +} + +static void +LogProgramErrors(vlc_object_t *obj, const opengl_vtable_t *vt, GLuint id) +{ + GLint info_len; + vt->GetProgramiv(id, GL_INFO_LOG_LENGTH, &info_len); + if (info_len > 0) + { + char *info_log = malloc(info_len); + if (info_log) + { + GLsizei written; + vt->GetProgramInfoLog(id, info_len, &written, info_log); + msg_Err(obj, "program: %s", info_log); + free(info_log); + } + } +} + +static GLuint +CreateShader(vlc_object_t *obj, const opengl_vtable_t *vt, GLenum type, + GLsizei count, const GLchar **src) +{ + GLuint shader = vt->CreateShader(type); + if (!shader) + return 0; + + vt->ShaderSource(shader, count, src, NULL); + vt->CompileShader(shader); + + LogShaderErrors(obj, vt, shader); + + GLint compiled; + vt->GetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if (!compiled) + { + msg_Err(obj, "Failed to compile shader"); + vt->DeleteShader(shader); + return 0; + } + + return shader; +} + + +GLuint +vlc_gl_BuildProgram(vlc_object_t *obj, const opengl_vtable_t *vt, + GLsizei vstring_count, const GLchar **vstrings, + GLsizei fstring_count, const GLchar **fstrings) +{ + GLuint program = 0; + + GLuint vertex_shader = CreateShader(obj, vt, GL_VERTEX_SHADER, + vstring_count, vstrings); + if (!vertex_shader) + return 0; + + GLuint fragment_shader = CreateShader(obj, vt, GL_FRAGMENT_SHADER, + fstring_count, fstrings); + if (!fragment_shader) + goto finally_1; + + program = vt->CreateProgram(); + if (!program) + goto finally_2; + + vt->AttachShader(program, vertex_shader); + vt->AttachShader(program, fragment_shader); + + vt->LinkProgram(program); + + LogProgramErrors(obj, vt, program); + + GLint linked; + vt->GetProgramiv(program, GL_LINK_STATUS, &linked); + if (!linked) + { + msg_Err(obj, "Failed to link program"); + vt->DeleteProgram(program); + program = 0; + } + +finally_2: + vt->DeleteShader(fragment_shader); +finally_1: + vt->DeleteShader(vertex_shader); + + return program; +} diff --git a/modules/video_output/opengl/gl_util.h b/modules/video_output/opengl/gl_util.h index 21734aae21..051c034d3f 100644 --- a/modules/video_output/opengl/gl_util.h +++ b/modules/video_output/opengl/gl_util.h @@ -2,6 +2,7 @@ * gl_util.h ***************************************************************************** * Copyright (C) 2020 VLC authors and VideoLAN + * Copyright (C) 2020 Videolabs * * 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 @@ -26,6 +27,7 @@ #endif #include +#include "gl_common.h" /** Return the smallest larger or equal power of 2 */ static inline unsigned vlc_align_pot(unsigned x) @@ -34,4 +36,25 @@ static inline unsigned vlc_align_pot(unsigned x) return ((align >> 1) == x) ? x : align; } +/** + * Build an OpenGL program + * + * Both the fragment shader and fragment shader are passed as a list of + * strings, forming the shader source code once concatenated, like + * glShaderSource(). + * + * \param obj a VLC object, used to log messages + * \param vt the OpenGL virtual table + * \param vstring_count the number of strings in vstrings + * \param vstrings a list of NUL-terminated strings containing the vertex + * shader source code + * \param fstring_count the number of strings in fstrings + * \param fstrings a list of NUL-terminated strings containing the fragment + * shader source code + */ +GLuint +vlc_gl_BuildProgram(vlc_object_t *obj, const opengl_vtable_t *vt, + GLsizei vstring_count, const GLchar **vstrings, + GLsizei fstring_count, const GLchar **fstrings); + #endif diff --git a/modules/video_output/opengl/internal.h b/modules/video_output/opengl/internal.h index e7143e1a05..f4ed495252 100644 --- a/modules/video_output/opengl/internal.h +++ b/modules/video_output/opengl/internal.h @@ -28,7 +28,7 @@ int opengl_interop_init_impl(struct vlc_gl_interop *interop, GLenum tex_target, vlc_fourcc_t chroma, video_color_space_t yuv_space); -GLuint +char * opengl_fragment_shader_init(struct vlc_gl_renderer *rendeer, GLenum, vlc_fourcc_t, video_color_space_t); int diff --git a/modules/video_output/opengl/renderer.c b/modules/video_output/opengl/renderer.c index 0969c99dc2..416530ffea 100644 --- a/modules/video_output/opengl/renderer.c +++ b/modules/video_output/opengl/renderer.c @@ -169,11 +169,9 @@ static void getOrientationTransformMatrix(video_orientation_t orientation, } } -static GLuint BuildVertexShader(const struct vlc_gl_renderer *renderer, - unsigned plane_count) +static char * +BuildVertexShader(const struct vlc_gl_renderer *renderer, unsigned plane_count) { - const opengl_vtable_t *vt = renderer->vt; - /* Basic vertex shader */ static const char *template = "#version %u\n" @@ -205,16 +203,12 @@ static GLuint BuildVertexShader(const struct vlc_gl_renderer *renderer, char *code; if (asprintf(&code, template, renderer->glsl_version, coord1_header, coord2_header, coord1_code, coord2_code) < 0) - return 0; + return NULL; - GLuint shader = vt->CreateShader(GL_VERTEX_SHADER); - vt->ShaderSource(shader, 1, (const char **) &code, NULL); if (renderer->b_dump_shaders) msg_Dbg(renderer->gl, "\n=== Vertex shader for fourcc: %4.4s ===\n%s\n", (const char *) &renderer->interop->fmt.i_chroma, code); - vt->CompileShader(shader); - free(code); - return shader; + return code; } static int @@ -223,16 +217,19 @@ opengl_link_program(struct vlc_gl_renderer *renderer) struct vlc_gl_interop *interop = renderer->interop; const opengl_vtable_t *vt = renderer->vt; - GLuint vertex_shader = BuildVertexShader(renderer, interop->tex_count); + char *vertex_shader = BuildVertexShader(renderer, interop->tex_count); if (!vertex_shader) return VLC_EGENERIC; - GLuint fragment_shader = + char *fragment_shader = opengl_fragment_shader_init(renderer, interop->tex_target, interop->sw_fmt.i_chroma, interop->sw_fmt.space); if (!fragment_shader) + { + free(vertex_shader); return VLC_EGENERIC; + } assert(interop->tex_target != 0 && interop->tex_count > 0 && @@ -240,58 +237,14 @@ opengl_link_program(struct vlc_gl_renderer *renderer) renderer->pf_fetch_locations != NULL && renderer->pf_prepare_shader != NULL); - GLuint shaders[] = { fragment_shader, vertex_shader }; - - /* Check shaders messages */ - for (unsigned i = 0; i < 2; i++) { - int infoLength; - vt->GetShaderiv(shaders[i], GL_INFO_LOG_LENGTH, &infoLength); - if (infoLength <= 1) - continue; - - char *infolog = malloc(infoLength); - if (infolog != NULL) - { - int charsWritten; - vt->GetShaderInfoLog(shaders[i], infoLength, &charsWritten, - infolog); - msg_Err(renderer->gl, "shader %u: %s", i, infolog); - free(infolog); - } - } - - GLuint program_id = renderer->program_id = vt->CreateProgram(); - vt->AttachShader(program_id, fragment_shader); - vt->AttachShader(program_id, vertex_shader); - vt->LinkProgram(program_id); - - vt->DeleteShader(vertex_shader); - vt->DeleteShader(fragment_shader); - - /* Check program messages */ - int infoLength = 0; - vt->GetProgramiv(program_id, GL_INFO_LOG_LENGTH, &infoLength); - if (infoLength > 1) - { - char *infolog = malloc(infoLength); - if (infolog != NULL) - { - int charsWritten; - vt->GetProgramInfoLog(program_id, infoLength, &charsWritten, - infolog); - msg_Err(renderer->gl, "shader program: %s", infolog); - free(infolog); - } - - /* If there is some message, better to check linking is ok */ - GLint link_status = GL_TRUE; - vt->GetProgramiv(program_id, GL_LINK_STATUS, &link_status); - if (link_status == GL_FALSE) - { - msg_Err(renderer->gl, "Unable to use program"); - goto error; - } - } + GLuint program_id = + vlc_gl_BuildProgram(VLC_OBJECT(renderer->gl), vt, + 1, (const char **) &vertex_shader, + 1, (const char **) &fragment_shader); + free(vertex_shader); + free(fragment_shader); + if (!program_id) + return VLC_EGENERIC; /* Fetch UniformLocations and AttribLocations */ #define GET_LOC(type, x, str) do { \ @@ -332,6 +285,8 @@ opengl_link_program(struct vlc_gl_renderer *renderer) goto error; } + renderer->program_id = program_id; + return VLC_SUCCESS; error: diff --git a/modules/video_output/opengl/sub_renderer.c b/modules/video_output/opengl/sub_renderer.c index a900f90473..e434f2ee30 100644 --- a/modules/video_output/opengl/sub_renderer.c +++ b/modules/video_output/opengl/sub_renderer.c @@ -82,140 +82,6 @@ struct vlc_gl_sub_renderer unsigned buffer_object_count; }; -static void -LogShaderErrors(vlc_object_t *obj, const opengl_vtable_t *vt, GLuint id) -{ - GLint info_len; - vt->GetShaderiv(id, GL_INFO_LOG_LENGTH, &info_len); - if (info_len > 0) - { - char *info_log = malloc(info_len); - if (info_log) - { - GLsizei written; - vt->GetShaderInfoLog(id, info_len, &written, info_log); - msg_Err(obj, "shader: %s", info_log); - free(info_log); - } - } -} - -static void -LogProgramErrors(vlc_object_t *obj, const opengl_vtable_t *vt, GLuint id) -{ - GLint info_len; - vt->GetProgramiv(id, GL_INFO_LOG_LENGTH, &info_len); - if (info_len > 0) - { - char *info_log = malloc(info_len); - if (info_log) - { - GLsizei written; - vt->GetProgramInfoLog(id, info_len, &written, info_log); - msg_Err(obj, "program: %s", info_log); - free(info_log); - } - } -} - -static GLuint -CreateShader(vlc_object_t *obj, const opengl_vtable_t *vt, GLenum type, - const char *src) -{ - GLuint shader = vt->CreateShader(type); - if (!shader) - return 0; - - vt->ShaderSource(shader, 1, &src, NULL); - vt->CompileShader(shader); - - LogShaderErrors(obj, vt, shader); - - GLint compiled; - vt->GetShaderiv(shader, GL_COMPILE_STATUS, &compiled); - if (!compiled) - { - msg_Err(obj, "Failed to compile shader"); - vt->DeleteShader(shader); - return 0; - } - - return shader; -} - -static GLuint -CreateProgram(vlc_object_t *obj, const opengl_vtable_t *vt) -{ - static const char *const VERTEX_SHADER_SRC = -#if defined(USE_OPENGL_ES2) - "#version 100\n" -#else - "#version 120\n" -#endif - "attribute vec2 vertex_pos;\n" - "attribute vec2 tex_coords_in;\n" - "varying vec2 tex_coords;\n" - "void main() {\n" - " tex_coords = tex_coords_in;\n" - " gl_Position = vec4(vertex_pos, 0.0, 1.0);\n" - "}\n"; - - static const char *const FRAGMENT_SHADER_SRC = -#if defined(USE_OPENGL_ES2) - "#version 100\n" - "precision mediump float;\n" -#else - "#version 120\n" -#endif - "uniform sampler2D sampler;\n" - "uniform float alpha;\n" - "varying vec2 tex_coords;\n" - "void main() {\n" - " vec4 color = texture2D(sampler, tex_coords);\n" - " color.a *= alpha;\n" - " gl_FragColor = color;\n" - "}\n"; - - GLuint program = 0; - - GLuint vertex_shader = CreateShader(obj, vt, GL_VERTEX_SHADER, - VERTEX_SHADER_SRC); - if (!vertex_shader) - return 0; - - GLuint fragment_shader = CreateShader(obj, vt, GL_FRAGMENT_SHADER, - FRAGMENT_SHADER_SRC); - if (!fragment_shader) - goto finally_1; - - program = vt->CreateProgram(); - if (!program) - goto finally_2; - - vt->AttachShader(program, vertex_shader); - vt->AttachShader(program, fragment_shader); - - vt->LinkProgram(program); - - LogProgramErrors(obj, vt, program); - - GLint linked; - vt->GetProgramiv(program, GL_LINK_STATUS, &linked); - if (!linked) - { - msg_Err(obj, "Failed to link program"); - vt->DeleteProgram(program); - program = 0; - } - -finally_2: - vt->DeleteShader(fragment_shader); -finally_1: - vt->DeleteShader(vertex_shader); - - return program; -} - static int FetchLocations(struct vlc_gl_sub_renderer *sr) { @@ -268,7 +134,40 @@ vlc_gl_sub_renderer_New(vlc_gl_t *gl, const opengl_vtable_t *vt, sr->region_count = 0; sr->regions = NULL; - sr->program_id = CreateProgram(VLC_OBJECT(sr->gl), vt); + static const char *const VERTEX_SHADER_SRC = +#if defined(USE_OPENGL_ES2) + "#version 100\n" +#else + "#version 120\n" +#endif + "attribute vec2 vertex_pos;\n" + "attribute vec2 tex_coords_in;\n" + "varying vec2 tex_coords;\n" + "void main() {\n" + " tex_coords = tex_coords_in;\n" + " gl_Position = vec4(vertex_pos, 0.0, 1.0);\n" + "}\n"; + + static const char *const FRAGMENT_SHADER_SRC = +#if defined(USE_OPENGL_ES2) + "#version 100\n" + "precision mediump float;\n" +#else + "#version 120\n" +#endif + "uniform sampler2D sampler;\n" + "uniform float alpha;\n" + "varying vec2 tex_coords;\n" + "void main() {\n" + " vec4 color = texture2D(sampler, tex_coords);\n" + " color.a *= alpha;\n" + " gl_FragColor = color;\n" + "}\n"; + + sr->program_id = + vlc_gl_BuildProgram(VLC_OBJECT(sr->gl), vt, + 1, (const char **) &VERTEX_SHADER_SRC, + 1, (const char **) &FRAGMENT_SHADER_SRC); if (!sr->program_id) goto error_2; -- 2.11.4.GIT