diff --git a/src/libvgcode/src/ViewerImpl.cpp b/src/libvgcode/src/ViewerImpl.cpp index da1a601149..441b234e9f 100644 --- a/src/libvgcode/src/ViewerImpl.cpp +++ b/src/libvgcode/src/ViewerImpl.cpp @@ -875,6 +875,12 @@ void ViewerImpl::reset() m_travels_time = { 0.0f, 0.0f }; m_vertices.clear(); m_vertices_colors.clear(); + // swap rather than clear: these are sized by the print, and a reset means the memory + // should go back, not sit reserved until the next load + for (std::vector& times : m_layer_start_times) + std::vector().swap(times); + std::vector().swap(m_layer_first_vertex); + std::vector().swap(m_colors_scratch); m_valid_lines_bitset.clear(); #if VGCODE_ENABLE_COG_AND_TOOL_MARKERS m_cog_marker.reset(); @@ -1048,6 +1054,37 @@ void ViewerImpl::load(GCodeInputData&& gcode_data) v.layer_duration = m_layers.get_layer_time(m_settings.time_mode, static_cast(v.layer_id)); } + // Index of the first vertex of each layer, walked back to front so that a layer with no + // vertex of its own inherits the next layer's index and the array stays non-decreasing. + if (!m_layers.empty()) { + const uint32_t vertices_count = static_cast(m_vertices.size()); + m_layer_first_vertex.assign(m_layers.count(), vertices_count); + for (uint32_t i = vertices_count; i > 0; --i) { + const uint32_t layer_id = m_vertices[i - 1].layer_id; + if (layer_id < m_layer_first_vertex.size()) + m_layer_first_vertex[layer_id] = i - 1; + } + for (size_t i = m_layer_first_vertex.size() - 1; i > 0; --i) + m_layer_first_vertex[i - 1] = std::min(m_layer_first_vertex[i - 1], m_layer_first_vertex[i]); + + // the running time at each layer's first vertex, summed in vertex order so that + // get_estimated_time_at() matches a full accumulation exactly + std::array running{}; + for (std::vector& times : m_layer_start_times) + times.assign(m_layer_first_vertex.size(), 0.0f); + size_t layer = 0; + for (size_t i = 0; i <= m_vertices.size(); ++i) { + for (; layer < m_layer_first_vertex.size() && m_layer_first_vertex[layer] == i; ++layer) { + for (size_t j = 0; j < TIME_MODES_COUNT; ++j) + m_layer_start_times[j][layer] = running[j]; + } + if (i < m_vertices.size()) { + for (size_t j = 0; j < TIME_MODES_COUNT; ++j) + running[j] += m_vertices[i].times[j]; + } + } + } + if (!m_layers.empty()) m_layers.set_view_range(0, static_cast(m_layers.count()) - 1); @@ -1261,7 +1298,10 @@ void ViewerImpl::update_colors_texture() // Based on current settings and slider position, we might want to render some // vertices as dark grey (or darkened, see above). Use either that or the normal color (from the cache). - std::vector colors(m_vertices_colors.size()); + // Reused across calls: this runs on every slider tick, and the allocation alone is + // 4 bytes per vertex of the whole print each time. + std::vector& colors = m_colors_scratch; + colors.resize(m_vertices_colors.size()); assert(colors.size() == m_vertices.size() && m_vertices_colors.size() == m_vertices.size()); for (size_t i=0; i(m_settings.time_mode)]; }); + const size_t mode = static_cast(m_settings.time_mode); + if (mode >= TIME_MODES_COUNT || id >= m_vertices.size()) + return 0.0f; + size_t first = 0; + float time = 0.0f; + const size_t layer = static_cast(m_vertices[id].layer_id); + if (layer < m_layer_first_vertex.size() && m_layer_first_vertex[layer] <= id) { + first = m_layer_first_vertex[layer]; + time = m_layer_start_times[mode][layer]; + } + for (size_t i = first; i <= id; ++i) + time += m_vertices[i].times[mode]; + return time; } Color ViewerImpl::get_vertex_color(const PathVertex& v) const @@ -1722,6 +1773,10 @@ size_t ViewerImpl::get_used_cpu_memory() const ret += sizeof(m_extrusion_roles_colors); ret += sizeof(m_options_colors); ret += STDVEC_MEMSIZE(m_vertices, PathVertex); + for (const std::vector& times : m_layer_start_times) + ret += STDVEC_MEMSIZE(times, float); + ret += STDVEC_MEMSIZE(m_layer_first_vertex, uint32_t); + ret += STDVEC_MEMSIZE(m_colors_scratch, float); ret += m_valid_lines_bitset.size_in_bytes_cpu(); ret += m_height_range.size_in_bytes_cpu(); ret += m_width_range.size_in_bytes_cpu(); @@ -1787,7 +1842,11 @@ void ViewerImpl::update_view_full_range() const bool travels_visible = m_settings.options_visibility[size_t(EOptionType::Travels)]; const bool wipes_visible = m_settings.options_visibility[size_t(EOptionType::Wipes)]; + // every vertex before m_layer_first_vertex[layers_range[0]] has a smaller layer_id, so the loop + // below would skip all of them anyway auto first_it = m_vertices.begin(); + if (layers_range[0] < m_layer_first_vertex.size()) + first_it += m_layer_first_vertex[layers_range[0]]; while (first_it != m_vertices.end() && (first_it->layer_id < layers_range[0] || !is_visible(*first_it, m_settings))) { ++first_it; diff --git a/src/libvgcode/src/ViewerImpl.hpp b/src/libvgcode/src/ViewerImpl.hpp index 4da312fc0e..80f23287a6 100644 --- a/src/libvgcode/src/ViewerImpl.hpp +++ b/src/libvgcode/src/ViewerImpl.hpp @@ -234,6 +234,20 @@ private: // std::array m_total_time{ 0.0f, 0.0f }; // + // Running sum of the vertex estimated times at each layer's first vertex, for each time mode, + // so that get_estimated_time_at() only accumulates the vertices of one layer. + // + std::array, TIME_MODES_COUNT> m_layer_start_times; + // + // For each layer L, the index of the first vertex whose layer_id is >= L (m_vertices.size() + // if there is none). Derived from the vertices, so it stays exact whatever order they arrive in. + // + std::vector m_layer_first_vertex; + // + // Scratch buffer for update_colors_texture(), kept alive across slider steps + // + std::vector m_colors_scratch; + // // Detected travel moves times // std::array m_travels_time{ 0.0f, 0.0f }; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 3b3354d86f..19eff76965 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1627,7 +1627,7 @@ void GCodeViewer::render_scene(int canvas_width, int canvas_height) glsafe(::glEnable(GL_DEPTH_TEST)); render_shells(canvas_width, canvas_height); - if (m_viewer.get_extrusion_roles().empty()) + if (m_viewer.get_extrusion_roles_count() == 0) return; render_toolpaths(); @@ -3426,6 +3426,12 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv std::vector>> ret; ret.reserve(custom_gcode_per_print_z.size()); + // Loop invariant, but built lazily: this lambda runs once per extruder on every frame + // and most prints reach neither colour change below, so fetching it up front would cost + // more than the per-item fetch it replaces. + std::vector zs; + bool zs_built = false; + for (const auto& item : custom_gcode_per_print_z) { if (extruder_id + 1 != static_cast(item.extruder)) continue; @@ -3433,7 +3439,10 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv if (item.type != ColorChange) continue; - const std::vector zs = m_viewer.get_layers_zs(); + if (!zs_built) { + zs = m_viewer.get_layers_zs(); + zs_built = true; + } auto lower_b = std::lower_bound(zs.begin(), zs.end(), static_cast(item.print_z - epsilon())); if (lower_b == zs.end()) @@ -4582,6 +4591,8 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv // ORCA: Get layer Zs as doubles std::vector layer_zs = get_layers_zs(); + // loop invariant, same reason as the layer Zs above + const std::vector layer_times = m_viewer.get_layers_estimated_times(); for (Slic3r::CustomGCode::Item custom_gcode : custom_gcode_per_print_z) { ImGui::Dummy({window_padding, window_padding}); @@ -4601,7 +4612,6 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv imgui.text(buf); ImGui::SameLine(max_len * 1.5); - std::vector layer_times = m_viewer.get_layers_estimated_times(); float custom_gcode_time = 0; if (layer > 0) { @@ -4650,7 +4660,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv std::string print_str = _u8L("Model printing time"); std::string total_str = _u8L("Total time"); float max_len = window_padding + 2 * ImGui::GetStyle().ItemSpacing.x; - if (m_viewer.get_layers_estimated_times().empty()) + if (m_viewer.get_layers_count() == 0) max_len += ImGui::CalcTextSize(total_str.c_str()).x; else { if (m_viewer.get_view_type() == libvgcode::EViewType::FeatureType)