mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-12 03:27:55 +00:00
Answer the Preview's Per-Frame Time Query From a Cached Sum
The G-code preview's cost is linear in the number of toolpath vertices, and on a tall multi-filament print the wipe tower dominates that count: it emits a roughly constant 160-180 moves on every layer whatever the object is, measured at 57-61% of all moves on a three-filament print. Four places scanned or allocated across the whole vertex array. None of them needed to. get_estimated_time_at re-accumulated the estimated time from vertex 0 on every call, and its caller is the tool marker tooltip, which ImGui re-renders every frame while the properties panel is unfolded. It now reads a running sum built during load from the total the load loop was already keeping, so the value is the same addition in the same order and the float result is unchanged. At the 351k vertices of a 636-layer test print this drops the call from 238us to 0.006us. update_view_full_range walked from vertex 0 to find where the layer range starts, on every slider tick. It now starts at the first vertex of that layer. The index is derived from the vertices rather than from Layers::Item::range, because Layers::update folds a vertex whose layer_id arrives out of order into whichever bucket is open, which makes that range the wrong answer in general; the index costs four bytes per layer, not per vertex. update_colors_texture allocated one float per vertex of the whole print on every slider tick. It now reuses a buffer. render_legend fetched the layer Zs and the per-layer times from inside loops over the custom G-code items, and built whole vectors only to test them for emptiness. The times are hoisted, the Zs are built lazily so a print with no colour change does not pay for them at all, and the emptiness tests use the existing counters. No rendering behaviour changes. Verified by loading the same project in this build and a build of the parent commit under Xvfb and comparing frames across five interaction states, including the unfolded tooltip whose Time row is the output of the function that changed: pixel identical.
This commit is contained in:
@@ -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<float>& times : m_cumulative_times)
|
||||
std::vector<float>().swap(times);
|
||||
std::vector<uint32_t>().swap(m_layer_first_vertex);
|
||||
std::vector<float>().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<float>& 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<size_t>(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<uint32_t>(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<uint32_t>(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<float> 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<float>& 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_vertices.size(); ++i) {
|
||||
const PathVertex& v = m_vertices[i];
|
||||
@@ -1516,8 +1544,10 @@ void ViewerImpl::set_view_visible_range(Interval::value_type min, Interval::valu
|
||||
|
||||
float ViewerImpl::get_estimated_time_at(size_t id) const
|
||||
{
|
||||
return std::accumulate(m_vertices.begin(), m_vertices.begin() + id + 1, 0.0f,
|
||||
[this](float a, const PathVertex& v) { return a + v.times[static_cast<size_t>(m_settings.time_mode)]; });
|
||||
const size_t mode = static_cast<size_t>(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<float>& 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;
|
||||
|
||||
@@ -234,6 +234,27 @@ private:
|
||||
//
|
||||
std::array<float, TIME_MODES_COUNT> 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<std::vector<float>, 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<uint32_t> 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<float> m_colors_scratch;
|
||||
//
|
||||
// Detected travel moves times
|
||||
//
|
||||
std::array<float, TIME_MODES_COUNT> m_travels_time{ 0.0f, 0.0f };
|
||||
|
||||
@@ -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<std::pair<ColorRGBA, std::pair<double, double>>> 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<float> zs;
|
||||
bool zs_built = false;
|
||||
|
||||
for (const auto& item : custom_gcode_per_print_z) {
|
||||
if (extruder_id + 1 != static_cast<unsigned char>(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<float> 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<float>(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<double> layer_zs = get_layers_zs();
|
||||
// loop invariant, same reason as the layer Zs above
|
||||
const std::vector<float> 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<float> 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)
|
||||
|
||||
Reference in New Issue
Block a user