Optimization of the tool path preview generation algorithm:

1) Replaced linear search with logarithmic search.
2) Templated the travel path generation, replaced 3 functions with one.
This commit is contained in:
bubnikv 2019-08-26 15:52:56 +02:00
parent 66535b41d5
commit 80490550b5
4 changed files with 161 additions and 309 deletions

View file

@ -13,22 +13,6 @@ namespace Slic3r {
const GCodePreviewData::Color GCodePreviewData::Color::Dummy(0.0f, 0.0f, 0.0f, 0.0f); const GCodePreviewData::Color GCodePreviewData::Color::Dummy(0.0f, 0.0f, 0.0f, 0.0f);
GCodePreviewData::Color::Color()
{
rgba[0] = 1.0f;
rgba[1] = 1.0f;
rgba[2] = 1.0f;
rgba[3] = 1.0f;
}
GCodePreviewData::Color::Color(float r, float g, float b, float a)
{
rgba[0] = r;
rgba[1] = g;
rgba[2] = b;
rgba[3] = a;
}
std::vector<unsigned char> GCodePreviewData::Color::as_bytes() const std::vector<unsigned char> GCodePreviewData::Color::as_bytes() const
{ {
std::vector<unsigned char> ret; std::vector<unsigned char> ret;

View file

@ -14,8 +14,8 @@ public:
{ {
float rgba[4]; float rgba[4];
Color(); Color(const float *argba) { memcpy(this->rgba, argba, sizeof(float) * 4); }
Color(float r, float g, float b, float a); Color(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f) { rgba[0] = r; rgba[1] = g; rgba[2] = b; rgba[3] = a; }
std::vector<unsigned char> as_bytes() const; std::vector<unsigned char> as_bytes() const;

View file

@ -5027,10 +5027,10 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
ExtrusionRole role; ExtrusionRole role;
GLVolume* volume; GLVolume* volume;
Filter(float value, ExtrusionRole role) Filter(float value, ExtrusionRole role, GLVolume *volume = nullptr)
: value(value) : value(value)
, role(role) , role(role)
, volume(nullptr) , volume(volume)
{ {
} }
@ -5046,19 +5046,31 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
} }
}; };
typedef std::vector<Filter> FiltersList;
size_t initial_volumes_count = m_volumes.volumes.size(); size_t initial_volumes_count = m_volumes.volumes.size();
size_t initial_volume_index_count = m_gcode_preview_volume_index.first_volumes.size();
try
{
BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - create volumes" << m_volumes.log_memory_info() << log_memory_info();
// detects filters // detects filters
typedef std::vector<Filter> FiltersList;
FiltersList filters; FiltersList filters;
{
size_t num_paths = 0;
for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers)
num_paths += layer.paths.size();
std::vector<std::pair<float, unsigned int>> values;
values.reserve(num_paths);
for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers)
{
for (const ExtrusionPath& path : layer.paths) for (const ExtrusionPath& path : layer.paths)
{ values.emplace_back(Helper::path_filter(preview_data.extrusion.view_type, path), (unsigned int)path.role());
ExtrusionRole role = path.role(); sort_remove_duplicates(values);
float path_filter = Helper::path_filter(preview_data.extrusion.view_type, path); filters.reserve(values.size());
if (std::find(filters.begin(), filters.end(), Filter(path_filter, role)) == filters.end()) for (const std::pair<float, unsigned int> &value : values) {
filters.emplace_back(path_filter, role); 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));
} }
} }
@ -5066,30 +5078,6 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
if (filters.empty()) if (filters.empty())
return; return;
BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - create volumes" << m_volumes.log_memory_info() << log_memory_info();
// creates a new volume for each filter
for (Filter& filter : filters)
{
m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Extrusion, (unsigned int)filter.role, (unsigned int)m_volumes.volumes.size());
try {
filter.volume = m_volumes.new_toolpath_volume(Helper::path_color(preview_data, tool_colors, filter.value).rgba, VERTEX_BUFFER_RESERVE_SIZE);
} catch (std::bad_alloc& /* err */) {
// an error occourred - restore to previous state and return
m_gcode_preview_volume_index.first_volumes.pop_back();
if (initial_volumes_count != m_volumes.volumes.size())
{
GLVolumePtrs::iterator begin = m_volumes.volumes.begin() + initial_volumes_count;
GLVolumePtrs::iterator end = m_volumes.volumes.end();
for (GLVolumePtrs::iterator it = begin; it < end; ++it)
delete *it;
m_volumes.volumes.erase(begin, end);
//FIXME rethrow bad_alloc?
return;
}
}
}
BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - populate volumes" << m_volumes.log_memory_info() << log_memory_info(); BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - populate volumes" << m_volumes.log_memory_info() << log_memory_info();
// populates volumes // populates volumes
@ -5097,23 +5085,24 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
{ {
for (const ExtrusionPath& path : layer.paths) for (const ExtrusionPath& path : layer.paths)
{ {
float path_filter = Helper::path_filter(preview_data.extrusion.view_type, path); Filter key(Helper::path_filter(preview_data.extrusion.view_type, path), path.role());
FiltersList::iterator filter = std::find(filters.begin(), filters.end(), Filter(path_filter, path.role())); FiltersList::iterator filter = std::lower_bound(filters.begin(), filters.end(), key,
if (filter != filters.end()) [](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);
GLVolume &vol = *filter->volume;
GLVolume& vol = *filter->volume;
vol.print_zs.push_back(layer.z); 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.quad_indices.size());
vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size()); vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
_3DScene::extrusionentity_to_verts(path, layer.z, vol); _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. // Ensure that no volume grows over the limits. If the volume is too large, allocate a new one.
if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) { for (Filter &filter : filters)
filter->volume = m_volumes.new_toolpath_volume(vol.color); if (filter.volume->indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) {
reserve_new_volume_finalize_old_volume(*filter->volume, vol, m_initialized); GLVolume& vol = *filter.volume;
} filter.volume = m_volumes.new_toolpath_volume(vol.color);
} reserve_new_volume_finalize_old_volume(*filter.volume, vol, m_initialized);
} }
} }
@ -5122,96 +5111,54 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
filter.volume->indexed_vertex_array.finalize_geometry(m_initialized); filter.volume->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(); BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - end" << m_volumes.log_memory_info() << log_memory_info();
}
void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
{
size_t initial_volumes_count = m_volumes.volumes.size();
m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Travel, 0, (unsigned int)initial_volumes_count);
bool res = true;
switch (preview_data.extrusion.view_type)
{
case GCodePreviewData::Extrusion::Feedrate:
{
res = _travel_paths_by_feedrate(preview_data);
break;
} }
case GCodePreviewData::Extrusion::Tool: catch (const std::bad_alloc & /* err */)
{
res = _travel_paths_by_tool(preview_data, tool_colors);
break;
}
default:
{
res = _travel_paths_by_type(preview_data);
break;
}
}
if (!res)
{ {
// an error occourred - restore to previous state and return // an error occourred - restore to previous state and return
if (initial_volumes_count != m_volumes.volumes.size())
{
GLVolumePtrs::iterator begin = m_volumes.volumes.begin() + initial_volumes_count; GLVolumePtrs::iterator begin = m_volumes.volumes.begin() + initial_volumes_count;
GLVolumePtrs::iterator end = m_volumes.volumes.end(); GLVolumePtrs::iterator end = m_volumes.volumes.end();
for (GLVolumePtrs::iterator it = begin; it < end; ++it) for (GLVolumePtrs::iterator it = begin; it < end; ++it)
delete *it; delete *it;
m_volumes.volumes.erase(begin, end); m_volumes.volumes.erase(begin, end);
} m_gcode_preview_volume_index.first_volumes.erase(m_gcode_preview_volume_index.first_volumes.begin() + initial_volume_index_count, m_gcode_preview_volume_index.first_volumes.end());
BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - failed on low memory" << m_volumes.log_memory_info() << log_memory_info();
return; //FIXME rethrow bad_alloc?
} }
} }
bool GLCanvas3D::_travel_paths_by_type(const GCodePreviewData& preview_data) template<typename TYPE, typename FUNC_VALUE, typename FUNC_COLOR>
inline void travel_paths_internal(
// input
const GCodePreviewData &preview_data,
// accessors
FUNC_VALUE func_value, FUNC_COLOR func_color,
// output
GLVolumeCollection &volumes, bool gl_initialized)
{ {
// Helper structure for types // colors travels by type
struct Type std::vector<std::pair<TYPE, GLVolume*>> by_type;
{ {
GCodePreviewData::Travel::EType value; std::vector<TYPE> values;
GLVolume* volume; values.reserve(preview_data.travel.polylines.size());
explicit Type(GCodePreviewData::Travel::EType value)
: value(value)
, volume(nullptr)
{
}
bool operator == (const Type& other) const
{
return value == other.value;
}
};
typedef std::vector<Type> TypesList;
// colors travels by travel type
// detects types
TypesList types;
for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
{ values.emplace_back(func_value(polyline));
if (std::find(types.begin(), types.end(), Type(polyline.type)) == types.end()) sort_remove_duplicates(values);
types.emplace_back(polyline.type); by_type.reserve(values.size());
// creates a new volume for each feedrate
for (TYPE type : values)
by_type.emplace_back(type, volumes.new_nontoolpath_volume(func_color(type).rgba, VERTEX_BUFFER_RESERVE_SIZE));
} }
// nothing to render, return
if (types.empty())
return true;
// creates a new volume for each type
for (Type& type : types)
type.volume = m_volumes.new_nontoolpath_volume(preview_data.travel.type_colors[type.value].rgba, VERTEX_BUFFER_RESERVE_SIZE);
// populates volumes // populates volumes
std::pair<TYPE, GLVolume*> key(0.f, nullptr);
for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
{ {
TypesList::iterator type = std::find(types.begin(), types.end(), Type(polyline.type)); key.first = func_value(polyline);
if (type != types.end()) auto it = std::lower_bound(by_type.begin(), by_type.end(), key, [](const std::pair<TYPE, GLVolume*>& l, const std::pair<TYPE, GLVolume*>& r) { return l.first < r.first; });
{ assert(it != by_type.end() && it->first == func_value(polyline));
GLVolume &vol = *type->volume;
GLVolume& vol = *it->second;
vol.print_zs.push_back(unscale<double>(polyline.polyline.bounding_box().min(2))); vol.print_zs.push_back(unscale<double>(polyline.polyline.bounding_box().min(2)));
vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size()); vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size()); vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
@ -5220,136 +5167,60 @@ bool GLCanvas3D::_travel_paths_by_type(const GCodePreviewData& preview_data)
// Ensure that no volume grows over the limits. If the volume is too large, allocate a new one. // Ensure that no volume grows over the limits. If the volume is too large, allocate a new one.
if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) { if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) {
type->volume = m_volumes.new_nontoolpath_volume(vol.color); it->second = volumes.new_nontoolpath_volume(vol.color);
reserve_new_volume_finalize_old_volume(*type->volume, vol, m_initialized); reserve_new_volume_finalize_old_volume(*it->second, vol, gl_initialized);
}
} }
} }
for (Type &type : types) for (auto &feedrate : by_type)
type.volume->finalize_geometry(m_initialized); feedrate.second->finalize_geometry(gl_initialized);
return true;
} }
bool GLCanvas3D::_travel_paths_by_feedrate(const GCodePreviewData& preview_data) void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
{ {
// nothing to render, return // nothing to render, return
if (preview_data.travel.polylines.empty()) if (preview_data.travel.polylines.empty())
return true; return;
// colors travels by feedrate size_t initial_volumes_count = m_volumes.volumes.size();
// detects feedrates size_t volume_index_allocated = false;
std::vector<std::pair<float, GLVolume*>> feedrates;
try {
m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Travel, 0, (unsigned int)initial_volumes_count);
volume_index_allocated = true;
switch (preview_data.extrusion.view_type)
{ {
std::vector<float> values; case GCodePreviewData::Extrusion::Feedrate:
values.reserve(preview_data.travel.polylines.size()); travel_paths_internal<float>(preview_data,
for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) [](const GCodePreviewData::Travel::Polyline &polyline) { return polyline.feedrate; },
values.emplace_back(polyline.feedrate); [&preview_data](const float feedrate) -> const GCodePreviewData::Color { return preview_data.get_feedrate_color(feedrate); },
sort_remove_duplicates(values); m_volumes, m_initialized);
feedrates.reserve(values.size()); break;
// creates a new volume for each feedrate case GCodePreviewData::Extrusion::Tool:
for (float feedrate : values) travel_paths_internal<unsigned int>(preview_data,
feedrates.emplace_back(feedrate, m_volumes.new_nontoolpath_volume(preview_data.get_feedrate_color(feedrate).rgba, VERTEX_BUFFER_RESERVE_SIZE)); [](const GCodePreviewData::Travel::Polyline &polyline) { return polyline.extruder_id; },
[&tool_colors](const unsigned int extruder_id) -> const GCodePreviewData::Color { assert((extruder_id + 1) * 4 <= tool_colors.size()); return GCodePreviewData::Color(tool_colors.data() + extruder_id * 4); },
m_volumes, m_initialized);
break;
default:
travel_paths_internal<unsigned int>(preview_data,
[](const GCodePreviewData::Travel::Polyline &polyline) { return polyline.type; },
[&preview_data](const unsigned int type) -> const GCodePreviewData::Color& { return preview_data.travel.type_colors[type]; },
m_volumes, m_initialized);
break;
} }
} catch (const std::bad_alloc & /* ex */) {
// populates volumes // an error occourred - restore to previous state and return
std::pair<float, GLVolume*> key(0.f, nullptr); GLVolumePtrs::iterator begin = m_volumes.volumes.begin() + initial_volumes_count;
for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) GLVolumePtrs::iterator end = m_volumes.volumes.end();
{ for (GLVolumePtrs::iterator it = begin; it < end; ++it)
key.first = polyline.feedrate; delete *it;
auto it_feedrate = std::lower_bound(feedrates.begin(), feedrates.end(), key, [](const std::pair<float, GLVolume*> &l, const std::pair<float, GLVolume*> &r) { return l.first < r.first; }); m_volumes.volumes.erase(begin, end);
assert(it_feedrate != feedrates.end() && it_feedrate->first == polyline.feedrate); if (volume_index_allocated)
m_gcode_preview_volume_index.first_volumes.pop_back();
GLVolume &vol = *it_feedrate->second; //FIXME report the memory issue?
vol.print_zs.push_back(unscale<double>(polyline.polyline.bounding_box().min(2)));
vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
_3DScene::polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, vol);
// Ensure that no volume grows over the limits. If the volume is too large, allocate a new one.
if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) {
it_feedrate->second = m_volumes.new_nontoolpath_volume(vol.color);
reserve_new_volume_finalize_old_volume(*it_feedrate->second, vol, m_initialized);
} }
}
for (auto &feedrate : feedrates)
feedrate.second->finalize_geometry(m_initialized);
return true;
}
bool GLCanvas3D::_travel_paths_by_tool(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
{
// Helper structure for tool
struct Tool
{
unsigned int value;
GLVolume* volume;
explicit Tool(unsigned int value)
: value(value)
, volume(nullptr)
{
}
bool operator == (const Tool& other) const
{
return value == other.value;
}
};
typedef std::vector<Tool> ToolsList;
// colors travels by tool
// detects tools
ToolsList tools;
for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
{
if (std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id)) == tools.end())
tools.emplace_back(polyline.extruder_id);
}
// nothing to render, return
if (tools.empty())
return true;
// creates a new volume for each tool
for (Tool& tool : tools)
{
// tool.value could be invalid (as it was with https://github.com/prusa3d/PrusaSlicer/issues/2179), we better check
if (tool.value < tool_colors.size())
tool.volume = m_volumes.new_nontoolpath_volume(tool_colors.data() + tool.value * 4, VERTEX_BUFFER_RESERVE_SIZE);
}
// populates volumes
for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
{
ToolsList::iterator tool = std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id));
if (tool != tools.end() && tool->volume != nullptr)
{
GLVolume &vol = *tool->volume;
vol.print_zs.push_back(unscale<double>(polyline.polyline.bounding_box().min(2)));
vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
_3DScene::polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *tool->volume);
// Ensure that no volume grows over the limits. If the volume is too large, allocate a new one.
if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) {
tool->volume = m_volumes.new_nontoolpath_volume(vol.color);
reserve_new_volume_finalize_old_volume(*tool->volume, vol, m_initialized);
}
}
}
for (Tool& tool : tools)
tool.volume->finalize_geometry(m_initialized);
return true;
} }
void GLCanvas3D::_load_fff_shells() void GLCanvas3D::_load_fff_shells()

View file

@ -719,9 +719,6 @@ private:
void _load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors); void _load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
// generates gcode travel paths geometry // generates gcode travel paths geometry
void _load_gcode_travel_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors); void _load_gcode_travel_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
bool _travel_paths_by_type(const GCodePreviewData& preview_data);
bool _travel_paths_by_feedrate(const GCodePreviewData& preview_data);
bool _travel_paths_by_tool(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
// generates objects and wipe tower geometry // generates objects and wipe tower geometry
void _load_fff_shells(); void _load_fff_shells();
// Load SLA objects and support structures for objects, for which the slaposSliceSupports step has been finished. // Load SLA objects and support structures for objects, for which the slaposSliceSupports step has been finished.