diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 3c3459f9db..059d871ae0 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -156,6 +156,7 @@ add_library(libslic3r STATIC TriangleMesh.hpp utils.cpp Utils.hpp + MTUtils.hpp SLA/SLABoilerPlate.hpp SLA/SLABasePool.hpp SLA/SLABasePool.cpp diff --git a/src/libslic3r/MTUtils.hpp b/src/libslic3r/MTUtils.hpp new file mode 100644 index 0000000000..f07e8832d6 --- /dev/null +++ b/src/libslic3r/MTUtils.hpp @@ -0,0 +1,63 @@ +#ifndef MTUTILS_HPP +#define MTUTILS_HPP + +#include // for std::atomic_flag +#include // for std::lock_guard +#include // for std::function +#include // for std::forward + +namespace Slic3r { + +// TODO: these classes are untested + +/// Handy little spin mutex for the cached meshes. +/// Implements the "Lockable" concept +class SpinMutex { + std::atomic_flag m_flg; + static const /*constexpr*/ auto MO_ACQ = std::memory_order_acquire; + static const /*constexpr*/ auto MO_REL = std::memory_order_release; +public: + inline SpinMutex() { m_flg.clear(MO_REL); } + inline void lock() { while(m_flg.test_and_set(MO_ACQ)); } + inline bool try_lock() { return !m_flg.test_and_set(MO_ACQ); } + inline void unlock() { m_flg.clear(MO_REL); } +}; + +/// A wrapper class around arbitrary object that needs thread safe caching. +template class CachedObject { +public: + // Method type which refreshes the object when it has been invalidated + using Setter = std::function; +private: + T m_obj; // the object itself + bool m_valid; // invalidation flag + SpinMutex m_lck; // to make the caching thread safe + + // the setter will be called just before the object's const value is about + // to be retrieved. + std::function m_setter; +public: + + // Forwarded constructor + template inline CachedObject(Setter fn, Args&&...args): + m_obj(std::forward(args)...), m_valid(false), m_setter(fn) {} + + // invalidate the value of the object. The object will be refreshed at the + // next retrieval (Setter will be called). The data that is used in + // the setter function should be guarded as well if it is modified so the + // modification has to take place in fn. + inline void invalidate(std::function fn) { + std::lock_guard lck(m_lck); fn(); m_valid = false; + } + + // Get the const object properly updated. + inline const T& get() { + std::lock_guard lck(m_lck); + if(!m_valid) { m_setter(m_obj); m_valid = true; } + return m_obj; + } +}; + +} + +#endif // MTUTILS_HPP diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index f42f3b4ac3..46f4d27e89 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -730,6 +730,9 @@ public: meshcache.merge(mesh(bs.mesh)); } + // TODO: Is this necessary? + meshcache.repair(); + BoundingBoxf3&& bb = meshcache.bounding_box(); model_height = bb.max(Z) - bb.min(Z); @@ -1616,13 +1619,13 @@ bool SLASupportTree::generate(const PointSet &points, return pc == ABORT; } -void SLASupportTree::merged_mesh(TriangleMesh &outmesh) const +const TriangleMesh &SLASupportTree::merged_mesh() const { - outmesh.merge(get().merged_mesh()); + return get().merged_mesh(); } void SLASupportTree::merged_mesh_with_pad(TriangleMesh &outmesh) const { - merged_mesh(outmesh); + outmesh.merge(merged_mesh()); outmesh.merge(get_pad()); } @@ -1658,14 +1661,12 @@ const TriangleMesh &SLASupportTree::add_pad(const SliceLayer& baseplate, double max_merge_distance_mm, double edge_radius_mm) const { - TriangleMesh mm; - merged_mesh(mm); PoolConfig pcfg; pcfg.min_wall_thickness_mm = min_wall_thickness_mm; pcfg.min_wall_height_mm = min_wall_height_mm; pcfg.max_merge_distance_mm = max_merge_distance_mm; pcfg.edge_radius_mm = edge_radius_mm; - return m_impl->create_pad(mm, baseplate, pcfg).tmesh; + return m_impl->create_pad(merged_mesh(), baseplate, pcfg).tmesh; } const TriangleMesh &SLASupportTree::get_pad() const diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index ce562946c4..163cacbde9 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -144,7 +144,7 @@ public: /// Get the whole mesh united into the output TriangleMesh /// WITHOUT THE PAD - void merged_mesh(TriangleMesh& outmesh) const; + const TriangleMesh& merged_mesh() const; void merged_mesh_with_pad(TriangleMesh&) const; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index c8f6c1409e..92b5bff8b0 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -500,7 +500,10 @@ void SLAPrint::process() SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object): Inherited(print, model_object), - m_stepmask(slaposCount, true) + m_stepmask(slaposCount, true), + m_transformed_rmesh( [this](TriangleMesh& obj){ + obj = m_model_object->raw_mesh(); obj.transform(m_trafo); + }) { } @@ -526,28 +529,29 @@ double SLAPrintObject::get_elevation() const { return ret; } -//const std::vector &SLAPrintObject::get_support_slices() const -//{ -// // I don't want to return a copy but the points may not exist, so ... -// static const std::vector dummy_empty; +namespace { // dummy empty static containers for return values in some methods +const std::vector EMPTY_SLICES; +const TriangleMesh EMPTY_MESH; +} -// if(!m_supportdata) return dummy_empty; -// return m_supportdata->support_slices; -//} +const std::vector &SLAPrintObject::get_support_slices() const +{ + if(!is_step_done(slaposSliceSupports) || !m_supportdata) return EMPTY_SLICES; + return m_supportdata->support_slices; +} -//const std::vector &SLAPrintObject::get_model_slices() const -//{ -// return m_model_slices; -//} +const std::vector &SLAPrintObject::get_model_slices() const +{ + if(!is_step_done(slaposObjectSlice)) return EMPTY_SLICES; + return m_model_slices; +} bool SLAPrintObject::has_mesh(SLAPrintObjectStep step) const { switch (step) { case slaposSupportTree: -// return m_supportdata && m_supportdata->support_tree_ptr && ! m_supportdata->support_tree_ptr->get().merged_mesh().empty(); return ! this->support_mesh().empty(); case slaposBasePool: -// return m_supportdata && m_supportdata->support_tree_ptr && ! m_supportdata->support_tree_ptr->get_pad().empty(); return ! this->pad_mesh().empty(); default: return false; @@ -566,22 +570,19 @@ TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const } } -TriangleMesh SLAPrintObject::support_mesh() const + + +const TriangleMesh& SLAPrintObject::support_mesh() const { - TriangleMesh trm; - if(m_supportdata && m_supportdata->support_tree_ptr) - m_supportdata->support_tree_ptr->merged_mesh(trm); + return m_supportdata->support_tree_ptr->merged_mesh(); - // TODO: is this necessary? - trm.repair(); - - return trm; + return EMPTY_MESH; } -TriangleMesh SLAPrintObject::pad_mesh() const +const TriangleMesh& SLAPrintObject::pad_mesh() const { - if(!m_supportdata || !m_supportdata->support_tree_ptr) return {}; + if(!m_supportdata || !m_supportdata->support_tree_ptr) return EMPTY_MESH; return m_supportdata->support_tree_ptr->get_pad(); } @@ -596,11 +597,7 @@ const TriangleMesh &SLAPrintObject::transformed_mesh() const { // or apply an inverse transformation on the support structure after it // has been created. - if(m_trmesh_valid) return m_transformed_rmesh; - m_transformed_rmesh = m_model_object->raw_mesh(); - m_transformed_rmesh.transform(m_trafo); - m_trmesh_valid = true; - return m_transformed_rmesh; + return m_transformed_rmesh.get(); } std::vector SLAPrintObject::transformed_support_points() const diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 67a1db21a5..ae797e9fbe 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -1,9 +1,12 @@ #ifndef slic3r_SLAPrint_hpp_ #define slic3r_SLAPrint_hpp_ +#include + #include "PrintBase.hpp" #include "PrintExport.hpp" #include "Point.hpp" +#include "MTUtils.hpp" namespace Slic3r { @@ -53,10 +56,10 @@ public: // Get a support mesh centered around origin in XY, and with zero rotation around Z applied. // Support mesh is only valid if this->is_step_done(slaposSupportTree) is true. - TriangleMesh support_mesh() const; + const TriangleMesh& support_mesh() const; // Get a pad mesh centered around origin in XY, and with zero rotation around Z applied. // Support mesh is only valid if this->is_step_done(slaposPad) is true. - TriangleMesh pad_mesh() const; + const TriangleMesh& pad_mesh() const; // This will return the transformed mesh which is cached const TriangleMesh& transformed_mesh() const; @@ -69,8 +72,9 @@ public: // as the pad height also needs to be considered. double get_elevation() const; -// const std::vector& get_support_slices() const; -// const std::vector& get_model_slices() const; + // Should be obvious + const std::vector& get_support_slices() const; + const std::vector& get_model_slices() const; // I refuse to grantee copying (Tamas) SLAPrintObject(const SLAPrintObject&) = delete; @@ -86,7 +90,10 @@ protected: void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); } void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); } - void set_trafo(const Transform3d& trafo) { m_trafo = trafo; m_trmesh_valid = false; } + + void set_trafo(const Transform3d& trafo) { + m_transformed_rmesh.invalidate([this, &trafo](){ m_trafo = trafo; }); + } bool set_instances(const std::vector &instances); // Invalidates the step, and its depending steps in SLAPrintObject and SLAPrint. @@ -105,8 +112,7 @@ private: std::vector m_model_slices; // Caching the transformed (m_trafo) raw mesh of the object - mutable TriangleMesh m_transformed_rmesh; - mutable bool m_trmesh_valid = false; + mutable CachedObject m_transformed_rmesh; class SupportData; std::unique_ptr m_supportdata;