void Video3DUberShader::create(std::set<std::string> const& material_names) { UberShader::create(material_names); // create depth shader std::vector<ShaderProgramStage> warp_pass_stages; warp_pass_stages.push_back( ShaderProgramStage( scm::gl::STAGE_VERTEX_SHADER, _warp_pass_vertex_shader())); warp_pass_stages.push_back( ShaderProgramStage( scm::gl::STAGE_GEOMETRY_SHADER, _warp_pass_geometry_shader())); warp_pass_stages.push_back( ShaderProgramStage( scm::gl::STAGE_FRAGMENT_SHADER, _warp_pass_fragment_shader())); auto warp_pass_program = std::make_shared<ShaderProgram>(); warp_pass_program->set_shaders(warp_pass_stages); add_program(warp_pass_program); // create final shader std::vector<ShaderProgramStage> blend_pass_stages; blend_pass_stages.push_back(ShaderProgramStage(scm::gl::STAGE_VERTEX_SHADER, _blend_pass_vertex_shader())); blend_pass_stages.push_back(ShaderProgramStage(scm::gl::STAGE_FRAGMENT_SHADER, _blend_pass_fragment_shader())); auto blend_pass_program = std::make_shared<ShaderProgram>(); blend_pass_program->set_shaders(blend_pass_stages); add_program(blend_pass_program); }
void LightingUberShader:: create(std::set<std::string> const& material_names, std::vector<LayerMapping const*> const& inputs) { fshader_factory_ = gua::make_unique<UberShaderFactory>( ShadingModel::LIGHTING_STAGE, material_names ); fshader_factory_->add_inputs_to_main_functions(inputs, ShadingModel::GBUFFER_FRAGMENT_STAGE); UberShader::set_uniform_mapping(fshader_factory_->get_uniform_mapping()); UberShader::set_output_mapping(fshader_factory_->get_output_mapping()); // VERTEX SHADER ------------------------------------------------------------- std::string vertex_shader( Resources::lookup_shader(Resources::shaders_uber_shaders_lighting_lighting_vert) ); // FRAGMENT SHADER ----------------------------------------------------------- std::string fragment_shader( Resources::lookup_shader(Resources::shaders_uber_shaders_lighting_lighting_frag) ); // input from gbuffer std::stringstream s; for (unsigned i(0); i<inputs.size(); ++i) s << inputs[i]->get_gbuffer_input_definition(ShadingModel::StageID(i+1)); string_utils::replace(fragment_shader, "@input_definition", s.str()); // material specific uniforms string_utils::replace(fragment_shader, "@uniform_definition", get_uniform_mapping()->get_uniform_definition()); // outputs string_utils::replace(fragment_shader, "@output_definition", get_gbuffer_mapping()->get_gbuffer_output_definition(false, false)); // print material specific methods string_utils::replace(fragment_shader, "@material_methods", UberShader::print_material_methods(*fshader_factory_)); // print main switch(es) string_utils::replace(fragment_shader, "@material_switch", UberShader::print_material_switch(*fshader_factory_)); auto lighting_program = std::make_shared<ShaderProgram>(); lighting_program->create_from_sources(vertex_shader, fragment_shader); add_program(lighting_program); }
void pipeline::reload_shadow_mapping() { auto vertex_file = shader_directory / "null.vertex.glsl"; auto fragment_file = shader_directory / "null.fragment.glsl"; if (!is_regular_file(vertex_file) || !is_regular_file(fragment_file)) return; shadow_mapping = basic_pass{ "shadow_mapping", add_program(program::configuration() .vertex_shader(shader_directory / "null.vertex.glsl") .fragment_shader(shader_directory / "null.fragment.glsl") .preprocessor_commands("#version 330\n")), GL_DEPTH_BUFFER_BIT, GL_BACK, render_mode{} .set(render_mode::statics) .set(render_mode::dynamics) .set(render_mode::test_depth)}; }
bool pipeline::import( path pipeline_file ) { BOOST_LOG_TRIVIAL(info) << "Importing pipeline file " << pipeline_file << "..."; programs.clear(); textures.clear(); views.clear(); buffers.clear(); index_bound_buffers.clear(); buffers_to_reset_pre_first_frame.clear(); passes.clear(); data_offsets = make_shared<std::vector<glm::uvec4>>(); try { pipeline_file = canonical_and_preferred(pipeline_file, shader_directory); ptree root; read_json(pipeline_file.string(), root); static auto get_vec3 = [] ( const ptree& root ) { assert(3 == root.size()); auto first = root.begin(); auto x = (*first++).second.get<float>(""); auto y = (*first++).second.get<float>(""); auto z = (*first++).second.get<float>(""); return glm::vec3(x, y, z); }; //////////////////////////////////////////////////////////////////////////////// /// Views //////////////////////////////////////////////////////////////////////////////// pass::view_container allocated_views; for (auto view_root : root.get_child("views") | boost::adaptors::map_values) { auto name = view_root.get<string>("name"); auto width = view_root.get<int>("width"); auto height = view_root.get<int>("height"); // Reserved names static const auto reserved_names = {"user"}; if (end(reserved_names) != boost::find(reserved_names, name)) throw exception{("Name \"" + name + "\" is reserved.").c_str()}; shared_ptr<view> view; // Orthographic if (auto orthographic_root = view_root.get_child_optional("orthographic")) { auto eye = get_vec3(view_root.get_child("eye")); auto target = get_vec3(view_root.get_child("target")); view = make_shared<black_label::rendering::view>( eye, target, glm::vec3{0.0f, 1.0f, 0.0f}, width, height, orthographic_root->get<float>("left"), orthographic_root->get<float>("right"), orthographic_root->get<float>("bottom"), orthographic_root->get<float>("top"), orthographic_root->get<float>("near"), orthographic_root->get<float>("far") ); } // Perspective else if (auto perspective_root = view_root.get_child_optional("perspective")) { auto eye = get_vec3(view_root.get_child("eye")); auto target = get_vec3(view_root.get_child("target")); view = make_shared<black_label::rendering::view>( eye, target, glm::vec3{0.0f, 1.0f, 0.0f}, width, height, perspective_root->get<float>("near"), perspective_root->get<float>("far") ); } // None else view = make_shared<black_label::rendering::view>(width, height); allocated_views.emplace_back(name, view); views.emplace(name, view); } //////////////////////////////////////////////////////////////////////////////// /// LDM Views //////////////////////////////////////////////////////////////////////////////// ldm_view_count = 0; auto ldm_size = root.get<int>("ldm_size", 100); auto ldm_scale = root.get<float>("ldm_scale", 2000.0f); auto ldm_offset = get_vec3(root.get_child("ldm_offset")); for (auto view_root : root.get_child("ldm_views") | boost::adaptors::map_values) { auto eye = get_vec3(view_root) + ldm_offset; auto name = "ldm_view" + to_string(ldm_view_count); auto width = ldm_size; auto height = ldm_size; auto left = -ldm_scale; auto right = ldm_scale; auto top = ldm_scale; auto bottom = -ldm_scale; auto near_ = -ldm_scale; auto far_ = ldm_scale; auto view = make_shared<black_label::rendering::view>( eye, ldm_offset, glm::vec3{ 0.0f, 1.0f, 0.0f }, width, height, left, right, bottom, top, near_, far_ ); allocated_views.emplace_back(name, view); views.emplace(name, view); ++ldm_view_count; } //////////////////////////////////////////////////////////////////////////////// /// Textures //////////////////////////////////////////////////////////////////////////////// pass::texture_container allocated_textures; for (auto child : root.get_child("textures")) { auto texture_ptree = child.second; auto name = texture_ptree.get<string>("name"); auto format_name = texture_ptree.get<string>("format"); auto filter_name = texture_ptree.get<string>("filter"); auto wrap_name = texture_ptree.get<string>("wrap"); auto data = texture_ptree.get_optional<string>("data"); format::type format; if ("rgba8" == format_name) format = format::rgba8; else if ("rgba16f" == format_name) format = format::rgba16f; else if ("rgba32f" == format_name) format = format::rgba32f; else if ("depth16" == format_name) format = format::depth16; else if ("depth24" == format_name) format = format::depth24; else if ("depth32f" == format_name) format = format::depth32f; else throw exception{"Unknown format type."}; filter::type filter; if ("nearest" == filter_name) filter = filter::nearest; else if ("linear" == filter_name) filter = filter::linear; else throw exception{"Unknown filter type."}; wrap::type wrap; if ("clamp_to_edge" == wrap_name) wrap = wrap::clamp_to_edge; else if ("repeat" == wrap_name) wrap = wrap::repeat; else if ("mirrored_repeat" == wrap_name) wrap = wrap::mirrored_repeat; else throw exception{"Unknown wrap type."}; auto width = texture_ptree.get<float>("width", 1.0); auto height = texture_ptree.get<float>("height", 1.0); auto texture = make_shared<gpu::storage_texture>(target::texture_2d, format, filter, wrap, width, height); if (data) { if ("random" == *data) { mt19937 random_number_generator; uniform_real_distribution<float> distribution(-1.0f, 1.0f); int random_texture_size = 800; auto random_data = vector<glm::vec3>(random_texture_size * random_texture_size); std::generate(random_data.begin(), random_data.end(), [&] () -> glm::vec3 { glm::vec3 v; do { v.x = distribution(random_number_generator); v.y = distribution(random_number_generator); v.z = distribution(random_number_generator); } while (glm::length(v) > 1.0f); return v * 0.5f + glm::vec3(0.5f); }); texture->bind_and_update(random_texture_size, random_texture_size, random_data.data()); } else throw exception{"Unknown data type."}; } allocated_textures.emplace_back(name, texture); textures.emplace(name, texture); } //////////////////////////////////////////////////////////////////////////////// /// Buffers //////////////////////////////////////////////////////////////////////////////// pass::buffer_container allocated_buffers; pass::index_bound_buffer_container allocated_index_bound_buffers; for (auto child : root.get_child("buffers")) { auto buffer_ptree = child.second; auto name = buffer_ptree.get<string>("name"); auto target_name = buffer_ptree.get<string>("target"); auto size = buffer_ptree.get<buffer::size_type>("size"); auto binding = buffer_ptree.get_optional<buffer::size_type>("binding"); auto data_name = buffer_ptree.get_optional<string>("data"); auto reset_name = buffer_ptree.get_optional<string>("reset"); target::type target; if ("shader_storage" == target_name) target = target::shader_storage; else if ("atomic_counter" == target_name) target = target::atomic_counter; else throw exception{"Unknown target type."}; vector<uint8_t> data; if (data_name) { if ("null" == *data_name) data.resize(size); else throw exception{"Unknown data type."}; } shared_ptr<gpu::buffer> buffer; if (binding) { auto index_bound_buffer = make_shared<gpu::index_bound_buffer>(target, usage::dynamic_copy, *binding, size, (data.empty()) ? nullptr : data.data()); allocated_index_bound_buffers.emplace_back(name, index_bound_buffer); index_bound_buffers.emplace(name, index_bound_buffer); buffer = index_bound_buffer; } else { buffer = make_shared<gpu::buffer>(target, usage::dynamic_copy, size, (data.empty()) ? nullptr : data.data()); allocated_buffers.emplace_back(name, buffer); buffers.emplace(name, buffer); } if (reset_name) { if (data.empty()) throw exception{"Reset is specified without data."}; if ("pre_first_pass" == *reset_name) buffers_to_reset_pre_first_frame.emplace_back(move(buffer), move(data)); else throw exception{"Unknown reset type."}; } } //////////////////////////////////////////////////////////////////////////////// /// Passes //////////////////////////////////////////////////////////////////////////////// for (auto pass_child : root.get_child("passes")) { auto pass_configuration = pass_child.second; auto name = pass_configuration.get<string>("name"); if ("ldm_passes" == name) { program::configuration configuration; configuration.vertex_shader(shader_directory / "ldm.vertex.glsl"); configuration.fragment_shader(shader_directory / "ldm.fragment.glsl"); configuration.preprocessor_commands("#version 430\n"); auto program_ = add_program(configuration); for (int id{0}; ldm_view_count > id; ++id) { const view* view = views.find("ldm_view" + to_string(id))->second.lock().get(); pass::buffer_container pass_buffers; pass::index_bound_buffer_container pass_index_bound_buffers; pass_buffers.emplace_back("data_buffer", buffers.find("data_buffer")->second.lock()); pass_index_bound_buffers.emplace_back("counter", index_bound_buffers.at("counter").lock()); unsigned int clearing_mask{ 0u }; unsigned int face_culling_mode{ 0u }; render_mode render_mode; render_mode.set(render_mode::statics); render_mode.set(render_mode::dynamics); render_mode.set(render_mode::test_depth, false); render_mode.set(render_mode::materials, pass_configuration.get<bool>("materials", true)); unsigned int post_memory_barrier_mask{GL_SHADER_STORAGE_BARRIER_BIT}; auto preincrement_buffer_counter = 40000; pass::texture_container input_textures, output_textures; pass::view_container auxiliary_views; passes.emplace_back( "ldm_view" + to_string(id), program_, move(input_textures), move(output_textures), move(auxiliary_views), move(pass_buffers), move(pass_index_bound_buffers), clearing_mask, post_memory_barrier_mask, face_culling_mode, render_mode, view, user_view, preincrement_buffer_counter, 0, data_offsets); } continue; } auto vertex_program = pass_configuration.get<string>("vertex_program"); auto geometry_program = pass_configuration.get("geometry_program", ""); auto fragment_program = pass_configuration.get<string>("fragment_program"); string preprocessor_commands; for (const auto& preprocessor_command_child : pass_configuration.get_child("preprocessor_commands")) { const auto& preprocessor_command = preprocessor_command_child.second.get<string>(""); preprocessor_commands += preprocessor_command + "\n"; } const view* view; auto view_name = pass_configuration.get<string>("view", "user"); if ("user" == view_name) view = user_view; else { auto result = views.find(view_name); if (views.end() == result) throw exception{("View \"" + view_name + "\" is not defined.").c_str()}; view = result->second.lock().get(); } pass::view_container auxiliary_views; if (auto auxiliary_views_root = pass_configuration.get_child_optional("auxiliary_views")) for (const auto& auxiliary_view : *auxiliary_views_root | boost::adaptors::map_values) { auto name = auxiliary_view.get<string>(""); if ("ldm_views" == name) { for (int id{0}; ldm_view_count > id; ++id) { auto ldm_name = "ldm_view" + to_string(id); auto view = views.at(ldm_name); auxiliary_views.emplace_back(move(ldm_name), view.lock()); } continue; } auto view = views.at(name); auxiliary_views.emplace_back(move(name), view.lock()); } pass::texture_container input_textures; if (auto input_texture_children = pass_configuration.get_child_optional("textures.input")) for (const auto& input_textures_child : *input_texture_children) { auto texture_name = input_textures_child.second.get<string>(""); auto texture = textures.at(texture_name); input_textures.emplace_back(move(texture_name), texture.lock()); } pass::texture_container output_textures; if (auto output_texture_children = pass_configuration.get_child_optional("textures.output")) for (const auto& output_textures_child : *output_texture_children) { auto texture_name = output_textures_child.second.get<string>(""); auto texture = textures.at(texture_name); output_textures.emplace_back(move(texture_name), texture.lock()); } pass::buffer_container pass_buffers; pass::index_bound_buffer_container pass_index_bound_buffers; if (auto buffer_children = pass_configuration.get_child_optional("buffers")) for (const auto& buffers_child : *buffer_children) { auto buffer_name = buffers_child.second.get<string>(""); // First search through the regular buffers. auto buffer = buffers.find(buffer_name); if (buffers.cend() != buffer) pass_buffers.emplace_back(move(buffer_name), buffer->second.lock()); // Otherwise, use the index-bound buffers. else pass_index_bound_buffers.emplace_back(move(buffer_name), index_bound_buffers.at(buffer_name).lock()); } render_mode render_mode; for (const auto& model_child : pass_configuration.get_child("models")) { const auto& model_value = model_child.second.get<string>(""); if ("statics" == model_value) render_mode.set(render_mode::statics); else if ("dynamics" == model_value) render_mode.set(render_mode::dynamics); else if ("screen_aligned_quad" == model_value) render_mode.set(render_mode::screen_aligned_quad); else if ("photons" == model_value) render_mode.set(render_mode::photons); else throw exception{"Invalid model type."}; } unsigned int clearing_mask{0u}; if (auto clear_children = pass_configuration.get_child_optional("clear")) for (const auto& clear_child : *clear_children) { const auto& clear = clear_child.second.get<string>(""); if ("depth" == clear) clearing_mask |= GL_DEPTH_BUFFER_BIT; else if ("color" == clear) clearing_mask |= GL_COLOR_BUFFER_BIT; else throw exception{"Unknown clear type."}; } unsigned int post_memory_barrier_mask{0u}; if (auto post_memory_barrier_children = pass_configuration.get_child_optional("memory_barrier.post")) for (const auto& post_memory_barrier_child : *post_memory_barrier_children) { const auto& post_memory_barrier = post_memory_barrier_child.second.get<string>(""); if ("shader_storage" == post_memory_barrier) post_memory_barrier_mask |= GL_SHADER_STORAGE_BARRIER_BIT; else throw exception{"Unknown memory_barrier type."}; } unsigned int face_culling_mode{0u}; if (auto culling_children = pass_configuration.get_child_optional("culling")) for (const auto& culling_child : *culling_children) { const auto& culling = culling_child.second.get<string>(""); if ("front_faces" == culling) face_culling_mode = (GL_BACK == face_culling_mode) ? GL_FRONT_AND_BACK : GL_FRONT; else if ("back_faces" == culling) face_culling_mode = (GL_FRONT == face_culling_mode) ? GL_FRONT_AND_BACK : GL_BACK; else throw exception{"Unknown culling type."}; } // TODO: Generalize this feature. auto preincrement_buffer_counter = pass_configuration.get<int>("preincrement_buffer.counter", 0); render_mode.set(render_mode::test_depth, pass_configuration.get<bool>("test_depth", false)); render_mode.set(render_mode::materials, pass_configuration.get<bool>("materials", true)); program::configuration configuration; configuration.vertex_shader(shader_directory / vertex_program); if (!geometry_program.empty()) configuration.geometry_shader(shader_directory / geometry_program); configuration.fragment_shader(shader_directory / fragment_program); configuration.preprocessor_commands(preprocessor_commands); for (const auto& input_textures_value : input_textures) configuration.add_vertex_attribute(input_textures_value.first); for (const auto& output_textures_value : output_textures) configuration.add_fragment_output(output_textures_value.first); auto program_ = add_program(configuration); passes.emplace_back( move(name), move(program_), move(input_textures), move(output_textures), move(auxiliary_views), move(pass_buffers), move(pass_index_bound_buffers), clearing_mask, post_memory_barrier_mask, face_culling_mode, render_mode, view, user_view, preincrement_buffer_counter, ldm_view_count, data_offsets); } } catch (const exception& exception) { BOOST_LOG_TRIVIAL(warning) << "Error reading " << pipeline_file << ": " << exception.what(); return complete = false; } reload_shadow_mapping(); on_window_resized(user_view->window.x, user_view->window.y); BOOST_LOG_TRIVIAL(info) << "Imported pipeline file " << pipeline_file << "."; return complete = true; }
void ygen_emit::codegen_function( yssa_function* function ) { ygen_program* p = add_program( function ); // Every program has one hidden parameter, the function object. assert( p->stackcount == 0 ); p->stackcount += 1; // Extract all constants. for ( size_t i = 0; i < function->ops.size(); ++i ) { yssa_opinst* op = function->ops.at( i ); switch ( op->opcode ) { case YL_GLOBAL: case YL_SETGLOBAL: case YL_KEY: case YL_SETKEY: case YL_DELKEY: { symkey k( op->key ); auto i = p->strvals.find( k ); if ( i != p->strvals.end() ) { // Upgrade to key, in case value was used earlier as string. ygen_value& value = p->values.at( i->second ); value.kind = YGEN_KEY; value.string->iskey = true; } else { size_t index = p->values.size(); ygen_value value; value.kind = YGEN_KEY; value.string = add_string( k ); value.string->iskey = true; p->values.push_back( value ); p->strvals.emplace( k, index ); } break; } case YL_NUMBER: { if ( ! p->numvals.count( op->number ) ) { size_t index = p->values.size(); ygen_value value; value.kind = YGEN_NUMBER; value.number = op->number; p->values.push_back( value ); p->numvals.emplace( op->number, index ); } break; } case YL_STRING: { symkey k( op->string->string, op->string->length ); if ( ! p->strvals.count( k ) ) { size_t index = p->values.size(); ygen_value value; value.kind = YGEN_STRING; value.string = add_string( k ); p->values.push_back( value ); p->strvals.emplace( k, index ); } break; } case YL_FUNCTION: { if ( ! p->funvals.count( op->function ) ) { size_t index = p->values.size(); ygen_value value; value.kind = YGEN_PROGRAM; value.program = add_program( op->function ); p->values.push_back( value ); p->funvals.emplace( op->function, index ); } break; } default: break; } } // And sort the values. std::vector< size_t > sorted; sorted.reserve( p->values.size() ); for ( size_t i = 0; i < p->values.size(); ++i ) { sorted.push_back( i ); } std::sort ( sorted.begin(), sorted.end(), [=]( size_t a, size_t b ) { const ygen_value& aval = p->values.at( a ); const ygen_value& bval = p->values.at( b ); if ( aval.kind < bval.kind ) return true; if ( aval.kind != bval.kind ) return false; switch ( aval.kind ) { case YGEN_KEY: case YGEN_STRING: return strcmp( aval.string->text, bval.string->text ) < 0; case YGEN_NUMBER: return aval.number < bval.number; case YGEN_PROGRAM: return a < b; } } ); // Construct each block. std::vector< size_t > indexes; std::vector< std::pair< size_t, size_t > > jumps; for ( size_t i = 0; i < function->blocks.size(); ++i ) { yssa_block* block = function->blocks.at( i ).get(); // Compile each op in block. size_t count = 0; for ( size_t i = block->lstart; i < block->lfinal; i += count ) { // Ops should be in sequential block order, with no gaps. assert( indexes.size() == i ); // 'count' SSA ops map to one or more VM ops. size_t opindex = p->ops.size(); count = opgen( p, i ); indexes.insert( indexes.end(), count, opindex ); } // Find next block. yssa_block* next_block = nullptr; if ( i + 1 < function->blocks.size() ) { next_block = function->blocks.at( i + 1 ).get(); } // Compile jumps. if ( block->test ) { assert( block->next ); assert( block->fail ); assert( block->test->r != yl_opinst::NOVAL ); unsigned r = block->test->r; yl_opcode jmpt; yl_opcode jmpf; if ( block->test->opcode != YSSA_ITEREACH ) { jmpt = YL_JMPT; jmpf = YL_JMPF; } else { jmpt = YL_JMPV; jmpf = YL_JMPN; } if ( block->next == next_block ) { size_t index = p->ops.size(); p->ops.emplace_back( jmpf, r, (signed)0 ); p->slocs.push_back( block->test->sloc ); jumps.emplace_back( index, block->fail->lstart ); } else if ( block->fail == next_block ) { size_t index = p->ops.size(); p->ops.emplace_back( jmpt, r, (signed)0 ); p->slocs.push_back( block->test->sloc ); jumps.emplace_back( index, block->next->lstart ); } else { size_t index = p->ops.size(); p->ops.emplace_back( jmpt, r, (signed)0 ); p->slocs.push_back( block->test->sloc ); jumps.emplace_back( index, block->next->lstart ); index = p->ops.size(); p->ops.emplace_back( YL_JMP, 0, (signed)0 ); p->slocs.push_back( block->test->sloc ); jumps.emplace_back( index, block->fail->lstart ); } } else if ( block->next && block->next != next_block ) { size_t index = p->ops.size(); p->ops.emplace_back( YL_JMP, 0, (signed)0 ); p->slocs.push_back( block->ops.size() ? block->ops.back()->sloc : -1 ); jumps.emplace_back( index, block->next->lstart ); } } indexes.push_back( p->ops.size() ); // Fix up jumps. for ( auto jump : jumps ) { size_t index = jump.first; size_t target = indexes.at( jump.second ); signed j = (signed)target - (signed)( index + 1 ); yl_opinst& op = p->ops.at( index ); op = yl_opinst( op.opcode(), op.r(), j ); } // Construct xframes. for ( size_t i = 0; i < function->blocks.size(); ++i ) { yssa_block* block = function->blocks.at( i ).get(); // Skip blocks without exception handlers. if ( ! block->xchandler ) { continue; } // Construct xframe. yl_xframe xf; xf.start = (unsigned)indexes.at( block->lstart ); xf.end = (unsigned)indexes.at( block->lfinal ); xf.close_upvals = block->xchandler->xclocalups; xf.close_iterators = block->xchandler->xcitercount; xf.handler = (unsigned)indexes.at( block->xchandler->lstart ); // Check if we can just extend the previous xframe. if ( p->xframes.size() && p->xframes.back().end == xf.start && p->xframes.back().handler == xf.handler ) { yl_xframe& merge = p->xframes.back(); assert( merge.close_upvals == xf.close_upvals ); assert( merge.close_iterators == xf.close_iterators ); merge.end = xf.end; continue; } // Otherwise add it. p->xframes.push_back( xf ); } // The first n debugvars are the names of upvals. for ( size_t i = 0; i < function->upnames.size(); ++i ) { p->debugvars.push_back( nullptr ); } // Construct debug information for variables. std::unordered_set< yssa_variable* > variables; for ( size_t i = 0; i < function->ops.size(); ++i ) { yssa_opinst* op = function->ops.at( i ); if ( op->has_associated() || ! op->variable ) { continue; } yssa_variable* v = op->variable; if ( variables.count( v ) ) { continue; } unsigned varindex = (unsigned)p->debugvars.size(); p->debugvars.push_back( v ); variables.insert( v ); for ( yssa_live_range* live = v->live; live; live = live->next ) { ygen_debugspan span; span.varindex = varindex; span.start = (unsigned)indexes.at( live->start ); span.end = (unsigned)indexes.at( live->final ); if ( p->debugspans.size() && p->debugspans.back().varindex == varindex && p->debugspans.back().end == span.start ) { p->debugspans.back().end = span.end; continue; } p->debugspans.push_back( span ); } } std::sort ( p->debugspans.begin(), p->debugspans.end(), []( const ygen_debugspan& a, const ygen_debugspan& b ) { if ( a.start < b.start ) return true; if ( a.start == b.start && a.end < b.end ) return true; return false; } ); }