]> git.sesse.net Git - movit/commitdiff
Explicitly bind fragment shader outputs in order.
authorSteinar H. Gunderson <steinar+vlc@gunderson.no>
Sat, 12 Dec 2015 13:31:40 +0000 (14:31 +0100)
committerSteinar H. Gunderson <steinar+vlc@gunderson.no>
Sat, 12 Dec 2015 13:32:49 +0000 (14:32 +0100)
Evidently ATI drivers use the freedom the standard gives them to assign
these in another order than they are specified in the shader source,
so we need to explicitly bind them, or YCbCrConversionEffectTest will fail
in the multi-output tests.

Originally reported by Iwan Gabovitch.

effect_chain.cpp
init.cpp
resource_pool.cpp
resource_pool.h
version.h
widgets.cpp

index 8a72e1756dd90a98502de0944ffc00225a676227..c2402daeaef400d9ded80dcbce3378be9f94ac42 100644 (file)
@@ -376,16 +376,23 @@ void EffectChain::compile_glsl_program(Phase *phase)
        frag_shader += string("#define INPUT ") + phase->effect_ids[phase->effects.back()] + "\n";
 
        // If we're the last phase, add the right #defines for Y'CbCr multi-output as needed.
+       vector<string> frag_shader_outputs;  // In order.
        if (phase->output_node->outgoing_links.empty() && output_color_ycbcr) {
                switch (output_ycbcr_splitting) {
                case YCBCR_OUTPUT_INTERLEAVED:
                        // No #defines set.
+                       frag_shader_outputs.push_back("FragColor");
                        break;
                case YCBCR_OUTPUT_SPLIT_Y_AND_CBCR:
                        frag_shader += "#define YCBCR_OUTPUT_SPLIT_Y_AND_CBCR 1\n";
+                       frag_shader_outputs.push_back("Y");
+                       frag_shader_outputs.push_back("Chroma");
                        break;
                case YCBCR_OUTPUT_PLANAR:
                        frag_shader += "#define YCBCR_OUTPUT_PLANAR 1\n";
+                       frag_shader_outputs.push_back("Y");
+                       frag_shader_outputs.push_back("Cb");
+                       frag_shader_outputs.push_back("Cr");
                        break;
                default:
                        assert(false);
@@ -396,6 +403,7 @@ void EffectChain::compile_glsl_program(Phase *phase)
                        // output needs to see it (YCbCrConversionEffect and DitherEffect
                        // do, too).
                        frag_shader_header += "#define YCBCR_ALSO_OUTPUT_RGBA 1\n";
+                       frag_shader_outputs.push_back("RGBA");
                }
        }
        frag_shader.append(read_file("footer.frag"));
@@ -439,7 +447,7 @@ void EffectChain::compile_glsl_program(Phase *phase)
                vert_shader[pos + needle.size() - 1] = '1';
        }
 
-       phase->glsl_program_num = resource_pool->compile_glsl_program(vert_shader, frag_shader);
+       phase->glsl_program_num = resource_pool->compile_glsl_program(vert_shader, frag_shader, frag_shader_outputs);
 
        // Collect the resulting location numbers for each uniform.
        collect_uniform_locations(phase->glsl_program_num, &phase->uniforms_sampler2d);
index 7781ba24ccd7e8fb76c1e5f305ecff8ba75c35bc..733d24689b5f5c4f2ef56135a32030e92fa9738f 100644 (file)
--- a/init.cpp
+++ b/init.cpp
@@ -79,9 +79,11 @@ void measure_texel_subpixel_precision()
 
        glViewport(0, 0, width, 1);
 
+       vector<string> frag_shader_outputs;
        GLuint glsl_program_num = resource_pool.compile_glsl_program(
                read_version_dependent_file("vs", "vert"),
-               read_version_dependent_file("texture1d", "frag"));
+               read_version_dependent_file("texture1d", "frag"),
+               frag_shader_outputs);
        glUseProgram(glsl_program_num);
        check_error();
        glUniform1i(glGetUniformLocation(glsl_program_num, "tex"), 0);  // Bind the 2D sampler.
@@ -211,9 +213,11 @@ void measure_roundoff_problems()
 
        glViewport(0, 0, 512, 1);
 
+       vector<string> frag_shader_outputs;
        GLuint glsl_program_num = resource_pool.compile_glsl_program(
                read_version_dependent_file("vs", "vert"),
-               read_version_dependent_file("texture1d", "frag"));
+               read_version_dependent_file("texture1d", "frag"),
+               frag_shader_outputs);
        glUseProgram(glsl_program_num);
        check_error();
        glUniform1i(glGetUniformLocation(glsl_program_num, "tex"), 0);  // Bind the 2D sampler.
index be74b3d96a563120dde3c8a4f3cc50ff6c5539ed..944910bde3829320abcbdd859b861c3995b3da4f 100644 (file)
@@ -100,11 +100,23 @@ void ResourcePool::delete_program(GLuint glsl_program_num)
        program_shaders.erase(shader_it);
 }
 
-GLuint ResourcePool::compile_glsl_program(const string& vertex_shader, const string& fragment_shader)
+GLuint ResourcePool::compile_glsl_program(const string& vertex_shader,
+                                          const string& fragment_shader,
+                                          const vector<string>& fragment_shader_outputs)
 {
        GLuint glsl_program_num;
        pthread_mutex_lock(&lock);
-       const pair<string, string> key(vertex_shader, fragment_shader);
+
+       // Augment the fragment shader program text with the outputs, so that they become
+       // part of the key. Also potentially useful for debugging.
+       string fragment_shader_processed = fragment_shader;
+       for (unsigned output_index = 0; output_index < fragment_shader_outputs.size(); ++output_index) {
+               char buf[256];
+               snprintf(buf, sizeof(buf), "// Bound output: %s\n", fragment_shader_outputs[output_index].c_str());
+               fragment_shader_processed += buf;
+       }
+
+       const pair<string, string> key(vertex_shader, fragment_shader_processed);
        if (programs.count(key)) {
                // Already in the cache. Increment the refcount, or take it off the freelist
                // if it's zero.
@@ -125,12 +137,21 @@ GLuint ResourcePool::compile_glsl_program(const string& vertex_shader, const str
                check_error();
                GLuint vs_obj = compile_shader(vertex_shader, GL_VERTEX_SHADER);
                check_error();
-               GLuint fs_obj = compile_shader(fragment_shader, GL_FRAGMENT_SHADER);
+               GLuint fs_obj = compile_shader(fragment_shader_processed, GL_FRAGMENT_SHADER);
                check_error();
                glAttachShader(glsl_program_num, vs_obj);
                check_error();
                glAttachShader(glsl_program_num, fs_obj);
                check_error();
+
+               // Bind the outputs, if we have multiple ones.
+               if (fragment_shader_outputs.size() > 1) {
+                       for (unsigned output_index = 0; output_index < fragment_shader_outputs.size(); ++output_index) {
+                               glBindFragDataLocation(glsl_program_num, output_index,
+                                                      fragment_shader_outputs[output_index].c_str());
+                       }
+               }
+
                glLinkProgram(glsl_program_num);
                check_error();
 
@@ -153,7 +174,7 @@ GLuint ResourcePool::compile_glsl_program(const string& vertex_shader, const str
                                perror(filename);
                                exit(1);
                        }
-                       fprintf(fp, "%s\n", fragment_shader.c_str());
+                       fprintf(fp, "%s\n", fragment_shader_processed.c_str());
                        fclose(fp);
                }
 
index 8881958d27b0b1c4d2a082bbb649dcecc2304347..5cc2e829e8f836635b04980978372973d3618639 100644 (file)
@@ -29,6 +29,7 @@
 #include <map>
 #include <string>
 #include <utility>
+#include <vector>
 
 namespace movit {
 
@@ -56,7 +57,14 @@ public:
        // compiled program from the cache if possible. Keeps ownership of the
        // program; you must call release_glsl_program() instead of deleting it
        // when you no longer want it.
-       GLuint compile_glsl_program(const std::string& vertex_shader, const std::string& fragment_shader);
+       //
+       // If <fragment_shader_outputs> contains more than one value, the given
+       // outputs will be bound to fragment shader output colors in the order
+       // they appear in the vector. Otherwise, output order is undefined and
+       // determined by the OpenGL driver.
+       GLuint compile_glsl_program(const std::string& vertex_shader,
+                                   const std::string& fragment_shader,
+                                   const std::vector<std::string>& frag_shader_outputs);
        void release_glsl_program(GLuint glsl_program_num);
 
        // Allocate a 2D texture of the given internal format and dimensions,
index 1d6ab91d41bc8502f32a0c719ab3c0019cb98816..3f863c00cc71745353000acb16c3c8d4d6db50d4 100644 (file)
--- a/version.h
+++ b/version.h
@@ -5,6 +5,6 @@
 // changes, even within git versions. There is no specific version
 // documentation outside the regular changelogs, though.
 
-#define MOVIT_VERSION 14
+#define MOVIT_VERSION 15
 
 #endif // !defined(_MOVIT_VERSION_H)
index 529d902e878443c704ae5721a78aa1f4df40b34b..66ec7c5c9ab932c6dbeaf7e02fb007e7fad87489 100644 (file)
@@ -1,12 +1,17 @@
 #include <epoxy/gl.h>
 #include <math.h>
 
+#include <string>
+#include <vector>
+
 #include "resource_pool.h"
 #include "widgets.h"
 #include "util.h"
 
 #define HSV_WHEEL_SIZE 128
 
+using namespace std;
+
 namespace movit {
 
 GLuint hsv_wheel_texnum = 0;
@@ -188,12 +193,15 @@ void make_hsv_wheel_texture()
 
 void init_hsv_resources()
 {
+       vector<string> frag_shader_outputs;
        textured_program_num = resource_pool.compile_glsl_program(
                read_version_dependent_file("vs", "vert"),
-               read_version_dependent_file("texture1d", "frag"));
+               read_version_dependent_file("texture1d", "frag"),
+               frag_shader_outputs);
        colored_program_num = resource_pool.compile_glsl_program(
                read_version_dependent_file("vs-color", "vert"),
-               read_version_dependent_file("color", "frag"));
+               read_version_dependent_file("color", "frag"),
+               frag_shader_outputs);
        make_hsv_wheel_texture();
 }