From 90ac58d3cd3043498ace5090d3e03e0fce3fea28 Mon Sep 17 00:00:00 2001 From: Kris Austin Date: Sun, 4 Oct 2026 03:45:19 -0500 Subject: [PATCH] perf: skip the unused curled wall estimate to speed up slicing by up to 9% (#16113) perf: skip estimating curled walls when nothing reads them The curled extrusion estimate ran whenever a region had overhang speed on, which is the default, but only the slowdown for curled perimeters reads the curled lines it produces, and that slowdown is off by default. The step now also requires a region with the slowdown on, and clears the curled lines when it skips the estimate, so none are left from an earlier slice. Also fixes stale fan commands due to the stale curled lines on the reused layers. --- src/libslic3r/GCode.cpp | 5 -- src/libslic3r/PrintConfig.hpp | 6 +++ src/libslic3r/PrintObject.cpp | 14 ++++-- tests/fff_print/test_extrusion_processor.cpp | 53 ++++++++++++++++++++ 4 files changed, 69 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 620b658f96..a440bb87a0 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -4414,11 +4414,6 @@ struct PrecomputedLayer }; } // namespace -template static bool any_enabled(const BoolsOption &option) -{ - return std::any_of(option.values.begin(), option.values.end(), [](unsigned char enabled) { return enabled != 0; }); -} - // Whether process_layer() prepares the overhang estimator for `layer`. template static bool prepares_overhang_estimator(const Layer &layer, bool overhang_fan, OverhangSpeed overhang_speed) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 0a478d66dc..68e35db40d 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -789,6 +789,12 @@ class StaticPrintConfig; // Minimum object distance for arrangement, based on printer technology. double min_object_distance(const ConfigBase &cfg); +// Whether any value is set, a nil value included. +template bool any_enabled(const ConfigOptionBoolsTempl &option) +{ + return std::any_of(option.values.begin(), option.values.end(), [](unsigned char enabled) { return enabled != 0; }); +} + // One (extruder type x nozzle volume type) parameter variant a filament prints through, plus a // representative physical extruder observed using it. Ordering (and set-dedup identity) covers // the variant pair only, so the same variant reached through two extruders keeps one config slot. diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 05d608f935..17ca44d526 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1025,10 +1025,13 @@ void PrintObject::generate_support_material() void PrintObject::estimate_curled_extrusions() { if (this->set_started(posEstimateCurledExtrusions)) { - if ( std::any_of(this->print()->m_print_regions.begin(), this->print()->m_print_regions.end(), [](const PrintRegion* region) { - const auto& cfg = region->config().enable_overhang_speed.values; - return std::any_of(cfg.begin(), cfg.end(), [](const unsigned char v) { return (bool) v; }); - })) { + const auto any_region_enables = [this](ConfigOptionBoolsNullable PrintRegionConfig::*option) { + return std::any_of(this->print()->m_print_regions.begin(), this->print()->m_print_regions.end(), + [option](const PrintRegion* region) { return any_enabled(region->config().*option); }); + }; + // Only the slowdown for curled perimeters reads the curled lines, and they stay empty unless some region has overhang speed on. + if (any_region_enables(&PrintRegionConfig::enable_overhang_speed) && + any_region_enables(&PrintRegionConfig::slowdown_for_curled_perimeters)) { // Estimate curling of support material and add it to the malformaition lines of each layer float support_flow_width = support_material_flow(this, this->config().layer_height).width(); @@ -1038,6 +1041,9 @@ void PrintObject::estimate_curled_extrusions() float(this->config().brim_width.getFloat())}; SupportSpotsGenerator::estimate_malformations(this->layers(), params); m_print->throw_if_canceled(); + } else { + for (Layer *layer : m_layers) + layer->curled_lines.clear(); } //this->set_done(posEstimateCurledExtrusions); } diff --git a/tests/fff_print/test_extrusion_processor.cpp b/tests/fff_print/test_extrusion_processor.cpp index bd6a203290..95a9d03f8a 100644 --- a/tests/fff_print/test_extrusion_processor.cpp +++ b/tests/fff_print/test_extrusion_processor.cpp @@ -566,6 +566,12 @@ struct SlicedCage using Walls = std::vector>; +bool has_curled_lines(const PrintObject &object) +{ + const auto layers = object.layers(); + return std::any_of(layers.begin(), layers.end(), [](const Layer *layer) { return !layer->curled_lines.empty(); }); +} + // Estimates every wall of `layer` against whatever layer `estimator` was last prepared with before it. Walls estimate_walls(ExtrusionQualityEstimator &estimator, const PrintObject *object, const Layer &layer) { @@ -707,6 +713,53 @@ TEST_CASE("Precomputed overhang data has the curled-line tree exactly when a reg CHECK((precomputed.front().lower_curled_lines != nullptr) == slowdown); } +TEST_CASE("Curled walls are estimated only when overhang speed and the slowdown for curled perimeters are both on", "[ExtrusionProcessor]") +{ + const auto [overhang_speed, slowdown, estimated] = GENERATE(table({ + {true, true, true}, + {true, false, false}, + {false, true, false}, + })); + DynamicPrintConfig config = caged_overhang_config("classic"); + config.set_deserialize_strict({{"enable_overhang_speed", overhang_speed ? "1" : "0"}, + {"slowdown_for_curled_perimeters", slowdown ? "1" : "0"}}); + const SlicedCage cage(config); + + CHECK(has_curled_lines(*cage.object) == estimated); +} + +TEST_CASE("Curled walls are estimated when overhang speed and the slowdown for curled perimeters are on in different objects", "[ExtrusionProcessor]") +{ + const std::vector> overrides{ + {{"enable_overhang_speed", "1"}, {"slowdown_for_curled_perimeters", "0"}}, + {{"enable_overhang_speed", "0"}, {"slowdown_for_curled_perimeters", "1"}}, + }; + Print print; + Model model; + init_print(std::vector{caged_overhang_mesh(), caged_overhang_mesh()}, print, model, caged_overhang_config("classic"), + &overrides); + print.process(); + + REQUIRE(print.objects().size() == 2); + for (const PrintObject *object : print.objects()) + CHECK(has_curled_lines(*object)); +} + +TEST_CASE("Curled walls from an earlier slice are dropped once overhang speed is off", "[ExtrusionProcessor]") +{ + DynamicPrintConfig config = caged_overhang_config("classic"); + config.set_deserialize_strict("slowdown_for_curled_perimeters", "1"); + SlicedCage cage(config); + const Layer *first_layer = cage.print.objects().front()->layers().front(); + REQUIRE(has_curled_lines(*cage.print.objects().front())); + + config.set_deserialize_strict("enable_overhang_speed", "0"); + cage.print.apply(cage.model, config); + cage.print.process(); + REQUIRE(cage.print.objects().front()->layers().front() == first_layer); + CHECK_FALSE(has_curled_lines(*cage.print.objects().front())); +} + TEST_CASE("Caged external overhangs are slowed when printed by object or through the pressure equalizer", "[ExtrusionProcessor]") { const auto [key, value] = GENERATE(table({