From 7af200475facc657a0bbffaa17520d3ec9d809af Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Wed, 31 Mar 2021 18:04:46 +0300 Subject: [PATCH] Add support for loading SPIR-V shaders --- extensions/arb_es2_compatibility.glext | 1 + extensions/arb_gl_spirv.glext | 1 + source/core/module.cpp | 507 +++++++++++++++++++++++++ source/core/module.h | 161 ++++++++ source/core/program.cpp | 170 ++++++++- source/core/program.h | 8 +- source/resources/resources.cpp | 12 +- 7 files changed, 848 insertions(+), 12 deletions(-) create mode 100644 extensions/arb_es2_compatibility.glext create mode 100644 extensions/arb_gl_spirv.glext diff --git a/extensions/arb_es2_compatibility.glext b/extensions/arb_es2_compatibility.glext new file mode 100644 index 00000000..df89c9ba --- /dev/null +++ b/extensions/arb_es2_compatibility.glext @@ -0,0 +1 @@ +extension ARB_ES2_compatibility diff --git a/extensions/arb_gl_spirv.glext b/extensions/arb_gl_spirv.glext new file mode 100644 index 00000000..b8d29668 --- /dev/null +++ b/extensions/arb_gl_spirv.glext @@ -0,0 +1 @@ +extension ARB_gl_spirv diff --git a/source/core/module.cpp b/source/core/module.cpp index 74e886b0..5287ae9c 100644 --- a/source/core/module.cpp +++ b/source/core/module.cpp @@ -4,6 +4,44 @@ using namespace std; +enum SpirVConstants +{ + SPIRV_MAGIC = 0x07230203, + SPIRV_MAGIC_REVERSED = 0x03022307, + + OP_NAME = 5, + OP_MEMBER_NAME = 6, + OP_ENTRY_POINT = 15, + OP_TYPE_VOID = 19, + OP_TYPE_BOOL = 20, + OP_TYPE_INT = 21, + OP_TYPE_FLOAT = 22, + OP_TYPE_VECTOR = 23, + OP_TYPE_MATRIX = 24, + OP_TYPE_IMAGE = 25, + OP_TYPE_SAMPLED_IMAGE = 27, + OP_TYPE_ARRAY = 28, + OP_TYPE_STRUCT = 30, + OP_TYPE_POINTER = 32, + OP_CONSTANT_TRUE = 41, + OP_CONSTANT_FALSE = 42, + OP_CONSTANT = 43, + OP_SPEC_CONSTANT_TRUE = 48, + OP_SPEC_CONSTANT_FALSE = 49, + OP_SPEC_CONSTANT = 50, + OP_VARIABLE = 59, + OP_DECORATE = 71, + OP_MEMBER_DECORATE = 72, + + DECO_SPEC_ID = 1, + DECO_ARRAY_STRIDE = 6, + DECO_MATRIX_STRIDE = 7, + DECO_LOCATION = 30, + DECO_BINDING = 33, + DECO_DESCRIPTOR_SET = 34, + DECO_OFFSET = 35 +}; + namespace Msp { namespace GL { @@ -39,5 +77,474 @@ void GlslModule::compile(SL::Compiler &compiler) #endif } + +SpirVModule::SpirVModule(const SpirVModule &other): + code(other.code), + entry_points(other.entry_points), + structs(other.structs), + variables(other.variables) +{ + remap_pointers_from(other); +} + +SpirVModule &SpirVModule::operator=(const SpirVModule &other) +{ + code = other.code; + entry_points = other.entry_points; + structs = other.structs; + variables = other.variables; + remap_pointers_from(other); + return *this; +} + +void SpirVModule::remap_pointers_from(const SpirVModule &other) +{ + for(vector::iterator i=entry_points.begin(); i!=entry_points.end(); ++i) + for(vector::iterator j=i->globals.begin(); j!=i->globals.end(); ++j) + *j = &variables[*j-&other.variables.front()]; + + for(vector::iterator i=variables.begin(); i!=variables.end(); ++i) + if(i->struct_type) + i->struct_type = &structs[i->struct_type-&other.structs.front()]; + + for(vector::iterator i=structs.begin(); i!=structs.end(); ++i) + for(vector::iterator j=i->members.begin(); j!=i->members.end(); ++j) + if(j->struct_type) + j->struct_type = &structs[j->struct_type-&other.structs.front()]; +} + +void SpirVModule::load_code(IO::Base &io) +{ + UInt32 buffer[1024]; + while(1) + { + unsigned len = io.read(reinterpret_cast(buffer), sizeof(buffer)); + if(!len) + break; + len /= 4; + code.reserve(code.size()+len); + code.insert(code.end(), buffer, buffer+len); + } + + if(code.empty()) + throw invalid_module("Empty SPIR-V code"); + + if(code[0]==SPIRV_MAGIC_REVERSED) + { + for(vector::iterator i=code.begin(); i!=code.end(); ++i) + *i = ((*i&0xFF)<<24) || ((*i&0xFF00)<<8) | ((*i>>8)&0xFF00) | ((*i>>24)&0xFF); + } + else if(code[0]!=SPIRV_MAGIC) + throw invalid_module("SPIR-V magic number not found"); + + Reflection reflection; + reflection.reflect_code(code); + + map struct_indices; + structs.reserve(reflection.structs.size()); + for(map::const_iterator i=reflection.structs.begin(); i!=reflection.structs.end(); ++i) + { + struct_indices[&i->second] = structs.size(); + structs.push_back(i->second); + } + + for(vector::iterator i=structs.begin(); i!=structs.end(); ++i) + { + for(vector::iterator j=i->members.begin(); j!=i->members.end(); ++j) + { + if(j->struct_type) + { + map::const_iterator k = struct_indices.find(j->struct_type); + j->struct_type = (k!=struct_indices.end() ? &structs[k->second] : 0); + } + } + + const StructMember *last_member = &i->members.back(); + unsigned last_offset = last_member->offset; + while(last_member->struct_type) + { + const StructMember *lm = &last_member->struct_type->members.back(); + if(last_member->array_size) + last_offset += last_member->array_stride*(last_member->array_size-1); + last_offset += lm->offset; + last_member = lm; + } + + i->size = last_offset+get_type_size(last_member->type); + i->size = (i->size+15)&~15; + } + + map var_indices; + variables.reserve(reflection.variables.size()); + for(map::const_iterator i=reflection.variables.begin(); i!=reflection.variables.end(); ++i) + { + int dup_index = -1; + for(vector::const_iterator j=variables.begin(); (dup_index<0 && j!=variables.end()); ++j) + if(*j==i->second) + dup_index = j-variables.begin(); + + if(dup_index>=0) + var_indices[&i->second] = dup_index; + else + { + var_indices[&i->second] = variables.size(); + variables.push_back(i->second); + } + } + + for(vector::iterator i=variables.begin(); i!=variables.end(); ++i) + if(i->struct_type) + { + map::const_iterator j = struct_indices.find(i->struct_type); + i->struct_type = (j!=struct_indices.end() ? &structs[j->second] : 0); + } + + entry_points.reserve(reflection.entry_points.size()); + for(map::const_iterator i=reflection.entry_points.begin(); i!=reflection.entry_points.end(); ++i) + { + entry_points.push_back(i->second); + EntryPoint &entry = entry_points.back(); + for(vector::iterator j=entry.globals.begin(); j!=entry.globals.end(); ++j) + { + map::const_iterator k = var_indices.find(*j); + *j = (k!=var_indices.end() ? &variables[k->second] : 0); + } + } + + for(map::const_iterator i=reflection.spec_constants.begin(); i!=reflection.spec_constants.end(); ++i) + spec_constants.push_back(i->second); +} + +void SpirVModule::compile(SL::Compiler &) +{ + throw logic_error("Not implemented yet"); +} + + +SpirVModule::EntryPoint::EntryPoint(): + stage(VERTEX) +{ } + + +SpirVModule::StructMember::StructMember(): + type(VOID), + struct_type(0), + offset(0), + array_size(0), + array_stride(0), + matrix_stride(0) +{ } + + +SpirVModule::Variable::Variable(): + type(VOID), + struct_type(0), + location(-1), + descriptor_set(-1), + binding(-1) +{ } + +bool SpirVModule::Variable::operator==(const Variable &other) const +{ + if(storage!=UNIFORM_CONSTANT && storage!=UNIFORM) + return false; + if(storage!=other.storage || type!=other.type || struct_type!=other.struct_type) + return false; + if(location!=other.location || descriptor_set!=other.descriptor_set || binding!=other.binding) + return false; + return true; +} + + +SpirVModule::TypeInfo::TypeInfo(): + type(VOID), + struct_type(0), + array_size(0), + array_stride(0), + storage(static_cast(-1)) +{ } + + +UInt32 SpirVModule::Reflection::get_opcode(UInt32 op) +{ + return op&0xFFFF; +} + +SpirVModule::Reflection::CodeIterator SpirVModule::Reflection::get_op_end(const CodeIterator &op) +{ + return op+(*op>>16); +} + +string SpirVModule::Reflection::read_string(CodeIterator &op, const CodeIterator &op_end) +{ + string result; + for(; op!=op_end; ++op) + { + unsigned word = *op; + for(unsigned i=0; i<4; ++i) + { + char c = word&0xFF; + if(!c) + { + ++op; + return result; + } + result += c; + word >>= 8; + } + } + + throw invalid_module("Unterminated SPIR-V string literal"); +} + +void SpirVModule::Reflection::reflect_code(const vector &code) +{ + for(CodeIterator op=code.begin()+5; op!=code.end(); ) + { + unsigned word_count = *op>>16; + if(word_count>code.end()-op) + throw invalid_module("Truncated SPIR-V instruction"); + + switch(get_opcode(*op)) + { + case OP_NAME: reflect_name(op); break; + case OP_MEMBER_NAME: reflect_member_name(op); break; + case OP_ENTRY_POINT: reflect_entry_point(op); break; + case OP_TYPE_VOID: reflect_void_type(op); break; + case OP_TYPE_BOOL: reflect_bool_type(op); break; + case OP_TYPE_INT: reflect_int_type(op); break; + case OP_TYPE_FLOAT: reflect_float_type(op); break; + case OP_TYPE_VECTOR: reflect_vector_type(op); break; + case OP_TYPE_MATRIX: reflect_vector_type(op); break; + case OP_TYPE_IMAGE: reflect_image_type(op); break; + case OP_TYPE_SAMPLED_IMAGE: reflect_sampled_image_type(op); break; + case OP_TYPE_ARRAY: reflect_array_type(op); break; + case OP_TYPE_STRUCT: reflect_struct_type(op); break; + case OP_TYPE_POINTER: reflect_pointer_type(op); break; + case OP_CONSTANT_TRUE: constants[*(op+2)] = true; break; + case OP_CONSTANT_FALSE: constants[*(op+2)] = false; break; + case OP_CONSTANT: reflect_constant(op); break; + case OP_SPEC_CONSTANT_TRUE: + case OP_SPEC_CONSTANT_FALSE: + case OP_SPEC_CONSTANT: reflect_spec_constant(op); break; + case OP_VARIABLE: reflect_variable(op); break; + case OP_DECORATE: reflect_decorate(op); break; + case OP_MEMBER_DECORATE: reflect_member_decorate(op); break; + } + + op += word_count; + } +} + +void SpirVModule::Reflection::reflect_name(CodeIterator op) +{ + CodeIterator op_end = get_op_end(op); + string &name = names[*(op+1)]; + op += 2; + name = read_string(op, op_end); +} + +void SpirVModule::Reflection::reflect_member_name(CodeIterator op) +{ + CodeIterator op_end = get_op_end(op); + Structure &strct = structs[*(op+1)]; + unsigned index = *(op+2); + if(index>=strct.members.size()) + strct.members.resize(index+1); + op += 3; + strct.members[index].name = read_string(op, op_end); +} + +void SpirVModule::Reflection::reflect_entry_point(CodeIterator op) +{ + CodeIterator op_end = get_op_end(op); + EntryPoint &entry = entry_points[*(op+2)]; + entry.stage = static_cast(*(op+1)); // Execution model in SPIR-V spec + op += 3; + entry.name = read_string(op, op_end); + + entry.globals.reserve(op_end-op); + for(; op!=op_end; ++op) + entry.globals.push_back(&variables[*op]); +} + +void SpirVModule::Reflection::reflect_void_type(CodeIterator op) +{ + types[*(op+1)].type = VOID; +} + +void SpirVModule::Reflection::reflect_bool_type(CodeIterator op) +{ + types[*(op+1)].type = BOOL; +} + +void SpirVModule::Reflection::reflect_int_type(CodeIterator op) +{ + TypeInfo &type = types[*(op+1)]; + unsigned size = *(op+2); + bool sign = *(op+3); + type.type = static_cast(size/8 | sign*0x100); +} + +void SpirVModule::Reflection::reflect_float_type(CodeIterator op) +{ + TypeInfo &type = types[*(op+1)]; + unsigned size = *(op+2); + type.type = static_cast(size/8 | 0x300); +} + +void SpirVModule::Reflection::reflect_vector_type(CodeIterator op) +{ + TypeInfo &type = types[*(op+1)]; + DataType component = types[*(op+2)].type; + unsigned count = *(op+3); + type.type = static_cast((count<<12) | (component&0xF00) | ((component&0xFF)*count)); +} + +void SpirVModule::Reflection::reflect_matrix_type(CodeIterator op) +{ + TypeInfo &type = types[*(op+1)]; + DataType column = types[*(op+2)].type; + unsigned count = *(op+3); + type.type = static_cast((count<<16) | (column&0xF00) | ((column&0xFF)*count)); +} + +void SpirVModule::Reflection::reflect_image_type(CodeIterator op) +{ + TypeInfo &type = types[*(op+1)]; + DataType sample = types[*(op+2)].type; + unsigned dimensions = *(op+3); + bool depth = *(op+4)==1; + bool array = *(op+5); + type.type = static_cast((depth*0x200000) | (array*0x80000) | (dimensions+1) | sample); +} + +void SpirVModule::Reflection::reflect_sampled_image_type(CodeIterator op) +{ + TypeInfo &type = types[*(op+1)]; + DataType image = types[*(op+2)].type; + type.type = static_cast(image | 0x100000); +} + +void SpirVModule::Reflection::reflect_array_type(CodeIterator op) +{ + TypeInfo &type = types[*(op+1)]; + const TypeInfo &elem = types[*(op+2)]; + type.type = elem.type; + type.struct_type = elem.struct_type; + const Variant &size = constants[*(op+3)]; + if(size.check_type()) + type.array_size = size.value(); + else if(size.check_type()) + type.array_size = size.value(); +} + +void SpirVModule::Reflection::reflect_struct_type(CodeIterator op) +{ + CodeIterator op_end = get_op_end(op); + unsigned id = *(op+1); + Structure &strct = structs[id]; + strct.name = names[id]; + types[id].struct_type = &strct; + op += 2; + + strct.members.resize(op_end-op); + vector::iterator mem = strct.members.begin(); + for(; op!=op_end; ++op, ++mem) + { + TypeInfo &type = types[*op]; + mem->type = type.type; + mem->struct_type = type.struct_type; + mem->array_size = type.array_size; + mem->array_stride = type.array_stride; + } +} + +void SpirVModule::Reflection::reflect_pointer_type(CodeIterator op) +{ + TypeInfo &type = types[*(op+1)]; + type = types[*(op+3)]; + type.storage = static_cast(*(op+2)); +} + +void SpirVModule::Reflection::reflect_constant(CodeIterator op) +{ + const TypeInfo &type = types[*(op+1)]; + unsigned id = *(op+2); + if(type.type==INT) + constants[id] = static_cast(*(op+3)); + else if(type.type==UNSIGNED_INT) + constants[id] = static_cast(*(op+3)); + else if(type.type==FLOAT) + constants[id] = *reinterpret_cast(&*(op+3)); +} + +void SpirVModule::Reflection::reflect_spec_constant(CodeIterator op) +{ + unsigned id = *(op+2); + SpecConstant &spec = spec_constants[id]; + spec.name = names[id]; + spec.type = types[*(op+1)].type; +} + +void SpirVModule::Reflection::reflect_variable(CodeIterator op) +{ + unsigned id = *(op+2); + Variable &var = variables[id]; + var.name = names[id]; + const TypeInfo &type = types[*(op+1)]; + var.storage = type.storage; + var.type = type.type; + var.struct_type = type.struct_type; + var.array_size = type.array_size; +} + +void SpirVModule::Reflection::reflect_decorate(CodeIterator op) +{ + unsigned id = *(op+1); + unsigned decoration = *(op+2); + op += 3; + + switch(decoration) + { + case DECO_SPEC_ID: + spec_constants[id].constant_id = *op; + break; + case DECO_ARRAY_STRIDE: + types[id].array_stride = *op; + break; + case DECO_LOCATION: + variables[id].location = *op; + break; + case DECO_BINDING: + variables[id].binding = *op; + break; + case DECO_DESCRIPTOR_SET: + variables[id].descriptor_set = *op; + break; + } +} + +void SpirVModule::Reflection::reflect_member_decorate(CodeIterator op) +{ + Structure &strct = structs[*(op+1)]; + unsigned index = *(op+2); + if(index>=strct.members.size()) + strct.members.resize(index+1); + unsigned decoration = *(op+3); + op += 4; + + StructMember &member = strct.members[index]; + switch(decoration) + { + case DECO_MATRIX_STRIDE: + member.matrix_stride = *op; + break; + case DECO_OFFSET: + member.offset = *op; + break; + } +} + } // namespace GL } // namespace Msp diff --git a/source/core/module.h b/source/core/module.h index 78afcfc9..4d819dac 100644 --- a/source/core/module.h +++ b/source/core/module.h @@ -3,12 +3,21 @@ #include #include +#include "datatype.h" +#include "gl.h" #include "glsl/compiler.h" #include "glsl/sourcemap.h" namespace Msp { namespace GL { +class invalid_module: public std::runtime_error +{ +public: + invalid_module(const std::string &w): runtime_error(w) { } + virtual ~invalid_module() throw() { } +}; + class Resources; class Module @@ -17,6 +26,7 @@ public: enum Format { GLSL, + SPIR_V }; protected: @@ -50,6 +60,157 @@ public: const SL::SourceMap &get_source_map() const { return source_map; } }; +class SpirVModule: public Module +{ +public: + enum Stage + { + VERTEX = 0, + GEOMETRY = 3, + FRAGMENT = 4 + }; + + enum StorageClass + { + UNIFORM_CONSTANT = 0, + INPUT = 1, + UNIFORM = 2, + OUTPUT = 3 + }; + + struct Structure; + struct Variable; + + struct EntryPoint + { + std::string name; + Stage stage; + std::vector globals; + + EntryPoint(); + }; + + struct StructMember + { + std::string name; + DataType type; + const Structure *struct_type; + unsigned offset; + unsigned array_size; + unsigned array_stride; + unsigned matrix_stride; + + StructMember(); + }; + + struct Structure + { + std::string name; + std::vector members; + unsigned size; + }; + + struct Variable + { + std::string name; + StorageClass storage; + DataType type; + const Structure *struct_type; + unsigned array_size; + int location; + int descriptor_set; + int binding; + + Variable(); + + bool operator==(const Variable &) const; + }; + + struct SpecConstant + { + std::string name; + unsigned constant_id; + DataType type; + }; + +private: + struct TypeInfo + { + DataType type; + const Structure *struct_type; + unsigned array_size; + unsigned array_stride; + StorageClass storage; + + TypeInfo(); + }; + + struct Reflection + { + typedef std::vector::const_iterator CodeIterator; + + std::map names; + std::map constants; + std::map types; + std::map entry_points; + std::map structs; + std::map variables; + std::map spec_constants; + + static UInt32 get_opcode(UInt32); + static CodeIterator get_op_end(const CodeIterator &); + static std::string read_string(CodeIterator &, const CodeIterator &); + + void reflect_code(const std::vector &); + void reflect_name(CodeIterator); + void reflect_member_name(CodeIterator); + void reflect_entry_point(CodeIterator); + void reflect_void_type(CodeIterator); + void reflect_bool_type(CodeIterator); + void reflect_int_type(CodeIterator); + void reflect_float_type(CodeIterator); + void reflect_vector_type(CodeIterator); + void reflect_matrix_type(CodeIterator); + void reflect_image_type(CodeIterator); + void reflect_sampled_image_type(CodeIterator); + void reflect_array_type(CodeIterator); + void reflect_struct_type(CodeIterator); + void reflect_pointer_type(CodeIterator); + void reflect_constant(CodeIterator); + void reflect_spec_constant_bool(CodeIterator); + void reflect_spec_constant(CodeIterator); + void reflect_variable(CodeIterator); + void reflect_decorate(CodeIterator); + void reflect_member_decorate(CodeIterator); + }; + + std::vector code; + std::vector entry_points; + std::vector structs; + std::vector variables; + std::vector spec_constants; + +public: + SpirVModule() { } + SpirVModule(const SpirVModule &); + SpirVModule &operator=(const SpirVModule &); +private: + void remap_pointers_from(const SpirVModule &); + +public: + virtual Format get_format() const { return SPIR_V; } + + void load_code(IO::Base &); +private: + virtual void compile(SL::Compiler &); + +public: + const std::vector &get_code() const { return code; } + const std::vector &get_entry_points() const { return entry_points; } + const std::vector &get_variables() const { return variables; } + const std::vector &get_spec_constants() const { return spec_constants; } +}; + } // namespace GL } // namespace Msp diff --git a/source/core/program.cpp b/source/core/program.cpp index dd22033e..23069392 100644 --- a/source/core/program.cpp +++ b/source/core/program.cpp @@ -3,7 +3,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -15,7 +17,6 @@ #include "buffer.h" #include "error.h" #include "misc.h" -#include "module.h" #include "program.h" #include "resources.h" #include "shader.h" @@ -86,6 +87,7 @@ void Program::add_stages(const Module &mod, const map &spec_values) switch(mod.get_format()) { case Module::GLSL: return add_glsl_stages(static_cast(mod), spec_values); + case Module::SPIR_V: return add_spirv_stages(static_cast(mod), spec_values); default: throw invalid_argument("Program::add_stages"); } } @@ -176,6 +178,54 @@ void Program::compile_glsl_stage(unsigned stage_id) #endif } +void Program::add_spirv_stages(const SpirVModule &mod, const map &spec_values) +{ + static Require _req(ARB_gl_spirv); + static Require _req2(ARB_ES2_compatibility); + + module = &mod; + + const vector &entry_points = mod.get_entry_points(); + std::set stages; + for(vector::const_iterator i=entry_points.begin(); i!=entry_points.end(); ++i) + { + if(stages.count(i->stage)) + throw invalid_argument("Program::add_spirv_stages"); + + switch(i->stage) + { + case SpirVModule::VERTEX: add_stage(GL_VERTEX_SHADER); break; + case SpirVModule::GEOMETRY: add_stage(GL_GEOMETRY_SHADER); break; + case SpirVModule::FRAGMENT: add_stage(GL_FRAGMENT_SHADER); break; + default: throw invalid_operation("Program::add_spirv_stages"); + } + + stages.insert(i->stage); + } + + const vector &code = mod.get_code(); + glShaderBinary(stage_ids.size(), &stage_ids[0], GL_SHADER_BINARY_FORMAT_SPIR_V, &code[0], code.size()*4); + + const vector &spec_consts = mod.get_spec_constants(); + vector spec_id_array; + vector spec_value_array; + spec_id_array.reserve(spec_consts.size()); + spec_value_array.reserve(spec_consts.size()); + for(vector::const_iterator i=spec_consts.begin(); i!=spec_consts.end(); ++i) + { + map::const_iterator j = spec_values.find(i->name); + if(j!=spec_values.end()) + { + spec_id_array.push_back(i->constant_id); + spec_value_array.push_back(j->second); + } + } + + vector::const_iterator j=entry_points.begin(); + for(vector::const_iterator i=stage_ids.begin(); i!=stage_ids.end(); ++i, ++j) + glSpecializeShader(*i, j->name.c_str(), spec_id_array.size(), &spec_id_array[0], &spec_value_array[0]); +} + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" void Program::attach_shader(Shader &shader) @@ -245,8 +295,16 @@ void Program::link() IO::print("Program link info log:\n%s", info_log); #endif - query_uniforms(); - query_attributes(); + if(module->get_format()==Module::GLSL) + { + query_uniforms(); + query_attributes(); + } + else if(module->get_format()==Module::SPIR_V) + { + collect_uniforms(); + collect_attributes(); + } for(UniformMap::const_iterator i=uniforms.begin(); i!=uniforms.end(); ++i) require_type(i->second.type); @@ -300,10 +358,7 @@ void Program::query_uniforms() default_block.layout_hash = compute_layout_hash(default_block.uniforms); - string layout_descriptor; - for(UniformBlockMap::const_iterator i=uniform_blocks.begin(); i!=uniform_blocks.end(); ++i) - layout_descriptor += format("%d:%x\n", i->second.bind_point, i->second.layout_hash); - uniform_layout_hash = hash32(layout_descriptor); + update_layout_hash(); } void Program::query_uniform_blocks(const vector &uniforms_by_index) @@ -401,6 +456,107 @@ void Program::query_attributes() } } +void Program::collect_uniforms() +{ + const SpirVModule &mod = static_cast(*module); + + UniformBlockInfo &default_block = uniform_blocks[string()]; + default_block.data_size = 0; + default_block.bind_point = -1; + + const vector &variables = mod.get_variables(); + for(vector::const_iterator i=variables.begin(); i!=variables.end(); ++i) + { + if(i->storage==SpirVModule::UNIFORM && i->struct_type) + { + UniformBlockInfo &info = uniform_blocks[i->struct_type->name]; + info.name = i->struct_type->name; + info.bind_point = i->binding; + info.data_size = i->struct_type->size; + + string prefix; + if(!i->name.empty()) + prefix = i->struct_type->name+"."; + collect_block_uniforms(info, *i->struct_type, prefix, 0); + + info.layout_hash = compute_layout_hash(info.uniforms); + } + else if(i->storage==SpirVModule::UNIFORM_CONSTANT && i->location>=0) + { + UniformInfo &info = uniforms[i->name]; + info.name = i->name; + info.block = &default_block; + info.location = i->location; + info.array_size = i->array_size; + info.array_stride = 0; + info.matrix_stride = 0; + info.type = i->type; + default_block.uniforms.push_back(&info); + } + } + + default_block.layout_hash = compute_layout_hash(default_block.uniforms); + + update_layout_hash(); +} + +void Program::collect_block_uniforms(UniformBlockInfo &block, const SpirVModule::Structure &strct, const string &prefix, unsigned base_offset) +{ + for(vector::const_iterator i=strct.members.begin(); i!=strct.members.end(); ++i) + { + if(i->struct_type) + { + if(i->array_size) + { + for(unsigned j=0; jarray_size; ++j) + collect_block_uniforms(block, *i->struct_type, format("%s%s[%d].", prefix, i->name, j), base_offset+i->offset+i->array_stride*j); + } + else + collect_block_uniforms(block, *i->struct_type, prefix+i->name+".", base_offset+i->offset); + } + else + { + string name = prefix+i->name; + UniformInfo &info = uniforms[name]; + info.name = name; + info.block = █ + info.location = i->offset; + info.array_size = i->array_size; + info.array_stride = i->array_stride; + info.matrix_stride = i->matrix_stride; + info.type = i->type; + block.uniforms.push_back(&info); + } + } +} + +void Program::collect_attributes() +{ + const SpirVModule &mod = static_cast(*module); + + const vector &entry_points = mod.get_entry_points(); + for(vector::const_iterator i=entry_points.begin(); i!=entry_points.end(); ++i) + if(i->stage==SpirVModule::VERTEX && i->name=="main") + { + for(vector::const_iterator j=i->globals.begin(); j!=i->globals.end(); ++j) + if((*j)->storage==SpirVModule::INPUT) + { + AttributeInfo &info = attributes[(*j)->name]; + info.location = (*j)->location; + info.array_size = (*j)->array_size; + info.type = (*j)->type; + } + } +} + +void Program::update_layout_hash() +{ + string layout_descriptor; + for(UniformBlockMap::const_iterator i=uniform_blocks.begin(); i!=uniform_blocks.end(); ++i) + layout_descriptor += format("%d:%x\n", i->second.bind_point, i->second.layout_hash); + uniform_layout_hash = hash32(layout_descriptor); +} + Program::LayoutHash Program::compute_layout_hash(const vector &uniforms) { string layout_descriptor; diff --git a/source/core/program.h b/source/core/program.h index 33413eb0..6f84cfd2 100644 --- a/source/core/program.h +++ b/source/core/program.h @@ -7,13 +7,12 @@ #include "bindable.h" #include "datatype.h" #include "gl.h" +#include "module.h" #include "vertexformat.h" namespace Msp { namespace GL { -class GlslModule; -class Module; class Shader; /** @@ -125,6 +124,7 @@ private: unsigned add_stage(GLenum); void add_glsl_stages(const GlslModule &, const std::map &); void compile_glsl_stage(unsigned); + void add_spirv_stages(const SpirVModule &, const std::map &); public: DEPRECATED void attach_shader(Shader &shader); @@ -141,6 +141,10 @@ private: void query_uniforms(); void query_uniform_blocks(const std::vector &); void query_attributes(); + void collect_uniforms(); + void collect_block_uniforms(UniformBlockInfo &, const SpirVModule::Structure &, const std::string &, unsigned); + void collect_attributes(); + void update_layout_hash(); static LayoutHash compute_layout_hash(const std::vector &); static bool uniform_location_compare(const UniformInfo *, const UniformInfo *); public: diff --git a/source/resources/resources.cpp b/source/resources/resources.cpp index bb3243f6..a2d9219b 100644 --- a/source/resources/resources.cpp +++ b/source/resources/resources.cpp @@ -45,7 +45,7 @@ Resources::Resources(): add_type().suffix(".lightn").keyword("lighting"); add_type().suffix(".mat").creator(&Resources::create_material); add_type().keyword("mesh").creator(&Resources::create_mesh); - add_type().suffix(".glsl").creator(&Resources::create_module); + add_type().suffix(".glsl").suffix(".spv").creator(&Resources::create_module); add_type().keyword("object"); add_type().suffix(".pipe").keyword("pipeline"); add_type().keyword("pose"); @@ -175,7 +175,7 @@ Texture2D *Resources::create_texture2d(const string &name) Module *Resources::create_module(const string &name) { string ext = FS::extpart(name); - if(ext!=".glsl") + if(ext!=".glsl" && ext!=".spv") return 0; if(RefPtr io = open_raw(name)) @@ -186,6 +186,12 @@ Module *Resources::create_module(const string &name) module->load_source(*io, this, name); return module.release(); } + else if(ext==".spv") + { + RefPtr module = new SpirVModule; + module->load_code(*io); + return module.release(); + } } return 0; @@ -196,7 +202,7 @@ Program *Resources::create_program(const string &name) string ext = FS::extpart(name); string base = FS::basepart(name); string ext2 = FS::extpart(base); - if(ext==".shader" && ext2==".glsl") + if(ext==".shader" && (ext2==".glsl" || ext2==".spv")) { Module &module = get(base); RefPtr shprog = new Program; -- 2.43.0