]> git.tdb.fi Git - libs/gl.git/blobdiff - source/glsl/validate.cpp
Validate location overlap and type matching for GLSL interfaces
[libs/gl.git] / source / glsl / validate.cpp
index 154189aa2bc8c353398b9b92a6a521c47c5afe59..0096ff5e5106f5a0547b24472a2a3397968e5cad 100644 (file)
@@ -609,6 +609,139 @@ void ExpressionValidator::visit(Return &ret)
        TraversingVisitor::visit(ret);
 }
 
+
+int StageInterfaceValidator::get_location(const Layout &layout)
+{
+       for(vector<Layout::Qualifier>::const_iterator i=layout.qualifiers.begin(); i!=layout.qualifiers.end(); ++i)
+               if(i->name=="location")
+                       return i->value;
+       return -1;
+}
+
+void StageInterfaceValidator::visit(VariableDeclaration &var)
+{
+       int location = (var.layout ? get_location(*var.layout) : -1);
+       if(var.interface=="in" && var.linked_declaration)
+       {
+               const Layout *linked_layout = var.linked_declaration->layout.get();
+               int linked_location = (linked_layout ? get_location(*linked_layout) : -1);
+               if(linked_location!=location)
+               {
+                       error(var, format("Mismatched location %d for 'in %s'", location, var.name));
+                       add_info(*var.linked_declaration, format("Linked to 'out %s' with location %d",
+                               var.linked_declaration->name, linked_location));
+               }
+               if(var.type_declaration && var.linked_declaration->type_declaration)
+               {
+                       const TypeDeclaration *type = var.type_declaration;
+                       if(stage->type==Stage::GEOMETRY)
+                       {
+                               if(const BasicTypeDeclaration *basic = dynamic_cast<const BasicTypeDeclaration *>(type))
+                                       if(basic->kind==BasicTypeDeclaration::ARRAY && basic->base_type)
+                                               type = basic->base_type;
+                       }
+                       if(!is_same_type(*type, *var.linked_declaration->type_declaration))
+                       {
+                               error(var, format("Mismatched type '%s' for 'in %s'", type->name, var.name));
+                               add_info(*var.linked_declaration, format("Linked to 'out %s' with type '%s'",
+                                       var.linked_declaration->name, var.linked_declaration->type_declaration->name));
+                       }
+               }
+       }
+
+       if(location>=0 && !var.interface.empty())
+       {
+               map<unsigned, VariableDeclaration *> &used = used_locations[var.interface];
+
+               unsigned loc_count = 1;
+               if(var.array)
+                       if(const Literal *literal = dynamic_cast<const Literal *>(var.array_size.get()))
+                               if(literal->value.check_type<int>())
+                                       loc_count = literal->value.value<int>();
+
+               for(unsigned i=0; i<loc_count; ++i)
+               {
+                       map<unsigned, VariableDeclaration *>::const_iterator j = used.find(location+i);
+                       if(j!=used.end())
+                       {
+                               error(var, format("Overlapping location %d for '%s %s'", location+i, var.interface, var.name));
+                               add_info(*j->second, format("Previously used here for '%s %s'", j->second->interface, j->second->name));
+                       }
+                       else
+                               used[location+i] = &var;
+               }
+       }
+}
+
+
+void GlobalInterfaceValidator::apply(Module &module)
+{
+       for(list<Stage>::iterator i=module.stages.begin(); i!=module.stages.end(); ++i)
+       {
+               stage = &*i;
+               i->content.visit(*this);
+       }
+}
+
+void GlobalInterfaceValidator::get_binding(const Layout &layout, unsigned &desc_set, int &binding)
+{
+       for(vector<Layout::Qualifier>::const_iterator i=layout.qualifiers.begin(); i!=layout.qualifiers.end(); ++i)
+       {
+               if(i->name=="set")
+                       desc_set = i->value;
+               else if(i->name=="binding")
+                       binding = i->value;
+       }
+}
+
+void GlobalInterfaceValidator::check_binding(const Layout &layout, const Binding &binding)
+{
+       unsigned desc_set = 0;
+       int bind_point = -1;
+       get_binding(layout, desc_set, bind_point);
+       if(bind_point<0)
+               return;
+
+       map<unsigned, Binding> &used = used_bindings[desc_set];
+       map<unsigned, Binding>::const_iterator i = used.find(bind_point);
+       if(i!=used.end())
+       {
+               if(i->second.name!=binding.name)
+               {
+                       error(*binding.node, format("Overlapping binding %d for '%s'", bind_point, binding.name));
+                       add_info(*i->second.node, format("Previously used here for '%s'", i->second.name));
+               }
+               if(i->second.type && binding.type)
+               {
+                       if(!is_same_type(*i->second.type, *binding.type))
+                       {
+                               string type_name = (dynamic_cast<const StructDeclaration *>(binding.type) ? "struct type" :
+                                       format("type '%s'", binding.type->name));
+                               error(*binding.node, format("Mismatched %s for binding %d '%s'", type_name, bind_point, binding.name));
+
+                               string message = "Previously used here";
+                               if(!dynamic_cast<const StructDeclaration *>(i->second.type))
+                                       message += format(" with type '%s'", i->second.type->name);
+                               add_info(*i->second.node, message);
+                       }
+               }
+       }
+       else
+               used.insert(make_pair(bind_point, binding));
+}
+
+void GlobalInterfaceValidator::visit(VariableDeclaration &var)
+{
+       if(var.interface=="uniform" && var.layout)
+               check_binding(*var.layout, var);
+}
+
+void GlobalInterfaceValidator::visit(InterfaceBlock &iface)
+{
+       if(iface.interface=="uniform" && iface.layout)
+               check_binding(*iface.layout, iface);
+}
+
 } // namespace SL
 } // namespace GL
 } // namespace Msp