writer(content)
{ }
-void SpirVGenerator::apply(Module &module)
+void SpirVGenerator::apply(Module &module, const Features &f)
{
+ features = f;
use_capability(CAP_SHADER);
for(Stage &s: module.stages)
return (i!=standard_type_ids.end() && i->second==type_id);
}
-SpirVGenerator::Id SpirVGenerator::get_array_type_id(TypeDeclaration &base_type, Id size_id)
+SpirVGenerator::Id SpirVGenerator::get_array_type_id(TypeDeclaration &base_type, Id size_id, bool extended_align)
{
Id base_type_id = get_id(base_type);
- Id &array_type_id = array_type_ids[TypeKey(base_type_id, size_id)];
+ Id &array_type_id = array_type_ids[TypeKey(base_type_id, extended_align*0x400000 | size_id)];
if(!array_type_id)
{
array_type_id = next_id++;
writer.write_op(content.globals, OP_TYPE_RUNTIME_ARRAY, array_type_id, base_type_id);
unsigned stride = MemoryRequirementsCalculator().apply(base_type).stride;
+ if(extended_align)
+ stride = (stride+15)&~15U;
writer.write_op_decorate(array_type_id, DECO_ARRAY_STRIDE, stride);
}
if(const BasicTypeDeclaration *basic = dynamic_cast<const BasicTypeDeclaration *>(var.type_declaration))
if(basic->kind==BasicTypeDeclaration::ARRAY)
{
- Id size_id = 0;
- if(var.array_size)
- {
- SetFlag set_const(constant_expression);
- r_expression_result_id = 0;
- var.array_size->visit(*this);
- size_id = r_expression_result_id;
- }
- else
- size_id = get_constant_id(get_standard_type_id(BasicTypeDeclaration::INT, 1), 1);
- return get_array_type_id(*basic->base_type, size_id);
+ if(!var.array_size)
+ throw logic_error("array without size");
+
+ SetFlag set_const(constant_expression);
+ r_expression_result_id = 0;
+ var.array_size->visit(*this);
+ return get_array_type_id(*basic->base_type, r_expression_result_id, basic->extended_alignment);
}
return get_id(*var.type_declaration);
r_constant_result = false;
if(composite_access)
{
- r_composite_base = var.declaration;
r_expression_result_id = 0;
+ if(!assignment_source_id)
+ {
+ auto i = variable_load_ids.find(var.declaration);
+ if(i!=variable_load_ids.end())
+ r_expression_result_id = i->second;
+ }
+ if(!r_expression_result_id)
+ r_composite_base = var.declaration;
}
else if(assignment_source_id)
{
r_expression_result_id = get_load_id(*var.declaration);
}
-void SpirVGenerator::visit(InterfaceBlockReference &iface)
-{
- if(!composite_access || !current_function)
- throw internal_error("invalid interface block reference");
-
- r_composite_base = iface.declaration;
- r_expression_result_id = 0;
- r_constant_result = false;
-}
-
void SpirVGenerator::generate_composite_access(TypeDeclaration &result_type)
{
Opcode opcode;
void SpirVGenerator::visit(UnaryExpression &unary)
{
+ if(composite_access)
+ return visit_isolated(unary);
+
unary.expression->visit(*this);
char oper = unary.oper->token[0];
visit_isolated(*binary.right);
return visit_composite(*binary.left, 0x400000|r_expression_result_id, *binary.type);
}
+ else if(composite_access)
+ return visit_isolated(binary);
if(assignment_source_id)
throw internal_error("invalid binary expression in assignment target");
void SpirVGenerator::visit(TernaryExpression &ternary)
{
+ if(composite_access)
+ return visit_isolated(ternary);
if(constant_expression)
{
ternary.condition->visit(*this);
writer.write_op_label(true_label_id);
ternary.true_expr->visit(*this);
Id true_result_id = r_expression_result_id;
+ true_label_id = writer.get_current_block();
writer.write_op(content.function_body, OP_BRANCH, merge_block_id);
writer.write_op_label(false_label_id);
ternary.false_expr->visit(*this);
Id false_result_id = r_expression_result_id;
+ false_label_id = writer.get_current_block();
writer.write_op_label(merge_block_id);
r_expression_result_id = begin_expression(OP_PHI, get_id(*ternary.type), 4);
return;
Id type_id = allocate_id(strct, 0);
- writer.write_op_name(type_id, strct.name);
+ writer.write_op_name(type_id, (strct.block_name.empty() ? strct.name : strct.block_name));
- if(strct.interface_block)
+ if(!strct.block_name.empty())
writer.write_op_decorate(type_id, DECO_BLOCK);
- bool builtin = (strct.interface_block && !strct.interface_block->block_name.compare(0, 3, "gl_"));
+ bool builtin = !strct.block_name.compare(0, 3, "gl_");
vector<Id> member_type_ids;
member_type_ids.reserve(strct.members.body.size());
for(const RefPtr<Statement> &s: strct.members.body)
void SpirVGenerator::visit(VariableDeclaration &var)
{
- const vector<Layout::Qualifier> *layout_ql = (var.layout ? &var.layout->qualifiers : 0);
-
- int spec_id = -1;
- if(layout_ql)
- {
- auto i = find_member(*layout_ql, string("constant_id"), &Layout::Qualifier::name);
- if(i!=layout_ql->end())
- spec_id = i->value;
- }
-
Id type_id = get_variable_type_id(var);
Id var_id;
if(!var.init_expression)
throw internal_error("const variable without initializer");
+ int spec_id = get_layout_value(var.layout.get(), "constant_id");
+ Id *spec_var_id = (spec_id>=0 ? &declared_spec_ids[spec_id] : 0);
+ if(spec_id>=0 && *spec_var_id)
+ {
+ insert_unique(declared_ids, &var, Declaration(*spec_var_id, type_id));
+ return;
+ }
+
SetFlag set_const(constant_expression);
SetFlag set_spec(spec_constant, spec_id>=0);
r_expression_result_id = 0;
var.init_expression->visit(*this);
var_id = r_expression_result_id;
insert_unique(declared_ids, &var, Declaration(var_id, type_id));
- writer.write_op_decorate(var_id, DECO_SPEC_ID, spec_id);
-
- /* It's unclear what should be done if a specialization constant is
- initialized with anything other than a literal. GLSL doesn't seem to
- prohibit that but SPIR-V says OpSpecConstantOp can't be updated via
- specialization. */
+ if(spec_id>=0)
+ {
+ writer.write_op_decorate(var_id, DECO_SPEC_ID, spec_id);
+ *spec_var_id = var_id;
+ }
}
else
{
- StorageClass storage = (current_function ? STORAGE_FUNCTION : get_interface_storage(var.interface, false));
+ bool push_const = has_layout_qualifier(var.layout.get(), "push_constant");
+
+ StorageClass storage;
+ if(current_function)
+ storage = STORAGE_FUNCTION;
+ else if(push_const)
+ storage = STORAGE_PUSH_CONSTANT;
+ else
+ storage = get_interface_storage(var.interface, var.block_declaration);
+
Id ptr_type_id = get_pointer_type_id(type_id, storage);
if(var.interface=="uniform")
{
- Id &uni_id = declared_uniform_ids["v"+var.name];
+ Id &uni_id = declared_uniform_ids[var.block_declaration ? "b"+var.block_declaration->block_name : "v"+var.name];
if(uni_id)
{
insert_unique(declared_ids, &var, Declaration(uni_id, ptr_type_id));
writer.write(init_id);
writer.end_op(OP_VARIABLE);
- if(layout_ql)
+ if(var.layout)
{
- for(const Layout::Qualifier &q: *layout_ql)
+ for(const Layout::Qualifier &q: var.layout->qualifiers)
{
if(q.name=="location")
writer.write_op_decorate(var_id, DECO_LOCATION, q.value);
writer.write_op_decorate(var_id, DECO_BINDING, q.value);
}
}
- if(!var.name.compare(0, 3, "gl_"))
+ if(!var.block_declaration && !var.name.compare(0, 3, "gl_"))
{
BuiltinSemantic semantic = get_builtin_semantic(var.name);
writer.write_op_decorate(var_id, DECO_BUILTIN, semantic);
}
}
- writer.write_op_name(var_id, var.name);
-}
-
-void SpirVGenerator::visit(InterfaceBlock &iface)
-{
- StorageClass storage = get_interface_storage(iface.interface, true);
- Id type_id;
- if(iface.array)
- type_id = get_array_type_id(*iface.struct_declaration, 0);
- else
- type_id = get_id(*iface.struct_declaration);
- Id ptr_type_id = get_pointer_type_id(type_id, storage);
-
- Id block_id;
- if(iface.interface=="uniform")
- {
- Id &uni_id = declared_uniform_ids["b"+iface.block_name];
- if(uni_id)
- {
- insert_unique(declared_ids, &iface, Declaration(uni_id, ptr_type_id));
- return;
- }
-
- uni_id = block_id = allocate_id(iface, ptr_type_id);
- }
- else
- block_id = allocate_id(iface, ptr_type_id);
- writer.write_op_name(block_id, iface.instance_name);
-
- writer.write_op(content.globals, OP_VARIABLE, ptr_type_id, block_id, storage);
-
- if(iface.layout)
- {
- auto i = find_member(iface.layout->qualifiers, string("binding"), &Layout::Qualifier::name);
- if(i!=iface.layout->qualifiers.end())
- writer.write_op_decorate(block_id, DECO_BINDING, i->value);
- }
+ if(var.name.find(' ')==string::npos)
+ writer.write_op_name(var_id, var.name);
}
void SpirVGenerator::visit_entry_point(FunctionDeclaration &func, Id func_id)
set<Node *> dependencies = DependencyCollector().apply(func);
for(Node *n: dependencies)
- {
if(const VariableDeclaration *var = dynamic_cast<const VariableDeclaration *>(n))
- {
if(!var->interface.empty())
writer.write(get_id(*n));
- }
- else if(dynamic_cast<InterfaceBlock *>(n))
- writer.write(get_id(*n));
- }
writer.end_op(OP_ENTRY_POINT);
if(stage->type==Stage::FRAGMENT)
- writer.write_op(content.exec_modes, OP_EXECUTION_MODE, func_id, EXEC_ORIGIN_LOWER_LEFT);
+ {
+ SpirVExecutionMode origin = (features.target_api==VULKAN ? EXEC_ORIGIN_UPPER_LEFT : EXEC_ORIGIN_LOWER_LEFT);
+ writer.write_op(content.exec_modes, OP_EXECUTION_MODE, func_id, origin);
+ }
else if(stage->type==Stage::GEOMETRY)
+ {
use_capability(CAP_GEOMETRY);
+ writer.write_op(content.exec_modes, OP_EXECUTION_MODE, func_id, EXEC_INVOCATIONS, 1);
+ }
for(const InterfaceLayout *i: interface_layouts)
{
{
Id param_id = allocate_id(*func.parameters[i], param_type_ids[i]);
writer.write_op(content.functions, OP_FUNCTION_PARAMETER, param_type_ids[i], param_id);
+ writer.write_op_name(param_id, func.parameters[i]->name);
// TODO This is probably incorrect if the parameter is assigned to.
variable_load_ids[func.parameters[i].get()] = param_id;
}
+ reachable = true;
writer.begin_function_body(next_id++);
SetForScope<FunctionDeclaration *> set_func(current_function, &func);
func.body.visit(*this);
- if(writer.has_current_block())
+ if(writer.get_current_block())
{
if(!reachable)
writer.write_op(content.function_body, OP_UNREACHABLE);
writer.write_op(content.function_body, OP_SELECTION_MERGE, merge_block_id, 0); // Selection control (none)
writer.write_op(content.function_body, OP_BRANCH_CONDITIONAL, r_expression_result_id, true_label_id, false_label_id);
+ std::map<const VariableDeclaration *, Id> saved_load_ids = variable_load_ids;
+
writer.write_op_label(true_label_id);
cond.body.visit(*this);
- if(writer.has_current_block())
+ if(writer.get_current_block())
writer.write_op(content.function_body, OP_BRANCH, merge_block_id);
bool reachable_if_true = reachable;
reachable = true;
if(!cond.else_body.body.empty())
{
+ swap(saved_load_ids, variable_load_ids);
writer.write_op_label(false_label_id);
cond.else_body.visit(*this);
reachable |= reachable_if_true;
if(iter.init_statement)
iter.init_statement->visit(*this);
- for(VariableDeclaration *v: AssignmentCollector().apply(iter))
- variable_load_ids.erase(v);
+ for(Node *n: AssignmentCollector().apply(iter))
+ if(VariableDeclaration *var = dynamic_cast<VariableDeclaration *>(n))
+ variable_load_ids.erase(var);
Id header_id = next_id++;
Id continue_id = next_id++;