Generate walls and split solid infill in parallel

Same output, ~2.2 min for the LOTR map plate, was ~2.6.
This commit is contained in:
ExPikaPaka
2026-09-21 16:40:22 +02:00
parent 67cbf31e8b
commit 5e6cf9619b
3 changed files with 417 additions and 379 deletions
+22 -11
View File
@@ -9,6 +9,8 @@
#include "../PrintConfig.hpp" #include "../PrintConfig.hpp"
#include "../Surface.hpp" #include "../Surface.hpp"
#include <tbb/parallel_for.h>
#include "AABBTreeLines.hpp" #include "AABBTreeLines.hpp"
#include "ExtrusionEntity.hpp" #include "ExtrusionEntity.hpp"
#include "Fill.hpp" #include "Fill.hpp"
@@ -630,24 +632,28 @@ void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &n
if (!line_based_pattern) { if (!line_based_pattern) {
const coord_t scaled_spacing = scaled<coord_t>(fill.params.spacing); const coord_t scaled_spacing = scaled<coord_t>(fill.params.spacing);
for (const ExPolygon &expolygon : fill.expolygons) { // Each expolygon is split on its own, so they run in parallel and are collected in their original order.
std::vector<std::pair<ExPolygons, ExPolygons>> split_parts(fill.expolygons.size()); // normal, narrow
tbb::parallel_for(size_t(0), fill.expolygons.size(), [&](size_t idx) {
const ExPolygon &expolygon = fill.expolygons[idx];
Polygons filled_area = to_polygons(expolygon); Polygons filled_area = to_polygons(expolygon);
// "Core" area: open (erode+dilate) to drop thin features, then clamp back to the original polygon. // "Core" area: open (erode+dilate) to drop thin features, then clamp back to the original polygon.
Polygons inner_area = intersection(filled_area, opening(filled_area, scaled_spacing, scaled_spacing)); Polygons inner_area = intersection(filled_area, opening(filled_area, scaled_spacing, scaled_spacing));
if (inner_area.empty()) { if (inner_area.empty()) {
narrow_infill.emplace_back(expolygon); split_parts[idx].second.emplace_back(expolygon);
continue; return;
} }
ExPolygons inner_ex = union_ex(inner_area); ExPolygons inner_ex = union_ex(inner_area);
ExPolygons expolys{expolygon}; ExPolygons expolys{expolygon};
ExPolygons narrow_ex = diff_ex(expolys, inner_ex); split_parts[idx].second = diff_ex(expolys, inner_ex); // narrow infill area
ExPolygons normal_ex = intersection_ex(expolys, inner_ex); split_parts[idx].first = intersection_ex(expolys, inner_ex); // normal infill area
});
append(normal_infill, normal_ex); // normal infill area for (auto &[normal_ex, narrow_ex] : split_parts) {
append(narrow_infill, narrow_ex); // narrow infill area append(normal_infill, std::move(normal_ex));
append(narrow_infill, std::move(narrow_ex));
} }
return; return;
@@ -669,7 +675,10 @@ void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &n
} }
const double aligning_angle = -base_angle + PI; const double aligning_angle = -base_angle + PI;
for (const ExPolygon &expolygon : fill.expolygons) { // Each expolygon is reconstructed on its own, so they run in parallel and are collected in their original order.
std::vector<Polygons> split_reconstructed(fill.expolygons.size());
tbb::parallel_for(size_t(0), fill.expolygons.size(), [&](size_t expolygon_idx) {
const ExPolygon &expolygon = fill.expolygons[expolygon_idx];
Polygons filled_area = to_polygons(expolygon); Polygons filled_area = to_polygons(expolygon);
polygons_rotate(filled_area, aligning_angle); polygons_rotate(filled_area, aligning_angle);
BoundingBox bb = get_extents(filled_area); BoundingBox bb = get_extents(filled_area);
@@ -800,8 +809,10 @@ void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &n
} }
} }
polygons_append(normal_fill_areas, reconstructed_area); split_reconstructed[expolygon_idx] = std::move(reconstructed_area);
} });
for (Polygons &reconstructed_area : split_reconstructed)
polygons_append(normal_fill_areas, std::move(reconstructed_area));
polygons_rotate(normal_fill_areas, -aligning_angle); polygons_rotate(normal_fill_areas, -aligning_angle);
+10 -7
View File
@@ -1448,22 +1448,25 @@ static inline std::vector<std::vector<ExPolygons>> segmentation_top_and_bottom_l
&shell_triangles_by_color_top, &shell_triangles_by_color_bottom](const tbb::blocked_range<size_t> &range) { &shell_triangles_by_color_top, &shell_triangles_by_color_bottom](const tbb::blocked_range<size_t> &range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
throw_on_cancel_callback(); throw_on_cancel_callback();
ExPolygons painted_exploys; // The per-colour unions below are independent of each other, so they run in parallel (a painted top or
for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) { // bottom face puts all of its colours on one layer); whatever combines the colours stays in colour order.
const auto merge_colour_union = [&](size_t color_idx) {
auto &self = triangles_by_color_merged[color_idx][layer_idx]; auto &self = triangles_by_color_merged[color_idx][layer_idx];
append(self, std::move(triangles_by_color_bottom[color_idx][layer_idx])); append(self, std::move(triangles_by_color_bottom[color_idx][layer_idx]));
append(self, std::move(triangles_by_color_bottom[color_idx][layer_idx + num_layers])); append(self, std::move(triangles_by_color_bottom[color_idx][layer_idx + num_layers]));
append(self, std::move(triangles_by_color_top[color_idx][layer_idx])); append(self, std::move(triangles_by_color_top[color_idx][layer_idx]));
append(self, std::move(triangles_by_color_top[color_idx][layer_idx + num_layers])); append(self, std::move(triangles_by_color_top[color_idx][layer_idx + num_layers]));
self = union_ex(self); self = union_ex(self);
};
tbb::parallel_for(size_t(0), triangles_by_color_merged.size(), merge_colour_union);
append(painted_exploys, self); ExPolygons painted_exploys;
} for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx)
append(painted_exploys, triangles_by_color_merged[color_idx][layer_idx]);
painted_exploys = union_ex(painted_exploys); painted_exploys = union_ex(painted_exploys);
//BBS: merge the top and bottom shell layers //BBS: merge the top and bottom shell layers
for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) { tbb::parallel_for(size_t(0), triangles_by_color_merged.size(), [&](size_t color_idx) {
auto &self = triangles_by_color_merged[color_idx][layer_idx]; auto &self = triangles_by_color_merged[color_idx][layer_idx];
auto top_area = diff_ex(union_ex(shell_triangles_by_color_top[color_idx][layer_idx], auto top_area = diff_ex(union_ex(shell_triangles_by_color_top[color_idx][layer_idx],
@@ -1477,7 +1480,7 @@ static inline std::vector<std::vector<ExPolygons>> segmentation_top_and_bottom_l
append(self, top_area); append(self, top_area);
append(self, bottom_area); append(self, bottom_area);
self = union_ex(self); self = union_ex(self);
} });
// Trim one region by the other if some of the regions overlap. // Trim one region by the other if some of the regions overlap.
ExPolygons painted_regions; ExPolygons painted_regions;
for (size_t color_idx = 1; color_idx < triangles_by_color_merged.size(); ++color_idx) { for (size_t color_idx = 1; color_idx < triangles_by_color_merged.size(); ++color_idx) {
+30 -6
View File
@@ -17,6 +17,8 @@
#include <cassert> #include <cassert>
#include <unordered_set> #include <unordered_set>
#include <thread> #include <thread>
#include <tbb/blocked_range.h>
#include <tbb/parallel_for.h>
#include "libslic3r/AABBTreeLines.hpp" #include "libslic3r/AABBTreeLines.hpp"
#include "Print.hpp" #include "Print.hpp"
static const int overhang_sampling_number = 6; static const int overhang_sampling_number = 6;
@@ -2456,7 +2458,22 @@ void PerimeterGenerator::process_arachne()
const bool only_one_wall_first_layer = this->config->only_one_wall_first_layer && has_bottom_shell_layers(*this->config); const bool only_one_wall_first_layer = this->config->only_one_wall_first_layer && has_bottom_shell_layers(*this->config);
// we need to process each island separately because we might have different // we need to process each island separately because we might have different
// extra perimeters for each one // extra perimeters for each one
for (const Surface& surface : all_surfaces) { // Each island is independent up to its outputs, so they are generated in parallel - a layer split into thousands
// of islands (e.g. by colour painting) otherwise ran on one thread - and the outputs are then committed in the
// original island order, which is what the extra overhang perimeters (applied to the last island's loops and to
// all fill surfaces so far) depend on.
struct ArachneSurfaceResult
{
ExtrusionEntityCollection loops;
bool has_loops = false;
ExPolygons infill;
ExPolygons no_overlap;
};
std::vector<ArachneSurfaceResult> results(all_surfaces.size());
tbb::parallel_for(tbb::blocked_range<size_t>(0, all_surfaces.size()), [&](const tbb::blocked_range<size_t> &range) {
for (size_t surface_idx = range.begin(); surface_idx < range.end(); ++surface_idx) {
const Surface &surface = all_surfaces[surface_idx];
ArachneSurfaceResult &result = results[surface_idx];
coord_t bead_width_0 = ext_perimeter_spacing; coord_t bead_width_0 = ext_perimeter_spacing;
// detect how many perimeters must be generated for this island // detect how many perimeters must be generated for this island
int loop_number = this->config->wall_loops + surface.extra_perimeters - 1; // 0-indexed loops int loop_number = this->config->wall_loops + surface.extra_perimeters - 1; // 0-indexed loops
@@ -2813,7 +2830,8 @@ void PerimeterGenerator::process_arachne()
this->config->overhang_reverse_internal_only); this->config->overhang_reverse_internal_only);
} }
defer_unsupported_loops(*this, extrusion_coll); defer_unsupported_loops(*this, extrusion_coll);
this->loops->append(extrusion_coll); result.loops = std::move(extrusion_coll);
result.has_loops = true;
} }
const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing; const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
@@ -2856,9 +2874,7 @@ void PerimeterGenerator::process_arachne()
if (!top_expolygons.empty()) { if (!top_expolygons.empty()) {
infill_exp = union_ex(infill_exp, offset_ex(top_expolygons, double(top_inset))); infill_exp = union_ex(infill_exp, offset_ex(top_expolygons, double(top_inset)));
} }
this->fill_surfaces->append(infill_exp, stInternal); result.infill = std::move(infill_exp);
apply_extra_perimeters(infill_exp);
// BBS: get the no-overlap infill expolygons // BBS: get the no-overlap infill expolygons
{ {
@@ -2869,9 +2885,17 @@ void PerimeterGenerator::process_arachne()
float(+min_perimeter_infill_spacing / 2.)); float(+min_perimeter_infill_spacing / 2.));
if (!top_expolygons.empty()) if (!top_expolygons.empty())
polyWithoutOverlap = union_ex(polyWithoutOverlap, top_expolygons); polyWithoutOverlap = union_ex(polyWithoutOverlap, top_expolygons);
this->fill_no_overlap->insert(this->fill_no_overlap->end(), polyWithoutOverlap.begin(), polyWithoutOverlap.end()); result.no_overlap = std::move(polyWithoutOverlap);
} }
} }
});
for (ArachneSurfaceResult &result : results) {
if (result.has_loops)
this->loops->append(result.loops);
this->fill_surfaces->append(result.infill, stInternal);
apply_extra_perimeters(result.infill);
this->fill_no_overlap->insert(this->fill_no_overlap->end(), result.no_overlap.begin(), result.no_overlap.end());
}
} }
bool PerimeterGeneratorLoop::is_internal_contour() const bool PerimeterGeneratorLoop::is_internal_contour() const