+vector<Stage::Type> Compiler::get_stages() const
+{
+ vector<Stage::Type> stage_types;
+ stage_types.reserve(module->stages.size());
+ for(const Stage &s: module->stages)
+ stage_types.push_back(s.type);
+ return stage_types;
+}
+
+string Compiler::get_stage_glsl(Stage::Type stage_type) const
+{
+ if(!compiled)
+ throw invalid_operation("Compiler::get_stage_glsl");
+ auto i = find_member(module->stages, stage_type, &Stage::type);
+ if(i!=module->stages.end())
+ return Formatter().apply(*i);
+ throw key_error(Stage::get_stage_name(stage_type));
+}
+
+vector<uint32_t> Compiler::get_combined_spirv() const
+{
+ if(!compiled)
+ throw invalid_operation("Compiler::get_combined_spirv");
+ SpirVGenerator gen;
+ gen.apply(*module, features);
+ return gen.get_code();
+}
+
+const map<string, unsigned> &Compiler::get_vertex_attributes() const
+{
+ if(!compiled)
+ throw invalid_operation("Compiler::get_vertex_attributes");
+ auto i = find_member(module->stages, Stage::VERTEX, &Stage::type);
+ if(i!=module->stages.end())
+ return i->locations;
+ throw invalid_operation("Compiler::get_vertex_attributes");
+}
+
+const map<string, unsigned> &Compiler::get_fragment_outputs() const
+{
+ if(!compiled)
+ throw invalid_operation("Compiler::get_fragment_outputs");
+ auto i = find_member(module->stages, Stage::FRAGMENT, &Stage::type);
+ if(i!=module->stages.end())
+ return i->locations;
+ throw invalid_operation("Compiler::get_fragment_outputs");
+}
+
+const map<string, unsigned> &Compiler::get_texture_bindings() const
+{
+ if(!compiled)
+ throw invalid_operation("Compiler::get_texture_bindings");
+ return module->shared.texture_bindings;
+}
+
+const map<string, unsigned> &Compiler::get_uniform_block_bindings() const
+{
+ if(!compiled)
+ throw invalid_operation("Compiler::get_uniform_block_bindings");
+ return module->shared.uniform_block_bindings;
+}
+
+unsigned Compiler::get_n_clip_distances() const
+{
+ if(!compiled)
+ throw invalid_operation("Compiler::get_n_clip_distances");
+ auto i = find_member(module->stages, Stage::VERTEX, &Stage::type);
+ return (i!=module->stages.end() ? i->n_clip_distances : 0);
+}
+
+const SourceMap &Compiler::get_source_map() const
+{
+ return module->source_map;
+}
+
+string Compiler::get_stage_debug(Stage::Type stage_type, bool use_colors) const
+{
+ auto i = find_member(module->stages, stage_type, &Stage::type);
+ if(i!=module->stages.end())
+ return DumpTree(use_colors).apply(*i);
+ throw key_error(Stage::get_stage_name(stage_type));
+}
+
+string Compiler::get_diagnostics() const
+{
+ string combined;
+ for(const Stage &s: module->stages)
+ for(const Diagnostic &d: s.diagnostics)
+ if(d.source!=INTERNAL_SOURCE)
+ append(combined, "\n", format("%s:%d: %s", module->source_map.get_name(d.source), d.line, d.message));
+ return combined;
+}
+
+void Compiler::append_module(const Module &mod, ModuleCache &mod_cache)