mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-12 01:07:57 -06:00
Partial update of 3D scene (GLVolumes derived from Model and from
the SLAPrintObject). Updated PrintBase to generate a unique timestamp at each step invalidation.
This commit is contained in:
parent
a468078df3
commit
c9a4c6c73c
15 changed files with 670 additions and 373 deletions
|
@ -2,13 +2,11 @@
|
|||
#define slic3r_PrintBase_hpp_
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include <atomic>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
#include "tbb/atomic.h"
|
||||
// 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
|
||||
|
@ -25,68 +23,117 @@ public:
|
|||
const char* what() const throw() { return "Background processing has been canceled"; }
|
||||
};
|
||||
|
||||
// To be instantiated over PrintStep or PrintObjectStep enums.
|
||||
template <class StepType, size_t COUNT>
|
||||
class PrintState
|
||||
{
|
||||
class PrintStateBase {
|
||||
public:
|
||||
PrintState() { for (size_t i = 0; i < COUNT; ++ i) m_state[i].store(INVALID, std::memory_order_relaxed); }
|
||||
|
||||
enum State {
|
||||
INVALID,
|
||||
STARTED,
|
||||
DONE,
|
||||
};
|
||||
|
||||
// With full memory barrier.
|
||||
bool is_done(StepType step) const { return m_state[step] == DONE; }
|
||||
|
||||
// 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;
|
||||
size_t timestamp;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
bool is_done_unguarded(StepType step) const {
|
||||
return this->state_with_timestamp_unguarded(step).state == DONE;
|
||||
}
|
||||
|
||||
// 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_config() updates its state, which may
|
||||
// influence the processing step being entered.
|
||||
void set_started(StepType step, tbb::mutex &mtx) {
|
||||
mtx.lock();
|
||||
m_state[step].store(STARTED, std::memory_order_relaxed);
|
||||
mtx.unlock();
|
||||
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;
|
||||
}
|
||||
|
||||
// Set the step as done. Block on mutex while the Print / PrintObject / PrintRegion objects are being
|
||||
// modified by the UI thread.
|
||||
void set_done(StepType step, tbb::mutex &mtx) {
|
||||
mtx.lock();
|
||||
m_state[step].store(DONE, std::memory_order_relaxed);
|
||||
mtx.unlock();
|
||||
template<typename ThrowIfCanceled>
|
||||
void set_done(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();
|
||||
assert(m_state[step].state != DONE);
|
||||
m_state[step].state = DONE;
|
||||
m_state[step].timestamp = ++ g_last_timestamp;
|
||||
}
|
||||
|
||||
// Make the step invalid.
|
||||
// The provided mutex should be locked at this point, guarding access to m_state.
|
||||
// PrintBase::m_state_mutex should be locked at this point, guarding access to m_state.
|
||||
// In case the step has already been entered or finished, cancel the background
|
||||
// processing by calling the cancel callback.
|
||||
template<typename CancelationCallback>
|
||||
bool invalidate(StepType step, tbb::mutex &mtx, CancelationCallback cancel) {
|
||||
bool invalidated = m_state[step].load(std::memory_order_relaxed) != INVALID;
|
||||
bool invalidate(StepType step, CancelationCallback cancel) {
|
||||
bool invalidated = m_state[step].state != INVALID;
|
||||
if (invalidated) {
|
||||
#if 0
|
||||
if (mtx.state != mtx.HELD) {
|
||||
printf("Not held!\n");
|
||||
}
|
||||
#endif
|
||||
m_state[step].state = INVALID;
|
||||
m_state[step].timestamp = ++ g_last_timestamp;
|
||||
// Raise the mutex, so that the following cancel() callback could cancel
|
||||
// the background processing.
|
||||
mtx.unlock();
|
||||
// Internally the cancel() callback shall unlock the PrintBase::m_status_mutex to let
|
||||
// the working thread to proceed.
|
||||
cancel();
|
||||
m_state[step] = INVALID;
|
||||
mtx.lock();
|
||||
}
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
template<typename CancelationCallback, typename StepTypeIterator>
|
||||
bool invalidate_multiple(StepTypeIterator step_begin, StepTypeIterator step_end, tbb::mutex &mtx, CancelationCallback cancel) {
|
||||
bool invalidate_multiple(StepTypeIterator step_begin, StepTypeIterator step_end, CancelationCallback cancel) {
|
||||
bool invalidated = false;
|
||||
for (StepTypeIterator it = step_begin; ! invalidated && it != step_end; ++ it)
|
||||
invalidated = m_state[*it].load(std::memory_order_relaxed) != INVALID;
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (invalidated) {
|
||||
#if 0
|
||||
if (mtx.state != mtx.HELD) {
|
||||
|
@ -95,50 +142,56 @@ public:
|
|||
#endif
|
||||
// Raise the mutex, so that the following cancel() callback could cancel
|
||||
// the background processing.
|
||||
mtx.unlock();
|
||||
// Internally the cancel() callback shall unlock the PrintBase::m_status_mutex to let
|
||||
// the working thread to proceed.
|
||||
cancel();
|
||||
for (StepTypeIterator it = step_begin; it != step_end; ++ it)
|
||||
m_state[*it] = INVALID;
|
||||
mtx.lock();
|
||||
}
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
// Make all steps invalid.
|
||||
// The provided mutex should be locked at this point, guarding access to m_state.
|
||||
// PrintBase::m_state_mutex should be locked at this point, guarding access to m_state.
|
||||
// In case any step has already been entered or finished, cancel the background
|
||||
// processing by calling the cancel callback.
|
||||
template<typename CancelationCallback>
|
||||
bool invalidate_all(tbb::mutex &mtx, CancelationCallback cancel) {
|
||||
bool invalidate_all(CancelationCallback cancel) {
|
||||
bool invalidated = false;
|
||||
for (size_t i = 0; i < COUNT; ++ i)
|
||||
if (m_state[i].load(std::memory_order_relaxed) != INVALID) {
|
||||
for (size_t i = 0; i < COUNT; ++ i) {
|
||||
StateWithTimeStamp &state = m_state[i];
|
||||
if (state.state != INVALID) {
|
||||
invalidated = true;
|
||||
break;
|
||||
state.state = INVALID;
|
||||
state.timestamp = ++ g_last_timestamp;
|
||||
}
|
||||
if (invalidated) {
|
||||
mtx.unlock();
|
||||
cancel();
|
||||
for (size_t i = 0; i < COUNT; ++ i)
|
||||
m_state[i].store(INVALID, std::memory_order_relaxed);
|
||||
mtx.lock();
|
||||
}
|
||||
if (invalidated)
|
||||
cancel();
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<State> m_state[COUNT];
|
||||
StateWithTimeStamp m_state[COUNT];
|
||||
};
|
||||
|
||||
class PrintBase;
|
||||
|
||||
class PrintObjectBase
|
||||
{
|
||||
public:
|
||||
const ModelObject* model_object() const { return m_model_object; }
|
||||
ModelObject* model_object() { return m_model_object; }
|
||||
|
||||
protected:
|
||||
PrintObjectBase(ModelObject *model_object) : m_model_object(model_object) {}
|
||||
virtual ~PrintObjectBase() {}
|
||||
// Declared here to allow access from PrintBase through friendship.
|
||||
static tbb::mutex& cancel_mutex(PrintBase *print);
|
||||
static tbb::mutex& state_mutex(PrintBase *print);
|
||||
static std::function<void()> cancel_callback(PrintBase *print);
|
||||
// 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.
|
||||
static void throw_if_canceled(PrintBase *print);
|
||||
|
||||
ModelObject *m_model_object;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -179,6 +232,7 @@ public:
|
|||
APPLY_STATUS_INVALIDATED,
|
||||
};
|
||||
virtual ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) = 0;
|
||||
const Model& model() const { return m_model; }
|
||||
|
||||
virtual void process() = 0;
|
||||
|
||||
|
@ -220,8 +274,9 @@ public:
|
|||
|
||||
protected:
|
||||
friend class PrintObjectBase;
|
||||
friend class BackgroundSlicingProcess;
|
||||
|
||||
tbb::mutex& cancel_mutex() { return m_cancel_mutex; }
|
||||
tbb::mutex& state_mutex() const { return m_state_mutex; }
|
||||
std::function<void()> cancel_callback() { return m_cancel_callback; }
|
||||
void call_cancell_callback() { m_cancel_callback(); }
|
||||
|
||||
|
@ -229,6 +284,8 @@ protected:
|
|||
// 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(); }
|
||||
|
||||
Model m_model;
|
||||
|
||||
private:
|
||||
tbb::atomic<CancelStatus> m_cancel_status;
|
||||
// Callback to be evoked regularly to update state of the UI thread.
|
||||
|
@ -240,27 +297,28 @@ private:
|
|||
// 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.
|
||||
mutable tbb::mutex m_cancel_mutex;
|
||||
mutable tbb::mutex m_state_mutex;
|
||||
};
|
||||
|
||||
template<typename PrintStepEnum, const size_t COUNT>
|
||||
class PrintBaseWithState : public PrintBase
|
||||
{
|
||||
public:
|
||||
bool is_step_done(PrintStepEnum step) const { return m_state.is_done(step); }
|
||||
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()); }
|
||||
|
||||
protected:
|
||||
void set_started(PrintStepEnum step) { m_state.set_started(step, this->cancel_mutex()); throw_if_canceled(); }
|
||||
void set_done(PrintStepEnum step) { m_state.set_done(step, this->cancel_mutex()); throw_if_canceled(); }
|
||||
bool set_started(PrintStepEnum step) { return m_state.set_started(step, this->state_mutex(), [this](){ this->throw_if_canceled(); }); }
|
||||
void set_done(PrintStepEnum step) { m_state.set_done(step, this->state_mutex(), [this](){ this->throw_if_canceled(); }); }
|
||||
bool invalidate_step(PrintStepEnum step)
|
||||
{ return m_state.invalidate(step, this->cancel_mutex(), this->cancel_callback()); }
|
||||
{ return m_state.invalidate(step, this->cancel_callback()); }
|
||||
template<typename StepTypeIterator>
|
||||
bool invalidate_steps(StepTypeIterator step_begin, StepTypeIterator step_end)
|
||||
{ return m_state.invalidate_multiple(step_begin, step_end, this->cancel_mutex(), this->cancel_callback()); }
|
||||
{ return m_state.invalidate_multiple(step_begin, step_end, this->cancel_callback()); }
|
||||
bool invalidate_steps(std::initializer_list<PrintStepEnum> il)
|
||||
{ return m_state.invalidate_multiple(il.begin(), il.end(), this->cancel_mutex(), this->cancel_callback()); }
|
||||
{ return m_state.invalidate_multiple(il.begin(), il.end(), this->cancel_callback()); }
|
||||
bool invalidate_all_steps()
|
||||
{ return m_state.invalidate_all(this->cancel_mutex(), this->cancel_callback()); }
|
||||
{ return m_state.invalidate_all(this->cancel_callback()); }
|
||||
|
||||
private:
|
||||
PrintState<PrintStepEnum, COUNT> m_state;
|
||||
|
@ -273,22 +331,27 @@ public:
|
|||
PrintType* print() { return m_print; }
|
||||
const PrintType* print() const { return m_print; }
|
||||
|
||||
bool is_step_done(PrintObjectStepEnum step) const { return m_state.is_done(step); }
|
||||
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)); }
|
||||
|
||||
protected:
|
||||
PrintObjectBaseWithState(PrintType *print) : m_print(print) {}
|
||||
PrintObjectBaseWithState(PrintType *print, ModelObject *model_object) : PrintObjectBase(model_object), m_print(print) {}
|
||||
|
||||
void set_started(PrintObjectStepEnum step) { m_state.set_started(step, PrintObjectBase::cancel_mutex(m_print)); }
|
||||
void set_done(PrintObjectStepEnum step) { m_state.set_done(step, PrintObjectBase::cancel_mutex(m_print)); }
|
||||
bool set_started(PrintObjectStepEnum step)
|
||||
{ return m_state.set_started(step, PrintObjectBase::state_mutex(m_print), [this](){ PrintObjectBase::throw_if_canceled(this->m_print); }); }
|
||||
void set_done(PrintObjectStepEnum step)
|
||||
{ m_state.set_done(step, PrintObjectBase::state_mutex(m_print), [this](){ PrintObjectBase::throw_if_canceled(this->m_print); }); }
|
||||
|
||||
bool invalidate_step(PrintObjectStepEnum step)
|
||||
{ return m_state.invalidate(step, PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); }
|
||||
{ return m_state.invalidate(step, PrintObjectBase::cancel_callback(m_print)); }
|
||||
template<typename StepTypeIterator>
|
||||
bool invalidate_steps(StepTypeIterator step_begin, StepTypeIterator step_end)
|
||||
{ return m_state.invalidate_multiple(step_begin, step_end, PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); }
|
||||
{ return m_state.invalidate_multiple(step_begin, step_end, PrintObjectBase::cancel_callback(m_print)); }
|
||||
bool invalidate_steps(std::initializer_list<PrintObjectStepEnum> il)
|
||||
{ return m_state.invalidate_multiple(il.begin(), il.end(), PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); }
|
||||
bool invalidate_all_steps() { return m_state.invalidate_all(PrintObjectBase::cancel_mutex(m_print), PrintObjectBase::cancel_callback(m_print)); }
|
||||
{ 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)); }
|
||||
|
||||
protected:
|
||||
friend PrintType;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue