From 4c872b03528b292975bb60729076cbd6efb1054a Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 27 Apr 2023 15:36:21 +0200 Subject: [PATCH] Paralellize SupportSpotGenerator! Fix extra perimeters crash - problem with new ankerl hash map Fix progress bar --- src/libslic3r/PerimeterGenerator.cpp | 5 +- src/libslic3r/Print.cpp | 1 - src/libslic3r/SupportSpotsGenerator.cpp | 289 +++++++++++++----------- 3 files changed, 162 insertions(+), 133 deletions(-) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 1a487cba8..e1068d763 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -681,7 +682,7 @@ Polylines reconnect_polylines(const Polylines &polylines, double limit_distance) if (polylines.empty()) return polylines; - ankerl::unordered_dense::map connected; + std::unordered_map connected; connected.reserve(polylines.size()); for (size_t i = 0; i < polylines.size(); i++) { if (!polylines[i].empty()) { @@ -731,7 +732,7 @@ ExtrusionPaths sort_extra_perimeters(ExtrusionPaths extra_perims, int index_of_f { if (extra_perims.empty()) return {}; - std::vector> dependencies(extra_perims.size()); + std::vector> dependencies(extra_perims.size()); for (size_t path_idx = 0; path_idx < extra_perims.size(); path_idx++) { for (size_t prev_path_idx = 0; prev_path_idx < path_idx; prev_path_idx++) { if (paths_touch(extra_perims[path_idx], extra_perims[prev_path_idx], extrusion_spacing * 1.5f)) { diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 49d17e5c0..cf8b5c577 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -880,7 +880,6 @@ void Print::process() BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info(); for (PrintObject *obj : m_objects) obj->make_perimeters(); - this->set_status(70, _u8L("Infilling layers")); for (PrintObject *obj : m_objects) obj->infill(); for (PrintObject *obj : m_objects) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index e5748fad3..4c13cdd48 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include #include @@ -172,6 +174,69 @@ struct SliceConnection } }; +SliceConnection estimate_slice_connection(size_t slice_idx, const Layer *layer) +{ + SliceConnection connection; + + const LayerSlice &slice = layer->lslices_ex[slice_idx]; + Polygons slice_polys = to_polygons(layer->lslices[slice_idx]); + BoundingBox slice_bb = get_extents(slice_polys); + const Layer *lower_layer = layer->lower_layer; + + ExPolygons below{}; + for (const auto &link : slice.overlaps_below) { below.push_back(lower_layer->lslices[link.slice_idx]); } + Polygons below_polys = to_polygons(below); + + BoundingBox below_bb = get_extents(below_polys); + + Polygons overlap = intersection(ClipperUtils::clip_clipper_polygons_with_subject_bbox(slice_polys, below_bb), + ClipperUtils::clip_clipper_polygons_with_subject_bbox(below_polys, slice_bb)); + + for (const Polygon &poly : overlap) { + Vec2f p0 = unscaled(poly.first_point()).cast(); + for (size_t i = 2; i < poly.points.size(); i++) { + Vec2f p1 = unscaled(poly.points[i - 1]).cast(); + Vec2f p2 = unscaled(poly.points[i]).cast(); + + float sign = cross2(p1 - p0, p2 - p1) > 0 ? 1.0f : -1.0f; + + auto [area, first_moment_of_area, second_moment_area, + second_moment_of_area_covariance] = compute_moments_of_area_of_triangle(p0, p1, p2); + connection.area += sign * area; + connection.centroid_accumulator += sign * Vec3f(first_moment_of_area.x(), first_moment_of_area.y(), layer->print_z * area); + connection.second_moment_of_area_accumulator += sign * second_moment_area; + connection.second_moment_of_area_covariance_accumulator += sign * second_moment_of_area_covariance; + } + } + + return connection; +}; + +using PrecomputedSliceConnections = std::vector>; +PrecomputedSliceConnections precompute_slices_connections(const PrintObject *po) +{ + PrecomputedSliceConnections result{}; + for (size_t lidx = 0; lidx < po->layer_count(); lidx++) { + result.emplace_back(std::vector{}); + for (size_t slice_idx = 0; slice_idx < po->get_layer(lidx)->lslices_ex.size(); slice_idx++) { + result[lidx].push_back(SliceConnection{}); + } + } + + tbb::parallel_for(tbb::blocked_range(0, po->layers().size()), [po, &result](tbb::blocked_range r) { + for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { + const Layer *l = po->get_layer(lidx); + tbb::parallel_for(tbb::blocked_range(0, l->lslices_ex.size()), [lidx, l, &result](tbb::blocked_range r2) { + for (size_t slice_idx = r2.begin(); slice_idx < r2.end(); slice_idx++) { + result[lidx][slice_idx] = estimate_slice_connection(slice_idx, l); + } + }); + } + }); + + return result; +}; + float get_flow_width(const LayerRegion *region, ExtrusionRole role) { if (role == ExtrusionRole::BridgeInfill) return region->flow(FlowRole::frExternalPerimeter).width(); @@ -253,15 +318,8 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit const AABBTreeLines::LinesDistancer &prev_layer_boundary, const Params ¶ms) { - if (entity->is_collection()) { - std::vector checked_lines_out; - checked_lines_out.reserve(prev_layer_lines.get_lines().size() / 3); - for (const auto *e : static_cast(entity)->entities) { - auto tmp = check_extrusion_entity_stability(e, layer_region, prev_layer_lines, prev_layer_boundary, params); - checked_lines_out.insert(checked_lines_out.end(), tmp.begin(), tmp.end()); - } - return checked_lines_out; - } else if (entity->role().is_bridge() && !entity->role().is_perimeter()) { + assert(!entity->is_collection()); + if (entity->role().is_bridge() && !entity->role().is_perimeter()) { // pure bridges are handled separately, beacuse we need to align the forward and backward direction support points if (entity->length() < scale_(params.min_distance_to_allow_local_supports)) { return {}; @@ -344,9 +402,10 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit curr_point.distance *= sign; SupportPointCause potential_cause = SupportPointCause::FloatingExtrusion; - if (bridged_distance + line_len > params.bridge_distance * 0.8 && std::abs(curr_point.curvature) < 0.1) { - potential_cause = SupportPointCause::FloatingExtrusion; - } + // Bridges are now separated. While long overhang perimeter is technically bridge, it would confuse the users + // if (bridged_distance + line_len > params.bridge_distance * 0.8 && std::abs(curr_point.curvature) < 0.1) { + // potential_cause = SupportPointCause::FloatingExtrusion; + // } float max_bridge_len = std::max(params.support_points_interface_radius * 2.0f, params.bridge_distance / @@ -383,44 +442,6 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit } } -SliceConnection estimate_slice_connection(size_t slice_idx, const Layer *layer) -{ - SliceConnection connection; - - const LayerSlice &slice = layer->lslices_ex[slice_idx]; - Polygons slice_polys = to_polygons(layer->lslices[slice_idx]); - BoundingBox slice_bb = get_extents(slice_polys); - const Layer *lower_layer = layer->lower_layer; - - ExPolygons below{}; - for (const auto &link : slice.overlaps_below) { below.push_back(lower_layer->lslices[link.slice_idx]); } - Polygons below_polys = to_polygons(below); - - BoundingBox below_bb = get_extents(below_polys); - - Polygons overlap = intersection(ClipperUtils::clip_clipper_polygons_with_subject_bbox(slice_polys, below_bb), - ClipperUtils::clip_clipper_polygons_with_subject_bbox(below_polys, slice_bb)); - - for (const Polygon &poly : overlap) { - Vec2f p0 = unscaled(poly.first_point()).cast(); - for (size_t i = 2; i < poly.points.size(); i++) { - Vec2f p1 = unscaled(poly.points[i - 1]).cast(); - Vec2f p2 = unscaled(poly.points[i]).cast(); - - float sign = cross2(p1 - p0, p2 - p1) > 0 ? 1.0f : -1.0f; - - auto [area, first_moment_of_area, second_moment_area, - second_moment_of_area_covariance] = compute_moments_of_area_of_triangle(p0, p1, p2); - connection.area += sign * area; - connection.centroid_accumulator += sign * Vec3f(first_moment_of_area.x(), first_moment_of_area.y(), layer->print_z * area); - connection.second_moment_of_area_accumulator += sign * second_moment_area; - connection.second_moment_of_area_covariance_accumulator += sign * second_moment_of_area_covariance; - } - } - - return connection; -}; - class ObjectPart { public: @@ -761,7 +782,10 @@ public: } }; -std::tuple check_stability(const PrintObject *po, const PrintTryCancel &cancel_func, const Params ¶ms) +std::tuple check_stability(const PrintObject *po, + const PrecomputedSliceConnections &precomputed_slices_connections, + const PrintTryCancel &cancel_func, + const Params ¶ms) { SupportPoints supp_points{}; SupportGridFilter supports_presence_grid(po, params.min_distance_between_support_points); @@ -790,8 +814,8 @@ std::tuple check_stability(const PrintObject *po, for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) { const LayerSlice &slice = layer->lslices_ex.at(slice_idx); - auto [new_part, covered_area] = build_object_part_from_slice(slice_idx, layer, params); - SliceConnection connection_to_below = estimate_slice_connection(slice_idx, layer); + auto [new_part, covered_area] = build_object_part_from_slice(slice_idx, layer, params); + const SliceConnection &connection_to_below = precomputed_slices_connections[layer_idx][slice_idx]; #ifdef DETAILED_DEBUG_LOGS std::cout << "SLICE IDX: " << slice_idx << std::endl; @@ -858,25 +882,87 @@ std::tuple check_stability(const PrintObject *po, prev_slice_idx_to_weakest_connection = next_slice_idx_to_weakest_connection; next_slice_idx_to_weakest_connection.clear(); + auto get_flat_entities = [](const ExtrusionEntity *e) { + std::vector entities; + std::vector queue{e}; + while (!queue.empty()) { + const ExtrusionEntity *next = queue.back(); + queue.pop_back(); + if (next->is_collection()) { + for (const ExtrusionEntity *e : static_cast(next)->entities) { + queue.push_back(e); + } + } else { + entities.push_back(next); + } + } + return entities; + }; + + struct EnitityToCheck + { + const ExtrusionEntity *e; + const LayerRegion *region; + size_t slice_idx; + }; + std::vector entities_to_check; + for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) { + const LayerSlice &slice = layer->lslices_ex.at(slice_idx); + for (const auto &island : slice.islands) { + for (const LayerExtrusionRange &fill_range : island.fills) { + const LayerRegion *fill_region = layer->get_region(fill_range.region()); + for (const auto &fill_idx : fill_range) { + for (const ExtrusionEntity *e : get_flat_entities(fill_region->fills().entities[fill_idx])) { + if (e->role() == ExtrusionRole::BridgeInfill) { + entities_to_check.push_back({e, fill_region, slice_idx}); + } + } + } + } + + const LayerRegion *perimeter_region = layer->get_region(island.perimeters.region()); + for (const size_t &perimeter_idx : island.perimeters) { + for (const ExtrusionEntity *e : get_flat_entities(perimeter_region->perimeters().entities[perimeter_idx])) { + entities_to_check.push_back({e, perimeter_region, slice_idx}); + } + } + } + } + + AABBTreeLines::LinesDistancer prev_layer_boundary = layer->lower_layer != nullptr ? + AABBTreeLines::LinesDistancer{ + to_unscaled_linesf(layer->lower_layer->lslices)} : + AABBTreeLines::LinesDistancer{}; + + std::vector> unstable_lines_per_slice(layer->lslices_ex.size()); + std::vector> ext_perim_lines_per_slice(layer->lslices_ex.size()); + + tbb::parallel_for(tbb::blocked_range(0, entities_to_check.size()), + [&entities_to_check, &prev_layer_ext_perim_lines, &prev_layer_boundary, &unstable_lines_per_slice, + &ext_perim_lines_per_slice, ¶ms](tbb::blocked_range r) { + for (size_t entity_idx = r.begin(); entity_idx < r.end(); ++entity_idx) { + const auto &e_to_check = entities_to_check[entity_idx]; + for (const auto &line : + check_extrusion_entity_stability(e_to_check.e, e_to_check.region, prev_layer_ext_perim_lines, + prev_layer_boundary, params)) { + if (line.support_point_generated.has_value()) { + unstable_lines_per_slice[e_to_check.slice_idx].push_back(line); + } + if (line.is_external_perimeter()) { + ext_perim_lines_per_slice[e_to_check.slice_idx].push_back(line); + } + } + } + }); + std::vector current_layer_ext_perims_lines{}; current_layer_ext_perims_lines.reserve(prev_layer_ext_perim_lines.get_lines().size()); // All object parts updated, and for each slice we have coresponding weakest connection. // We can now check each slice and its corresponding weakest connection and object part for stability. for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) { - const LayerSlice &slice = layer->lslices_ex.at(slice_idx); ObjectPart &part = active_object_parts.access(prev_slice_idx_to_object_part_mapping[slice_idx]); SliceConnection &weakest_conn = prev_slice_idx_to_weakest_connection[slice_idx]; - std::vector boundary_lines; - for (const auto &link : slice.overlaps_below) { - auto ls = to_unscaled_linesf({layer->lower_layer->lslices[link.slice_idx]}); - boundary_lines.insert(boundary_lines.end(), ls.begin(), ls.end()); - } - AABBTreeLines::LinesDistancer prev_layer_boundary{std::move(boundary_lines)}; - - - std::vector current_slice_ext_perims_lines{}; - current_slice_ext_perims_lines.reserve(prev_layer_ext_perim_lines.get_lines().size() / layer->lslices_ex.size()); #ifdef DETAILED_DEBUG_LOGS weakest_conn.print_info("weakest connection info: "); #endif @@ -911,73 +997,15 @@ std::tuple check_stability(const PrintObject *po, } }; - // first we will check local extrusion stability of bridges, then of perimeters. Perimeters are more important, they - // account for most of the curling and possible crashes, so on them we will run also global stability check - for (const auto &island : slice.islands) { - // Support bridges where needed. - for (const LayerExtrusionRange &fill_range : island.fills) { - const LayerRegion *fill_region = layer->get_region(fill_range.region()); - for (const auto &fill_idx : fill_range) { - const ExtrusionEntity *entity = fill_region->fills().entities[fill_idx]; - if (entity->role() == ExtrusionRole::BridgeInfill) { - for (const ExtrusionLine &bridge : - check_extrusion_entity_stability(entity, fill_region, prev_layer_ext_perim_lines, prev_layer_boundary, - params)) { - if (bridge.support_point_generated.has_value()) { - reckon_new_support_point(*bridge.support_point_generated, create_support_point_position(bridge.b), - float(-EPSILON), Vec2f::Zero()); - } - } - } - } - } - - const LayerRegion *perimeter_region = layer->get_region(island.perimeters.region()); - for (const auto &perimeter_idx : island.perimeters) { - const ExtrusionEntity *entity = perimeter_region->perimeters().entities[perimeter_idx]; - std::vector perims = check_extrusion_entity_stability(entity, perimeter_region, - prev_layer_ext_perim_lines, prev_layer_boundary, - params); - for (const ExtrusionLine &perim : perims) { - if (perim.support_point_generated.has_value()) { - reckon_new_support_point(*perim.support_point_generated, create_support_point_position(perim.b), float(-EPSILON), - Vec2f::Zero()); - } - if (perim.is_external_perimeter()) { - current_slice_ext_perims_lines.push_back(perim); - } - } - } - // DEBUG EXPORT, NOT USED NOW - // if (BR_bridge) { - // Lines scaledl; - // for (const auto &l : prev_layer_boundary.get_lines()) { - // scaledl.emplace_back(Point::new_scale(l.a), Point::new_scale(l.b)); - // } - - // Lines perimsl; - // for (const auto &l : current_slice_ext_perims_lines) { - // perimsl.emplace_back(Point::new_scale(l.a), Point::new_scale(l.b)); - // } - - // BoundingBox bb = get_extents(scaledl); - // bb.merge(get_extents(perimsl)); - - // ::Slic3r::SVG svg(debug_out_path( - // ("slice" + std::to_string(slice_idx) + "_" + std::to_string(layer_idx).c_str()).c_str()), - // get_extents(scaledl)); - // svg.draw(scaledl, "red", scale_(0.4)); - // svg.draw(perimsl, "blue", scale_(0.25)); - - - // svg.Close(); - // } + for (const auto &l : unstable_lines_per_slice[slice_idx]) { + assert(l.support_point_generated.has_value()); + reckon_new_support_point(*l.support_point_generated, create_support_point_position(l.b), float(-EPSILON), Vec2f::Zero()); } - LD current_slice_lines_distancer(current_slice_ext_perims_lines); + LD current_slice_lines_distancer({ext_perim_lines_per_slice[slice_idx].begin(), ext_perim_lines_per_slice[slice_idx].end()}); float unchecked_dist = params.min_distance_between_support_points + 1.0f; - for (const ExtrusionLine &line : current_slice_ext_perims_lines) { + for (const ExtrusionLine &line : current_slice_lines_distancer.get_lines()) { if ((unchecked_dist + line.len < params.min_distance_between_support_points && line.curled_up_height < params.curling_tolerance_limit) || line.len < EPSILON) { unchecked_dist += line.len; @@ -993,8 +1021,8 @@ std::tuple check_stability(const PrintObject *po, } } } - current_layer_ext_perims_lines.insert(current_layer_ext_perims_lines.end(), current_slice_ext_perims_lines.begin(), - current_slice_ext_perims_lines.end()); + current_layer_ext_perims_lines.insert(current_layer_ext_perims_lines.end(), current_slice_lines_distancer.get_lines().begin(), + current_slice_lines_distancer.get_lines().end()); } // slice iterations prev_layer_ext_perim_lines = LD(current_layer_ext_perims_lines); } // layer iterations @@ -1048,7 +1076,8 @@ void debug_export(const SupportPoints& support_points,const PartialObjects& obje std::tuple full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params ¶ms) { - auto results = check_stability(po, cancel_func, params); + auto precomputed_slices_connections = precompute_slices_connections(po); + auto results = check_stability(po, precomputed_slices_connections, cancel_func, params); #ifdef DEBUG_FILES auto [supp_points, objects] = results; debug_export(supp_points, objects, "issues");