+void Program::collect_uniforms()
+{
+ const SpirVModule &mod = static_cast<const SpirVModule &>(*module);
+
+ // Prepare the default block
+ uniform_blocks.push_back(UniformBlockInfo());
+ vector<vector<string> > block_uniform_names(1);
+
+ for(const SpirVModule::Variable &v: mod.get_variables())
+ {
+ if(v.storage==SpirVModule::UNIFORM && v.struct_type)
+ {
+ uniform_blocks.push_back(UniformBlockInfo());
+ UniformBlockInfo &info = uniform_blocks.back();
+ info.name = v.struct_type->name;
+ info.bind_point = v.binding;
+ info.data_size = v.struct_type->size;
+
+ string prefix;
+ if(!v.name.empty())
+ prefix = v.struct_type->name+".";
+ block_uniform_names.push_back(vector<string>());
+ collect_block_uniforms(*v.struct_type, prefix, 0, block_uniform_names.back());
+ }
+ else if(v.storage==SpirVModule::UNIFORM_CONSTANT && v.location>=0)
+ {
+ block_uniform_names[0].push_back(v.name);
+ uniforms.push_back(UniformInfo());
+ UniformInfo &info = uniforms.back();
+ info.name = v.name;
+ info.tag = v.name;
+ info.location = v.location;
+ info.binding = v.binding;
+ info.array_size = v.array_size;
+ info.type = v.type;
+ }
+ }
+
+ sort_member(uniforms, &UniformInfo::tag);
+
+ for(unsigned i=0; i<uniform_blocks.size(); ++i)
+ {
+ UniformBlockInfo &block = uniform_blocks[i];
+ for(const string &n: block_uniform_names[i])
+ {
+ // The element is already known to be present
+ UniformInfo &uni = *lower_bound_member(uniforms, Tag(n), &UniformInfo::tag);
+ block.uniforms.push_back(&uni);
+ uni.block = █
+ }
+ sort(block.uniforms, uniform_location_compare);
+ block.layout_hash = compute_layout_hash(block.uniforms);
+ }
+
+ update_layout_hash();
+}
+
+void Program::collect_block_uniforms(const SpirVModule::Structure &strct, const string &prefix, unsigned base_offset, vector<string> &uniform_names)
+{
+ for(const SpirVModule::StructMember &m: strct.members)
+ {
+ unsigned offset = base_offset+m.offset;
+ if(m.struct_type)
+ {
+ unsigned array_size = m.array_size;
+ if(m.array_size_spec)
+ {
+ array_size = m.array_size_spec->i_value;
+ if(transient)
+ {
+ auto j = transient->spec_values.find(m.array_size_spec->constant_id);
+ if(j!=transient->spec_values.end())
+ array_size = j->second;
+ }
+ }
+
+ if(array_size)
+ {
+ for(unsigned j=0; j<array_size; ++j, offset+=m.array_stride)
+ collect_block_uniforms(*m.struct_type, format("%s%s[%d].", prefix, m.name, j), offset, uniform_names);
+ }
+ else
+ collect_block_uniforms(*m.struct_type, prefix+m.name+".", offset, uniform_names);
+ }
+ else
+ {
+ string name = prefix+m.name;
+ uniform_names.push_back(name);
+ uniforms.push_back(UniformInfo());
+ UniformInfo &info = uniforms.back();
+ info.name = name;
+ info.tag = name;
+ info.offset = offset;
+ info.array_size = m.array_size;
+ info.array_stride = m.array_stride;
+ info.matrix_stride = m.matrix_stride;
+ info.type = m.type;
+ }
+ }
+}
+
+void Program::collect_attributes()
+{
+ const SpirVModule &mod = static_cast<const SpirVModule &>(*module);
+
+ for(const SpirVModule::EntryPoint &e: mod.get_entry_points())
+ if(e.stage==SpirVModule::VERTEX && e.name=="main")
+ {
+ for(const SpirVModule::Variable *v: e.globals)
+ if(v->storage==SpirVModule::INPUT)
+ {
+ attributes.push_back(AttributeInfo());
+ AttributeInfo &info = attributes.back();
+ info.name = v->name;
+ info.location = v->location;
+ info.array_size = v->array_size;
+ info.type = v->type;
+ }
+ }
+}
+
+void Program::update_layout_hash()
+{
+ string layout_descriptor;
+ for(const UniformBlockInfo &b: uniform_blocks)
+ layout_descriptor += format("%d:%x\n", b.bind_point, b.layout_hash);
+ uniform_layout_hash = hash32(layout_descriptor);
+}
+