2018-11-08 14:23:17 +01:00
#ifndef slic3r_PrintBase_hpp_
#define slic3r_PrintBase_hpp_
#include "libslic3r.h"
#include <set>
#include <vector>
#include <string>
#include <functional>
// tbb/mutex.h includes Windows, which in turn defines min/max macros. Convince Windows.h to not define these min/max macros.
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include "tbb/mutex.h"
#include "Model.hpp"
2018-12-03 13:14:28 +01:00
#include "PlaceholderParser.hpp"
2018-11-08 14:23:17 +01:00
#include "PrintConfig.hpp"
namespace Slic3r {
class CanceledException : public std :: exception {
public :
const char * what () const throw () { return "Background processing has been canceled" ; }
};
2018-11-16 18:28:50 +01:00
class PrintStateBase {
2018-11-08 14:23:17 +01:00
public :
enum State {
INVALID ,
STARTED ,
DONE ,
};
2018-11-16 18:28:50 +01:00
2018-11-20 13:22:26 +01:00
typedef size_t TimeStamp ;
2018-11-16 18:28:50 +01:00
// A new unique timestamp is being assigned to the step every time the step changes its state.
struct StateWithTimeStamp
{
StateWithTimeStamp () : state ( INVALID ), timestamp ( 0 ) {}
2018-11-20 13:22:26 +01:00
State state ;
TimeStamp timestamp ;
2018-11-16 18:28:50 +01:00
};
protected :
//FIXME last timestamp is shared between Print & SLAPrint,
// and if multiple Print or SLAPrint instances are executed in parallel, modification of g_last_timestamp
// is not synchronized!
static size_t g_last_timestamp ;
};
// To be instantiated over PrintStep or PrintObjectStep enums.
template < class StepType , size_t COUNT >
class PrintState : public PrintStateBase
{
public :
PrintState () {}
StateWithTimeStamp state_with_timestamp ( StepType step , tbb :: mutex & mtx ) const {
tbb :: mutex :: scoped_lock lock ( mtx );
StateWithTimeStamp state = m_state [ step ];
return state ;
}
2019-02-21 08:44:07 +01:00
bool is_started ( StepType step , tbb :: mutex & mtx ) const {
return this -> state_with_timestamp ( step , mtx ). state == STARTED ;
}
2018-11-16 18:28:50 +01:00
bool is_done ( StepType step , tbb :: mutex & mtx ) const {
return this -> state_with_timestamp ( step , mtx ). state == DONE ;
}
StateWithTimeStamp state_with_timestamp_unguarded ( StepType step ) const {
return m_state [ step ];
}
2019-02-21 08:44:07 +01:00
bool is_started_unguarded ( StepType step ) const {
return this -> state_with_timestamp_unguarded ( step ). state == STARTED ;
}
2018-11-16 18:28:50 +01:00
bool is_done_unguarded ( StepType step ) const {
return this -> state_with_timestamp_unguarded ( step ). state == DONE ;
}
2018-11-08 14:23:17 +01:00
// Set the step as started. Block on mutex while the Print / PrintObject / PrintRegion objects are being
// modified by the UI thread.
2019-05-22 16:43:14 +02:00
// This is necessary to block until the Print::apply() updates its state, which may
2018-11-08 14:23:17 +01:00
// influence the processing step being entered.
2018-11-16 18:28:50 +01:00
template < typename ThrowIfCanceled >
bool set_started ( StepType step , tbb :: mutex & mtx , ThrowIfCanceled throw_if_canceled ) {
tbb :: mutex :: scoped_lock lock ( mtx );
// If canceled, throw before changing the step state.
throw_if_canceled ();
if ( m_state [ step ]. state == DONE )
return false ;
m_state [ step ]. state = STARTED ;
m_state [ step ]. timestamp = ++ g_last_timestamp ;
return true ;
2018-11-08 14:23:17 +01:00
}
// Set the step as done. Block on mutex while the Print / PrintObject / PrintRegion objects are being
// modified by the UI thread.
2018-11-16 18:28:50 +01:00
template < typename ThrowIfCanceled >
2018-11-20 13:22:26 +01:00
TimeStamp set_done ( StepType step , tbb :: mutex & mtx , ThrowIfCanceled throw_if_canceled ) {
2018-11-16 18:28:50 +01:00
tbb :: mutex :: scoped_lock lock ( mtx );
// If canceled, throw before changing the step state.
throw_if_canceled ();
assert ( m_state [ step ]. state != DONE );
m_state [ step ]. state = DONE ;
m_state [ step ]. timestamp = ++ g_last_timestamp ;
2018-11-20 13:22:26 +01:00
return m_state [ step ]. timestamp ;
2018-11-08 14:23:17 +01:00
}
// Make the step invalid.
2018-11-16 18:28:50 +01:00
// PrintBase::m_state_mutex should be locked at this point, guarding access to m_state.
2018-11-08 14:23:17 +01:00
// In case the step has already been entered or finished, cancel the background
// processing by calling the cancel callback.
template < typename CancelationCallback >
2018-11-16 18:28:50 +01:00
bool invalidate ( StepType step , CancelationCallback cancel ) {
bool invalidated = m_state [ step ]. state != INVALID ;
2018-11-08 14:23:17 +01:00
if ( invalidated ) {
#if 0
if (mtx.state != mtx.HELD) {
printf("Not held!\n");
}
#endif
2018-11-16 18:28:50 +01:00
m_state [ step ]. state = INVALID ;
m_state [ step ]. timestamp = ++ g_last_timestamp ;
2018-11-08 14:23:17 +01:00
// Raise the mutex, so that the following cancel() callback could cancel
// the background processing.
2018-11-16 18:28:50 +01:00
// Internally the cancel() callback shall unlock the PrintBase::m_status_mutex to let
// the working thread to proceed.
2018-11-08 14:23:17 +01:00
cancel ();
}
return invalidated ;
}
template < typename CancelationCallback , typename StepTypeIterator >
2018-11-16 18:28:50 +01:00
bool invalidate_multiple ( StepTypeIterator step_begin , StepTypeIterator step_end , CancelationCallback cancel ) {
2018-11-08 14:23:17 +01:00
bool invalidated = false ;
2018-11-16 18:28:50 +01:00
for ( StepTypeIterator it = step_begin ; it != step_end ; ++ it ) {
StateWithTimeStamp & state = m_state [ * it ];
if ( state . state != INVALID ) {
invalidated = true ;
state . state = INVALID ;
state . timestamp = ++ g_last_timestamp ;
}
}
2018-11-08 14:23:17 +01:00
if ( invalidated ) {
#if 0
if (mtx.state != mtx.HELD) {
printf("Not held!\n");
}
#endif
// Raise the mutex, so that the following cancel() callback could cancel
// the background processing.
2018-11-16 18:28:50 +01:00
// Internally the cancel() callback shall unlock the PrintBase::m_status_mutex to let
// the working thread to proceed.
2018-11-08 14:23:17 +01:00
cancel ();
}
return invalidated ;
}
// Make all steps invalid.
2018-11-16 18:28:50 +01:00
// PrintBase::m_state_mutex should be locked at this point, guarding access to m_state.
2018-11-08 14:23:17 +01:00
// In case any step has already been entered or finished, cancel the background
// processing by calling the cancel callback.
template < typename CancelationCallback >
2018-11-16 18:28:50 +01:00
bool invalidate_all ( CancelationCallback cancel ) {
2018-11-08 14:23:17 +01:00
bool invalidated = false ;
2018-11-16 18:28:50 +01:00
for ( size_t i = 0 ; i < COUNT ; ++ i ) {
StateWithTimeStamp & state = m_state [ i ];
if ( state . state != INVALID ) {
2018-11-08 14:23:17 +01:00
invalidated = true ;
2018-11-16 18:28:50 +01:00
state . state = INVALID ;
state . timestamp = ++ g_last_timestamp ;
2018-11-08 14:23:17 +01:00
}
}
2018-11-16 18:28:50 +01:00
if ( invalidated )
cancel ();
2018-11-08 14:23:17 +01:00
return invalidated ;
}
private :
2018-11-16 18:28:50 +01:00
StateWithTimeStamp m_state [ COUNT ];
2018-11-08 14:23:17 +01:00
};
class PrintBase ;
class PrintObjectBase
{
2018-11-16 18:28:50 +01:00
public :
const ModelObject * model_object () const { return m_model_object ; }
ModelObject * model_object () { return m_model_object ; }
2018-11-08 14:23:17 +01:00
protected :
2018-11-16 18:28:50 +01:00
PrintObjectBase ( ModelObject * model_object ) : m_model_object ( model_object ) {}
2018-11-08 14:23:17 +01:00
virtual ~ PrintObjectBase () {}
// Declared here to allow access from PrintBase through friendship.
2018-11-16 18:28:50 +01:00
static tbb :: mutex & state_mutex ( PrintBase * print );
2018-11-08 14:23:17 +01:00
static std :: function < void () > cancel_callback ( PrintBase * print );
2018-11-16 18:28:50 +01:00
ModelObject * m_model_object ;
2018-11-08 14:23:17 +01:00
};
/**
* @brief Printing involves slicing and export of device dependent instructions.
*
* Every technology has a potentially different set of requirements for
* slicing, support structures and output print instructions. The pipeline
* however remains roughly the same:
* slice -> convert to instructions -> send to printer
*
* The PrintBase class will abstract this flow for different technologies.
*
*/
class PrintBase
{
public :
2019-07-25 14:39:19 +02:00
PrintBase () : m_placeholder_parser ( & m_full_print_config ) { this -> restart (); }
2018-11-08 14:23:17 +01:00
inline virtual ~ PrintBase () {}
virtual PrinterTechnology technology () const noexcept = 0 ;
// Reset the print status including the copy of the Model / ModelObject hierarchy.
virtual void clear () = 0 ;
2018-11-08 20:18:40 +01:00
// The Print is empty either after clear() or after apply() over an empty model,
// or after apply() over a model, where no object is printable (all outside the print volume).
virtual bool empty () const = 0 ;
// Validate the print, return empty string if valid, return error if process() cannot (or should not) be started.
virtual std :: string validate () const { return std :: string (); }
2018-11-08 14:23:17 +01:00
enum ApplyStatus {
// No change after the Print::apply() call.
APPLY_STATUS_UNCHANGED ,
// Some of the Print / PrintObject / PrintObjectInstance data was changed,
// but no result was invalidated (only data influencing not yet calculated results were changed).
APPLY_STATUS_CHANGED ,
// Some data was changed, which in turn invalidated already calculated steps.
APPLY_STATUS_INVALIDATED ,
};
2019-07-25 14:39:19 +02:00
virtual ApplyStatus apply ( const Model & model , DynamicPrintConfig config ) = 0 ;
2018-11-16 18:28:50 +01:00
const Model & model () const { return m_model ; }
2018-11-08 14:23:17 +01:00
2019-02-21 08:44:07 +01:00
struct TaskParams {
TaskParams () : single_model_object ( 0 ), single_model_instance_only ( false ), to_object_step ( - 1 ), to_print_step ( - 1 ) {}
// If non-empty, limit the processing to this ModelObject.
2019-06-27 11:02:45 +02:00
ObjectID single_model_object ;
2019-02-21 08:44:07 +01:00
// If set, only process single_model_object. Otherwise process everything, but single_model_object first.
bool single_model_instance_only ;
// If non-negative, stop processing at the successive object step.
int to_object_step ;
// If non-negative, stop processing at the successive print step.
int to_print_step ;
};
2019-02-21 11:40:56 +01:00
// After calling the apply() function, call set_task() to limit the task to be processed by process().
virtual void set_task ( const TaskParams & params ) {}
// Perform the calculation. This is the only method that is to be called at a worker thread.
2018-11-08 14:23:17 +01:00
virtual void process () = 0 ;
2019-02-21 11:40:56 +01:00
// 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 () {}
2018-11-08 14:23:17 +01:00
2018-11-20 16:27:30 +01:00
struct SlicingStatus {
SlicingStatus ( int percent , const std :: string & text , unsigned int flags = 0 ) : percent ( percent ), text ( text ), flags ( flags ) {}
2018-11-20 13:22:26 +01:00
int percent ;
std :: string text ;
// Bitmap of flags.
enum FlagBits {
2019-09-25 12:06:38 +02:00
DEFAULT = 0 ,
2019-02-06 14:18:04 +01:00
RELOAD_SCENE = 1 << 1 ,
RELOAD_SLA_SUPPORT_POINTS = 1 << 2 ,
2019-03-14 15:11:27 +01:00
RELOAD_SLA_PREVIEW = 1 << 3 ,
2018-11-20 13:22:26 +01:00
};
// Bitmap of FlagBits
unsigned int flags ;
};
2018-11-20 16:27:30 +01:00
typedef std :: function < void ( const SlicingStatus & ) > status_callback_type ;
2018-11-08 14:23:17 +01:00
// Default status console print out in the form of percent => message.
void set_status_default () { m_status_callback = nullptr ; }
// No status output or callback whatsoever, useful mostly for automatic tests.
2018-11-20 16:27:30 +01:00
void set_status_silent () { m_status_callback = []( const SlicingStatus & ){}; }
2018-11-08 14:23:17 +01:00
// Register a custom status callback.
void set_status_callback ( status_callback_type cb ) { m_status_callback = cb ; }
// Calls a registered callback to update the status, or print out the default message.
2018-11-21 12:10:10 +01:00
void set_status ( int percent , const std :: string & message , unsigned int flags = SlicingStatus :: DEFAULT ) {
2018-11-20 16:27:30 +01:00
if ( m_status_callback ) m_status_callback ( SlicingStatus ( percent , message , flags ));
2018-11-08 14:23:17 +01:00
else printf ( "%d => %s \n " , percent , message . c_str ());
}
typedef std :: function < void () > cancel_callback_type ;
// Various methods will call this callback to stop the background processing (the Print::process() call)
// in case a successive change of the Print / PrintObject / PrintRegion instances changed
// the state of the finished or running calculations.
2018-12-03 13:14:28 +01:00
void set_cancel_callback ( cancel_callback_type cancel_callback ) { m_cancel_callback = cancel_callback ; }
2018-11-08 14:23:17 +01:00
// Has the calculation been canceled?
enum CancelStatus {
// No cancelation, background processing should run.
NOT_CANCELED = 0 ,
// Canceled by user from the user interface (user pressed the "Cancel" button or user closed the application).
CANCELED_BY_USER = 1 ,
// Canceled internally from Print::apply() through the Print/PrintObject::invalidate_step() or ::invalidate_all_steps().
CANCELED_INTERNAL = 2
};
2018-12-03 13:14:28 +01:00
CancelStatus cancel_status () const { return m_cancel_status ; }
2018-11-08 14:23:17 +01:00
// Has the calculation been canceled?
2018-12-03 13:14:28 +01:00
bool canceled () const { return m_cancel_status != NOT_CANCELED ; }
2018-11-08 14:23:17 +01:00
// Cancel the running computation. Stop execution of all the background threads.
2018-12-03 13:14:28 +01:00
void cancel () { m_cancel_status = CANCELED_BY_USER ; }
void cancel_internal () { m_cancel_status = CANCELED_INTERNAL ; }
2018-11-08 14:23:17 +01:00
// Cancel the running computation. Stop execution of all the background threads.
2018-12-03 13:14:28 +01:00
void restart () { m_cancel_status = NOT_CANCELED ; }
2018-12-11 17:49:31 +01:00
// Returns true if the last step was finished with success.
virtual bool finished () const = 0 ;
2018-12-03 13:14:28 +01:00
const PlaceholderParser & placeholder_parser () const { return m_placeholder_parser ; }
2019-07-25 14:39:19 +02:00
const DynamicPrintConfig & full_print_config () const { return m_full_print_config ; }
2018-12-03 13:14:28 +01:00
2019-05-17 16:27:00 +02:00
virtual std :: string output_filename ( const std :: string & filename_base = std :: string ()) const = 0 ;
// If the filename_base is set, it is used as the input for the template processing. In that case the path is expected to be the directory (may be empty).
// If filename_set is empty, than the path may be a file or directory. If it is a file, then the macro will not be processed.
std :: string output_filepath ( const std :: string & path , const std :: string & filename_base = std :: string ()) const ;
2018-11-08 14:23:17 +01:00
protected :
friend class PrintObjectBase ;
2018-11-16 18:28:50 +01:00
friend class BackgroundSlicingProcess ;
2018-11-08 14:23:17 +01:00
2018-11-16 18:28:50 +01:00
tbb :: mutex & state_mutex () const { return m_state_mutex ; }
2018-11-08 14:23:17 +01:00
std :: function < void () > cancel_callback () { return m_cancel_callback ; }
2019-02-21 12:39:38 +01:00
void call_cancel_callback () { m_cancel_callback (); }
2018-11-08 14:23:17 +01:00
// If the background processing stop was requested, throw CanceledException.
// To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly.
void throw_if_canceled () const { if ( m_cancel_status ) throw CanceledException (); }
2018-12-03 13:14:28 +01:00
// To be called by this->output_filename() with the format string pulled from the configuration layer.
2019-05-17 16:27:00 +02:00
std :: string output_filename ( const std :: string & format , const std :: string & default_ext , const std :: string & filename_base , const DynamicConfig * config_override = nullptr ) const ;
2018-12-03 13:14:28 +01:00
// Update "scale", "input_filename", "input_filename_base" placeholders from the current printable ModelObjects.
2019-05-17 16:27:00 +02:00
void update_object_placeholders ( DynamicConfig & config , const std :: string & default_ext ) const ;
2018-12-03 13:14:28 +01:00
2018-11-16 18:28:50 +01:00
Model m_model ;
2019-07-25 14:39:19 +02:00
DynamicPrintConfig m_full_print_config ;
2019-07-25 17:08:31 +02:00
PlaceholderParser m_placeholder_parser ;
2018-11-16 18:28:50 +01:00
2018-11-08 14:23:17 +01:00
private :
tbb :: atomic < CancelStatus > m_cancel_status ;
// Callback to be evoked regularly to update state of the UI thread.
status_callback_type m_status_callback ;
// Callback to be evoked to stop the background processing before a state is updated.
cancel_callback_type m_cancel_callback = [](){};
// Mutex used for synchronization of the worker thread with the UI thread:
// The mutex will be used to guard the worker thread against entering a stage
// while the data influencing the stage is modified.
2018-11-16 18:28:50 +01:00
mutable tbb :: mutex m_state_mutex ;
2018-11-08 14:23:17 +01:00
};
template < typename PrintStepEnum , const size_t COUNT >
class PrintBaseWithState : public PrintBase
{
public :
2018-11-16 18:28:50 +01:00
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 ()); }
2018-11-08 14:23:17 +01:00
protected :
2018-11-16 18:28:50 +01:00
bool set_started ( PrintStepEnum step ) { return m_state . set_started ( step , this -> state_mutex (), [ this ](){ this -> throw_if_canceled (); }); }
2018-11-20 13:22:26 +01:00
PrintStateBase :: TimeStamp set_done ( PrintStepEnum step ) { return m_state . set_done ( step , this -> state_mutex (), [ this ](){ this -> throw_if_canceled (); }); }
2018-11-08 14:23:17 +01:00
bool invalidate_step ( PrintStepEnum step )
2018-11-16 18:28:50 +01:00
{ return m_state . invalidate ( step , this -> cancel_callback ()); }
2018-11-08 14:23:17 +01:00
template < typename StepTypeIterator >
bool invalidate_steps ( StepTypeIterator step_begin , StepTypeIterator step_end )
2018-11-16 18:28:50 +01:00
{ return m_state . invalidate_multiple ( step_begin , step_end , this -> cancel_callback ()); }
2018-11-08 14:23:17 +01:00
bool invalidate_steps ( std :: initializer_list < PrintStepEnum > il )
2018-11-16 18:28:50 +01:00
{ return m_state . invalidate_multiple ( il . begin (), il . end (), this -> cancel_callback ()); }
2018-11-08 14:23:17 +01:00
bool invalidate_all_steps ()
2018-11-16 18:28:50 +01:00
{ return m_state . invalidate_all ( this -> cancel_callback ()); }
2018-11-08 14:23:17 +01:00
2019-02-21 08:44:07 +01:00
bool is_step_started_unguarded ( PrintStepEnum step ) const { return m_state . is_started_unguarded ( step ); }
bool is_step_done_unguarded ( PrintStepEnum step ) const { return m_state . is_done_unguarded ( step ); }
2018-11-08 14:23:17 +01:00
private :
PrintState < PrintStepEnum , COUNT > m_state ;
};
template < typename PrintType , typename PrintObjectStepEnum , const size_t COUNT >
class PrintObjectBaseWithState : public PrintObjectBase
{
public :
2018-11-09 12:02:42 +01:00
PrintType * print () { return m_print ; }
const PrintType * print () const { return m_print ; }
2018-11-08 14:23:17 +01:00
2018-11-16 18:28:50 +01:00
typedef PrintState < PrintObjectStepEnum , COUNT > PrintObjectState ;
bool is_step_done ( PrintObjectStepEnum step ) const { return m_state . is_done ( step , PrintObjectBase :: state_mutex ( m_print )); }
PrintStateBase :: StateWithTimeStamp step_state_with_timestamp ( PrintObjectStepEnum step ) const { return m_state . state_with_timestamp ( step , PrintObjectBase :: state_mutex ( m_print )); }
2018-11-08 14:23:17 +01:00
protected :
2018-11-16 18:28:50 +01:00
PrintObjectBaseWithState ( PrintType * print , ModelObject * model_object ) : PrintObjectBase ( model_object ), m_print ( print ) {}
2018-11-08 14:23:17 +01:00
2018-11-16 18:28:50 +01:00
bool set_started ( PrintObjectStepEnum step )
2018-11-20 15:18:46 +01:00
{ return m_state . set_started ( step , PrintObjectBase :: state_mutex ( m_print ), [ this ](){ this -> throw_if_canceled (); }); }
2018-11-20 13:22:26 +01:00
PrintStateBase :: TimeStamp set_done ( PrintObjectStepEnum step )
2018-11-20 15:18:46 +01:00
{ return m_state . set_done ( step , PrintObjectBase :: state_mutex ( m_print ), [ this ](){ this -> throw_if_canceled (); }); }
2018-11-08 14:23:17 +01:00
bool invalidate_step ( PrintObjectStepEnum step )
2018-11-16 18:28:50 +01:00
{ return m_state . invalidate ( step , PrintObjectBase :: cancel_callback ( m_print )); }
2018-11-08 14:23:17 +01:00
template < typename StepTypeIterator >
bool invalidate_steps ( StepTypeIterator step_begin , StepTypeIterator step_end )
2018-11-16 18:28:50 +01:00
{ return m_state . invalidate_multiple ( step_begin , step_end , PrintObjectBase :: cancel_callback ( m_print )); }
2018-11-08 14:23:17 +01:00
bool invalidate_steps ( std :: initializer_list < PrintObjectStepEnum > il )
2018-11-16 18:28:50 +01:00
{ return m_state . invalidate_multiple ( il . begin (), il . end (), PrintObjectBase :: cancel_callback ( m_print )); }
bool invalidate_all_steps ()
{ return m_state . invalidate_all ( PrintObjectBase :: cancel_callback ( m_print )); }
2018-11-08 14:23:17 +01:00
2019-02-21 08:44:07 +01:00
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 ); }
2018-11-08 14:23:17 +01:00
protected :
2018-11-20 15:18:46 +01:00
// If the background processing stop was requested, throw CanceledException.
// To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly.
void throw_if_canceled () { if ( m_print -> canceled ()) throw CanceledException (); }
2018-11-08 14:57:42 +01:00
friend PrintType ;
2018-11-08 14:23:17 +01:00
PrintType * m_print ;
private :
PrintState < PrintObjectStepEnum , COUNT > m_state ;
};
} // namespace Slic3r
#endif /* slic3r_PrintBase_hpp_ */