Add support for multiple shader models.
authorSteinar H. Gunderson <steinar+vlc@gunderson.no>
Fri, 21 Mar 2014 00:32:42 +0000 (01:32 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Fri, 21 Mar 2014 00:58:56 +0000 (01:58 +0100)
We support 1.10 (for OpenGL 2.1 cards), 1.30 (for OpenGL 3.2 core contexts),
and 3.00 ES (for GLES3). There's some code duplication, but thankfully
not a whole lot.

With this, we compile in core contexts without any warning from ATI's driver,
and should also in theory be GLES3 compliant (tested on NVidia's desktop driver).

20 files changed:
Makefile.in
dither_effect.frag
effect_chain.cpp
fft_pass_effect.frag
flat_input.frag
footer.130.frag [new file with mode: 0644]
footer.300es.frag [new file with mode: 0644]
header.130.frag [new file with mode: 0644]
header.300es.frag [new file with mode: 0644]
header.frag
init.cpp
init.h
resample_effect.frag
texture1d.130.frag [new file with mode: 0644]
texture1d.300es.frag [new file with mode: 0644]
util.cpp
util.h
vs.130.vert [new file with mode: 0644]
vs.300es.vert [new file with mode: 0644]
ycbcr_input.frag

index 69fa663..3b5f6c3 100644 (file)
@@ -163,12 +163,14 @@ HDRS = effect_chain.h effect_util.h effect.h input.h image_format.h init.h util.
 HDRS += $(INPUTS:=.h)
 HDRS += $(EFFECTS:=.h)
 
-SHADERS = vs.vert header.frag footer.frag
+SHADERS = vs.vert vs.130.vert vs.300es.vert
+SHADERS += header.frag header.130.frag header.300es.frag
+SHADERS += footer.frag footer.130.frag footer.300es.frag
+SHADERS += texture1d.frag texture1d.130.frag footer.300es.frag
 SHADERS += $(INPUTS:=.frag)
 SHADERS += $(EFFECTS:=.frag)
 SHADERS += highlight_cutoff_effect.frag
 SHADERS += overlay_matte_effect.frag
-SHADERS += texture1d.frag
 
 # These purposefully do not exist.
 MISSING_SHADERS = diffusion_effect.frag glow_effect.frag unsharp_mask_effect.frag resize_effect.frag
index 6994e84..3b6b892 100644 (file)
@@ -9,7 +9,7 @@ vec4 FUNCNAME(vec2 tc) {
        // and if there's any inaccuracy earlier in the chain so that it becomes e.g.
        // 254.8, it's better to just get it rounded off than to dither and have it
        // possibly get down to 254. This is not the case for the color components.
-       result.rgb += texture2D(PREFIX(dither_tex), tc * PREFIX(tc_scale)).xxx;
+       result.rgb += tex2D(PREFIX(dither_tex), tc * PREFIX(tc_scale)).xxx;
 
        // NEED_EXPLICIT_ROUND will be #defined to 1 if the GPU has inaccurate
        // fp32 -> int8 framebuffer rounding, and 0 otherwise.
index 7f6c943..82f1b29 100644 (file)
@@ -243,7 +243,7 @@ string replace_prefix(const string &text, const string &prefix)
 
 void EffectChain::compile_glsl_program(Phase *phase)
 {
-       string frag_shader = read_file("header.frag");
+       string frag_shader = read_version_dependent_file("header", "frag");
 
        // Create functions for all the texture inputs that we need.
        for (unsigned i = 0; i < phase->inputs.size(); ++i) {
@@ -254,7 +254,7 @@ void EffectChain::compile_glsl_program(Phase *phase)
        
                frag_shader += string("uniform sampler2D tex_") + effect_id + ";\n";
                frag_shader += string("vec4 ") + effect_id + "(vec2 tc) {\n";
-               frag_shader += "\treturn texture2D(tex_" + string(effect_id) + ", tc);\n";
+               frag_shader += "\treturn tex2D(tex_" + string(effect_id) + ", tc);\n";
                frag_shader += "}\n";
                frag_shader += "\n";
        }
@@ -293,9 +293,10 @@ void EffectChain::compile_glsl_program(Phase *phase)
                frag_shader += "\n";
        }
        frag_shader += string("#define INPUT ") + phase->effect_ids[phase->effects.back()] + "\n";
-       frag_shader.append(read_file("footer.frag"));
+       frag_shader.append(read_version_dependent_file("footer", "frag"));
 
-       phase->glsl_program_num = resource_pool->compile_glsl_program(read_file("vs.vert"), frag_shader);
+       string vert_shader = read_version_dependent_file("vs", "vert");
+       phase->glsl_program_num = resource_pool->compile_glsl_program(vert_shader, frag_shader);
 
        // Prepare the geometry for the fullscreen quad used in this phase.
        // (We have separate VAOs per shader, since the bindings can in theory
index edcf695..607f86a 100644 (file)
@@ -6,11 +6,11 @@ uniform sampler2D PREFIX(support_tex);
 
 vec4 FUNCNAME(vec2 tc) {
 #if DIRECTION_VERTICAL
-       vec4 support = texture2D(PREFIX(support_tex), vec2(tc.y * PREFIX(num_repeats), 0.0));
+       vec4 support = tex2D(PREFIX(support_tex), vec2(tc.y * PREFIX(num_repeats), 0.0));
         vec4 c1 = INPUT(vec2(tc.x, tc.y + support.x));
         vec4 c2 = INPUT(vec2(tc.x, tc.y + support.y));
 #else
-       vec4 support = texture2D(PREFIX(support_tex), vec2(tc.x * PREFIX(num_repeats), 0.0));
+       vec4 support = tex2D(PREFIX(support_tex), vec2(tc.x * PREFIX(num_repeats), 0.0));
         vec4 c1 = INPUT(vec2(tc.x + support.x, tc.y));
         vec4 c2 = INPUT(vec2(tc.x + support.y, tc.y));
 #endif
index 007675e..855d4fb 100644 (file)
@@ -6,7 +6,7 @@ vec4 FUNCNAME(vec2 tc) {
        // we flip the y coordinate.
        tc.y = 1.0 - tc.y;
 
-       vec4 pixel = texture2D(PREFIX(tex), tc);
+       vec4 pixel = tex2D(PREFIX(tex), tc);
 
        // These two are #defined to 0 or 1 in flat_input.cpp.
 #if FIXUP_SWAP_RB
diff --git a/footer.130.frag b/footer.130.frag
new file mode 100644 (file)
index 0000000..83f615f
--- /dev/null
@@ -0,0 +1,6 @@
+out vec4 FragColor;
+
+void main()
+{
+       FragColor = INPUT(tc);
+}
diff --git a/footer.300es.frag b/footer.300es.frag
new file mode 100644 (file)
index 0000000..83f615f
--- /dev/null
@@ -0,0 +1,6 @@
+out vec4 FragColor;
+
+void main()
+{
+       FragColor = INPUT(tc);
+}
diff --git a/header.130.frag b/header.130.frag
new file mode 100644 (file)
index 0000000..129f493
--- /dev/null
@@ -0,0 +1,8 @@
+#version 130
+
+in vec2 tc;
+
+vec4 tex2D(sampler2D s, vec2 coord)
+{
+       return texture(s, coord);
+}
diff --git a/header.300es.frag b/header.300es.frag
new file mode 100644 (file)
index 0000000..ea52263
--- /dev/null
@@ -0,0 +1,10 @@
+#version 300 es
+
+precision highp float;
+
+in vec2 tc;
+
+vec4 tex2D(sampler2D s, vec2 coord)
+{
+       return texture(s, coord);
+}
index fe05ef5..44ce5b4 100644 (file)
@@ -8,3 +8,8 @@ precision highp float;
 #endif
 
 varying vec2 tc;
+
+vec4 tex2D(sampler2D s, vec2 coord)
+{
+       return texture2D(s, coord);
+}
index f6f45a0..4802c68 100644 (file)
--- a/init.cpp
+++ b/init.cpp
@@ -18,6 +18,7 @@ float movit_texel_subpixel_precision;
 bool movit_srgb_textures_supported;
 int movit_num_wrongly_rounded;
 bool movit_shader_rounding_supported;
+MovitShaderModel movit_shader_model;
 
 // The rules for objects with nontrivial constructors in static scope
 // are somewhat convoluted, and easy to mess up. We simply have a
@@ -78,7 +79,8 @@ void measure_texel_subpixel_precision()
        glViewport(0, 0, width, 1);
 
        GLuint glsl_program_num = resource_pool.compile_glsl_program(
-               read_file("vs.vert"), read_file("texture1d.frag"));
+               read_version_dependent_file("vs", "vert"),
+               read_version_dependent_file("texture1d", "frag"));
        glUseProgram(glsl_program_num);
        check_error();
        glUniform1i(glGetUniformLocation(glsl_program_num, "tex"), 0);  // Bind the 2D sampler.
@@ -209,7 +211,8 @@ void measure_roundoff_problems()
        glViewport(0, 0, 512, 1);
 
        GLuint glsl_program_num = resource_pool.compile_glsl_program(
-               read_file("vs.vert"), read_file("texture1d.frag"));
+               read_version_dependent_file("vs", "vert"),
+               read_version_dependent_file("texture1d", "frag"));
        glUseProgram(glsl_program_num);
        check_error();
        glUniform1i(glGetUniformLocation(glsl_program_num, "tex"), 0);  // Bind the 2D sampler.
@@ -334,6 +337,31 @@ bool check_extensions()
        return true;
 }
 
+double get_glsl_version()
+{
+       char *glsl_version_str = strdup((const char *)glGetString(GL_SHADING_LANGUAGE_VERSION));
+
+       // Skip past the first period.
+       char *ptr = strchr(glsl_version_str, '.');
+       assert(ptr != NULL);
+       ++ptr;
+
+       // Now cut the string off at the next period or space, whatever comes first
+       // (unless the string ends first).
+       while (*ptr && *ptr != '.' && *ptr != ' ') {
+               ++ptr;
+       }
+       *ptr = '\0';
+
+       // Now we have something on the form X.YY. We convert it to a float, and hope
+       // that if it's inexact (e.g. 1.30), atof() will round the same way the
+       // compiler will.
+       float glsl_version = atof(glsl_version_str);
+       free(glsl_version_str);
+
+       return glsl_version;
+}
+
 }  // namespace
 
 bool init_movit(const string& data_directory, MovitDebugLevel debug_level)
@@ -353,6 +381,18 @@ bool init_movit(const string& data_directory, MovitDebugLevel debug_level)
        if (!check_extensions()) {
                return false;
        }
+
+       // Find out what shader model we should compile for.
+       if (epoxy_is_desktop_gl()) {
+               if (get_glsl_version() >= 1.30) {
+                       movit_shader_model = MOVIT_GLSL_130;
+               } else {
+                       movit_shader_model = MOVIT_GLSL_110;
+               }
+       } else {
+               movit_shader_model = MOVIT_ESSL_300;
+       }
+
        measure_texel_subpixel_precision();
        measure_roundoff_problems();
 
diff --git a/init.h b/init.h
index d6f6dd4..628915d 100644 (file)
--- a/init.h
+++ b/init.h
@@ -67,6 +67,15 @@ extern bool movit_shader_rounding_supported;
 // Whether the GPU in use supports GL_EXT_texture_sRGB.
 extern bool movit_srgb_textures_supported;
 
+// What shader model we are compiling for. This only affects the choice
+// of a few files (like header.frag); most of the shaders are the same.
+enum MovitShaderModel {
+       MOVIT_GLSL_110,
+       MOVIT_GLSL_130,
+       MOVIT_ESSL_300
+};
+extern MovitShaderModel movit_shader_model;
+
 }  // namespace movit
 
 #endif  // !defined(_MOVIT_INIT_H)
index a31df17..d71b5f5 100644 (file)
@@ -19,7 +19,7 @@ vec4 PREFIX(do_sample)(vec2 tc, int i)
 #else
        sample_tc.y = tc.x * PREFIX(num_loops);
 #endif
-       vec2 sample = texture2D(PREFIX(sample_tex), sample_tc).rg;
+       vec2 sample = tex2D(PREFIX(sample_tex), sample_tc).rg;
 
 #if DIRECTION_VERTICAL
        tc.y = sample.g + floor(sample_tc.y) * PREFIX(slice_height);
diff --git a/texture1d.130.frag b/texture1d.130.frag
new file mode 100644 (file)
index 0000000..a71f90e
--- /dev/null
@@ -0,0 +1,11 @@
+#version 130
+
+uniform sampler2D tex;
+in vec2 tc;
+
+out vec4 FragColor;
+
+void main()
+{
+       FragColor = texture(tex, tc);  // Second component is irrelevant.
+}
diff --git a/texture1d.300es.frag b/texture1d.300es.frag
new file mode 100644 (file)
index 0000000..ddea589
--- /dev/null
@@ -0,0 +1,13 @@
+#version 300 es
+
+precision highp float;
+
+uniform sampler2D tex;
+in vec2 tc;
+
+out vec4 FragColor;
+
+void main()
+{
+       FragColor = texture(tex, tc);  // Second component is irrelevant.
+}
index cce3d54..01787f0 100644 (file)
--- a/util.cpp
+++ b/util.cpp
@@ -96,6 +96,19 @@ string read_file(const string &filename)
        return string(buf, len);
 }
 
+string read_version_dependent_file(const string &base, const string &extension)
+{
+       if (movit_shader_model == MOVIT_GLSL_110) {
+               return read_file(base + "." + extension);
+       } else if (movit_shader_model == MOVIT_GLSL_130) {
+               return read_file(base + ".130." + extension);
+       } else if (movit_shader_model == MOVIT_ESSL_300) {
+               return read_file(base + ".300es." + extension);
+       } else {
+               assert(false);
+       }
+}
+
 GLuint compile_shader(const string &shader_src, GLenum type)
 {
        GLuint obj = glCreateShader(type);
diff --git a/util.h b/util.h
index 6def56d..1fa4e78 100644 (file)
--- a/util.h
+++ b/util.h
@@ -24,6 +24,10 @@ void hsv2rgb_normalized(float h, float s, float v, float *r, float *g, float *b)
 // Dies if the file does not exist.
 std::string read_file(const std::string &filename);
 
+// Reads <base>.<extension>, <base>.130.<extension> or <base>.300es.<extension> and
+// returns its contents, depending on <movit_shader_level>.
+std::string read_version_dependent_file(const std::string &base, const std::string &extension);
+
 // Compile the given GLSL shader (typically a vertex or fragment shader)
 // and return the object number.
 GLuint compile_shader(const std::string &shader_src, GLenum type);
diff --git a/vs.130.vert b/vs.130.vert
new file mode 100644 (file)
index 0000000..7b4ebab
--- /dev/null
@@ -0,0 +1,17 @@
+#version 130
+
+in vec2 position;
+in vec2 texcoord;
+out vec2 tc;
+
+void main()
+{
+       // The result of glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0) is:
+       //
+       //   2.000  0.000  0.000 -1.000
+       //   0.000  2.000  0.000 -1.000
+       //   0.000  0.000 -2.000 -1.000
+       //   0.000  0.000  0.000  1.000
+       gl_Position = vec4(2.0 * position.x - 1.0, 2.0 * position.y - 1.0, -1.0, 1.0);
+       tc = texcoord;
+}
diff --git a/vs.300es.vert b/vs.300es.vert
new file mode 100644 (file)
index 0000000..542be89
--- /dev/null
@@ -0,0 +1,19 @@
+#version 300 es
+
+precision highp float;
+
+in vec2 position;
+in vec2 texcoord;
+out vec2 tc;
+
+void main()
+{
+       // The result of glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0) is:
+       //
+       //   2.000  0.000  0.000 -1.000
+       //   0.000  2.000  0.000 -1.000
+       //   0.000  0.000 -2.000 -1.000
+       //   0.000  0.000  0.000  1.000
+       gl_Position = vec4(2.0 * position.x - 1.0, 2.0 * position.y - 1.0, -1.0, 1.0);
+       tc = texcoord;
+}
index 8da8256..fbf20d1 100644 (file)
@@ -9,9 +9,9 @@ vec4 FUNCNAME(vec2 tc) {
        tc.y = 1.0 - tc.y;
 
        vec3 ycbcr;
-       ycbcr.x = texture2D(PREFIX(tex_y), tc).x;
-       ycbcr.y = texture2D(PREFIX(tex_cb), tc + PREFIX(cb_offset)).x;
-       ycbcr.z = texture2D(PREFIX(tex_cr), tc + PREFIX(cr_offset)).x;
+       ycbcr.x = tex2D(PREFIX(tex_y), tc).x;
+       ycbcr.y = tex2D(PREFIX(tex_cb), tc + PREFIX(cb_offset)).x;
+       ycbcr.z = tex2D(PREFIX(tex_cr), tc + PREFIX(cr_offset)).x;
 
        ycbcr -= PREFIX(offset);
        ycbcr *= PREFIX(scale);