From 9f0933ba6e17feab1cd8099c82df3f8c89a86c38 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 27 Aug 2019 15:55:53 +0200 Subject: [PATCH] Fixed regression issue in G-code preview since 2.1.0-beta2: Excessive amount of memory was pre-allocated for G-code preview by volumetric speed. --- src/libslic3r/ExtrusionEntity.hpp | 1 + src/slic3r/GUI/GLCanvas3D.cpp | 118 +++++++++++++++--------------- 2 files changed, 58 insertions(+), 61 deletions(-) diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index a62a189ce..b32943b7d 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -29,6 +29,7 @@ enum ExtrusionRole { erCustom, // Extrusion role for a collection with multiple extrusion roles. erMixed, + erCount, }; inline bool is_perimeter(ExtrusionRole role) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 65655b421..d9b948c2f 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -8,6 +8,7 @@ #include "libslic3r/PrintConfig.hpp" #include "libslic3r/GCode/PreviewData.hpp" #include "libslic3r/Geometry.hpp" +#include "libslic3r/ExtrusionEntity.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/Technologies.hpp" #include "libslic3r/Tesselate.hpp" @@ -71,9 +72,11 @@ static const float ERROR_BG_LIGHT_COLOR[3] = { 0.753f, 0.192f, 0.039f }; //static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } }; // Number of floats -static const float MAX_VERTEX_BUFFER_SIZE = 131072 * 6; // 3.15MB +static const size_t MAX_VERTEX_BUFFER_SIZE = 131072 * 6; // 3.15MB // Reserve size in number of floats. -static const float VERTEX_BUFFER_RESERVE_SIZE = 131072 * 2; // 1.05MB +static const size_t VERTEX_BUFFER_RESERVE_SIZE = 131072 * 2; // 1.05MB +// Reserve size in number of floats, maximum sum of all preallocated buffers. +static const size_t VERTEX_BUFFER_RESERVE_SIZE_SUM_MAX = 1024 * 1024 * 128 / 4; // 128MB namespace Slic3r { namespace GUI { @@ -2083,7 +2086,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re m_dirty = true; } -static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume& vol_old, bool gl_initialized) +static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume& vol_old, bool gl_initialized, size_t prealloc_size = VERTEX_BUFFER_RESERVE_SIZE) { // Assign the large pre-allocated buffers to the new GLVolume. vol_new.indexed_vertex_array = std::move(vol_old.indexed_vertex_array); @@ -2093,7 +2096,7 @@ static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume& vol_new.indexed_vertex_array.clear(); // Just make sure that clear did not clear the reserved memory. // Reserving number of vertices (3x position + 3x color) - vol_new.indexed_vertex_array.reserve(VERTEX_BUFFER_RESERVE_SIZE / 6); + vol_new.indexed_vertex_array.reserve(prealloc_size / 6); // Finalize the old geometry, possibly move data to the graphics card. vol_old.finalize_geometry(gl_initialized); } @@ -4964,6 +4967,7 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat switch (type) { case GCodePreviewData::Extrusion::FeatureType: + // The role here is used for coloring. return (float)path.role(); case GCodePreviewData::Extrusion::Height: return path.height; @@ -5025,32 +5029,6 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat } }; - // Helper structure for filters - struct Filter - { - float value; - ExtrusionRole role; - GLVolume* volume; - - Filter(float value, ExtrusionRole role, GLVolume *volume = nullptr) - : value(value) - , role(role) - , volume(volume) - { - } - - bool operator == (const Filter& other) const - { - if (value != other.value) - return false; - - if (role != other.role) - return false; - - return true; - } - }; - size_t initial_volumes_count = m_volumes.volumes.size(); size_t initial_volume_index_count = m_gcode_preview_volume_index.first_volumes.size(); @@ -5059,30 +5037,42 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - create volumes" << m_volumes.log_memory_info() << log_memory_info(); // detects filters - typedef std::vector FiltersList; - FiltersList filters; + size_t vertex_buffer_prealloc_size = 0; + std::vector>> roles_filters; { - size_t num_paths = 0; - for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) - num_paths += layer.paths.size(); - std::vector> values; - values.reserve(num_paths); + std::vector num_paths_per_role(size_t(erCount), 0); + for (const GCodePreviewData::Extrusion::Layer &layer : preview_data.extrusion.layers) + for (const ExtrusionPath &path : layer.paths) + ++ num_paths_per_role[size_t(path.role())]; + std::vector> roles_values; + roles_values.assign(size_t(erCount), std::vector()); + for (size_t i = 0; i < roles_values.size(); ++ i) + roles_values[i].reserve(num_paths_per_role[i]); for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) for (const ExtrusionPath& path : layer.paths) - values.emplace_back(Helper::path_filter(preview_data.extrusion.view_type, path), (unsigned int)path.role()); - sort_remove_duplicates(values); - filters.reserve(values.size()); - for (const std::pair &value : values) { - m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Extrusion, value.second /* role */, (unsigned int)m_volumes.volumes.size()); - filters.emplace_back(value.first, (ExtrusionRole)value.second, - m_volumes.new_toolpath_volume(Helper::path_color(preview_data, tool_colors, value.first).rgba, VERTEX_BUFFER_RESERVE_SIZE)); + roles_values[size_t(path.role())].emplace_back(Helper::path_filter(preview_data.extrusion.view_type, path)); + roles_filters.reserve(size_t(erCount)); + size_t num_buffers = 0; + for (std::vector &values : roles_values) { + sort_remove_duplicates(values); + num_buffers += values.size(); + } + if (num_buffers == 0) + // nothing to render, return + return; + vertex_buffer_prealloc_size = (uint64_t(num_buffers) * uint64_t(VERTEX_BUFFER_RESERVE_SIZE) < VERTEX_BUFFER_RESERVE_SIZE_SUM_MAX) ? + VERTEX_BUFFER_RESERVE_SIZE : next_highest_power_of_2(VERTEX_BUFFER_RESERVE_SIZE_SUM_MAX / num_buffers) / 2; + for (std::vector &values : roles_values) { + size_t role = &values - &roles_values.front(); + roles_filters.emplace_back(); + if (! values.empty()) { + m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Extrusion, role, (unsigned int)m_volumes.volumes.size()); + for (const float value : values) + roles_filters.back().emplace_back(value, m_volumes.new_toolpath_volume(Helper::path_color(preview_data, tool_colors, value).rgba, vertex_buffer_prealloc_size)); + } } } - // nothing to render, return - if (filters.empty()) - return; - BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - populate volumes" << m_volumes.log_memory_info() << log_memory_info(); // populates volumes @@ -5090,12 +5080,12 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat { for (const ExtrusionPath& path : layer.paths) { - Filter key(Helper::path_filter(preview_data.extrusion.view_type, path), path.role()); - FiltersList::iterator filter = std::lower_bound(filters.begin(), filters.end(), key, - [](const Filter& l, const Filter& r) { return (l.value < r.value) || (l.value == r.value && l.role < r.role); }); - assert(filter != filters.end() && key.value == filter->value && key.role == filter->role); + std::vector> &filters = roles_filters[size_t(path.role())]; + auto key = std::make_pair(Helper::path_filter(preview_data.extrusion.view_type, path), nullptr); + auto it_filter = std::lower_bound(filters.begin(), filters.end(), key); + assert(it_filter != filters.end() && key.first == it_filter->first); - GLVolume& vol = *filter->volume; + GLVolume& vol = *it_filter->second; vol.print_zs.push_back(layer.z); vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size()); vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size()); @@ -5103,17 +5093,23 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat _3DScene::extrusionentity_to_verts(path, layer.z, vol); } // Ensure that no volume grows over the limits. If the volume is too large, allocate a new one. - for (Filter &filter : filters) - if (filter.volume->indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) { - GLVolume& vol = *filter.volume; - filter.volume = m_volumes.new_toolpath_volume(vol.color); - reserve_new_volume_finalize_old_volume(*filter.volume, vol, m_initialized); - } + for (std::vector> &filters : roles_filters) { + unsigned int role = (unsigned int)(&filters - &roles_filters.front()); + for (std::pair &filter : filters) + if (filter.second->indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) { + if (m_gcode_preview_volume_index.first_volumes.back().id != role) + m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Extrusion, role, (unsigned int)m_volumes.volumes.size()); + GLVolume& vol = *filter.second; + filter.second = m_volumes.new_toolpath_volume(vol.color); + reserve_new_volume_finalize_old_volume(*filter.second, vol, m_initialized, vertex_buffer_prealloc_size); + } + } } // Finalize volumes and sends geometry to gpu - for (Filter &filter : filters) - filter.volume->indexed_vertex_array.finalize_geometry(m_initialized); + for (std::vector> &filters : roles_filters) + for (std::pair &filter : filters) + filter.second->indexed_vertex_array.finalize_geometry(m_initialized); BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - end" << m_volumes.log_memory_info() << log_memory_info(); }