}
-string InterfaceGenerator::get_out_prefix(Stage::Type type)
+void PotentialInterfaceCollector::apply(Stage &s)
+{
+ stage = &s;
+ out_prefix = get_out_prefix(stage->type);
+ s.content.visit(*this);
+ if(stage->previous)
+ {
+ for(auto &kvp: stage->previous->potential_outs)
+ if(!stage->potential_outs.count(kvp.first))
+ {
+ PotentialInterface &pot_out = stage->potential_outs[kvp.first];
+ pot_out.name = kvp.first;
+ if(!kvp.second.block_member)
+ pot_out.link_name = out_prefix+kvp.first;
+ pot_out.block_member = kvp.second.block_member;
+ pot_out.passthrough = true;
+ pot_out.referenced = false;
+ pot_out.chained_from = &kvp.second;
+ }
+ }
+}
+
+string PotentialInterfaceCollector::get_out_prefix(Stage::Type type)
{
if(type==Stage::VERTEX)
return "_vs_out_";
return string();
}
+void PotentialInterfaceCollector::visit(VariableReference &var)
+{
+ if(var.declaration || !stage->previous)
+ return;
+
+ auto i = stage->previous->potential_outs.find(var.name);
+ if(i!=stage->previous->potential_outs.end())
+ {
+ for(PotentialInterface *p=&i->second; p; p=p->chained_from)
+ p->referenced = true;
+ }
+}
+
+void PotentialInterfaceCollector::visit(StructDeclaration &strct)
+{
+ SetForScope set_block(current_block, strct.block_declaration);
+ TraversingVisitor::visit(strct);
+}
+
+void PotentialInterfaceCollector::visit(VariableDeclaration &var)
+{
+ const string &iface = (current_block ? current_block->interface : var.interface);
+ bool builtin = (!var.name.compare(0, 3, "gl_") || (var.block_declaration && !var.block_declaration->block_name.compare(0, 3, "gl_")));
+ if(stage->type!=Stage::FRAGMENT && !builtin && (iface=="in" || iface=="out") &&
+ (!current_block || current_block->name.find(' ')!=string::npos))
+ {
+ PotentialInterface &pot_out = stage->potential_outs[var.name];
+ pot_out.name = var.name;
+ if(current_block)
+ pot_out.link_name.clear();
+ else if(var.interface=="in")
+ pot_out.link_name = out_prefix+var.name;
+ else
+ pot_out.link_name = var.name;
+ pot_out.block_member = current_block;
+ pot_out.passthrough = (iface=="in");
+ pot_out.referenced = false;
+ pot_out.declaration = (current_block ? current_block : &var);
+ }
+
+ TraversingVisitor::visit(var);
+}
+
+
void InterfaceGenerator::apply(Stage &s)
{
stage = &s;
iface_target_block = &stage->content;
- if(stage->previous)
- in_prefix = get_out_prefix(stage->previous->type);
- out_prefix = get_out_prefix(stage->type);
s.content.visit(*this);
NodeRemover().apply(s, nodes_to_remove);
}
}
}
-string InterfaceGenerator::change_prefix(const string &name, const string &prefix) const
-{
- unsigned offset = (name.compare(0, in_prefix.size(), in_prefix) ? 0 : in_prefix.size());
- return prefix+name.substr(offset);
-}
-
VariableDeclaration *InterfaceGenerator::generate_interface(VariableDeclaration &other_var, const string &iface, const string &name)
{
if(stage->content.variables.count(name))
iface_target_block->variables.insert(make_pair(name, var.get()));
if(iface_target_block==&stage->content && iface=="in")
- declared_inputs.push_back(var.get());
+ declared_inputs.push_back(name);
VariableDeclaration *result = var.get();
iface_target_block->body.insert(iface_insert_point, move(var));
if(stage->content.variables.count(var.name))
return;
- const map<string, VariableDeclaration *> &prev_vars = stage->previous->content.variables;
- auto i = prev_vars.find(var.name);
- if(i==prev_vars.end() || i->second->interface!="out")
- i = prev_vars.find(in_prefix+var.name);
- if(i!=prev_vars.end() && i->second->interface=="out")
+ map<string, PotentialInterface> &prev_outs = stage->previous->potential_outs;
+ auto j = prev_outs.find(var.name);
+ if(j!=prev_outs.end() && j->second.declaration && j->second.declaration->interface=="out")
{
- if(stage->type==Stage::GEOMETRY && i->second->array)
- stage->diagnostics.emplace_back(Diagnostic::WARN, var.source, var.line,
- format("Can't access '%s' through automatic interface because it's an array", var.name));
- else
- {
- generate_interface(*i->second, "in", i->second->name);
- var.name = i->second->name;
- }
- return;
+ generate_interface(*j->second.declaration, "in", j->second.link_name);
+ if(!j->second.block_member)
+ var.name = j->second.link_name;
}
-
- for(const auto &kvp: stage->previous->interface_blocks)
- if(kvp.second->name.find(' ')!=string::npos)
- {
- const map<string, VariableDeclaration *> &block_vars = kvp.second->block_declaration->members.variables;
- i = block_vars.find(var.name);
- if(i!=block_vars.end())
- {
- generate_interface(*kvp.second, "in", string());
- return;
- }
- }
}
void InterfaceGenerator::visit(VariableDeclaration &var)
VariableDeclaration *out_var = nullptr;
if(function_scope && (out_var=generate_interface(var, "out", var.name)))
{
+ auto i = stage->potential_outs.find(var.name);
+ if(i!=stage->potential_outs.end())
+ i->second.declaration = out_var;
+
out_var->source = var.source;
out_var->line = var.line;
nodes_to_remove.insert(&var);
else if(var.interface=="in" && current_block==&stage->content)
{
if(var.name.compare(0, 3, "gl_"))
- declared_inputs.push_back(&var);
+ declared_inputs.push_back(var.name);
/* Try to link input variables in global scope with output variables from
previous stage. */
void InterfaceGenerator::visit(Passthrough &pass)
{
- // Pass through all input variables declared so far.
- vector<VariableDeclaration *> pass_vars = declared_inputs;
-
- if(stage->previous)
- {
- for(const auto &kvp: stage->previous->content.variables)
- {
- if(kvp.second->interface!="out")
- continue;
-
- /* Pass through output variables from the previous stage, but only
- those which are not already linked to an input here. */
- if(!kvp.second->linked_declaration && generate_interface(*kvp.second, "in", kvp.second->name))
- pass_vars.push_back(kvp.second);
- }
- }
-
if(stage->type==Stage::GEOMETRY)
{
/* Special case for geometry shader: copy gl_Position from input to
insert_assignment("out gl_PerVertex.gl_Position", move(memacc));
}
- for(VariableDeclaration *v: pass_vars)
+ for(auto &kvp: stage->potential_outs)
{
- string out_name = change_prefix(v->name, out_prefix);
- generate_interface(*v, "out", out_name);
+ if(!kvp.second.referenced || !kvp.second.passthrough)
+ continue;
+
+ if(!kvp.second.declaration && kvp.second.chained_from && kvp.second.chained_from->declaration)
+ kvp.second.declaration = generate_interface(*kvp.second.chained_from->declaration, "in", kvp.second.chained_from->link_name);
+ if(!kvp.second.declaration)
+ continue;
+
+ if(kvp.second.declaration->interface!="out")
+ kvp.second.declaration = generate_interface(*kvp.second.declaration, "out", kvp.second.link_name);
+
+ string src_name = (kvp.second.chained_from ? kvp.second.chained_from->link_name : kvp.second.name);
+ if(!any_equals(declared_inputs, src_name))
+ continue;
unique_ptr<VariableReference> ref = make_unique<VariableReference>();
- ref->name = v->name;
+ ref->name = src_name;
if(pass.subscript)
{
unique_ptr<BinaryExpression> subscript = make_unique<BinaryExpression>();
subscript->left = move(ref);
subscript->oper = &Operator::get_operator("[", Operator::BINARY);
subscript->right = pass.subscript.clone();
- insert_assignment(out_name, move(subscript));
+ insert_assignment(kvp.second.link_name, move(subscript));
}
else
- insert_assignment(out_name, move(ref));
+ insert_assignment(kvp.second.link_name, move(ref));
}
nodes_to_remove.insert(&pass);