diff --git a/src/clipper/clipper.hpp b/src/clipper/clipper.hpp index 1d63616536..d190d09b5d 100644 --- a/src/clipper/clipper.hpp +++ b/src/clipper/clipper.hpp @@ -116,7 +116,8 @@ using DoublePoint = Eigen::Matrix; template using Allocator = tbb::scalable_allocator; -using Path = std::vector>; +//using Allocator = std::allocator; +using Path = std::vector>; using Paths = std::vector>; inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index d83fe4e48e..95a5337185 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -3,6 +3,20 @@ #include "ShortestPath.hpp" #include "Utils.hpp" +// #define CLIPPER_UTILS_TIMING + +#ifdef CLIPPER_UTILS_TIMING + // time limit for one ClipperLib operation (union / diff / offset), in ms + #define CLIPPER_UTILS_TIME_LIMIT_DEFAULT 50 + #include + #include "Timer.hpp" + #define CLIPPER_UTILS_TIME_LIMIT_SECONDS(limit) Timing::TimeLimitAlarm time_limit_alarm(uint64_t(limit) * 1000000000l, BOOST_CURRENT_FUNCTION) + #define CLIPPER_UTILS_TIME_LIMIT_MILLIS(limit) Timing::TimeLimitAlarm time_limit_alarm(uint64_t(limit) * 1000000l, BOOST_CURRENT_FUNCTION) +#else + #define CLIPPER_UTILS_TIME_LIMIT_SECONDS(limit) do {} while(false) + #define CLIPPER_UTILS_TIME_LIMIT_MILLIS(limit) do {} while(false) +#endif // CLIPPER_UTILS_TIMING + // #define CLIPPER_UTILS_DEBUG #ifdef CLIPPER_UTILS_DEBUG @@ -260,6 +274,8 @@ bool has_duplicate_points(const ClipperLib::PolyTree &polytree) template static ClipperLib::Paths raw_offset(PathsProvider &&paths, float offset, ClipperLib::JoinType joinType, double miterLimit) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::ClipperOffset co; ClipperLib::Paths out; out.reserve(paths.size()); @@ -301,6 +317,8 @@ TResult clipper_do( TClip && clip, const ClipperLib::PolyFillType fillType) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::Clipper clipper; clipper.AddPaths(std::forward(subject), ClipperLib::ptSubject, true); clipper.AddPaths(std::forward(clip), ClipperLib::ptClip, true); @@ -330,6 +348,8 @@ TResult clipper_union( // fillType pftNonZero and pftPositive "should" produce the same result for "normalized with implicit union" set of polygons const ClipperLib::PolyFillType fillType = ClipperLib::pftNonZero) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::Clipper clipper; clipper.AddPaths(std::forward(subject), ClipperLib::ptSubject, true); TResult retval; @@ -368,6 +388,8 @@ template<> void remove_outermost_polygon(ClipperLib::PolyT template static TResult shrink_paths(PathsProvider &&paths, float offset, ClipperLib::JoinType joinType, double miterLimit) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + assert(offset > 0); TResult out; if (auto raw = raw_offset(std::forward(paths), - offset, joinType, miterLimit); ! raw.empty()) { @@ -407,6 +429,8 @@ Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, C // returns number of expolygons collected (0 or 1). static int offset_expolygon_inner(const Slic3r::ExPolygon &expoly, const float delta, ClipperLib::JoinType joinType, double miterLimit, ClipperLib::Paths &out) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + // 1) Offset the outer contour. ClipperLib::Paths contours; { @@ -615,6 +639,8 @@ inline ClipperLib::PolyTree clipper_do_polytree( PathProvider2 &&clip, const ClipperLib::PolyFillType fillType) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + // Perform the operation with the output to input_subject. // This pass does not generate a PolyTree, which is a very expensive operation with the current Clipper library // if there are overapping edges. @@ -753,6 +779,8 @@ Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject) template Polylines _clipper_pl_open(ClipperLib::ClipType clipType, PathsProvider1 &&subject, PathsProvider2 &&clip) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::Clipper clipper; clipper.AddPaths(std::forward(subject), ClipperLib::ptSubject, false); clipper.AddPaths(std::forward(clip), ClipperLib::ptClip, true); @@ -938,6 +966,8 @@ Polygons union_pt_chained_outside_in(const Polygons &subject) Polygons simplify_polygons(const Polygons &subject, bool preserve_collinear) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::Paths output; if (preserve_collinear) { ClipperLib::Clipper c; @@ -955,6 +985,8 @@ Polygons simplify_polygons(const Polygons &subject, bool preserve_collinear) ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + if (! preserve_collinear) return union_ex(simplify_polygons(subject, false)); @@ -971,6 +1003,8 @@ ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear Polygons top_level_islands(const Slic3r::Polygons &polygons) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + // init Clipper ClipperLib::Clipper clipper; clipper.Clear(); @@ -994,6 +1028,8 @@ ClipperLib::Paths fix_after_outer_offset( ClipperLib::PolyFillType filltype, // = ClipperLib::pftPositive bool reverse_result) // = false { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::Paths solution; if (! input.empty()) { ClipperLib::Clipper clipper; @@ -1012,6 +1048,8 @@ ClipperLib::Paths fix_after_inner_offset( ClipperLib::PolyFillType filltype, // = ClipperLib::pftNegative bool reverse_result) // = true { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::Paths solution; if (! input.empty()) { ClipperLib::Clipper clipper; @@ -1032,6 +1070,8 @@ ClipperLib::Paths fix_after_inner_offset( ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::vector &deltas, double miter_limit) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + assert(contour.size() == deltas.size()); #ifndef NDEBUG @@ -1172,6 +1212,8 @@ ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::v static void variable_offset_inner_raw(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit, ClipperLib::Paths &contours, ClipperLib::Paths &holes) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + #ifndef NDEBUG // Verify that the deltas are all non positive. for (const std::vector &ds : deltas) @@ -1205,6 +1247,8 @@ static void variable_offset_inner_raw(const ExPolygon &expoly, const std::vector Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::Paths contours, holes; variable_offset_inner_raw(expoly, deltas, miter_limit, contours, holes); @@ -1227,6 +1271,8 @@ Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::Paths contours, holes; variable_offset_inner_raw(expoly, deltas, miter_limit, contours, holes); @@ -1253,6 +1299,8 @@ ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit, ClipperLib::Paths &contours, ClipperLib::Paths &holes) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + #ifndef NDEBUG // Verify that the deltas are all non positive. for (const std::vector &ds : deltas) @@ -1288,6 +1336,8 @@ static void variable_offset_outer_raw(const ExPolygon &expoly, const std::vector Polygons variable_offset_outer(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::Paths contours, holes; variable_offset_outer_raw(expoly, deltas, miter_limit, contours, holes); @@ -1309,6 +1359,8 @@ Polygons variable_offset_outer(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit) { + CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); + ClipperLib::Paths contours, holes; variable_offset_outer_raw(expoly, deltas, miter_limit, contours, holes); diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index b482129cf6..c4b821ca6e 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -54,6 +54,7 @@ using Vec4d = Eigen::Matrix; template using PointsAllocator = tbb::scalable_allocator; +//using PointsAllocator = std::allocator; using Points = std::vector>; using PointPtrs = std::vector; using PointConstPtrs = std::vector; diff --git a/src/libslic3r/Timer.cpp b/src/libslic3r/Timer.cpp index b361427a65..91f3b0f09e 100644 --- a/src/libslic3r/Timer.cpp +++ b/src/libslic3r/Timer.cpp @@ -10,3 +10,12 @@ Slic3r::Timer::~Timer() BOOST_LOG_TRIVIAL(debug) << "Timer '" << m_name << "' spend " << duration_cast(steady_clock::now() - m_start).count() << "ms"; } + + +namespace Slic3r::Timing { + +void TimeLimitAlarm::report_time_exceeded() const { + BOOST_LOG_TRIVIAL(error) << "Time limit exceeded for " << m_limit_exceeded_message << ": " << m_timer.elapsed_seconds() << "s"; +} + +} diff --git a/src/libslic3r/Timer.hpp b/src/libslic3r/Timer.hpp index b8f9736a17..f2e5dde1a8 100644 --- a/src/libslic3r/Timer.hpp +++ b/src/libslic3r/Timer.hpp @@ -27,5 +27,66 @@ public: ~Timer(); }; +namespace Timing { + + // Timing code from Catch2 unit testing library + static inline uint64_t nanoseconds_since_epoch() { + return std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + } + + // Timing code from Catch2 unit testing library + class Timer { + public: + void start() { + m_nanoseconds = nanoseconds_since_epoch(); + } + uint64_t elapsed_nanoseconds() const { + return nanoseconds_since_epoch() - m_nanoseconds; + } + uint64_t elapsed_microseconds() const { + return elapsed_nanoseconds() / 1000; + } + unsigned int elapsed_milliseconds() const { + return static_cast(elapsed_microseconds()/1000); + } + double elapsed_seconds() const { + return elapsed_microseconds() / 1000000.0; + } + private: + uint64_t m_nanoseconds = 0; + }; + + // Emits a Boost::log error if the life time of this timing object exceeds a limit. + class TimeLimitAlarm { + public: + TimeLimitAlarm(uint64_t time_limit_nanoseconds, std::string_view limit_exceeded_message) : + m_time_limit_nanoseconds(time_limit_nanoseconds), m_limit_exceeded_message(limit_exceeded_message) { + m_timer.start(); + } + ~TimeLimitAlarm() { + auto elapsed = m_timer.elapsed_nanoseconds(); + if (elapsed > m_time_limit_nanoseconds) + this->report_time_exceeded(); + } + static TimeLimitAlarm new_nanos(uint64_t time_limit_nanoseconds, std::string_view limit_exceeded_message) { + return TimeLimitAlarm(time_limit_nanoseconds, limit_exceeded_message); + } + static TimeLimitAlarm new_milis(uint64_t time_limit_milis, std::string_view limit_exceeded_message) { + return TimeLimitAlarm(uint64_t(time_limit_milis) * 1000000l, limit_exceeded_message); + } + static TimeLimitAlarm new_seconds(uint64_t time_limit_seconds, std::string_view limit_exceeded_message) { + return TimeLimitAlarm(uint64_t(time_limit_seconds) * 1000000000l, limit_exceeded_message); + } + private: + void report_time_exceeded() const; + + Timer m_timer; + uint64_t m_time_limit_nanoseconds; + std::string_view m_limit_exceeded_message; + }; + +} // namespace Catch + } // namespace Slic3r -#endif // libslic3r_Timer_hpp_ \ No newline at end of file + +#endif // libslic3r_Timer_hpp_