From dacf26b06f3d8abace44752e35793a3e573bcf5c Mon Sep 17 00:00:00 2001 From: SoftFever Date: Sat, 11 Jul 2026 22:11:35 +0800 Subject: [PATCH] fix(engine): keep engine-derived filament_map_2 out of the apply diff Print::apply rebuilds m_config.filament_map_2 to the real per-filament slot map on every apply, while the incoming full config only ever carries the ConfigDef default. The resulting phantom one-key print_diff hit the invalidator's catch-all branch and killed every print-level step on each apply, so on multi-extruder printers a fresh slice result was invalidated the moment the GUI re-applied after slicing completed. Dropping the key from print_diff loses no information: it is never a user input, and the rebuild derives it from filament_map, filament_volume_map and the variant slots, each of which is diffed and invalidation-listed on its own. Regression test: re-applying an unchanged config after process() must not invalidate psSlicingFinished (fails with APPLY_STATUS_INVALIDATED without the fix). Suites green (libslic3r 48891/154, fff_print 631/59); 19-fixture byte gate identical incl. the Hybrid repro project, determinism x2. --- src/libslic3r/PrintApply.cpp | 7 ++++ .../test_toolordering_nozzle_group.cpp | 36 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index 49909ec270..e4f421414a 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -1205,6 +1205,13 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ DynamicPrintConfig filament_overrides; //BBS: add plate index t_config_option_keys print_diff = print_config_diffs(m_config, new_full_config, filament_overrides, this->m_plate_index, filament_maps); + // Orca: filament_map_2 is engine-derived state, never a user input: the rebuild below + // recomputes it from filament_map/filament_volume_map/the variant slots on every apply + // (all of which are diffed and invalidation-listed on their own), and the grouping + // write-back overwrites it during process(). The incoming full config only ever carries + // the ConfigDef default, so diffing it would invalidate every print step on each apply + // for any multi-extruder printer and permanently invalidate fresh slice results. + print_diff.erase(std::remove(print_diff.begin(), print_diff.end(), "filament_map_2"), print_diff.end()); t_config_option_keys full_config_diff = full_print_config_diffs(m_full_print_config, new_full_config, this->m_plate_index); // Collect changes to object and region configs. t_config_option_keys object_diff = m_default_object_config.diff(new_full_config); diff --git a/tests/libslic3r/test_toolordering_nozzle_group.cpp b/tests/libslic3r/test_toolordering_nozzle_group.cpp index fbc05d811f..dcc2ae9521 100644 --- a/tests/libslic3r/test_toolordering_nozzle_group.cpp +++ b/tests/libslic3r/test_toolordering_nozzle_group.cpp @@ -460,3 +460,39 @@ TEST_CASE("Print config-index resolvers pick per-filament Hybrid slots", "[Print REQUIRE(print.get_nozzle_config_index(2, 0) == 1); // extruder slot, not the High Flow slot } } + +TEST_CASE("Re-applying an unchanged config after slicing keeps the result valid", "[Print][H2C]") +{ + // apply() rebuilds m_config.filament_map_2 to the real per-filament slot map, while the + // incoming full config only ever carries the ConfigDef default for it. The engine-derived + // key must therefore be kept out of the apply diff: the GUI re-applies right after slicing + // completes, and a phantom filament_map_2 diff would invalidate every freshly sliced result + // on any multi-extruder printer. + DynamicPrintConfig config = DynamicPrintConfig::full_print_config(); + config.option("nozzle_diameter", true)->values = {0.4, 0.4}; + config.option("extruder_nozzle_stats", true)->values = {"Standard#1", "Standard#1|High Flow#2"}; + config.option("extruder_type", true)->values = {etDirectDrive, etDirectDrive}; + config.option("nozzle_volume_type", true)->values = {nvtStandard, nvtHybrid}; + config.option("extruder_variant_list", true)->values = {"Direct Drive Standard,Direct Drive High Flow", + "Direct Drive Standard,Direct Drive High Flow"}; + config.option("print_extruder_id", true)->values = {1, 1, 2, 2}; + config.option("print_extruder_variant", true)->values = {"Direct Drive Standard", "Direct Drive High Flow", + "Direct Drive Standard", "Direct Drive High Flow"}; + config.option("filament_diameter", true)->values = {1.75, 1.75, 1.75}; + config.option("filament_colour", true)->values = {"#FF0000", "#00FF00", "#0000FF"}; + config.option("filament_map", true)->values = {1, 2, 2}; + config.option("filament_volume_map", true)->values = {(int) nvtStandard, (int) nvtStandard, (int) nvtHighFlow}; + + Model model; + ModelObject *object = model.add_object("cube", "", make_cube(20, 20, 20)); + object->add_instance()->set_offset(Vec3d(100., 100., 0.)); + + Print print; + print.apply(model, config); + print.process(); + REQUIRE(print.is_step_done(psSlicingFinished)); + + auto status = print.apply(model, config); + REQUIRE(status != PrintBase::APPLY_STATUS_INVALIDATED); + REQUIRE(print.is_step_done(psSlicingFinished)); +}