X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=blobdiff_plain;f=source%2Fglsl%2Ffinalize.cpp;h=6e9a478012e0dad898065da952868d8bf03eff46;hp=fb76ab6d57dc49c22997ad7a64b07e030ed45b34;hb=8967d38bc578f1653c1dde01dce49a8f7b0c912e;hpb=c8de66203565f07f10898a74dbd55072432790fc diff --git a/source/glsl/finalize.cpp b/source/glsl/finalize.cpp index fb76ab6d..6e9a4780 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,73 +12,295 @@ namespace Msp { namespace GL { namespace SL { -DefaultPrecisionGenerator::DefaultPrecisionGenerator(): - stage(0) +StructOrganizer::StructOrganizer(): + offset(-1) { } -void DefaultPrecisionGenerator::apply(Stage &s) +void StructOrganizer::visit(StructDeclaration &strct) { - stage = &s; - s.content.visit(*this); + SetForScope set_offset(offset, 0); + TraversingVisitor::visit(strct); } -void DefaultPrecisionGenerator::visit(Block &block) +void StructOrganizer::visit(VariableDeclaration &var) { - for(NodeList::iterator i=block.body.begin(); i!=block.body.end(); ++i) + if(offset>=0) { - if(&block==&stage->content) - insert_point = i; - (*i)->visit(*this); + int *layout_offset = 0; + if(var.layout) + { + vector &qualifiers = var.layout->qualifiers; + for(vector::iterator i=qualifiers.begin(); i!=qualifiers.end(); ++i) + if(i->name=="offset" && i->has_value) + { + layout_offset = &i->value; + if(i->value>=offset) + offset = i->value; + break; + } + } + + MemoryRequirementsCalculator::Result mem_reqs = MemoryRequirementsCalculator().apply(var); + offset += mem_reqs.alignment-1; + offset -= offset%mem_reqs.alignment; + + if(layout_offset) + *layout_offset = offset; + else + { + if(!var.layout) + var.layout = new Layout; + + Layout::Qualifier qual; + qual.name = "offset"; + qual.has_value = true; + qual.value = offset; + var.layout->qualifiers.push_back(qual); + } + + offset += mem_reqs.size; } } -void DefaultPrecisionGenerator::visit(Precision &prec) + +void LocationAllocator::apply(Module &module, const Features &features) { - have_default.insert(prec.type); + for(list::iterator i=module.stages.begin(); i!=module.stages.end(); ++i) + apply(*i); + allocate_locations("uniform"); + + for(vector::const_iterator i=unbound_blocks.begin(); i!=unbound_blocks.end(); ++i) + bind_uniform((*i)->layout, (*i)->block_name, features.uniform_binding_range); + for(vector::const_iterator i=unbound_textures.begin(); i!=unbound_textures.end(); ++i) + bind_uniform((*i)->layout, (*i)->name, features.texture_binding_range); } -void DefaultPrecisionGenerator::visit(VariableDeclaration &var) +void LocationAllocator::apply(Stage &stage) { - if(var.type_declaration) - return; + swap(used_locations["in"], used_locations["out"]); + used_locations["out"].clear(); + + stage.content.visit(*this); + + allocate_locations("in"); + allocate_locations("out"); +} - string type = var.type; - if(!type.compare(0, 3, "vec") || !type.compare(0, 3, "mat")) - type = "float"; - else if(!type.compare(0, 3, "ivec") || type=="uint") - type = "int"; +void LocationAllocator::allocate_locations(const string &iface) +{ + vector::iterator write = unplaced_variables.begin(); + unsigned next = 0; + for(vector::const_iterator i=unplaced_variables.begin(); i!=unplaced_variables.end(); ++i) + { + if((*i)->interface!=iface) + { + if(write!=i) + *write = *i; + ++write; + continue; + } - if(!have_default.count(type)) + if((*i)->interface=="uniform") + { + map::const_iterator j = uniforms.find((*i)->name); + if(j!=uniforms.end() && j->second.location>=0) + { + add_layout_value((*i)->layout, "location", j->second.location); + continue; + } + } + + set &used = used_locations[(*i)->interface]; + + unsigned size = LocationCounter().apply(**i); + while(1) + { + int blocking = -1; + for(unsigned j=0; jlayout, "location", next); + if((*i)->interface=="uniform") + uniforms[(*i)->name].location = next; + + for(unsigned j=0; j &layout, const string &name, unsigned range) +{ + map::const_iterator i = uniforms.find(name); + if(i!=uniforms.end() && i->second.bind_point>=0) + add_layout_value(layout, "binding", i->second.bind_point); + else { - Precision *prec = new Precision; - if(!type.compare(0, 7, "sampler")) - prec->precision = "lowp"; - else if(stage->type==Stage::FRAGMENT) - prec->precision = "mediump"; + set &used = used_bindings[0]; + + unsigned bind_point = fold32(hash64(name))%range; + while(used.count(bind_point)) + bind_point = (bind_point+1)%range; + + add_layout_value(layout, "binding", bind_point); + uniforms[name].bind_point = bind_point; + used.insert(bind_point); + } +} + +void LocationAllocator::add_layout_value(RefPtr &layout, const string &name, unsigned value) +{ + if(!layout) + layout = new Layout; + + Layout::Qualifier qual; + qual.name = name; + qual.has_value = true; + qual.value = value; + layout->qualifiers.push_back(qual); +} + +void LocationAllocator::visit(VariableDeclaration &var) +{ + if(!var.name.compare(0, 3, "gl_")) + return; + + if(!var.interface.empty()) + { + int location = (var.layout ? get_layout_value(*var.layout, "location") : -1); + + if(location<0 && var.linked_declaration && var.linked_declaration->layout) + { + location = get_layout_value(*var.linked_declaration->layout, "location"); + if(location>=0) + add_layout_value(var.layout, "location", location); + } + + if(location>=0) + { + unsigned size = LocationCounter().apply(var); + for(unsigned i=0; iprecision = "highp"; - prec->type = type; - stage->content.body.insert(insert_point, prec); + unplaced_variables.push_back(&var); + } - have_default.insert(type); + if(var.interface=="uniform") + { + const TypeDeclaration *type = var.type_declaration; + while(const BasicTypeDeclaration *basic = dynamic_cast(type)) + type = basic->base_type; + if(dynamic_cast(type)) + { + int bind_point = (var.layout ? get_layout_value(*var.layout, "binding") : -1); + if(bind_point>=0) + { + used_bindings[0].insert(bind_point); + uniforms[var.name].bind_point = bind_point; + } + else + unbound_textures.push_back(&var); + } + } +} + +void LocationAllocator::visit(InterfaceBlock &iface) +{ + if(!iface.instance_name.compare(0, 3, "gl_")) + return; + + if(iface.interface=="uniform") + { + int bind_point = (iface.layout ? get_layout_value(*iface.layout, "binding") : -1); + + if(bind_point>=0) + { + used_bindings[0].insert(bind_point); + uniforms[iface.block_name].bind_point = bind_point; + } + else + unbound_blocks.push_back(&iface); } } -void PrecisionRemover::apply(Stage &stage) +PrecisionConverter::PrecisionConverter(): + stage(0) +{ } + +void PrecisionConverter::apply(Stage &s) { - stage.content.visit(*this); - NodeRemover().apply(stage, nodes_to_remove); + stage = &s; + s.content.visit(*this); + NodeRemover().apply(s, nodes_to_remove); } -void PrecisionRemover::visit(Precision &prec) +void PrecisionConverter::visit(Block &block) { - nodes_to_remove.insert(&prec); + for(NodeList::iterator i=block.body.begin(); i!=block.body.end(); ++i) + { + if(&block==&stage->content) + insert_point = i; + (*i)->visit(*this); + } +} + +void PrecisionConverter::visit(Precision &prec) +{ + if(stage->required_features.gl_api==OPENGL_ES2) + have_default.insert(prec.type); + else + nodes_to_remove.insert(&prec); } -void PrecisionRemover::visit(VariableDeclaration &var) +void PrecisionConverter::visit(VariableDeclaration &var) { - var.precision.clear(); + if(stage->required_features.gl_api!=OPENGL_ES2) + { + var.precision.clear(); + return; + } + + const char *default_prec = (stage->type==Stage::FRAGMENT ? "mediump" : "highp"); + const TypeDeclaration *type = var.type_declaration; + while(type) + { + if(dynamic_cast(type)) + { + default_prec = "lowp"; + break; + } + else if(const BasicTypeDeclaration *basic = dynamic_cast(type)) + { + if(basic->kind==BasicTypeDeclaration::INT || basic->kind==BasicTypeDeclaration::FLOAT) + break; + type = basic->base_type; + } + else + return; + } + if(!type) + return; + + if(!have_default.count(type->name)) + { + Precision *prec = new Precision; + prec->precision = default_prec; + prec->type = type->name; + stage->content.body.insert(insert_point, prec); + + have_default.insert(type->name); + } } @@ -89,7 +313,13 @@ void LegacyConverter::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))); } @@ -161,12 +391,7 @@ void LegacyConverter::visit(VariableReference &var) { var.name = "gl_FragColor"; var.declaration = 0; - r_type = "vec4"; } - else if(var.declaration) - r_type = var.declaration->type; - else - r_type.clear(); } void LegacyConverter::visit(Assignment &assign) @@ -186,58 +411,39 @@ bool LegacyConverter::supports_unified_sampling_functions() const void LegacyConverter::visit(FunctionCall &call) { - if(call.name=="texture") + if(call.declaration && call.declaration->source==BUILTIN_SOURCE) { - string sampler_type; - r_type.clear(); - NodeArray::iterator i = call.arguments.begin(); - if(i!=call.arguments.end()) - { - (*i)->visit(*this); - sampler_type = r_type; - - for(; i!=call.arguments.end(); ++i) - (*i)->visit(*this); - } - - if(!supports_unified_sampling_functions()) + if(!call.name.compare(0, 7, "texture") && call.arguments.size()>=1) { - if(sampler_type=="sampler1D") - call.name = "texture1D"; - else if(sampler_type=="sampler2D") - call.name = "texture2D"; - else if(sampler_type=="sampler3D") - call.name = "texture3D"; - else if(sampler_type=="samplerCube") - call.name = "textureCube"; - else if(sampler_type=="sampler1DShadow") - call.name = "shadow1D"; - else if(sampler_type=="sampler2DShadow") - call.name = "shadow2D"; - else if(sampler_type=="sampler1DArray") + const ImageTypeDeclaration *arg_image = dynamic_cast(call.arguments.front()->type); + if(arg_image && !supports_unified_sampling_functions()) { - check_extension(&Features::ext_texture_array); - call.name = "texture1DArray"; - } - else if(sampler_type=="sampler2DArray") - { - check_extension(&Features::ext_texture_array); - call.name = "texture2DArray"; - } - else if(sampler_type=="sampler1DArrayShadow") - { - check_extension(&Features::ext_texture_array); - call.name = "shadow1DArray"; - } - else if(sampler_type=="sampler2DArrayShadow") - { - check_extension(&Features::ext_texture_array); - call.name = "shadow2DArray"; + string suffix = call.name.substr(7); + call.name = (arg_image->shadow ? "shadow" : "texture"); + + switch(arg_image->dimensions) + { + case ImageTypeDeclaration::ONE: call.name += "1D"; break; + case ImageTypeDeclaration::TWO: call.name += "2D"; break; + case ImageTypeDeclaration::THREE: call.name += "3D"; break; + case ImageTypeDeclaration::CUBE: call.name += "Cube"; break; + } + + if(arg_image->array) + { + /* Array textures and the unified sampling function name were + both introduced in GLSL 1.30. */ + if(arg_image->dimensions==ImageTypeDeclaration::ONE || arg_image->dimensions==ImageTypeDeclaration::TWO) + check_extension(&Features::ext_texture_array); + call.name += "Array"; + } + + call.name += suffix; } } } - else - TraversingVisitor::visit(call); + + TraversingVisitor::visit(call); } bool LegacyConverter::supports_interface_layouts() const @@ -246,8 +452,20 @@ bool LegacyConverter::supports_interface_layouts() const return check_version(Version(3, 0)); else if(check_version(Version(3, 30))) return true; - else + else if(check_version(Version(1, 30))) return check_extension(&Features::arb_explicit_attrib_location); + else + return false; +} + +bool LegacyConverter::supports_stage_interface_layouts() const +{ + if(features.gl_api==OPENGL_ES2) + 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 LegacyConverter::supports_centroid_sampling() const @@ -270,31 +488,78 @@ bool LegacyConverter::supports_sample_sampling() const return check_extension(&Features::arb_gpu_shader5); } +bool LegacyConverter::supports_uniform_location() const +{ + if(features.gl_api==OPENGL_ES2) + 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 LegacyConverter::supports_binding() const +{ + if(features.gl_api==OPENGL_ES2) + return check_version(Version(3, 10)); + else + return check_version(Version(4, 20)); +} + void LegacyConverter::visit(VariableDeclaration &var) { - if(var.layout && !supports_interface_layouts()) + if(var.layout) { - vector::iterator i; - for(i=var.layout->qualifiers.begin(); (i!=var.layout->qualifiers.end() && i->name!="location"); ++i) ; - if(i!=var.layout->qualifiers.end()) + for(vector::const_iterator i=var.layout->qualifiers.begin(); i!=var.layout->qualifiers.end(); ) { - if(stage->type==Stage::VERTEX && var.interface=="in") + if(i->name=="location") { - stage->locations[var.name] = i->value; - var.layout->qualifiers.erase(i); + bool supported = true; + bool external = false; + if(var.interface=="in") + { + 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; + i = var.layout->qualifiers.erase(i); + } + else + ++i; } - else if(stage->type==Stage::FRAGMENT && var.interface=="out") + else if(i->name=="binding" && !supports_binding()) { - if(check_extension(&Features::ext_gpu_shader4)) - stage->locations[var.name] = i->value; - else if(i->value!=0) - unsupported("EXT_gpu_shader4 required for multiple fragment shader outputs"); - var.layout->qualifiers.erase(i); - } + const TypeDeclaration *type = var.type_declaration; + while(const BasicTypeDeclaration *basic = dynamic_cast(type)) + type = basic->base_type; + if(dynamic_cast(type)) + stage->texture_bindings[var.name] = i->value; - if(var.layout->qualifiers.empty()) - var.layout = 0; + i = var.layout->qualifiers.erase(i); + } + else + ++i; } + + if(var.layout->qualifiers.empty()) + var.layout = 0; } if(var.sampling=="centroid") @@ -337,8 +602,37 @@ bool LegacyConverter::supports_interface_blocks(const string &iface) const return false; } +bool LegacyConverter::supports_interface_block_location() const +{ + if(features.gl_api==OPENGL_ES2) + return check_version(Version(3, 20)); + else if(check_version(Version(4, 40))) + return true; + else + return check_extension(&Features::arb_enhanced_layouts); +} + void LegacyConverter::visit(InterfaceBlock &iface) { + if(iface.layout) + { + for(vector::const_iterator i=iface.layout->qualifiers.begin(); i!=iface.layout->qualifiers.end(); ) + { + if(i->name=="location" && !supports_interface_block_location()) + i = iface.layout->qualifiers.erase(i); + else if(i->name=="binding" && !supports_binding()) + { + stage->uniform_block_bindings[iface.block_name] = i->value; + i = iface.layout->qualifiers.erase(i); + } + else + ++i; + } + + if(iface.layout->qualifiers.empty()) + iface.layout = 0; + } + if(!supports_interface_blocks(iface.interface) && iface.type_declaration) { if(!iface.instance_name.empty())