From 60d756494286d9cff7627821d1321b7b29aa7387 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 29 Mar 2022 11:19:50 +0200 Subject: [PATCH] FFF background slicing for a single object only: Generalized the "enabled milestone" from SLA to both SLA and FFF. Merged the "milestone enabled" flag into the milestone status. Fixed some potential threading issues in SLAPrint::finalize() when resetting the "milestone enabled" flags. --- src/libslic3r/GCode.cpp | 8 +- src/libslic3r/Print.hpp | 9 ++- src/libslic3r/PrintBase.hpp | 153 +++++++++++++++++++++++++++++++++--- src/libslic3r/SLAPrint.cpp | 106 +------------------------ src/libslic3r/SLAPrint.hpp | 12 +-- src/slic3r/GUI/Plater.cpp | 12 ++- src/slic3r/GUI/Plater.hpp | 4 + 7 files changed, 174 insertions(+), 130 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ad32f5fac..d07e4f61e 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -736,9 +736,13 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu CNumericLocalesSetter locales_setter; // Does the file exist? If so, we hope that it is still valid. - if (print->is_step_done(psGCodeExport) && boost::filesystem::exists(boost::filesystem::path(path))) - return; + { + PrintStateBase::StateWithTimeStamp state = print->step_state_with_timestamp(psGCodeExport); + if (! state.enabled || (state.state == PrintStateBase::DONE && boost::filesystem::exists(boost::filesystem::path(path)))) + return; + } + // Enabled and either not done, or marked as done while the output file is missing. print->set_started(psGCodeExport); // check if any custom gcode contains keywords used by the gcode processor to diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index d99e24ca7..1cd983fb9 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -45,7 +45,7 @@ namespace FillLightning { // Print step IDs for keeping track of the print state. // The Print steps are applied in this order. -enum PrintStep { +enum PrintStep : unsigned int { psWipeTower, // Ordering of the tools on PrintObjects for a multi-material print. // psToolOrdering is a synonym to psWipeTower, as the Wipe Tower calculates and modifies the ToolOrdering, @@ -59,7 +59,7 @@ enum PrintStep { psCount, }; -enum PrintObjectStep { +enum PrintObjectStep : unsigned int { posSlice, posPerimeters, posPrepareInfill, posInfill, posIroning, posSupportMaterial, posCount, }; @@ -350,6 +350,7 @@ public: private: // to be called from Print only. friend class Print; + friend class PrintBaseWithState; PrintObject(Print* print, ModelObject* model_object, const Transform3d& trafo, PrintInstances&& instances); ~PrintObject() override { @@ -537,8 +538,10 @@ public: std::vector print_object_ids() const override; ApplyStatus apply(const Model &model, DynamicPrintConfig config) override; - + void set_task(const TaskParams ¶ms) override { PrintBaseWithState::set_task_impl(params, m_objects); } void process() override; + void finalize() override { PrintBaseWithState::finalize_impl(m_objects); } + // Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file. // If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r). std::string export_gcode(const std::string& path_template, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index 08711a7b2..13796abba 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -39,9 +39,9 @@ public: // A new unique timestamp is being assigned to the step every time the step changes its state. struct StateWithTimeStamp { - StateWithTimeStamp() : state(INVALID), timestamp(0) {} - State state; - TimeStamp timestamp; + State state { INVALID }; + TimeStamp timestamp { 0 }; + bool enabled { true }; }; struct Warning @@ -112,10 +112,24 @@ public: return this->state_with_timestamp_unguarded(step).state == DONE; } + void enable_unguarded(StepType step, bool enable) { + m_state[step].enabled = enable; + } + + void enable_all_unguarded(bool enable) { + for (size_t istep = 0; istep < COUNT; ++ istep) + m_state[istep].enabled = enable; + } + + bool is_enabled_unguarded(StepType step) const { + return this->state_with_timestamp_unguarded(step).enabled; + } + // Set the step as started. Block on mutex while the Print / PrintObject / PrintRegion objects are being // modified by the UI thread. // This is necessary to block until the Print::apply() updates its state, which may // influence the processing step being entered. + // Returns false if the step is not enabled or if the step has already been finished (it is done). template bool set_started(StepType step, std::mutex &mtx, ThrowIfCanceled throw_if_canceled) { std::scoped_lock lock(mtx); @@ -134,9 +148,9 @@ public: // for (int i = 0; i < int(COUNT); ++ i) // assert(m_state[i].state != STARTED); #endif // NDEBUG - if (m_state[step].state == DONE) - return false; PrintStateBase::StateWithWarnings &state = m_state[step]; + if (! state.enabled || state.state == DONE) + return false; state.state = STARTED; state.timestamp = ++ g_last_timestamp; state.mark_warnings_non_current(); @@ -388,12 +402,12 @@ public: int to_print_step; }; // After calling the apply() function, call set_task() to limit the task to be processed by process(). - virtual void set_task(const TaskParams ¶ms) {} + virtual void set_task(const TaskParams ¶ms) = 0; // Perform the calculation. This is the only method that is to be called at a worker thread. virtual void process() = 0; // Clean up after process() finished, either with success, error or if canceled. // The adjustments on the Print / PrintObject data due to set_task() are to be reverted here. - virtual void finalize() {} + virtual void finalize() = 0; struct SlicingStatus { SlicingStatus(int percent, const std::string &text, unsigned int flags = 0) : percent(percent), text(text), flags(flags) {} @@ -511,10 +525,15 @@ private: friend PrintTryCancel; }; -template +template class PrintBaseWithState : public PrintBase { public: + using PrintStepEnum = PrintStepEnumType; + static constexpr const size_t PrintStepEnumSize = COUNT; + + PrintBaseWithState() = default; + bool is_step_done(PrintStepEnum step) const { return m_state.is_done(step, this->state_mutex()); } PrintStateBase::StateWithTimeStamp step_state_with_timestamp(PrintStepEnum step) const { return m_state.state_with_timestamp(step, this->state_mutex()); } PrintStateBase::StateWithWarnings step_state_with_warnings(PrintStepEnum step) const { return m_state.state_with_warnings(step, this->state_mutex()); } @@ -549,14 +568,120 @@ protected: this->status_update_warnings(static_cast(active_step.first), warning_level, message); } + + // After calling the apply() function, set_task() may be called to limit the task to be processed by process(). + template + void set_task_impl(const TaskParams ¶ms, std::vector &print_objects) + { + static constexpr const auto PrintObjectStepEnumSize = int(PrintObject::PrintObjectStepEnumSize); + using PrintObjectStepEnum = typename PrintObject::PrintObjectStepEnum; + // Grab the lock for the Print / PrintObject milestones. + std::scoped_lock lock(this->state_mutex()); + + int n_object_steps = int(params.to_object_step) + 1; + if (n_object_steps == 0) + n_object_steps = PrintObjectStepEnumSize; + + if (params.single_model_object.valid()) { + // Find the print object to be processed with priority. + PrintObject *print_object = nullptr; + size_t idx_print_object = 0; + for (; idx_print_object < print_objects.size(); ++ idx_print_object) + if (print_objects[idx_print_object]->model_object()->id() == params.single_model_object) { + print_object = print_objects[idx_print_object]; + break; + } + assert(print_object != nullptr); + // Find out whether the priority print object is being currently processed. + bool running = false; + for (int istep = 0; istep < n_object_steps; ++ istep) { + if (! print_object->is_step_enabled_unguarded(PrintObjectStepEnum(istep))) + // Step was skipped, cancel. + break; + if (print_object->is_step_started_unguarded(PrintObjectStepEnum(istep))) { + // No step was skipped, and a wanted step is being processed. Don't cancel. + running = true; + break; + } + } + if (! running) + this->call_cancel_callback(); + + // Now the background process is either stopped, or it is inside one of the print object steps to be calculated anyway. + if (params.single_model_instance_only) { + // Suppress all the steps of other instances. + for (PrintObject *po : print_objects) + for (size_t istep = 0; istep < PrintObjectStepEnumSize; ++ istep) + po->enable_step_unguarded(PrintObjectStepEnum(istep), false); + } else if (! running) { + // Swap the print objects, so that the selected print_object is first in the row. + // At this point the background processing must be stopped, so it is safe to shuffle print objects. + if (idx_print_object != 0) + std::swap(print_objects.front(), print_objects[idx_print_object]); + } + // and set the steps for the current object. + for (int istep = 0; istep < n_object_steps; ++ istep) + print_object->enable_step_unguarded(PrintObjectStepEnum(istep), true); + for (int istep = n_object_steps; istep < PrintObjectStepEnumSize; ++ istep) + print_object->enable_step_unguarded(PrintObjectStepEnum(istep), false); + } else { + // Slicing all objects. + bool running = false; + for (PrintObject *print_object : print_objects) + for (int istep = 0; istep < n_object_steps; ++ istep) { + if (! print_object->is_step_enabled_unguarded(PrintObjectStepEnum(istep))) { + // Step may have been skipped. Restart. + goto loop_end; + } + if (print_object->is_step_started_unguarded(PrintObjectStepEnum(istep))) { + // This step is running, and the state cannot be changed due to the this->state_mutex() being locked. + // It is safe to manipulate m_stepmask of other PrintObjects and Print now. + running = true; + goto loop_end; + } + } + loop_end: + if (! running) + this->call_cancel_callback(); + for (PrintObject *po : print_objects) { + for (int istep = 0; istep < n_object_steps; ++ istep) + po->enable_step_unguarded(PrintObjectStepEnum(istep), true); + for (int istep = n_object_steps; istep < PrintObjectStepEnumSize; ++ istep) + po->enable_step_unguarded(PrintObjectStepEnum(istep), false); + } + } + + if (params.to_object_step != -1 || params.to_print_step != -1) { + // Limit the print steps. + size_t istep = (params.to_object_step != -1) ? 0 : size_t(params.to_print_step) + 1; + for (; istep < PrintStepEnumSize; ++ istep) + m_state.enable_unguarded(PrintStepEnum(istep), false); + } + } + + // Clean up after process() finished, either with success, error or if canceled. + // The adjustments on the Print / PrintObject m_stepmask data due to set_task() are to be reverted here. + template + void finalize_impl(std::vector &print_objects) + { + // Grab the lock for the Print / PrintObject milestones. + std::scoped_lock lock(this->state_mutex()); + for (auto *po : print_objects) + po->enable_all_steps_unguarded(true); + m_state.enable_all_unguarded(true); + } + private: - PrintState m_state; + PrintState m_state; }; -template +template class PrintObjectBaseWithState : public PrintObjectBase { public: + using PrintObjectStepEnum = PrintObjectStepEnumType; + static constexpr const size_t PrintObjectStepEnumSize = COUNT; + PrintType* print() { return m_print; } const PrintType* print() const { return m_print; } @@ -590,6 +715,10 @@ protected: bool is_step_started_unguarded(PrintObjectStepEnum step) const { return m_state.is_started_unguarded(step); } bool is_step_done_unguarded(PrintObjectStepEnum step) const { return m_state.is_done_unguarded(step); } + bool is_step_enabled_unguarded(PrintObjectStepEnum step) const { return m_state.is_enabled_unguarded(step); } + void enable_step_unguarded(PrintObjectStepEnum step, bool enable) { m_state.enable_unguarded(step, enable); } + void enable_all_steps_unguarded(bool enable) { m_state.enable_all_unguarded(enable); } + // Add a slicing warning to the active PrintObject step and send a status notification. // This method could be called multiple times between this->set_started() and this->set_done(). void active_step_add_warning(PrintStateBase::WarningLevel warning_level, const std::string &message, int message_id = 0) { @@ -604,10 +733,10 @@ protected: void throw_if_canceled() { if (m_print->canceled()) throw CanceledException(); } friend PrintType; - PrintType *m_print; + PrintType *m_print; private: - PrintState m_state; + PrintState m_state; }; } // namespace Slic3r diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 81ce0d6cb..203393e9c 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -513,104 +513,6 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con return static_cast(apply_status); } -// After calling the apply() function, set_task() may be called to limit the task to be processed by process(). -void SLAPrint::set_task(const TaskParams ¶ms) -{ - // Grab the lock for the Print / PrintObject milestones. - std::scoped_lock lock(this->state_mutex()); - - int n_object_steps = int(params.to_object_step) + 1; - if (n_object_steps == 0) - n_object_steps = int(slaposCount); - - if (params.single_model_object.valid()) { - // Find the print object to be processed with priority. - SLAPrintObject *print_object = nullptr; - size_t idx_print_object = 0; - for (; idx_print_object < m_objects.size(); ++ idx_print_object) - if (m_objects[idx_print_object]->model_object()->id() == params.single_model_object) { - print_object = m_objects[idx_print_object]; - break; - } - assert(print_object != nullptr); - // Find out whether the priority print object is being currently processed. - bool running = false; - for (int istep = 0; istep < n_object_steps; ++ istep) { - if (! print_object->m_stepmask[size_t(istep)]) - // Step was skipped, cancel. - break; - if (print_object->is_step_started_unguarded(SLAPrintObjectStep(istep))) { - // No step was skipped, and a wanted step is being processed. Don't cancel. - running = true; - break; - } - } - if (! running) - this->call_cancel_callback(); - - // Now the background process is either stopped, or it is inside one of the print object steps to be calculated anyway. - if (params.single_model_instance_only) { - // Suppress all the steps of other instances. - for (SLAPrintObject *po : m_objects) - for (size_t istep = 0; istep < slaposCount; ++ istep) - po->m_stepmask[istep] = false; - } else if (! running) { - // Swap the print objects, so that the selected print_object is first in the row. - // At this point the background processing must be stopped, so it is safe to shuffle print objects. - if (idx_print_object != 0) - std::swap(m_objects.front(), m_objects[idx_print_object]); - } - // and set the steps for the current object. - for (int istep = 0; istep < n_object_steps; ++ istep) - print_object->m_stepmask[size_t(istep)] = true; - for (int istep = n_object_steps; istep < int(slaposCount); ++ istep) - print_object->m_stepmask[size_t(istep)] = false; - } else { - // Slicing all objects. - bool running = false; - for (SLAPrintObject *print_object : m_objects) - for (int istep = 0; istep < n_object_steps; ++ istep) { - if (! print_object->m_stepmask[size_t(istep)]) { - // Step may have been skipped. Restart. - goto loop_end; - } - if (print_object->is_step_started_unguarded(SLAPrintObjectStep(istep))) { - // This step is running, and the state cannot be changed due to the this->state_mutex() being locked. - // It is safe to manipulate m_stepmask of other SLAPrintObjects and SLAPrint now. - running = true; - goto loop_end; - } - } - loop_end: - if (! running) - this->call_cancel_callback(); - for (SLAPrintObject *po : m_objects) { - for (int istep = 0; istep < n_object_steps; ++ istep) - po->m_stepmask[size_t(istep)] = true; - for (auto istep = size_t(n_object_steps); istep < slaposCount; ++ istep) - po->m_stepmask[istep] = false; - } - } - - if (params.to_object_step != -1 || params.to_print_step != -1) { - // Limit the print steps. - size_t istep = (params.to_object_step != -1) ? 0 : size_t(params.to_print_step) + 1; - for (; istep < m_stepmask.size(); ++ istep) - m_stepmask[istep] = false; - } -} - -// Clean up after process() finished, either with success, error or if canceled. -// The adjustments on the SLAPrint / SLAPrintObject data due to set_task() are to be reverted here. -void SLAPrint::finalize() -{ - for (SLAPrintObject *po : m_objects) - for (size_t istep = 0; istep < slaposCount; ++ istep) - po->m_stepmask[istep] = true; - for (size_t istep = 0; istep < slapsCount; ++ istep) - m_stepmask[istep] = true; -} - // Generate a recommended output file name based on the format template, default extension, and template parameters // (timestamps, object placeholders derived from the model, current placeholder prameters and print statistics. // Use the final print statistics if available, or just keep the print statistics placeholders if not available yet (before the output is finalized). @@ -741,7 +643,7 @@ void SLAPrint::process() st += incr; - if (po->m_stepmask[step] && po->set_started(step)) { + if (po->set_started(step)) { m_report_status(*this, st, printsteps.label(step)); bench.start(); printsteps.execute(step, *po); @@ -759,14 +661,11 @@ void SLAPrint::process() apply_steps_on_objects(level1_obj_steps); apply_steps_on_objects(level2_obj_steps); - // this would disable the rasterization step - // std::fill(m_stepmask.begin(), m_stepmask.end(), false); - st = Steps::max_objstatus; for(SLAPrintStep currentstep : print_steps) { throw_if_canceled(); - if (m_stepmask[currentstep] && set_started(currentstep)) { + if (set_started(currentstep)) { m_report_status(*this, st, printsteps.label(currentstep)); bench.start(); printsteps.execute(currentstep); @@ -898,7 +797,6 @@ bool SLAPrint::is_step_done(SLAPrintObjectStep step) const SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object) : Inherited(print, model_object) - , m_stepmask(slaposCount, true) , m_transformed_rmesh([this](TriangleMesh &obj) { obj = m_model_object->raw_mesh(); if (!obj.empty()) { diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 0723382b3..6d8dc058b 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -265,6 +265,7 @@ public: protected: // to be called from SLAPrint only. friend class SLAPrint; + friend class PrintBaseWithState; SLAPrintObject(SLAPrint* print, ModelObject* model_object); ~SLAPrintObject(); @@ -285,10 +286,6 @@ protected: // Invalidate steps based on a set of parameters changed. bool invalidate_state_by_config_options(const std::vector &opt_keys); - // Which steps have to be performed. Implicitly: all - // to be accessible from SLAPrint - std::vector m_stepmask; - private: // Object specific configuration, pulled from the configuration layer. SLAPrintObjectConfig m_config; @@ -408,7 +405,7 @@ private: // Prevents erroneous use by other classes. public: - SLAPrint(): m_stepmask(slapsCount, true) {} + SLAPrint() = default; virtual ~SLAPrint() override { this->clear(); } @@ -419,9 +416,9 @@ public: // List of existing PrintObject IDs, to remove notifications for non-existent IDs. std::vector print_object_ids() const override; ApplyStatus apply(const Model &model, DynamicPrintConfig config) override; - void set_task(const TaskParams ¶ms) override; + void set_task(const TaskParams ¶ms) override { PrintBaseWithState::set_task_impl(params, m_objects); } void process() override; - void finalize() override; + void finalize() override { PrintBaseWithState::finalize_impl(m_objects); } // Returns true if an object step is done on all objects and there's at least one object. bool is_step_done(SLAPrintObjectStep step) const; // Returns true if the last step was finished with success. @@ -522,7 +519,6 @@ private: SLAPrintObjectConfig m_default_object_config; PrintObjects m_objects; - std::vector m_stepmask; // Ready-made data for rasterization. std::vector m_printer_input; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3a5a49fd9..59a90d4c4 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6269,7 +6269,7 @@ void Plater::reslice_SLA_hollowing(const ModelObject &object, bool postpone_erro reslice_SLA_until_step(slaposDrillHoles, object, postpone_error_messages); } -void Plater::reslice_SLA_until_step(SLAPrintObjectStep step, const ModelObject &object, bool postpone_error_messages) +void Plater::reslice_until_step_inner(int step, const ModelObject &object, bool postpone_error_messages) { //FIXME Don't reslice if export of G-code or sending to OctoPrint is running. // bitmask of UpdateBackgroundProcessReturnState @@ -6295,6 +6295,16 @@ void Plater::reslice_SLA_until_step(SLAPrintObjectStep step, const ModelObject & this->p->restart_background_process(state | priv::UPDATE_BACKGROUND_PROCESS_FORCE_RESTART); } +void Plater::reslice_FFF_until_step(PrintObjectStep step, const ModelObject &object, bool postpone_error_messages) +{ + this->reslice_until_step_inner(PrintObjectStep(step), object, postpone_error_messages); +} + +void Plater::reslice_SLA_until_step(SLAPrintObjectStep step, const ModelObject &object, bool postpone_error_messages) +{ + this->reslice_until_step_inner(SLAPrintObjectStep(step), object, postpone_error_messages); +} + void Plater::send_gcode() { // if physical_printer is selected, send gcode for this printer diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index baa54480c..124725018 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -32,6 +32,7 @@ using ModelObjectCutAttributes = enum_bitmask; class ModelInstance; class Print; class SLAPrint; +enum PrintObjectStep : unsigned int; enum SLAPrintObjectStep : unsigned int; enum class ConversionType : int; @@ -265,6 +266,7 @@ public: bool has_toolpaths_to_export() const; void export_toolpaths_to_obj() const; void reslice(); + void reslice_FFF_until_step(PrintObjectStep step, const ModelObject &object, bool postpone_error_messages = false); void reslice_SLA_supports(const ModelObject &object, bool postpone_error_messages = false); void reslice_SLA_hollowing(const ModelObject &object, bool postpone_error_messages = false); void reslice_SLA_until_step(SLAPrintObjectStep step, const ModelObject &object, bool postpone_error_messages = false); @@ -477,6 +479,8 @@ public: static void show_illegal_characters_warning(wxWindow* parent); private: + void reslice_until_step_inner(int step, const ModelObject &object, bool postpone_error_messages); + struct priv; std::unique_ptr p;