diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index 0ce99a416b..bf92297530 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,4 +1,6 @@ min_slic3r_version = 2.4.0-alpha0 +1.4.0-alpha2 Updated Prusa MINI machine limits. +1.4.0-alpha1 Added new SL1S resin profiles. 1.4.0-alpha0 Bumped up config version. 1.3.0-alpha2 Added SL1S SPEED profiles. 1.3.0-alpha1 Added Prusament PCCF. Increased travel acceleration for Prusa MINI. Updated start g-code for Prusa MINI. Added multiple add:north and Extrudr filament profiles. Updated Z travel speed values. diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 3bc4a039bb..3dd9100570 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -5,7 +5,7 @@ name = Prusa Research # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 1.4.0-alpha0 +config_version = 1.4.0-alpha2 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/ changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% @@ -5426,13 +5426,6 @@ initial_exposure_time = 25 material_type = Tough material_vendor = Made for Prusa -[sla_material:Prusa Polymers Orange @0.025 SL1S] -inherits = *0.025_sl1s* -exposure_time = 1.8 -initial_exposure_time = 25 -material_type = Tough -material_vendor = Prusa Polymers - [sla_material:3DM-ABS Orange @0.025 SL1S] inherits = *0.025_sl1s* exposure_time = 1.8 @@ -5461,6 +5454,20 @@ initial_exposure_time = 25 material_type = Tough material_vendor = Peopoly +[sla_material:Peopoly Deft White @0.025 SL1S] +inherits = *0.025_sl1s* +exposure_time = 1.8 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Peopoly + +[sla_material:Peopoly Neo Clear @0.025 SL1S] +inherits = *0.025_sl1s* +exposure_time = 1.8 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Peopoly + ## 0.05 SL1S [sla_material:Prusa Orange Tough @0.05 SL1S] @@ -5547,13 +5554,6 @@ initial_exposure_time = 25 material_type = Tough material_vendor = Made for Prusa -[sla_material:Prusa Polymers Orange @0.05 SL1S] -inherits = *0.05_sl1s* -exposure_time = 2 -initial_exposure_time = 25 -material_type = Tough -material_vendor = Prusa Polymers - [sla_material:3DM-ABS Orange @0.05 SL1S] inherits = *0.05_sl1s* exposure_time = 2.6 @@ -5582,6 +5582,20 @@ initial_exposure_time = 25 material_type = Tough material_vendor = Peopoly +[sla_material:Peopoly Deft White @0.05 SL1S] +inherits = *0.05_sl1s* +exposure_time = 2 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Peopoly + +[sla_material:Peopoly Neo Clear @0.05 SL1S] +inherits = *0.05_sl1s* +exposure_time = 2 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Peopoly + ## 0.1 SL1S [sla_material:Prusa Orange Tough @0.1 SL1S] @@ -5668,13 +5682,6 @@ initial_exposure_time = 25 material_type = Tough material_vendor = Made for Prusa -[sla_material:Prusa Polymers Orange @0.1 SL1S] -inherits = *0.1_sl1s* -exposure_time = 2.6 -initial_exposure_time = 25 -material_type = Tough -material_vendor = Prusa Polymers - [sla_material:3DM-ABS Orange @0.1 SL1S] inherits = *0.1_sl1s* exposure_time = 3 @@ -5703,6 +5710,20 @@ initial_exposure_time = 25 material_type = Tough material_vendor = Peopoly +[sla_material:Peopoly Deft White @0.1 SL1S] +inherits = *0.1_sl1s* +exposure_time = 2.6 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Peopoly + +[sla_material:Peopoly Neo Clear @0.1 SL1S] +inherits = *0.1_sl1s* +exposure_time = 2.6 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Peopoly + [printer:*common*] printer_technology = FFF bed_shape = 0x0,250x0,250x210,0x210 @@ -6330,8 +6351,8 @@ machine_max_acceleration_e = 5000 machine_max_acceleration_extruding = 1250 machine_max_acceleration_retracting = 1250 machine_max_acceleration_travel = 2500 -machine_max_acceleration_x = 1250 -machine_max_acceleration_y = 1250 +machine_max_acceleration_x = 2500 +machine_max_acceleration_y = 2500 machine_max_acceleration_z = 400 machine_max_feedrate_e = 80 machine_max_feedrate_x = 180 diff --git a/src/libnest2d/include/libnest2d/utils/rotcalipers.hpp b/src/libnest2d/include/libnest2d/utils/rotcalipers.hpp index 76f91ba0cf..e703a38015 100644 --- a/src/libnest2d/include/libnest2d/utils/rotcalipers.hpp +++ b/src/libnest2d/include/libnest2d/utils/rotcalipers.hpp @@ -90,12 +90,29 @@ inline R rectarea(const Pt& w, const std::array& rect) return rectarea(w, *rect[0], *rect[1], *rect[2], *rect[3]); } +template, class R = TCompute> +inline R rectarea(const Pt& w, // the axis + const Unit& a, + const Unit& b) +{ + R m = R(a) / pl::magnsq(w); + m = m * b; + return m; +}; + +template +inline R rectarea(const RotatedBox &rb) +{ + return rectarea(rb.axis(), rb.bottom_extent(), rb.right_extent()); +}; + // This function is only applicable to counter-clockwise oriented convex // polygons where only two points can be collinear witch each other. -template , - class Ratio = TCompute> -RotatedBox, Unit> minAreaBoundingBox(const RawShape& sh) +template , + class Ratio = TCompute, + class VisitFn> +void rotcalipers(const RawShape& sh, VisitFn &&visitfn) { using Point = TPoint; using Iterator = typename TContour::const_iterator; @@ -104,21 +121,21 @@ RotatedBox, Unit> minAreaBoundingBox(const RawShape& sh) // Get the first and the last vertex iterator auto first = sl::cbegin(sh); auto last = std::prev(sl::cend(sh)); - + // Check conditions and return undefined box if input is not sane. - if(last == first) return {}; + if(last == first) return; if(getX(*first) == getX(*last) && getY(*first) == getY(*last)) --last; - if(last - first < 2) return {}; - + if(last - first < 2) return; + RawShape shcpy; // empty at this point - { + { Point p = *first, q = *std::next(first), r = *last; - + // Determine orientation from first 3 vertex (should be consistent) Unit d = (Unit(getY(q)) - getY(p)) * (Unit(getX(r)) - getX(p)) - (Unit(getX(q)) - getX(p)) * (Unit(getY(r)) - getY(p)); - - if(d > 0) { + + if(d > 0) { // The polygon is clockwise. A flip is needed (for now) sl::reserve(shcpy, last - first); auto it = last; while(it != first) sl::addVertex(shcpy, *it--); @@ -126,69 +143,69 @@ RotatedBox, Unit> minAreaBoundingBox(const RawShape& sh) first = sl::cbegin(shcpy); last = std::prev(sl::cend(shcpy)); } } - + // Cyclic iterator increment auto inc = [&first, &last](Iterator& it) { - if(it == last) it = first; else ++it; + if(it == last) it = first; else ++it; }; - + // Cyclic previous iterator - auto prev = [&first, &last](Iterator it) { - return it == first ? last : std::prev(it); + auto prev = [&first, &last](Iterator it) { + return it == first ? last : std::prev(it); }; - + // Cyclic next iterator auto next = [&first, &last](Iterator it) { - return it == last ? first : std::next(it); + return it == last ? first : std::next(it); }; - - // Establish initial (axis aligned) rectangle support verices by determining + + // Establish initial (axis aligned) rectangle support verices by determining // polygon extremes: - + auto it = first; Iterator minX = it, maxX = it, minY = it, maxY = it; - + do { // Linear walk through the vertices and save the extreme positions - + Point v = *it, d = v - *minX; if(getX(d) < 0 || (getX(d) == 0 && getY(d) < 0)) minX = it; - + d = v - *maxX; if(getX(d) > 0 || (getX(d) == 0 && getY(d) > 0)) maxX = it; - + d = v - *minY; if(getY(d) < 0 || (getY(d) == 0 && getX(d) > 0)) minY = it; - + d = v - *maxY; if(getY(d) > 0 || (getY(d) == 0 && getX(d) < 0)) maxY = it; - + } while(++it != std::next(last)); - + // Update the vertices defining the bounding rectangle. The rectangle with - // the smallest rotation is selected and the supporting vertices are + // the smallest rotation is selected and the supporting vertices are // returned in the 'rect' argument. auto update = [&next, &inc] - (const Point& w, std::array& rect) + (const Point& w, std::array& rect) { Iterator B = rect[0], Bn = next(B); Iterator R = rect[1], Rn = next(R); Iterator T = rect[2], Tn = next(T); Iterator L = rect[3], Ln = next(L); - + Point b = *Bn - *B, r = *Rn - *R, t = *Tn - *T, l = *Ln - *L; Point pw = perp(w); using Pt = Point; - + Unit dotwpb = dot( w, b), dotwpr = dot(-pw, r); Unit dotwpt = dot(-w, t), dotwpl = dot( pw, l); Unit dw = magnsq(w); - + std::array angles; angles[0] = (Ratio(dotwpb) / magnsq(b)) * dotwpb; angles[1] = (Ratio(dotwpr) / magnsq(r)) * dotwpr; angles[2] = (Ratio(dotwpt) / magnsq(t)) * dotwpt; angles[3] = (Ratio(dotwpl) / magnsq(l)) * dotwpl; - + using AngleIndex = std::pair; std::vector A; A.reserve(4); @@ -196,65 +213,84 @@ RotatedBox, Unit> minAreaBoundingBox(const RawShape& sh) if(rect[i] != rect[j] && angles[i] < dw) { auto iv = std::make_pair(angles[i], i); auto it = std::lower_bound(A.begin(), A.end(), iv, - [](const AngleIndex& ai, - const AngleIndex& aj) - { - return ai.first > aj.first; + [](const AngleIndex& ai, + const AngleIndex& aj) + { + return ai.first > aj.first; }); - + A.insert(it, iv); } } - + // The polygon is supposed to be a rectangle. if(A.empty()) return false; - + auto amin = A.front().first; auto imin = A.front().second; for(auto& a : A) if(a.first == amin) inc(rect[a.second]); - + std::rotate(rect.begin(), rect.begin() + imin, rect.end()); - + return true; }; - + Point w(1, 0); - Point w_min = w; - Ratio minarea((Unit(getX(*maxX)) - getX(*minX)) * - (Unit(getY(*maxY)) - getY(*minY))); - std::array rect = {minY, maxX, maxY, minX}; - std::array minrect = rect; - + + { + Unit a = dot(w, *rect[1] - *rect[3]); + Unit b = dot(-perp(w), *rect[2] - *rect[0]); + if (!visitfn(RotatedBox{w, a, b})) + return; + } + // An edge might be examined twice in which case the algorithm terminates. size_t c = 0, count = last - first + 1; std::vector edgemask(count, false); - - while(c++ < count) - { + + while(c++ < count) + { // Update the support vertices, if cannot be updated, break the cycle. if(! update(w, rect)) break; - + size_t eidx = size_t(rect[0] - first); - + if(edgemask[eidx]) break; edgemask[eidx] = true; - + // get the unnormalized direction vector w = *rect[0] - *prev(rect[0]); - - // get the area of the rotated rectangle - Ratio rarea = rectarea(w, rect); - - // Update min area and the direction of the min bounding box; - if(rarea <= minarea) { w_min = w; minarea = rarea; minrect = rect; } + + Unit a = dot(w, *rect[1] - *rect[3]); + Unit b = dot(-perp(w), *rect[2] - *rect[0]); + if (!visitfn(RotatedBox{w, a, b})) + break; } - - Unit a = dot(w_min, *minrect[1] - *minrect[3]); - Unit b = dot(-perp(w_min), *minrect[2] - *minrect[0]); - RotatedBox bb(w_min, a, b); - - return bb; +} + +// This function is only applicable to counter-clockwise oriented convex +// polygons where only two points can be collinear witch each other. +template , + class Ratio = TCompute> +RotatedBox, Unit> minAreaBoundingBox(const S& sh) +{ + RotatedBox, Unit> minbox; + Ratio minarea = std::numeric_limits::max(); + auto minfn = [&minarea, &minbox](const RotatedBox, Unit> &rbox){ + Ratio area = rectarea(rbox); + if (area <= minarea) { + minarea = area; + minbox = rbox; + } + + return true; // continue search + }; + + rotcalipers(sh, minfn); + + return minbox; } template Radians minAreaBoundingBoxRotation(const RawShape& sh) @@ -262,7 +298,75 @@ template Radians minAreaBoundingBoxRotation(const RawShape& sh) return minAreaBoundingBox(sh).angleToX(); } +// Function to find a rotation for a shape that makes it fit into a box. +// +// The method is based on finding a pair of rotations from the rotating calipers +// algorithm such that the aspect ratio is changing from being smaller than +// that of the target to being bigger or vice versa. So that the correct +// AR is somewhere between the obtained pair of angles. Then bisecting that +// interval is sufficient to find the correct angle. +// +// The argument eps is the absolute error limit for the searched angle interval. +template, class Ratio = TCompute> +Radians fitIntoBoxRotation(const S &shape, const _Box> &box, Radians eps = 1e-4) +{ + constexpr auto get_aspect_r = [](const auto &b) -> double { + return double(b.width()) / b.height(); + }; + auto aspect_r = get_aspect_r(box); + + RotatedBox, Unit> prev_rbox; + Radians a_from = 0., a_to = 0.; + auto visitfn = [&](const RotatedBox, Unit> &rbox) { + bool lower_prev = get_aspect_r(prev_rbox) < aspect_r; + bool lower_current = get_aspect_r(rbox) < aspect_r; + + if (lower_prev != lower_current) { + a_from = prev_rbox.angleToX(); + a_to = rbox.angleToX(); + return false; + } + + return true; + }; + + rotcalipers(shape, visitfn); + + auto rot_shape_bb = [&shape](Radians r) { + auto s = shape; + sl::rotate(s, r); + return sl::boundingBox(s); + }; + + auto rot_aspect_r = [&rot_shape_bb, &get_aspect_r](Radians r) { + return get_aspect_r(rot_shape_bb(r)); + }; + + // Lets bisect the retrieved interval where the correct aspect ratio is. + double ar_from = rot_aspect_r(a_from); + auto would_fit = [&box](const _Box> &b) { + return b.width() < box.width() && b.height() < box.height(); + }; + + Radians middle = (a_from + a_to) / 2.; + _Box> box_middle = rot_shape_bb(middle); + while (!would_fit(box_middle) && std::abs(a_to - a_from) > eps) + { + double ar_middle = get_aspect_r(box_middle); + if ((ar_from < aspect_r) != (ar_middle < aspect_r)) + a_to = middle; + else + a_from = middle; + + ar_from = rot_aspect_r(a_from); + middle = (a_from + a_to) / 2.; + box_middle = rot_shape_bb(middle); + } + + return middle; } +} // namespace libnest2d + #endif // ROTCALIPERS_HPP diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 9602ebdbd1..bf2a219d0d 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -379,7 +379,7 @@ public: }); if (stopcond) m_pck.stopCondition(stopcond); - + m_pck.configure(m_pconf); } @@ -472,6 +472,12 @@ template Radians min_area_boundingbox_rotation(const S &sh) .angleToX(); } +template +Radians fit_into_box_rotation(const S &sh, const _Box> &box) +{ + return fitIntoBoxRotation, boost::rational>(sh, box); +} + template // Arrange for arbitrary bin type void _arrange( std::vector & shapes, @@ -509,10 +515,19 @@ void _arrange( // Use the minimum bounding box rotation as a starting point. // TODO: This only works for convex hull. If we ever switch to concave // polygon nesting, a convex hull needs to be calculated. - if (params.allow_rotations) - for (auto &itm : shapes) + if (params.allow_rotations) { + for (auto &itm : shapes) { itm.rotation(min_area_boundingbox_rotation(itm.rawShape())); + // If the item is too big, try to find a rotation that makes it fit + if constexpr (std::is_same_v) { + auto bb = itm.boundingBox(); + if (bb.width() >= bin.width() || bb.height() >= bin.height()) + itm.rotate(fit_into_box_rotation(itm.transformedShape(), bin)); + } + } + } + arranger(inp.begin(), inp.end()); for (Item &itm : inp) itm.inflate(-infl); } diff --git a/src/libslic3r/Exception.hpp b/src/libslic3r/Exception.hpp index fababa47d5..1b8ad50a77 100644 --- a/src/libslic3r/Exception.hpp +++ b/src/libslic3r/Exception.hpp @@ -15,6 +15,7 @@ class Exception : public std::runtime_error { using std::runtime_error::runtime_ SLIC3R_DERIVE_EXCEPTION(CriticalException, Exception); SLIC3R_DERIVE_EXCEPTION(RuntimeError, CriticalException); SLIC3R_DERIVE_EXCEPTION(LogicError, CriticalException); +SLIC3R_DERIVE_EXCEPTION(HardCrash, CriticalException); SLIC3R_DERIVE_EXCEPTION(InvalidArgument, LogicError); SLIC3R_DERIVE_EXCEPTION(OutOfRange, LogicError); SLIC3R_DERIVE_EXCEPTION(IOError, CriticalException); diff --git a/src/libslic3r/Execution/ExecutionTBB.hpp b/src/libslic3r/Execution/ExecutionTBB.hpp index cf6373c466..2250b8e322 100644 --- a/src/libslic3r/Execution/ExecutionTBB.hpp +++ b/src/libslic3r/Execution/ExecutionTBB.hpp @@ -1,8 +1,9 @@ #ifndef EXECUTIONTBB_HPP #define EXECUTIONTBB_HPP +#include + #include -#include #include #include #include @@ -34,7 +35,7 @@ private: public: using SpinningMutex = tbb::spin_mutex; - using BlockingMutex = tbb::mutex; + using BlockingMutex = std::mutex; template static void for_each(const ExecutionTBB &, diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index f58ae0bfe0..599ac47845 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -208,16 +208,20 @@ static bool _cgal_intersection(CGALMesh &A, CGALMesh &B, CGALMesh &R) template void _cgal_do(Op &&op, CGALMesh &A, CGALMesh &B) { bool success = false; + bool hw_fail = false; try { CGALMesh result; try_catch_signal({SIGSEGV, SIGFPE}, [&success, &A, &B, &result, &op] { success = op(A, B, result); - }, [&] { success = false; }); + }, [&] { hw_fail = true; }); A = std::move(result); // In-place operation does not work } catch (...) { success = false; } + if (hw_fail) + throw Slic3r::HardCrash("CGAL mesh boolean operation crashed."); + if (! success) throw Slic3r::RuntimeError("CGAL mesh boolean operation failed."); } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 4561de247d..dca11a9209 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -36,7 +36,7 @@ PrintRegion::PrintRegion(PrintRegionConfig &&config) : PrintRegion(std::move(con void Print::clear() { - tbb::mutex::scoped_lock lock(this->state_mutex()); + std::scoped_lock lock(this->state_mutex()); // The following call should stop background processing if it is running. this->invalidate_all_steps(); for (PrintObject *object : m_objects) @@ -252,7 +252,7 @@ bool Print::is_step_done(PrintObjectStep step) const { if (m_objects.empty()) return false; - tbb::mutex::scoped_lock lock(this->state_mutex()); + std::scoped_lock lock(this->state_mutex()); for (const PrintObject *object : m_objects) if (! object->is_step_done_unguarded(step)) return false; diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index 698edbf3b6..a3b96bfe14 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -915,7 +915,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ update_apply_status(false); // Grab the lock for the Print / PrintObject milestones. - tbb::mutex::scoped_lock lock(this->state_mutex()); + std::scoped_lock lock(this->state_mutex()); // The following call may stop the background processing. if (! print_diff.empty()) diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp index d129aaad2b..c4b6b7ccea 100644 --- a/src/libslic3r/PrintBase.cpp +++ b/src/libslic3r/PrintBase.cpp @@ -104,7 +104,7 @@ void PrintBase::status_update_warnings(int step, PrintStateBase::WarningLevel /* printf("%s warning: %s\n", print_object ? "print_object" : "print", message.c_str()); } -tbb::mutex& PrintObjectBase::state_mutex(PrintBase *print) +std::mutex& PrintObjectBase::state_mutex(PrintBase *print) { return print->state_mutex(); } diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index e0aa56ba5a..8cb997c771 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -6,12 +6,8 @@ #include #include #include - -// 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 +#include #include "ObjectID.hpp" #include "Model.hpp" @@ -84,23 +80,23 @@ class PrintState : public PrintStateBase public: PrintState() {} - StateWithTimeStamp state_with_timestamp(StepType step, tbb::mutex &mtx) const { - tbb::mutex::scoped_lock lock(mtx); + StateWithTimeStamp state_with_timestamp(StepType step, std::mutex &mtx) const { + std::scoped_lock lock(mtx); StateWithTimeStamp state = m_state[step]; return state; } - StateWithWarnings state_with_warnings(StepType step, tbb::mutex &mtx) const { - tbb::mutex::scoped_lock lock(mtx); + StateWithWarnings state_with_warnings(StepType step, std::mutex &mtx) const { + std::scoped_lock lock(mtx); StateWithWarnings state = m_state[step]; return state; } - bool is_started(StepType step, tbb::mutex &mtx) const { + bool is_started(StepType step, std::mutex &mtx) const { return this->state_with_timestamp(step, mtx).state == STARTED; } - bool is_done(StepType step, tbb::mutex &mtx) const { + bool is_done(StepType step, std::mutex &mtx) const { return this->state_with_timestamp(step, mtx).state == DONE; } @@ -121,8 +117,8 @@ public: // This is necessary to block until the Print::apply() updates its state, which may // influence the processing step being entered. template - bool set_started(StepType step, tbb::mutex &mtx, ThrowIfCanceled throw_if_canceled) { - tbb::mutex::scoped_lock lock(mtx); + bool set_started(StepType step, std::mutex &mtx, ThrowIfCanceled throw_if_canceled) { + std::scoped_lock lock(mtx); // If canceled, throw before changing the step state. throw_if_canceled(); #ifndef NDEBUG @@ -154,8 +150,8 @@ public: // Timestamp when this stepentered the DONE state. // bool indicates whether the UI has to update the slicing warnings of this step or not. template - std::pair set_done(StepType step, tbb::mutex &mtx, ThrowIfCanceled throw_if_canceled) { - tbb::mutex::scoped_lock lock(mtx); + std::pair set_done(StepType step, std::mutex &mtx, ThrowIfCanceled throw_if_canceled) { + std::scoped_lock lock(mtx); // If canceled, throw before changing the step state. throw_if_canceled(); assert(m_state[step].state == STARTED); @@ -266,9 +262,9 @@ public: // Return value: // Current milestone (StepType). // bool indicates whether the UI has to be updated or not. - std::pair active_step_add_warning(PrintStateBase::WarningLevel warning_level, const std::string &message, int message_id, tbb::mutex &mtx) + std::pair active_step_add_warning(PrintStateBase::WarningLevel warning_level, const std::string &message, int message_id, std::mutex &mtx) { - tbb::mutex::scoped_lock lock(mtx); + std::scoped_lock lock(mtx); assert(m_step_active != -1); StateWithWarnings &state = m_state[m_step_active]; assert(state.state == STARTED); @@ -314,7 +310,7 @@ protected: PrintObjectBase(ModelObject *model_object) : m_model_object(model_object) {} virtual ~PrintObjectBase() {} // Declared here to allow access from PrintBase through friendship. - static tbb::mutex& state_mutex(PrintBase *print); + static std::mutex& state_mutex(PrintBase *print); static std::function cancel_callback(PrintBase *print); // Notify UI about a new warning of a milestone "step" on this PrintObjectBase. // The UI will be notified by calling a status callback registered on print. @@ -475,7 +471,7 @@ protected: friend class PrintObjectBase; friend class BackgroundSlicingProcess; - tbb::mutex& state_mutex() const { return m_state_mutex; } + std::mutex& state_mutex() const { return m_state_mutex; } std::function cancel_callback() { return m_cancel_callback; } void call_cancel_callback() { m_cancel_callback(); } // Notify UI about a new warning of a milestone "step" on this PrintBase. @@ -502,7 +498,7 @@ protected: status_callback_type m_status_callback; private: - tbb::atomic m_cancel_status; + std::atomic m_cancel_status; // Callback to be evoked to stop the background processing before a state is updated. cancel_callback_type m_cancel_callback = [](){}; @@ -510,7 +506,7 @@ 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_state_mutex; + mutable std::mutex m_state_mutex; friend PrintTryCancel; }; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index eb46537ec7..ec3f4f92a2 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -22,7 +22,6 @@ #include #include -#include #include diff --git a/src/libslic3r/SLA/SupportTree.cpp b/src/libslic3r/SLA/SupportTree.cpp index d9f8958a21..a0f88ed608 100644 --- a/src/libslic3r/SLA/SupportTree.cpp +++ b/src/libslic3r/SLA/SupportTree.cpp @@ -17,9 +17,6 @@ #include #include #include -#include -#include -#include #include //! macro used to mark string used at localization, diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 27423793ee..0b9dde1228 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -19,8 +19,6 @@ #include #endif -//#include //#include "tbb/mutex.h" - #include "I18N.hpp" //! macro used to mark string used at localization, @@ -118,7 +116,7 @@ bool validate_pad(const indexed_triangle_set &pad, const sla::PadConfig &pcfg) void SLAPrint::clear() { - tbb::mutex::scoped_lock lock(this->state_mutex()); + std::scoped_lock lock(this->state_mutex()); // The following call should stop background processing if it is running. this->invalidate_all_steps(); for (SLAPrintObject *object : m_objects) @@ -212,7 +210,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con update_apply_status(false); // Grab the lock for the Print / PrintObject milestones. - tbb::mutex::scoped_lock lock(this->state_mutex()); + std::scoped_lock lock(this->state_mutex()); // The following call may stop the background processing. bool invalidate_all_model_objects = false; @@ -514,7 +512,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con void SLAPrint::set_task(const TaskParams ¶ms) { // Grab the lock for the Print / PrintObject milestones. - tbb::mutex::scoped_lock lock(this->state_mutex()); + std::scoped_lock lock(this->state_mutex()); int n_object_steps = int(params.to_object_step) + 1; if (n_object_steps == 0) @@ -884,7 +882,7 @@ bool SLAPrint::is_step_done(SLAPrintObjectStep step) const { if (m_objects.empty()) return false; - tbb::mutex::scoped_lock lock(this->state_mutex()); + std::scoped_lock lock(this->state_mutex()); for (const SLAPrintObject *object : m_objects) if (! object->is_step_done_unguarded(step)) return false; diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index b267116e98..4a8d0d4bef 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -453,8 +453,7 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) sla::remove_inside_triangles(mesh_view, interior, exclude_mask); } - - } catch (const std::runtime_error &) { + } catch (const Slic3r::RuntimeError &) { throw Slic3r::SlicingError(L( "Drilling holes into the mesh failed. " "This is usually caused by broken model. Try to fix it first.")); diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 5eb9c9433a..8fb8756eb7 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -14,7 +14,6 @@ #include #include -#include #include #include diff --git a/src/libslic3r/Thread.cpp b/src/libslic3r/Thread.cpp index d203acb90e..51dec618e4 100644 --- a/src/libslic3r/Thread.cpp +++ b/src/libslic3r/Thread.cpp @@ -9,10 +9,10 @@ #include #include #include +#include +#include #include -#include #include -#include #include "Thread.hpp" @@ -206,13 +206,13 @@ void name_tbb_thread_pool_threads() nthreads = 1; #endif - if (nthreads != nthreads_hw) - new tbb::task_scheduler_init(int(nthreads)); + if (nthreads != nthreads_hw) + tbb::global_control(tbb::global_control::max_allowed_parallelism, nthreads); std::atomic nthreads_running(0); std::condition_variable cv; std::mutex cv_m; - auto master_thread_id = tbb::this_tbb_thread::get_id(); + auto master_thread_id = std::this_thread::get_id(); tbb::parallel_for( tbb::blocked_range(0, nthreads, 1), [&nthreads_running, nthreads, &master_thread_id, &cv, &cv_m](const tbb::blocked_range &range) { @@ -226,7 +226,7 @@ void name_tbb_thread_pool_threads() std::unique_lock lk(cv_m); cv.wait(lk, [&nthreads_running, nthreads]{return nthreads_running == nthreads;}); } - auto thread_id = tbb::this_tbb_thread::get_id(); + auto thread_id = std::this_thread::get_id(); if (thread_id == master_thread_id) { // The calling thread runs the 0'th task. assert(range.begin() == 0); diff --git a/src/libslic3r/pchheader.hpp b/src/libslic3r/pchheader.hpp index b55755b892..ad22b855a7 100644 --- a/src/libslic3r/pchheader.hpp +++ b/src/libslic3r/pchheader.hpp @@ -100,12 +100,9 @@ #include #include -#include #include #include -#include #include -#include #include #include diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index c6e409edc9..763be8af74 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -43,7 +43,7 @@ #include #include -#include +#include #if defined(__linux__) || defined(__GNUC__ ) #include @@ -118,9 +118,7 @@ void trace(unsigned int level, const char *message) void disable_multi_threading() { // Disable parallelization so the Shiny profiler works - static tbb::task_scheduler_init *tbb_init = nullptr; - if (tbb_init == nullptr) - tbb_init = new tbb::task_scheduler_init(1); + tbb::global_control(tbb::global_control::max_allowed_parallelism, 1); } static std::string g_var_dir; diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 8b3b375131..5503eefbfa 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -2,6 +2,7 @@ #include "GUI_App.hpp" #include "GUI.hpp" #include "MainFrame.hpp" +#include "format.hpp" #include #include @@ -74,11 +75,15 @@ std::pair SlicingProcessCompletedEvent::format_error_message( bool monospace = false; try { this->rethrow_exception(); - } catch (const std::bad_alloc& ex) { + } catch (const std::bad_alloc &ex) { wxString errmsg = GUI::from_u8((boost::format(_utf8(L("%s has encountered an error. It was likely caused by running out of memory. " "If you are sure you have enough RAM on your system, this may also be a bug and we would " "be glad if you reported it."))) % SLIC3R_APP_NAME).str()); error = std::string(errmsg.ToUTF8()) + "\n\n" + std::string(ex.what()); + } catch (const HardCrash &ex) { + error = GUI::format("PrusaSlicer has encountered a fatal error: \"%1%\"", ex.what()) + "\n\n" + + _u8L("Please save your project and restart PrusaSlicer. " + "We would be glad if you reported the issue."); } catch (PlaceholderParserError &ex) { error = ex.what(); monospace = true; @@ -277,19 +282,11 @@ void BackgroundSlicingProcess::thread_proc() m_state = STATE_RUNNING; lck.unlock(); std::exception_ptr exception; - try { - assert(m_print != nullptr); - switch(m_print->technology()) { - case ptFFF: this->process_fff(); break; - case ptSLA: this->process_sla(); break; - default: m_print->process(); break; - } - } catch (CanceledException & /* ex */) { - // Canceled, this is all right. - assert(m_print->canceled()); - } catch (...) { - exception = std::current_exception(); - } +#ifdef _WIN32 + this->call_process_seh_throw(exception); +#else + this->call_process(exception); +#endif m_print->finalize(); lck.lock(); m_state = m_print->canceled() ? STATE_CANCELED : STATE_FINISHED; @@ -312,7 +309,118 @@ void BackgroundSlicingProcess::thread_proc() // End of the background processing thread. The UI thread should join m_thread now. } -void BackgroundSlicingProcess::thread_proc_safe() +#ifdef _WIN32 +// Only these SEH exceptions will be catched and turned into Slic3r::HardCrash C++ exceptions. +static bool is_win32_seh_harware_exception(unsigned long ex) throw() { + return + ex == STATUS_ACCESS_VIOLATION || + ex == STATUS_DATATYPE_MISALIGNMENT || + ex == STATUS_FLOAT_DIVIDE_BY_ZERO || + ex == STATUS_FLOAT_OVERFLOW || + ex == STATUS_FLOAT_UNDERFLOW || +#ifdef STATUS_FLOATING_RESEVERED_OPERAND + ex == STATUS_FLOATING_RESEVERED_OPERAND || +#endif // STATUS_FLOATING_RESEVERED_OPERAND + ex == STATUS_ILLEGAL_INSTRUCTION || + ex == STATUS_PRIVILEGED_INSTRUCTION || + ex == STATUS_INTEGER_DIVIDE_BY_ZERO || + ex == STATUS_INTEGER_OVERFLOW || + ex == STATUS_STACK_OVERFLOW; +} + +// Rethrow some SEH exceptions as Slic3r::HardCrash C++ exceptions. +static void rethrow_seh_exception(unsigned long win32_seh_catched) +{ + if (win32_seh_catched) { + // Rethrow SEH exception as Slicer::HardCrash. + if (win32_seh_catched == STATUS_ACCESS_VIOLATION || win32_seh_catched == STATUS_DATATYPE_MISALIGNMENT) + throw Slic3r::HardCrash(_u8L("Access violation")); + if (win32_seh_catched == STATUS_ILLEGAL_INSTRUCTION || win32_seh_catched == STATUS_PRIVILEGED_INSTRUCTION) + throw Slic3r::HardCrash(_u8L("Illegal instruction")); + if (win32_seh_catched == STATUS_FLOAT_DIVIDE_BY_ZERO || win32_seh_catched == STATUS_INTEGER_DIVIDE_BY_ZERO) + throw Slic3r::HardCrash(_u8L("Divide by zero")); + if (win32_seh_catched == STATUS_FLOAT_OVERFLOW || win32_seh_catched == STATUS_INTEGER_OVERFLOW) + throw Slic3r::HardCrash(_u8L("Overflow")); + if (win32_seh_catched == STATUS_FLOAT_UNDERFLOW) + throw Slic3r::HardCrash(_u8L("Underflow")); +#ifdef STATUS_FLOATING_RESEVERED_OPERAND + if (win32_seh_catched == STATUS_FLOATING_RESEVERED_OPERAND) + throw Slic3r::HardCrash(_u8L("Floating reserved operand")); +#endif // STATUS_FLOATING_RESEVERED_OPERAND + if (win32_seh_catched == STATUS_STACK_OVERFLOW) + throw Slic3r::HardCrash(_u8L("Stack overflow")); + } +} + +// Wrapper for Win32 structured exceptions. Win32 structured exception blocks and C++ exception blocks cannot be mixed in the same function. +unsigned long BackgroundSlicingProcess::call_process_seh(std::exception_ptr &ex) throw() +{ + unsigned long win32_seh_catched = 0; + __try { + this->call_process(ex); + } __except (is_win32_seh_harware_exception(GetExceptionCode())) { + win32_seh_catched = GetExceptionCode(); + } + return win32_seh_catched; +} +void BackgroundSlicingProcess::call_process_seh_throw(std::exception_ptr &ex) throw() +{ + unsigned long win32_seh_catched = this->call_process_seh(ex); + if (win32_seh_catched) { + // Rethrow SEH exception as Slicer::HardCrash. + try { + rethrow_seh_exception(win32_seh_catched); + } catch (...) { + ex = std::current_exception(); + } + } +} +#endif // _WIN32 + +void BackgroundSlicingProcess::call_process(std::exception_ptr &ex) throw() +{ + try { + assert(m_print != nullptr); + switch (m_print->technology()) { + case ptFFF: this->process_fff(); break; + case ptSLA: this->process_sla(); break; + default: m_print->process(); break; + } + } catch (CanceledException& /* ex */) { + // Canceled, this is all right. + assert(m_print->canceled()); + ex = std::current_exception(); + } catch (...) { + ex = std::current_exception(); + } +} + +#ifdef _WIN32 +unsigned long BackgroundSlicingProcess::thread_proc_safe_seh() throw() +{ + unsigned long win32_seh_catched = 0; + __try { + this->thread_proc_safe(); + } __except (is_win32_seh_harware_exception(GetExceptionCode())) { + win32_seh_catched = GetExceptionCode(); + } + return win32_seh_catched; +} +void BackgroundSlicingProcess::thread_proc_safe_seh_throw() throw() +{ + unsigned long win32_seh_catched = this->thread_proc_safe_seh(); + if (win32_seh_catched) { + // Rethrow SEH exception as Slicer::HardCrash. + try { + rethrow_seh_exception(win32_seh_catched); + } catch (...) { + wxTheApp->OnUnhandledException(); + } + } +} +#endif // _WIN32 + +void BackgroundSlicingProcess::thread_proc_safe() throw() { try { this->thread_proc(); @@ -349,7 +457,13 @@ bool BackgroundSlicingProcess::start() if (m_state == STATE_INITIAL) { // The worker thread is not running yet. Start it. assert(! m_thread.joinable()); - m_thread = create_thread([this]{this->thread_proc_safe();}); + m_thread = create_thread([this]{ +#ifdef _WIN32 + this->thread_proc_safe_seh_throw(); +#else // _WIN32 + this->thread_proc_safe(); +#endif // _WIN32 + }); // Wait until the worker thread is ready to execute the background processing task. m_condition.wait(lck, [this](){ return m_state == STATE_IDLE; }); } @@ -531,7 +645,7 @@ void BackgroundSlicingProcess::schedule_export(const std::string &path, bool exp return; // Guard against entering the export step before changing the export path. - tbb::mutex::scoped_lock lock(m_print->state_mutex()); + std::scoped_lock lock(m_print->state_mutex()); this->invalidate_step(bspsGCodeFinalize); m_export_path = path; m_export_path_on_removable_media = export_path_on_removable_media; @@ -544,7 +658,7 @@ void BackgroundSlicingProcess::schedule_upload(Slic3r::PrintHostJob upload_job) return; // Guard against entering the export step before changing the export path. - tbb::mutex::scoped_lock lock(m_print->state_mutex()); + std::scoped_lock lock(m_print->state_mutex()); this->invalidate_step(bspsGCodeFinalize); m_export_path.clear(); m_upload_job = std::move(upload_job); @@ -557,7 +671,7 @@ void BackgroundSlicingProcess::reset_export() m_export_path.clear(); m_export_path_on_removable_media = false; // invalidate_step expects the mutex to be locked. - tbb::mutex::scoped_lock lock(m_print->state_mutex()); + std::scoped_lock lock(m_print->state_mutex()); this->invalidate_step(bspsGCodeFinalize); } } diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index 12bf6fe021..6f5cd88521 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -174,7 +174,16 @@ public: private: void thread_proc(); - void thread_proc_safe(); + // Calls thread_proc(), catches all C++ exceptions and shows them using wxApp::OnUnhandledException(). + void thread_proc_safe() throw(); +#ifdef _WIN32 + // Wrapper for Win32 structured exceptions. Win32 structured exception blocks and C++ exception blocks cannot be mixed in the same function. + // Catch a SEH exception and return its ID or zero if no SEH exception has been catched. + unsigned long thread_proc_safe_seh() throw(); + // Calls thread_proc_safe_seh(), rethrows a Slic3r::HardCrash exception based on SEH exception + // returned by thread_proc_safe_seh() and lets wxApp::OnUnhandledException() display it. + void thread_proc_safe_seh_throw() throw(); +#endif // _WIN32 void join_background_thread(); // To be called by Print::apply() through the Print::m_cancel_callback to stop the background // processing before changing any data of running or finalized milestones. @@ -187,6 +196,20 @@ private: // Temporary: for mimicking the fff file export behavior with the raster output void process_sla(); + // Call Print::process() and catch all exceptions into ex, thus no exception could be thrown + // by this method. This exception behavior is required to combine C++ exceptions with Win32 SEH exceptions + // on the same thread. + void call_process(std::exception_ptr &ex) throw(); + +#ifdef _WIN32 + // Wrapper for Win32 structured exceptions. Win32 structured exception blocks and C++ exception blocks cannot be mixed in the same function. + // Catch a SEH exception and return its ID or zero if no SEH exception has been catched. + unsigned long call_process_seh(std::exception_ptr &ex) throw(); + // Calls call_process_seh(), rethrows a Slic3r::HardCrash exception based on SEH exception + // returned by call_process_seh(). + void call_process_seh_throw(std::exception_ptr &ex) throw(); +#endif // _WIN32 + // Currently active print. It is one of m_fff_print and m_sla_print. PrintBase *m_print = nullptr; // Non-owned pointers to Print instances. diff --git a/src/slic3r/GUI/InstanceCheck.hpp b/src/slic3r/GUI/InstanceCheck.hpp index 907b831929..10ccf7b925 100644 --- a/src/slic3r/GUI/InstanceCheck.hpp +++ b/src/slic3r/GUI/InstanceCheck.hpp @@ -13,7 +13,7 @@ #if __linux__ #include -#include +#include #include #endif // __linux__ diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 3b902879c2..9c7533acd1 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -658,7 +658,7 @@ void MainFrame::init_tabpanel() #endif #if ENABLE_VALIDATE_CUSTOM_GCODE if (int old_selection = e.GetOldSelection(); - old_selection != wxNOT_FOUND && old_selection < m_tabpanel->GetPageCount()) { + old_selection != wxNOT_FOUND && old_selection < static_cast(m_tabpanel->GetPageCount())) { Tab* old_tab = dynamic_cast(m_tabpanel->GetPage(old_selection)); if (old_tab) old_tab->validate_custom_gcodes(); diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 57b82fc782..b89bb454b4 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -66,7 +66,7 @@ void update_maximum(std::atomic& maximum_value, T const& value) noexcept void Mouse3DController::State::append_translation(const Vec3d& translation, size_t input_queue_max_size) { - tbb::mutex::scoped_lock lock(m_input_queue_mutex); + std::scoped_lock lock(m_input_queue_mutex); while (m_input_queue.size() >= input_queue_max_size) m_input_queue.pop_front(); m_input_queue.emplace_back(QueueItem::translation(translation)); @@ -77,7 +77,7 @@ void Mouse3DController::State::append_translation(const Vec3d& translation, size void Mouse3DController::State::append_rotation(const Vec3f& rotation, size_t input_queue_max_size) { - tbb::mutex::scoped_lock lock(m_input_queue_mutex); + std::scoped_lock lock(m_input_queue_mutex); while (m_input_queue.size() >= input_queue_max_size) m_input_queue.pop_front(); m_input_queue.emplace_back(QueueItem::rotation(rotation.cast())); @@ -92,7 +92,7 @@ void Mouse3DController::State::append_rotation(const Vec3f& rotation, size_t inp void Mouse3DController::State::append_button(unsigned int id, size_t /* input_queue_max_size */) { - tbb::mutex::scoped_lock lock(m_input_queue_mutex); + std::scoped_lock lock(m_input_queue_mutex); m_input_queue.emplace_back(QueueItem::buttons(id)); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT update_maximum(input_queue_max_size_achieved, m_input_queue.size()); @@ -274,7 +274,7 @@ void Mouse3DController::device_attached(const std::string &device) m_stop_condition.notify_all(); m_device_str = format_device_string(vid, pid); if (auto it_params = m_params_by_device.find(m_device_str); it_params != m_params_by_device.end()) { - tbb::mutex::scoped_lock lock(m_params_ui_mutex); + std::scoped_lock lock(m_params_ui_mutex); m_params = m_params_ui = it_params->second; } else @@ -290,7 +290,7 @@ void Mouse3DController::device_detached(const std::string& device) int pid = 0; if (sscanf(device.c_str(), "\\\\?\\HID#VID_%x&PID_%x&", &vid, &pid) == 2) { if (std::find(_3DCONNEXION_VENDORS.begin(), _3DCONNEXION_VENDORS.end(), vid) != _3DCONNEXION_VENDORS.end()) { - tbb::mutex::scoped_lock lock(m_params_ui_mutex); + std::scoped_lock lock(m_params_ui_mutex); m_params_by_device[format_device_string(vid, pid)] = m_params_ui; } } @@ -301,12 +301,12 @@ void Mouse3DController::device_detached(const std::string& device) // Filter out mouse scroll events produced by the 3DConnexion driver. bool Mouse3DController::State::process_mouse_wheel() { - tbb::mutex::scoped_lock lock(m_input_queue_mutex); - if (m_mouse_wheel_counter == 0) - // No 3DConnexion rotation has been captured since the last mouse scroll event. + std::scoped_lock lock(m_input_queue_mutex); + if (m_mouse_wheel_counter == 0) + // No 3DConnexion rotation has been captured since the last mouse scroll event. return false; if (std::find_if(m_input_queue.begin(), m_input_queue.end(), [](const QueueItem &item){ return item.is_rotation(); }) != m_input_queue.end()) { - // There is a rotation stored in the queue. Suppress one mouse scroll event. + // There is a rotation stored in the queue. Suppress one mouse scroll event. -- m_mouse_wheel_counter; return true; } @@ -323,7 +323,7 @@ bool Mouse3DController::State::apply(const Mouse3DController::Params ¶ms, Ca std::deque input_queue; { // Atomically move m_input_queue to input_queue. - tbb::mutex::scoped_lock lock(m_input_queue_mutex); + std::scoped_lock lock(m_input_queue_mutex); input_queue = std::move(m_input_queue); m_input_queue.clear(); } @@ -411,7 +411,7 @@ bool Mouse3DController::apply(Camera& camera) #ifdef _WIN32 { - tbb::mutex::scoped_lock lock(m_params_ui_mutex); + std::scoped_lock lock(m_params_ui_mutex); if (m_params_ui_changed) { m_params = m_params_ui; m_params_ui_changed = false; @@ -439,7 +439,7 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const Params params_copy; bool params_changed = false; { - tbb::mutex::scoped_lock lock(m_params_ui_mutex); + std::scoped_lock lock(m_params_ui_mutex); params_copy = m_params_ui; } @@ -557,7 +557,7 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const if (params_changed) { // Synchronize front end parameters to back end. - tbb::mutex::scoped_lock lock(m_params_ui_mutex); + std::scoped_lock lock(m_params_ui_mutex); auto pthis = const_cast(this); #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT if (params_copy.input_queue_max_size != params_copy.input_queue_max_size) @@ -578,7 +578,7 @@ void Mouse3DController::connected(std::string device_name) m_device_str = device_name; // Copy the parameters for m_device_str into the current parameters. if (auto it_params = m_params_by_device.find(m_device_str); it_params != m_params_by_device.end()) { - tbb::mutex::scoped_lock lock(m_params_ui_mutex); + std::scoped_lock lock(m_params_ui_mutex); m_params = m_params_ui = it_params->second; } m_connected = true; @@ -589,7 +589,7 @@ void Mouse3DController::disconnected() // Copy the current parameters for m_device_str into the parameter database. assert(m_connected == ! m_device_str.empty()); if (m_connected) { - tbb::mutex::scoped_lock lock(m_params_ui_mutex); + std::scoped_lock lock(m_params_ui_mutex); m_params_by_device[m_device_str] = m_params_ui; m_device_str.clear(); m_connected = false; @@ -613,7 +613,7 @@ bool Mouse3DController::handle_input(const DataPacketAxis& packet) { // Synchronize parameters between the UI thread and the background thread. //FIXME is this necessary on OSX? Are these notifications triggered from the main thread or from a worker thread? - tbb::mutex::scoped_lock lock(m_params_ui_mutex); + std::scoped_lock lock(m_params_ui_mutex); if (m_params_ui_changed) { m_params = m_params_ui; m_params_ui_changed = false; @@ -721,7 +721,7 @@ void Mouse3DController::run() for (;;) { { - tbb::mutex::scoped_lock lock(m_params_ui_mutex); + std::scoped_lock lock(m_params_ui_mutex); if (m_stop) break; if (m_params_ui_changed) { @@ -986,7 +986,7 @@ bool Mouse3DController::connect_device() #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT // Copy the parameters for m_device_str into the current parameters. if (auto it_params = m_params_by_device.find(m_device_str); it_params != m_params_by_device.end()) { - tbb::mutex::scoped_lock lock(m_params_ui_mutex); + std::scoped_lock lock(m_params_ui_mutex); m_params = m_params_ui = it_params->second; } } @@ -1011,7 +1011,7 @@ void Mouse3DController::disconnect_device() BOOST_LOG_TRIVIAL(info) << "Disconnected device: " << m_device_str; // Copy the current parameters for m_device_str into the parameter database. { - tbb::mutex::scoped_lock lock(m_params_ui_mutex); + std::scoped_lock lock(m_params_ui_mutex); m_params_by_device[m_device_str] = m_params_ui; } m_device_str.clear(); diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 3376ef8140..12e9b7dc9e 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -10,12 +10,12 @@ #include #include +#include #include #include #include #include -#include namespace Slic3r { @@ -85,7 +85,7 @@ class Mouse3DController // m_input_queue is accessed by the background thread and by the UI thread. Access to m_input_queue // is guarded with m_input_queue_mutex. std::deque m_input_queue; - mutable tbb::mutex m_input_queue_mutex; + mutable std::mutex m_input_queue_mutex; #ifdef WIN32 // When the 3Dconnexion driver is running the system gets, by default, mouse wheel events when rotations around the X axis are detected. @@ -112,12 +112,12 @@ class Mouse3DController #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT Vec3d get_first_vector_of_type(unsigned int type) const { - tbb::mutex::scoped_lock lock(m_input_queue_mutex); + std::scoped_lock lock(m_input_queue_mutex); auto it = std::find_if(m_input_queue.begin(), m_input_queue.end(), [type](const QueueItem& item) { return item.type_or_buttons == type; }); return (it == m_input_queue.end()) ? Vec3d::Zero() : it->vector; } size_t input_queue_size_current() const { - tbb::mutex::scoped_lock lock(m_input_queue_mutex); + std::scoped_lock lock(m_input_queue_mutex); return m_input_queue.size(); } std::atomic input_queue_max_size_achieved; @@ -133,7 +133,7 @@ class Mouse3DController // UI thread will read / write this copy. Params m_params_ui; bool m_params_ui_changed { false }; - mutable tbb::mutex m_params_ui_mutex; + mutable std::mutex m_params_ui_mutex; // This is a database of parametes of all 3DConnexion devices ever connected. // This database is loaded from AppConfig on application start and it is stored to AppConfig on application exit. diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index 83f7077b4d..593d8241d4 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -84,7 +84,7 @@ void RemovableDriveManager::eject_drive() this->update(); #endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS BOOST_LOG_TRIVIAL(info) << "Ejecting started"; - tbb::mutex::scoped_lock lock(m_drives_mutex); + std::scoped_lock lock(m_drives_mutex); auto it_drive_data = this->find_last_save_path_drive_data(); if (it_drive_data != m_current_drives.end()) { // get handle to device @@ -130,7 +130,7 @@ std::string RemovableDriveManager::get_removable_drive_path(const std::string &p this->update(); #endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS - tbb::mutex::scoped_lock lock(m_drives_mutex); + std::scoped_lock lock(m_drives_mutex); if (m_current_drives.empty()) return std::string(); std::size_t found = path.find_last_of("\\"); @@ -146,7 +146,7 @@ std::string RemovableDriveManager::get_removable_drive_path(const std::string &p std::string RemovableDriveManager::get_removable_drive_from_path(const std::string& path) { - tbb::mutex::scoped_lock lock(m_drives_mutex); + std::scoped_lock lock(m_drives_mutex); std::size_t found = path.find_last_of("\\"); std::string new_path = path.substr(0, found); int letter = PathGetDriveNumberW(boost::nowide::widen(new_path).c_str()); @@ -287,7 +287,7 @@ void RemovableDriveManager::eject_drive() DriveData drive_data; { - tbb::mutex::scoped_lock lock(m_drives_mutex); + std::scoped_lock lock(m_drives_mutex); auto it_drive_data = this->find_last_save_path_drive_data(); if (it_drive_data == m_current_drives.end()) return; @@ -343,7 +343,7 @@ void RemovableDriveManager::eject_drive() if (success) { // Remove the drive_data from m_current drives, searching by value, not by pointer, as m_current_drives may get modified during // asynchronous execution on m_eject_thread. - tbb::mutex::scoped_lock lock(m_drives_mutex); + std::scoped_lock lock(m_drives_mutex); auto it = std::find(m_current_drives.begin(), m_current_drives.end(), drive_data); if (it != m_current_drives.end()) m_current_drives.erase(it); @@ -363,7 +363,7 @@ std::string RemovableDriveManager::get_removable_drive_path(const std::string &p std::size_t found = path.find_last_of("/"); std::string new_path = found == path.size() - 1 ? path.substr(0, found) : path; - tbb::mutex::scoped_lock lock(m_drives_mutex); + std::scoped_lock lock(m_drives_mutex); for (const DriveData &data : m_current_drives) if (search_for_drives_internal::compare_filesystem_id(new_path, data.path)) return path; @@ -379,7 +379,7 @@ std::string RemovableDriveManager::get_removable_drive_from_path(const std::stri new_path = new_path.substr(0, found); // check if same filesystem - tbb::mutex::scoped_lock lock(m_drives_mutex); + std::scoped_lock lock(m_drives_mutex); for (const DriveData &drive_data : m_current_drives) if (search_for_drives_internal::compare_filesystem_id(new_path, drive_data.path)) return drive_data.path; @@ -454,7 +454,7 @@ RemovableDriveManager::RemovableDrivesStatus RemovableDriveManager::status() RemovableDriveManager::RemovableDrivesStatus out; { - tbb::mutex::scoped_lock lock(m_drives_mutex); + std::scoped_lock lock(m_drives_mutex); out.has_eject = // Cannot control eject on Chromium. platform_flavor() != PlatformFlavor::LinuxOnChromium && @@ -470,17 +470,17 @@ RemovableDriveManager::RemovableDrivesStatus RemovableDriveManager::status() // Update is called from thread_proc() and from most of the public methods on demand. void RemovableDriveManager::update() { - tbb::mutex::scoped_lock inside_update_lock; + std::unique_lock inside_update_lock(m_inside_update_mutex, std::defer_lock); #ifdef _WIN32 // All wake up calls up to now are now consumed when the drive enumeration starts. m_wakeup = false; #endif // _WIN32 - if (inside_update_lock.try_acquire(m_inside_update_mutex)) { + if (inside_update_lock.try_lock()) { // Got the lock without waiting. That means, the update was not running. // Run the update. std::vector current_drives = this->search_for_removable_drives(); // Post update events. - tbb::mutex::scoped_lock lock(m_drives_mutex); + std::scoped_lock lock(m_drives_mutex); std::sort(current_drives.begin(), current_drives.end()); if (current_drives != m_current_drives) { assert(m_callback_evt_handler); @@ -491,7 +491,7 @@ void RemovableDriveManager::update() } else { // Acquiring the m_iniside_update lock failed, therefore another update is running. // Just block until the other instance of update() finishes. - inside_update_lock.acquire(m_inside_update_mutex); + inside_update_lock.lock(); } } diff --git a/src/slic3r/GUI/RemovableDriveManager.hpp b/src/slic3r/GUI/RemovableDriveManager.hpp index f707b40c32..29363647c8 100644 --- a/src/slic3r/GUI/RemovableDriveManager.hpp +++ b/src/slic3r/GUI/RemovableDriveManager.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include // Custom wxWidget events @@ -111,9 +111,9 @@ private: // m_current_drives is guarded by m_drives_mutex // sorted ascending by path std::vector m_current_drives; - mutable tbb::mutex m_drives_mutex; + mutable std::mutex m_drives_mutex; // Locking the update() function to avoid that the function is executed multiple times. - mutable tbb::mutex m_inside_update_mutex; + mutable std::mutex m_inside_update_mutex; // Returns drive path (same as path in DriveData) if exists otherwise empty string. std::string get_removable_drive_from_path(const std::string& path);