diff --git a/src/libvgcode/src/ViewerImpl.cpp b/src/libvgcode/src/ViewerImpl.cpp index da1a601149..77fca69fc5 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_cumulative_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(); @@ -996,6 +1002,8 @@ void ViewerImpl::load(GCodeInputData&& gcode_data) m_tool_colors = std::move(gcode_data.tools_colors); m_color_print_colors = std::move(gcode_data.color_print_colors); m_vertices_colors.resize(m_vertices.size()); + for (std::vector& times : m_cumulative_times) + times.resize(m_vertices.size()); m_settings.spiral_vase_mode = gcode_data.spiral_vase_mode; @@ -1006,6 +1014,9 @@ void ViewerImpl::load(GCodeInputData&& gcode_data) for (size_t j = 0; j < TIME_MODES_COUNT; ++j) { m_total_time[j] += v.times[j]; + // the running total up to and including this vertex is exactly what + // get_estimated_time_at() has to return for it + m_cumulative_times[j][i] = m_total_time[j]; if (v.type == EMoveType::Travel) m_travels_time[j] += v.times[j]; } @@ -1048,6 +1059,20 @@ 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]); + } + if (!m_layers.empty()) m_layers.set_view_range(0, static_cast(m_layers.count()) - 1); @@ -1261,7 +1286,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_cumulative_times[mode].size()) + return 0.0f; + return m_cumulative_times[mode][id]; } Color ViewerImpl::get_vertex_color(const PathVertex& v) const @@ -1722,6 +1752,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_cumulative_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 +1821,13 @@ 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 on its first condition alone. Starting there turns a scan + // from vertex 0 on every slider tick into a scan of the visible part only; what the loop + // settles on is unchanged. 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..e52317643d 100644 --- a/src/libvgcode/src/ViewerImpl.hpp +++ b/src/libvgcode/src/ViewerImpl.hpp @@ -234,6 +234,27 @@ private: // std::array m_total_time{ 0.0f, 0.0f }; // + // Running sum of the vertex estimated times, one entry per vertex for each time mode. + // get_estimated_time_at() answers from this instead of re-accumulating the whole vertex + // array, which it was doing once per frame from the tool marker tooltip. The sums are + // built by the same left-to-right addition the accumulate performed, so the value handed + // back is bit-identical, float rounding included. + // + std::array, TIME_MODES_COUNT> m_cumulative_times; + // + // For each layer L, the index of the first vertex whose layer_id is >= L (m_vertices.size() + // if there is none). Every vertex before it is guaranteed to belong to an earlier layer, so + // the scan in update_view_full_range() can start there rather than at vertex 0. Derived from + // the vertices themselves rather than from Layers, which buckets an out-of-order vertex into + // the layer that happens to be open, so this stays exact whatever order the vertices arrive in. + // + std::vector m_layer_first_vertex; + // + // Scratch buffer for update_colors_texture(), kept alive so that a slider drag does not + // allocate and free one float per vertex of the print on every step. + // + 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 49477ac7dd..82cd649fd4 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1622,7 +1622,7 @@ void GCodeViewer::render(int canvas_width, int canvas_height, int right_margin) 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(); @@ -3408,6 +3408,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; @@ -3415,7 +3421,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()) @@ -4562,6 +4571,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}); @@ -4581,7 +4592,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) { @@ -4630,7 +4640,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)