X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=blobdiff_plain;f=source%2Fglsl%2Ffinalize.cpp;h=87e185024f3175c669a639dd14032b04e0cee79e;hp=030cb432a4a0c835b2485773ffa8b92fb36b705d;hb=HEAD;hpb=9c1035c136c7c78f0cbf0205ec48befc49219de7 diff --git a/source/glsl/finalize.cpp b/source/glsl/finalize.cpp index 030cb432..469cb92d 100644 --- a/source/glsl/finalize.cpp +++ b/source/glsl/finalize.cpp @@ -1,8 +1,10 @@ #include +#include #include #include #include "finalize.h" #include "glsl_error.h" +#include "reflect.h" using namespace std; @@ -10,9 +12,328 @@ namespace Msp { namespace GL { namespace SL { -PrecisionConverter::PrecisionConverter(): - stage(0) -{ } +void StructOrganizer::visit(StructDeclaration &strct) +{ + SetForScope set_offset(offset, 0); + TraversingVisitor::visit(strct); +} + +void StructOrganizer::visit(VariableDeclaration &var) +{ + if(offset>=0) + { + int *layout_offset = 0; + bool has_matrix_order = false; + if(var.layout) + { + for(Layout::Qualifier &q: var.layout->qualifiers) + { + if(q.name=="offset" && q.has_value) + { + layout_offset = &q.value; + if(q.value>=offset) + offset = q.value; + } + else if(q.name=="column_major" || q.name=="row_major") + has_matrix_order = true; + } + } + + MemoryRequirementsCalculator::Result mem_reqs = MemoryRequirementsCalculator().apply(var); + offset += mem_reqs.alignment-1; + offset -= offset%mem_reqs.alignment; + + if(layout_offset) + *layout_offset = offset; + else + add_layout_qualifier(var.layout, Layout::Qualifier("offset", offset)); + + if(!has_matrix_order) + { + const BasicTypeDeclaration *basic = dynamic_cast(var.type_declaration); + while(basic && basic->kind==BasicTypeDeclaration::ARRAY) + basic = dynamic_cast(basic->base_type); + if(basic && basic->kind==BasicTypeDeclaration::MATRIX) + add_layout_qualifier(var.layout, Layout::Qualifier("column_major")); + } + + offset += mem_reqs.size; + } +} + + +void LocationAllocator::apply(Module &module, const Features &f, bool a) +{ + features = f; + alloc_new = a; + for(Stage &s: module.stages) + apply(s); + + if(features.target_api!=VULKAN) + allocate_locations("uniform"); + + for(VariableDeclaration *b: unbound_blocks) + bind_uniform(b->layout, b->block_declaration->block_name, features.uniform_binding_range); + for(VariableDeclaration *t: unbound_textures) + { + const TypeDeclaration *base_type = get_ultimate_base_type(t->type_declaration); + unsigned range = (static_cast(base_type)->sampled ? + features.texture_binding_range : features.storage_texture_binding_range); + bind_uniform(t->layout, t->name, range); + } +} + +void LocationAllocator::apply(Stage &stage) +{ + swap(used_locations["in"], used_locations["out"]); + used_locations["out"].clear(); + + stage.content.visit(*this); + + allocate_locations("in"); + if(stage.type==Stage::VERTEX) + swap(used_locations["in"], used_vertex_attribs); + allocate_locations("out"); +} + +void LocationAllocator::allocate_locations(const string &iface) +{ + auto write = unplaced_variables.begin(); + unsigned next = 0; + for(auto i=unplaced_variables.begin(); i!=unplaced_variables.end(); ++i) + { + if((*i)->interface!=iface) + { + if(write!=i) + *write = *i; + ++write; + continue; + } + + if((*i)->interface=="uniform") + { + auto j = uniforms.find((*i)->name); + if(j!=uniforms.end() && j->second.location>=0) + { + add_layout_qualifier((*i)->layout, Layout::Qualifier("location", j->second.location)); + continue; + } + } + + if(!alloc_new) + continue; + + bool flat = ((*i)->interpolation=="flat" || ((*i)->linked_declaration && (*i)->linked_declaration->interpolation=="flat")); + + set &used = used_locations[(*i)->interface]; + + unsigned size = LocationCounter().apply(**i); + while(1) + { + int blocking = -1; + for(unsigned j=0; jlayout, Layout::Qualifier("location", next)); + if((*i)->interface=="uniform") + uniforms[(*i)->name].location = next; + + for(unsigned j=0; j &layout, const string &name, unsigned range) +{ + auto i = uniforms.find(name); + + int desc_set = (i!=uniforms.end() ? i->second.desc_set : 0); + if(features.target_api==VULKAN && get_layout_value(layout.get(), "set")<0) + add_layout_qualifier(layout, Layout::Qualifier("set", desc_set)); + + if(i!=uniforms.end() && i->second.bind_point>=0) + add_layout_qualifier(layout, Layout::Qualifier("binding", i->second.bind_point)); + else if(alloc_new) + { + set &used = used_bindings[desc_set]; + + unsigned bind_point = hash_fold<32>(hash<64>(name))%range; + while(used.count(bind_point)) + bind_point = (bind_point+1)%range; + + add_layout_qualifier(layout, Layout::Qualifier("binding", bind_point)); + uniforms[name].bind_point = bind_point; + used.insert(bind_point); + } +} + +bool LocationAllocator::visit_uniform(const string &name, RefPtr &layout) +{ + int desc_set = get_layout_value(layout.get(), "set"); + int bind_point = get_layout_value(layout.get(), "binding"); + + if(features.target_api==VULKAN) + { + if(desc_set<0 && bind_point>=0) + { + desc_set = 0; + add_layout_qualifier(layout, Layout::Qualifier("set", desc_set)); + } + + if(desc_set>=0) + uniforms[name].desc_set = desc_set; + } + else if(desc_set>=0 && bind_point<0) + { + auto i = find_member(layout->qualifiers, string("set"), &Layout::Qualifier::name); + layout->qualifiers.erase(i); + } + + if(bind_point>=0) + { + used_bindings[desc_set].insert(bind_point); + uniforms[name].bind_point = bind_point; + } + + return bind_point>=0; +} + +void LocationAllocator::visit(VariableDeclaration &var) +{ + if(!var.name.compare(0, 3, "gl_")) + return; + + if(!var.interface.empty() && !var.block_declaration) + { + int location = get_layout_value(var.layout.get(), "location"); + + if(location<0 && var.linked_declaration && var.linked_declaration->layout) + { + location = get_layout_value(var.linked_declaration->layout.get(), "location"); + if(location>=0) + add_layout_qualifier(var.layout, Layout::Qualifier("location", location)); + } + + if(location>=0) + { + unsigned size = LocationCounter().apply(var); + for(unsigned i=0; iblock_name, var.layout)) + unbound_blocks.push_back(&var); + } + else + { + const TypeDeclaration *base_type = get_ultimate_base_type(var.type_declaration); + if(dynamic_cast(base_type) && !visit_uniform(var.name, var.layout)) + unbound_textures.push_back(&var); + } + } +} + + +void DepthRangeConverter::apply(Stage &stage, const Features &features) +{ + if(stage.type!=Stage::VERTEX || features.target_api==VULKAN) + return; + + stage.content.visit(*this); +} + +void DepthRangeConverter::visit(VariableReference &var) +{ + const StructDeclaration *strct = dynamic_cast(var.type); + r_gl_pervertex = (strct && strct->block_name=="gl_PerVertex"); +} + +void DepthRangeConverter::visit(MemberAccess &memacc) +{ + r_gl_pervertex = false; + memacc.left->visit(*this); + r_gl_position = (r_gl_pervertex && memacc.member=="gl_Position"); +} + +void DepthRangeConverter::visit(Swizzle &swiz) +{ + r_gl_position = false; + swiz.left->visit(*this); + if(assignment_target && r_gl_position && swiz.count==1 && swiz.components[0]==2) + r_position_z_assigned = true; +} + +void DepthRangeConverter::visit(Assignment &assign) +{ + { + SetFlag set_target(assignment_target); + assign.left->visit(*this); + } + assign.right->visit(*this); +} + +void DepthRangeConverter::visit(FunctionDeclaration &func) +{ + r_position_z_assigned = false; + TraversingVisitor::visit(func); + + if(func.definition==&func && func.name=="main" && !r_position_z_assigned) + { + VariableReference *position = new VariableReference; + position->name = "gl_Position"; + + MemberAccess *z = new MemberAccess; + z->left = position; + z->member = "z"; + + Literal *scale = new Literal; + scale->token = "2.0"; + scale->value = 2.0f; + + BinaryExpression *multiply = new BinaryExpression; + multiply->oper = &Operator::get_operator("*", Operator::BINARY); + multiply->left = z; + multiply->right = scale; + + MemberAccess *w = new MemberAccess; + w->left = position->clone(); + w->member = "w"; + + BinaryExpression *subtract = new BinaryExpression; + subtract->oper = &Operator::get_operator("-", Operator::BINARY); + subtract->left = multiply; + subtract->right = w; + + Assignment *assign = new Assignment; + assign->oper = &Operator::get_operator("=", Operator::BINARY); + assign->left = z->clone(); + assign->right = subtract; + + ExpressionStatement *statement = new ExpressionStatement; + statement->expression = assign; + + func.body.body.push_back(statement); + } +} + void PrecisionConverter::apply(Stage &s) { @@ -23,7 +344,7 @@ void PrecisionConverter::apply(Stage &s) void PrecisionConverter::visit(Block &block) { - for(NodeList::iterator i=block.body.begin(); i!=block.body.end(); ++i) + for(auto i=block.body.begin(); i!=block.body.end(); ++i) { if(&block==&stage->content) insert_point = i; @@ -33,7 +354,7 @@ void PrecisionConverter::visit(Block &block) void PrecisionConverter::visit(Precision &prec) { - if(stage->required_features.gl_api==OPENGL_ES2) + if(stage->required_features.target_api==OPENGL_ES) have_default.insert(prec.type); else nodes_to_remove.insert(&prec); @@ -41,7 +362,7 @@ void PrecisionConverter::visit(Precision &prec) void PrecisionConverter::visit(VariableDeclaration &var) { - if(stage->required_features.gl_api!=OPENGL_ES2) + if(stage->required_features.target_api!=OPENGL_ES) { var.precision.clear(); return; @@ -80,27 +401,18 @@ void PrecisionConverter::visit(VariableDeclaration &var) } -LegacyConverter::LegacyConverter(): - frag_out(0) -{ } - -void LegacyConverter::apply(Stage &s, const Features &feat) +void FeatureConverter::apply(Stage &s, const Features &feat) { stage = &s; features = feat; - if(supports_stage(s.type)) - { - s.content.visit(*this); - NodeRemover().apply(s, nodes_to_remove); - if(!stage->required_features.glsl_version) - stage->required_features.glsl_version = Version(1, (stage->required_features.gl_api==OPENGL_ES2 ? 0 : 10)); - } - else - unsupported(format("Stage %s is not supported", Stage::get_stage_name(s.type))); + if(!stage->required_features.glsl_version) + stage->required_features.glsl_version = Version(1, (stage->required_features.target_api==OPENGL_ES ? 0 : 10)); + + apply(); } -void LegacyConverter::unsupported(const string &reason) +void FeatureConverter::unsupported(const string &reason) { Diagnostic diagnostic; diagnostic.severity = Diagnostic::ERR; @@ -110,17 +422,7 @@ void LegacyConverter::unsupported(const string &reason) stage->diagnostics.push_back(diagnostic); } -void LegacyConverter::visit(Block &block) -{ - for(NodeList::iterator i=block.body.begin(); i!=block.body.end(); ++i) - { - if(&block==&stage->content) - uniform_insert_point = i; - (*i)->visit(*this); - } -} - -bool LegacyConverter::check_version(const Version &feature_version) const +bool FeatureConverter::check_version(const Version &feature_version) const { if(features.glsl_versiontype)) + { + stage->content.visit(*this); + NodeRemover().apply(*stage, nodes_to_remove); + } + else + unsupported(format("Stage %s is not supported", Stage::get_stage_name(stage->type))); +} + +void StructuralFeatureConverter::visit(Block &block) { - if(st==Stage::GEOMETRY) + for(auto i=block.body.begin(); i!=block.body.end(); ++i) { - if(features.gl_api==OPENGL_ES2) + if(&block==&stage->content) + uniform_insert_point = i; + (*i)->visit(*this); + } +} + +void StructuralFeatureConverter::visit(RefPtr &expr) +{ + r_replaced_reference = 0; + expr->visit(*this); + if(r_replaced_reference) + expr = r_replaced_reference; + r_replaced_reference = 0; +} + +bool StructuralFeatureConverter::supports_stage(Stage::Type st) const +{ + if(st==Stage::TESS_CONTROL || st==Stage::TESS_EVAL) + { + if(features.target_api==OPENGL_ES) + return check_version(Version(3, 20)); + else + return check_version(Version(4, 0)); + } + else if(st==Stage::GEOMETRY) + { + if(features.target_api==OPENGL_ES) return check_version(Version(3, 20)); else return check_version(Version(1, 50)); } + else if(st==Stage::COMPUTE) + { + if(features.target_api==OPENGL_ES) + return check_version(Version(3, 10)); + else + return check_version(Version(4, 30)); + } else return true; } -bool LegacyConverter::supports_unified_interface_syntax() const +bool StructuralFeatureConverter::supports_unified_interface_syntax() const { - if(features.gl_api==OPENGL_ES2) + if(features.target_api==OPENGL_ES) return check_version(Version(3, 0)); else return check_version(Version(1, 30)); } -void LegacyConverter::visit(VariableReference &var) +void StructuralFeatureConverter::visit(VariableReference &var) { if(var.declaration==frag_out && !supports_unified_interface_syntax()) { var.name = "gl_FragColor"; var.declaration = 0; } + + r_flattened_interface = nodes_to_remove.count(var.declaration); } -void LegacyConverter::visit(Assignment &assign) +void StructuralFeatureConverter::visit(MemberAccess &memacc) +{ + r_flattened_interface = false; + visit(memacc.left); + if(r_flattened_interface) + { + VariableReference *var = new VariableReference; + var->name = memacc.member; + r_replaced_reference = var; + } +} + +void StructuralFeatureConverter::visit(Assignment &assign) { TraversingVisitor::visit(assign); if(assign.target.declaration==frag_out && !supports_unified_interface_syntax()) assign.target.declaration = 0; } -bool LegacyConverter::supports_unified_sampling_functions() const +bool StructuralFeatureConverter::supports_unified_sampling_functions() const { - if(features.gl_api==OPENGL_ES2) + if(features.target_api==OPENGL_ES) return check_version(Version(3, 0)); else return check_version(Version(1, 30)); } -void LegacyConverter::visit(FunctionCall &call) +void StructuralFeatureConverter::visit(FunctionCall &call) { if(call.declaration && call.declaration->source==BUILTIN_SOURCE) { @@ -222,9 +583,63 @@ void LegacyConverter::visit(FunctionCall &call) TraversingVisitor::visit(call); } -bool LegacyConverter::supports_interface_layouts() const +bool StructuralFeatureConverter::supports_interface_blocks(const string &iface) const +{ + if(features.target_api==OPENGL_ES) + { + if(iface=="uniform") + return check_version(Version(3, 0)); + else + return check_version(Version(3, 20)); + } + else if(check_version(Version(1, 50))) + return true; + else if(iface=="uniform") + return check_extension(&Features::arb_uniform_buffer_object); + else + return false; +} + +void StructuralFeatureConverter::visit(VariableDeclaration &var) +{ + if(var.block_declaration) + { + bool push_constant = has_layout_qualifier(var.layout.get(), "push_constant"); + if(!supports_interface_blocks(var.interface) || (push_constant && features.target_api!=VULKAN)) + { + if(var.name.find(' ')==string::npos) + unsupported("ARB_uniform_buffer_object required for interface block instances"); + else + { + for(const RefPtr &s: var.block_declaration->members.body) + if(VariableDeclaration *mem = dynamic_cast(s.get())) + mem->interface = var.interface; + stage->content.body.splice(uniform_insert_point, var.block_declaration->members.body); + nodes_to_remove.insert(&var); + nodes_to_remove.insert(var.block_declaration); + } + } + } + + if((var.interface=="in" || var.interface=="out") && !supports_unified_interface_syntax()) + if(stage->type==Stage::FRAGMENT && var.interface=="out") + { + frag_out = &var; + nodes_to_remove.insert(&var); + } + + TraversingVisitor::visit(var); +} + + +void QualifierConverter::apply() +{ + stage->content.visit(*this); +} + +bool QualifierConverter::supports_interface_layouts() const { - if(features.gl_api==OPENGL_ES2) + if(features.target_api==OPENGL_ES) return check_version(Version(3, 0)); else if(check_version(Version(3, 30))) return true; @@ -234,9 +649,19 @@ bool LegacyConverter::supports_interface_layouts() const return false; } -bool LegacyConverter::supports_centroid_sampling() const +bool QualifierConverter::supports_stage_interface_layouts() const { - if(features.gl_api==OPENGL_ES2) + if(features.target_api==OPENGL_ES) + return check_version(Version(3, 10)); + else if(check_version(Version(4, 10))) + return true; + else + return check_extension(&Features::arb_separate_shader_objects); +} + +bool QualifierConverter::supports_centroid_sampling() const +{ + if(features.target_api==OPENGL_ES) return check_version(Version(3, 0)); else if(check_version(Version(1, 20))) return true; @@ -244,9 +669,9 @@ bool LegacyConverter::supports_centroid_sampling() const return check_extension(&Features::ext_gpu_shader4); } -bool LegacyConverter::supports_sample_sampling() const +bool QualifierConverter::supports_sample_sampling() const { - if(features.gl_api==OPENGL_ES2) + if(features.target_api==OPENGL_ES) return check_version(Version(3, 20)); else if(check_version(Version(4, 0))) return true; @@ -254,23 +679,80 @@ bool LegacyConverter::supports_sample_sampling() const return check_extension(&Features::arb_gpu_shader5); } -void LegacyConverter::visit(VariableDeclaration &var) +bool QualifierConverter::supports_uniform_location() const +{ + if(features.target_api==OPENGL_ES) + return check_version(Version(3, 10)); + else if(check_version(Version(4, 30))) + return true; + else + return check_extension(&Features::arb_explicit_uniform_location); +} + +bool QualifierConverter::supports_binding() const +{ + if(features.target_api==OPENGL_ES) + return check_version(Version(3, 10)); + else + return check_version(Version(4, 20)); +} + +bool QualifierConverter::supports_interface_block_location() const +{ + if(features.target_api==OPENGL_ES) + return check_version(Version(3, 20)); + else if(check_version(Version(4, 40))) + return true; + else + return check_extension(&Features::arb_enhanced_layouts); +} + +void QualifierConverter::visit(VariableDeclaration &var) { if(var.layout) { - for(vector::const_iterator i=var.layout->qualifiers.begin(); i!=var.layout->qualifiers.end(); ) + for(auto i=var.layout->qualifiers.begin(); i!=var.layout->qualifiers.end(); ) { - if(i->name=="location" && !supports_interface_layouts()) + if(i->name=="location") { - if(stage->type==Stage::VERTEX && var.interface=="in") - stage->locations[var.name] = i->value; - else if(stage->type==Stage::FRAGMENT && var.interface=="out") + bool supported = true; + bool external = false; + if(var.block_declaration) + supported = supports_interface_block_location(); + else if(var.interface=="in") { - if(check_extension(&Features::ext_gpu_shader4)) + external = (stage->type==Stage::VERTEX); + supported = (external ? supports_interface_layouts() : supports_stage_interface_layouts()); + } + else if(var.interface=="out") + { + external = (stage->type==Stage::FRAGMENT); + supported = (external ? supports_interface_layouts() : supports_stage_interface_layouts()); + if(external && !supported && !check_extension(&Features::ext_gpu_shader4)) + { + external = false; + if(i->value!=0) + unsupported("EXT_gpu_shader4 required for multiple fragment shader outputs"); + } + } + else if(var.interface=="uniform") + supported = supports_uniform_location(); + + if(!supported) + { + if(external) stage->locations[var.name] = i->value; - else if(i->value!=0) - unsupported("EXT_gpu_shader4 required for multiple fragment shader outputs"); + i = var.layout->qualifiers.erase(i); } + else + ++i; + } + else if(i->name=="binding" && !supports_binding()) + { + if(var.block_declaration) + stage->uniform_block_bindings[var.block_declaration->block_name] = i->value; + else if(dynamic_cast(get_ultimate_base_type(var.type_declaration))) + stage->texture_bindings[var.name] = i->value; i = var.layout->qualifiers.erase(i); } @@ -293,54 +775,13 @@ void LegacyConverter::visit(VariableDeclaration &var) var.sampling = string(); } - if((var.interface=="in" || var.interface=="out") && !supports_unified_interface_syntax()) - { - if(stage->type==Stage::FRAGMENT && var.interface=="out") - { - frag_out = &var; - nodes_to_remove.insert(&var); - } - } + if(var.name=="gl_ClipDistance") + if(const Literal *literal_size = dynamic_cast(var.array_size.get())) + stage->n_clip_distances = literal_size->value.value(); TraversingVisitor::visit(var); } -bool LegacyConverter::supports_interface_blocks(const string &iface) const -{ - if(features.gl_api==OPENGL_ES2) - { - if(iface=="uniform") - return check_version(Version(3, 0)); - else - return check_version(Version(3, 20)); - } - else if(check_version(Version(1, 50))) - return true; - else if(iface=="uniform") - return check_extension(&Features::arb_uniform_buffer_object); - else - return false; -} - -void LegacyConverter::visit(InterfaceBlock &iface) -{ - if(!supports_interface_blocks(iface.interface) && iface.type_declaration) - { - if(!iface.instance_name.empty()) - unsupported("ARB_uniform_buffer_object required for interface block instances"); - else if(iface.struct_declaration) - { - stage->content.body.splice(uniform_insert_point, iface.struct_declaration->members.body); - nodes_to_remove.insert(&iface); - nodes_to_remove.insert(iface.struct_declaration); - } - else - /* If the interface block is an array, it should have an instance - name too, so this should never be reached */ - throw logic_error("Unexpected interface block configuration"); - } -} - } // namespace SL } // namespace GL } // namespace Msp