]> git.tdb.fi Git - libs/gl.git/blobdiff - source/programbuilder.cpp
Generalize shader interface handling
[libs/gl.git] / source / programbuilder.cpp
index bf89e7db8270ddb552ab8d3eebe49bbd687047aa..67aaf57047ec08db186956b73120ac58c7f348a3 100644 (file)
@@ -116,7 +116,7 @@ const ProgramBuilder::VariableDefinition ProgramBuilder::standard_variables[] =
        { UNIFORM, "eye_obj_normal_matrix", "mat3", "gl_NormalMatrix", 0 },
        { UNIFORM, "projection_matrix", "mat4", "gl_ProjectionMatrix", 0 },
        { UNIFORM, "shd_eye_matrix", "mat4", 0, 0 },
-       { UNIFORM, "light_sources", "struct { vec4 position; vec4 diffuse; vec4 specular; } %s[2]", "gl_LightSource", 0 },
+       { UNIFORM, "light_sources", "struct { vec4 position; vec4 diffuse; vec4 specular; }[2]", "gl_LightSource", 0 },
        { UNIFORM, "ambient_color", "vec4", 0, 0 },
        { UNIFORM, "material", "struct { vec4 ambient; vec4 diffuse; vec4 specular; float shininess; }", "gl_FrontMaterial", 0 },
 
@@ -124,6 +124,8 @@ const ProgramBuilder::VariableDefinition ProgramBuilder::standard_variables[] =
        { NO_SCOPE, 0, 0, 0, 0 }
 };
 
+const char ProgramBuilder::interfaces[] = { 0, 0, 0, 'v', 0 };
+
 ProgramBuilder::ProgramBuilder(const StandardFeatures &f):
        features(f),
        feature_flags(features.create_flags()),
@@ -325,62 +327,45 @@ string ProgramBuilder::create_source(const list<ShaderVariable *> &variables, Va
 
        for(list<ShaderVariable *>::const_iterator i=variables.begin(); i!=variables.end(); ++i)
                if((*i)->variable->scope==UNIFORM && (*i)->is_referenced_from(scope) && !(*i)->inlined)
-               {
-                       if(strchr((*i)->variable->type, '%'))
-                               source += format("uniform %s;\n", format((*i)->variable->type, (*i)->resolved_name));
-                       else
-                               source += format("uniform %s %s;\n", (*i)->variable->type, (*i)->resolved_name);
-               }
+                       source += format("uniform %s;\n", (*i)->create_declaration());
 
-       if(scope==VERTEX)
+       /* Interface variables need to have global declarations. */
+       for(list<ShaderVariable *>::const_iterator i=variables.begin(); i!=variables.end(); ++i)
        {
-               const char *qualifier = (features.legacy ? "attribute" : "in");
-               for(list<ShaderVariable *>::const_iterator i=variables.begin(); i!=variables.end(); ++i)
-                       if((*i)->variable->scope==ATTRIBUTE && !(*i)->inlined)
-                               source += format("%s %s %s;\n", qualifier, (*i)->variable->type, (*i)->resolved_name);
-       }
+               if(!(*i)->resolved_name.compare(0, 3, "gl_"))
+                       continue;
 
-       /* Any variables defined in vertex scope but referenced from fragment scope
-       should be exported as varyings over the interface. */
-       list<ShaderVariable *> varyings;
-       for(list<ShaderVariable *>::const_iterator i=variables.begin(); i!=variables.end(); ++i)
-               if(((*i)->variable->scope==VERTEX || (*i)->variable->scope==ATTRIBUTE) && (*i)->is_referenced_from(FRAGMENT))
+               InterfaceFlags interface = (*i)->get_interface_flags(scope);
+
+               if(interface&INPUT)
                {
-                       varyings.push_back(*i);
-                       const char *qualifier;
-                       if(!features.legacy)
-                               qualifier = (scope==VERTEX ? "out" : "in");
-                       else
-                               qualifier = "varying";
-                       source += format("%s %s v_%s;\n", qualifier, (*i)->variable->type, (*i)->resolved_name);
+                       const char *qualifier = (features.legacy ? scope==VERTEX ? "attribute" : "varying" : "in");
+                       source += format("%s %s;\n", qualifier, (*i)->create_declaration(interfaces[scope-1]));
                }
 
-       for(list<ShaderVariable *>::const_iterator i=variables.begin(); i!=variables.end(); ++i)
-               if((*i)->referenced_by.empty() && (*i)->resolved_name.compare(0, 3, "gl_"))
-                       source += format("out %s %s;\n", (*i)->variable->type, (*i)->resolved_name);
+               if(interface&OUTPUT)
+               {
+                       const char *qualifier = (features.legacy ? "varying" : "out");
+                       source += format("%s %s;\n", qualifier, (*i)->create_declaration(interfaces[scope]));
+               }
+       }
 
        source += "void main()\n{\n";
 
        for(list<ShaderVariable *>::const_iterator i=variables.begin(); i!=variables.end(); ++i)
+       {
+               InterfaceFlags interface = (*i)->get_interface_flags(scope);
+
                if((*i)->variable->scope==scope && !(*i)->inlined)
                {
-                       source += '\t';
-                       if(!(*i)->referenced_by.empty())
-                       {
-                               source += (*i)->variable->type;
-                               source += ' ';
-                       }
-                       source += format("%s = %s;\n", (*i)->resolved_name, (*i)->get_expression());
+                       string decl = ((interface&GOAL) ? (*i)->resolved_name : (*i)->create_declaration());
+                       source += format("\t%s = %s;\n", decl, (*i)->create_expression());
                }
 
-       if(scope==VERTEX)
-       {
-               for(list<ShaderVariable *>::const_iterator i=varyings.begin(); i!=varyings.end(); ++i)
+               if((interface&(OUTPUT|GOAL))==OUTPUT)
                {
-                       if((*i)->inlined)
-                               source += format("\tv_%s = %s;\n", (*i)->resolved_name, (*i)->get_expression());
-                       else
-                               source += format("\tv_%s = %s;\n", (*i)->resolved_name, (*i)->resolved_name);
+                       string expr = ((*i)->inlined ? (*i)->create_expression() : (*i)->resolved_name);
+                       source += format("\t%c_%s = %s;\n", interfaces[scope], (*i)->resolved_name, expr);
                }
        }
 
@@ -678,24 +663,76 @@ bool ProgramBuilder::ShaderVariable::is_referenced_from(VariableScope scope) con
        return false;
 }
 
-string ProgramBuilder::ShaderVariable::get_expression() const
+ProgramBuilder::InterfaceFlags ProgramBuilder::ShaderVariable::get_interface_flags(VariableScope scope) const
 {
-       map<string, string> replace_map;
-       for(list<ShaderVariable *>::const_iterator i=referenced_vars.begin(); i!=referenced_vars.end(); ++i)
-               if((*i)->variable)
+       /* Uniforms are available to all stages and are not passed through
+       interfaces */
+       if(variable->scope==UNIFORM)
+               return NO_INTERFACE;
+
+       int flags = NO_INTERFACE;
+
+       for(list<ShaderVariable *>::const_iterator i=referenced_by.begin(); i!=referenced_by.end(); ++i)
+       {
+               /* Variables used in a later scope than they are declared in need to go
+               through the interface */
+               if((*i)->variable->scope>scope && variable->scope<=scope)
+                       flags |= OUTPUT;
+               if((*i)->variable->scope>=scope && variable->scope<scope)
+                       if(!inlined || variable->scope!=ATTRIBUTE || scope!=VERTEX)
+                               flags |= INPUT;
+       }
+
+       // Variables without any references are goals and also outputs.
+       if(referenced_by.empty() && variable->scope==scope)
+               flags |= OUTPUT|GOAL;
+
+       return static_cast<InterfaceFlags>(flags);
+}
+
+string ProgramBuilder::ShaderVariable::create_declaration(char interface) const
+{
+       if(variable->scope==UNIFORM)
+       {
+               const char *bracket = strrchr(variable->type, '[');
+               if(bracket)
+                       return format("%s %s%s", string(variable->type, bracket), resolved_name, bracket);
+       }
+
+       if(interface)
+               return format("%s %c_%s", variable->type, interface, resolved_name);
+       else
+               return format("%s %s", variable->type, resolved_name);
+}
+
+string ProgramBuilder::ShaderVariable::create_replacement(VariableScope from_scope) const
+{
+       string replacement = resolved_name;
+       if(variable)
+       {
+               InterfaceFlags interface = get_interface_flags(from_scope);
+               if((interface&INPUT) && interfaces[from_scope-1])
+                       replacement = format("%c_%s", interfaces[from_scope-1], replacement);
+               else if(inlined)
                {
-                       string replacement = (*i)->resolved_name;
-                       if(variable->scope==FRAGMENT && ((*i)->variable->scope==VERTEX || (*i)->variable->scope==ATTRIBUTE))
-                               replacement = "v_"+replacement;
-                       else if((*i)->inlined)
-                       {
-                               replacement = (*i)->get_expression();
-                               if((*i)->inline_parens)
-                                       replacement = "("+replacement+")";
-                       }
-                       if(replacement!=(*i)->name)
-                               replace_map[(*i)->name] = replacement;
+                       replacement = create_expression();
+                       if(inline_parens)
+                               replacement = "("+replacement+")";
                }
+       }
+
+       return replacement;
+}
+
+string ProgramBuilder::ShaderVariable::create_expression() const
+{
+       map<string, string> replace_map;
+       for(list<ShaderVariable *>::const_iterator i=referenced_vars.begin(); i!=referenced_vars.end(); ++i)
+       {
+               string replacement = (*i)->create_replacement(variable->scope);
+               if(replacement!=(*i)->name)
+                       replace_map[(*i)->name] = replacement;
+       }
 
        if(replace_map.empty())
                return variable->expression;