WIP TreeSupports: Improved speed of TreeModelVolumes by better

parallelization, cleaned up the code by better structuring
the collision caches with their mutexes.
This commit is contained in:
Vojtech Bubnik 2022-08-23 11:37:06 +02:00
parent 9aee934d53
commit b9e7cd2d7b
4 changed files with 454 additions and 491 deletions

File diff suppressed because it is too large Load Diff

View File

@ -211,7 +211,8 @@ public:
{ {
Slow, Slow,
FastSafe, FastSafe,
Fast Fast,
Count
}; };
/*! /*!
@ -220,7 +221,7 @@ public:
* Knowledge about branch angle is used to only calculate avoidances and collisions that may actually be needed. * Knowledge about branch angle is used to only calculate avoidances and collisions that may actually be needed.
* Not calling precalculate() will cause the class to lazily calculate avoidances and collisions as needed, which will be a lot slower on systems with more then one or two cores! * Not calling precalculate() will cause the class to lazily calculate avoidances and collisions as needed, which will be a lot slower on systems with more then one or two cores!
*/ */
void precalculate(coord_t max_layer); void precalculate(const coord_t max_layer);
/*! /*!
* \brief Provides the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. * \brief Provides the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer.
@ -233,7 +234,7 @@ public:
* \param min_xy_dist Is the minimum xy distance used. * \param min_xy_dist Is the minimum xy distance used.
* \return Polygons object * \return Polygons object
*/ */
const Polygons& getCollision(coord_t radius, LayerIndex layer_idx, bool min_xy_dist) const; const Polygons& getCollision(const coord_t radius, LayerIndex layer_idx, bool min_xy_dist) const;
/*! /*!
* \brief Provides the areas that have to be avoided by the tree's branches * \brief Provides the areas that have to be avoided by the tree's branches
@ -277,8 +278,12 @@ public:
* \param min_xy_dist is the minimum xy distance used. * \param min_xy_dist is the minimum xy distance used.
* \return The rounded radius * \return The rounded radius
*/ */
coord_t ceilRadius(coord_t radius, bool min_xy_dist) const { coord_t ceilRadius(const coord_t radius, const bool min_xy_dist) const {
return this->ceilRadius(min_xy_dist ? radius : radius + m_current_min_xy_dist_delta); assert(radius >= 0);
return min_xy_dist ?
this->ceilRadius(radius) :
// special case as if a radius 0 is requested it could be to ensure correct xy distance. As such it is beneficial if the collision is as close to the configured values as possible.
radius > 0 ? this->ceilRadius(radius + m_current_min_xy_dist_delta) : m_current_min_xy_dist_delta;
} }
/*! /*!
* \brief Round \p radius upwards to the maximum that would still round up to the same value as the provided one. * \brief Round \p radius upwards to the maximum that would still round up to the same value as the provided one.
@ -288,6 +293,7 @@ public:
* \return The maximum radius, resulting in the same rounding. * \return The maximum radius, resulting in the same rounding.
*/ */
coord_t getRadiusNextCeil(coord_t radius, bool min_xy_dist) const { coord_t getRadiusNextCeil(coord_t radius, bool min_xy_dist) const {
assert(radius > 0);
return min_xy_dist ? return min_xy_dist ?
this->ceilRadius(radius) : this->ceilRadius(radius) :
this->ceilRadius(radius + m_current_min_xy_dist_delta) - m_current_min_xy_dist_delta; this->ceilRadius(radius + m_current_min_xy_dist_delta) - m_current_min_xy_dist_delta;
@ -313,11 +319,22 @@ private:
for (auto& d : in) for (auto& d : in)
this->data.emplace(d.first, std::move(d.second)); this->data.emplace(d.first, std::move(d.second));
} }
void insert(std::vector<std::pair<RadiusLayerPair, Polygons>> && in) { void insert(std::vector<std::pair<RadiusLayerPair, Polygons>> &&in) {
std::lock_guard<std::mutex> guard(this->mutex); std::lock_guard<std::mutex> guard(this->mutex);
for (auto& d : in) for (auto& d : in)
this->data.emplace(d.first, std::move(d.second)); this->data.emplace(d.first, std::move(d.second));
} }
// by layer
void insert(std::vector<std::pair<coord_t, Polygons>> &&in, coord_t radius) {
std::lock_guard<std::mutex> guard(this->mutex);
for (auto &d : in)
this->data.emplace(RadiusLayerPair{ radius, d.first }, std::move(d.second));
}
void insert(std::vector<Polygons> &&in, coord_t first_layer_idx, coord_t radius) {
std::lock_guard<std::mutex> guard(this->mutex);
for (auto &d : in)
this->data.emplace(RadiusLayerPair{ radius, first_layer_idx ++ }, std::move(d));
}
/*! /*!
* \brief Checks a cache for a given RadiusLayerPair and returns it if it is found * \brief Checks a cache for a given RadiusLayerPair and returns it if it is found
* \param key RadiusLayerPair of the requested areas. The radius will be calculated up to the provided layer. * \param key RadiusLayerPair of the requested areas. The radius will be calculated up to the provided layer.
@ -359,19 +376,20 @@ private:
* The result is a 2D area that would cause nodes of given radius to * The result is a 2D area that would cause nodes of given radius to
* collide with the model or be inside a hole. * collide with the model or be inside a hole.
* A Hole is defined as an area, in which a branch with m_increase_until_radius radius would collide with the wall. * A Hole is defined as an area, in which a branch with m_increase_until_radius radius would collide with the wall.
* minimum xy distance is always used.
* \param radius The radius of the node of interest * \param radius The radius of the node of interest
* \param layer_idx The layer of interest * \param layer_idx The layer of interest
* \param min_xy_dist Is the minimum xy distance used. * \param min_xy_dist Is the minimum xy distance used.
* \return Polygons object * \return Polygons object
*/ */
const Polygons& getCollisionHolefree(coord_t radius, LayerIndex layer_idx, bool min_xy_dist) const; const Polygons& getCollisionHolefree(coord_t radius, LayerIndex layer_idx) const;
/*! /*!
* \brief Round \p radius upwards to either a multiple of m_radius_sample_resolution or a exponentially increasing value * \brief Round \p radius upwards to either a multiple of m_radius_sample_resolution or a exponentially increasing value
* *
* \param radius The radius of the node of interest * \param radius The radius of the node of interest
*/ */
coord_t ceilRadius(coord_t radius) const; coord_t ceilRadius(const coord_t radius) const;
/*! /*!
* \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. * \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer.
@ -380,18 +398,8 @@ private:
* collide with the model. Result is saved in the cache. * collide with the model. Result is saved in the cache.
* \param keys RadiusLayerPairs of all requested areas. Every radius will be calculated up to the provided layer. * \param keys RadiusLayerPairs of all requested areas. Every radius will be calculated up to the provided layer.
*/ */
void calculateCollision(std::deque<RadiusLayerPair> keys); void calculateCollision(const std::vector<RadiusLayerPair> &keys);
/*! void calculateCollision(const coord_t radius, const LayerIndex max_layer_idx);
* \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer.
*
* The result is a 2D area that would cause nodes of given radius to
* collide with the model. Result is saved in the cache.
* \param key RadiusLayerPairs the requested areas. The radius will be calculated up to the provided layer.
*/
void calculateCollision(RadiusLayerPair key)
{
calculateCollision(std::deque<RadiusLayerPair>{ RadiusLayerPair(key) });
}
/*! /*!
* \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. Holes are removed. * \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. Holes are removed.
* *
@ -400,7 +408,7 @@ private:
* A Hole is defined as an area, in which a branch with m_increase_until_radius radius would collide with the wall. * A Hole is defined as an area, in which a branch with m_increase_until_radius radius would collide with the wall.
* \param keys RadiusLayerPairs of all requested areas. Every radius will be calculated up to the provided layer. * \param keys RadiusLayerPairs of all requested areas. Every radius will be calculated up to the provided layer.
*/ */
void calculateCollisionHolefree(std::deque<RadiusLayerPair> keys); void calculateCollisionHolefree(const std::vector<RadiusLayerPair> &keys);
/*! /*!
* \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. Holes are removed. * \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer. Holes are removed.
@ -412,7 +420,7 @@ private:
*/ */
void calculateCollisionHolefree(RadiusLayerPair key) void calculateCollisionHolefree(RadiusLayerPair key)
{ {
calculateCollisionHolefree(std::deque<RadiusLayerPair>{ RadiusLayerPair(key) }); calculateCollisionHolefree(std::vector<RadiusLayerPair>{ RadiusLayerPair(key) });
} }
/*! /*!
@ -422,7 +430,7 @@ private:
* collide with the model. Result is saved in the cache. * collide with the model. Result is saved in the cache.
* \param keys RadiusLayerPairs of all requested areas. Every radius will be calculated up to the provided layer. * \param keys RadiusLayerPairs of all requested areas. Every radius will be calculated up to the provided layer.
*/ */
void calculateAvoidance(std::deque<RadiusLayerPair> keys); void calculateAvoidance(const std::vector<RadiusLayerPair> &keys, bool to_build_plate, bool to_model);
/*! /*!
* \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model. * \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model.
@ -431,9 +439,9 @@ private:
* collide with the model. Result is saved in the cache. * collide with the model. Result is saved in the cache.
* \param key RadiusLayerPair of the requested areas. It will be calculated up to the provided layer. * \param key RadiusLayerPair of the requested areas. It will be calculated up to the provided layer.
*/ */
void calculateAvoidance(RadiusLayerPair key) void calculateAvoidance(RadiusLayerPair key, bool to_build_plate, bool to_model)
{ {
calculateAvoidance(std::deque<RadiusLayerPair>{ RadiusLayerPair(key) }); calculateAvoidance(std::vector<RadiusLayerPair>{ RadiusLayerPair(key) }, to_build_plate, to_model);
} }
/*! /*!
@ -441,38 +449,16 @@ private:
* Result is saved in the cache. * Result is saved in the cache.
* \param key RadiusLayerPair of the requested areas. It will be calculated up to the provided layer. * \param key RadiusLayerPair of the requested areas. It will be calculated up to the provided layer.
*/ */
void calculatePlaceables(RadiusLayerPair key) void calculatePlaceables(const coord_t radius, const LayerIndex max_required_layer);
{
calculatePlaceables(std::deque<RadiusLayerPair>{ key });
}
/*! /*!
* \brief Creates the areas where a branch of a given radius can be placed on the model. * \brief Creates the areas where a branch of a given radius can be placed on the model.
* Result is saved in the cache. * Result is saved in the cache.
* \param keys RadiusLayerPair of the requested areas. The radius will be calculated up to the provided layer. * \param keys RadiusLayerPair of the requested areas. The radius will be calculated up to the provided layer.
*/ */
void calculatePlaceables(std::deque<RadiusLayerPair> keys); void calculatePlaceables(const std::vector<RadiusLayerPair> &keys);
/*!
* \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model without being able to place a branch with given radius on a single layer.
*
* The result is a 2D area that would cause nodes of radius \p radius to
* collide with the model in a not wanted way. Result is saved in the cache.
* \param keys RadiusLayerPairs of all requested areas. Every radius will be calculated up to the provided layer.
*/
void calculateAvoidanceToModel(std::deque<RadiusLayerPair> keys);
/*!
* \brief Creates the areas that have to be avoided by the tree's branches to prevent collision with the model without being able to place a branch with given radius on a single layer.
*
* The result is a 2D area that would cause nodes of radius \p radius to
* collide with the model in a not wanted way. Result is saved in the cache.
* \param key RadiusLayerPair of the requested areas. The radius will be calculated up to the provided layer.
*/
void calculateAvoidanceToModel(RadiusLayerPair key)
{
calculateAvoidanceToModel(std::deque<RadiusLayerPair>{ RadiusLayerPair(key) });
}
/*! /*!
* \brief Creates the areas that can not be passed when expanding an area downwards. As such these areas are an somewhat abstract representation of a wall (as in a printed object). * \brief Creates the areas that can not be passed when expanding an area downwards. As such these areas are an somewhat abstract representation of a wall (as in a printed object).
* *
@ -480,7 +466,7 @@ private:
* *
* \param keys RadiusLayerPairs of all requested areas. Every radius will be calculated up to the provided layer. * \param keys RadiusLayerPairs of all requested areas. Every radius will be calculated up to the provided layer.
*/ */
void calculateWallRestrictions(std::deque<RadiusLayerPair> keys); void calculateWallRestrictions(const std::vector<RadiusLayerPair> &keys);
/*! /*!
* \brief Creates the areas that can not be passed when expanding an area downwards. As such these areas are an somewhat abstract representation of a wall (as in a printed object). * \brief Creates the areas that can not be passed when expanding an area downwards. As such these areas are an somewhat abstract representation of a wall (as in a printed object).
@ -489,7 +475,7 @@ private:
*/ */
void calculateWallRestrictions(RadiusLayerPair key) void calculateWallRestrictions(RadiusLayerPair key)
{ {
calculateWallRestrictions(std::deque<RadiusLayerPair>{ RadiusLayerPair(key) }); calculateWallRestrictions(std::vector<RadiusLayerPair>{ RadiusLayerPair(key) });
} }
/*! /*!
@ -556,9 +542,9 @@ private:
*/ */
std::vector<Polygons> m_anti_overhang; std::vector<Polygons> m_anti_overhang;
/*! /*!
* \brief Radii that can be ignored by ceilRadius as they will never be requested. * \brief Radii that can be ignored by ceilRadius as they will never be requested, sorted.
*/ */
std::unordered_set<coord_t> m_ignorable_radii; std::vector<coord_t> m_ignorable_radii;
/*! /*!
* \brief Smallest radius a branch can have. This is the radius of a SupportElement with DTT=0. * \brief Smallest radius a branch can have. This is the radius of a SupportElement with DTT=0.
@ -581,18 +567,39 @@ private:
* \brief Caches to avoid holes smaller than the radius until which the radius is always increased, as they are free of holes. * \brief Caches to avoid holes smaller than the radius until which the radius is always increased, as they are free of holes.
* Also called safe avoidances, as they are safe regarding not running into holes. * Also called safe avoidances, as they are safe regarding not running into holes.
*/ */
RadiusLayerPolygonCache m_avoidance_cache_holefree; RadiusLayerPolygonCache m_avoidance_cache_holefree;
RadiusLayerPolygonCache m_avoidance_cache_holefree_to_model; RadiusLayerPolygonCache m_avoidance_cache_holefree_to_model;
RadiusLayerPolygonCache& avoidance_cache(const AvoidanceType type, const bool to_model) {
if (to_model) {
switch (type) {
case AvoidanceType::Fast: return m_avoidance_cache_to_model;
case AvoidanceType::Slow: return m_avoidance_cache_to_model_slow;
case AvoidanceType::FastSafe: return m_avoidance_cache_holefree_to_model;
}
} else {
switch (type) {
case AvoidanceType::Fast: return m_avoidance_cache;
case AvoidanceType::Slow: return m_avoidance_cache_slow;
case AvoidanceType::FastSafe: return m_avoidance_cache_holefree;
}
}
assert(false);
return m_avoidance_cache;
}
const RadiusLayerPolygonCache& avoidance_cache(const AvoidanceType type, const bool to_model) const {
return const_cast<TreeModelVolumes*>(this)->avoidance_cache(type, to_model);
}
/*! /*!
* \brief Caches to represent walls not allowed to be passed over. * \brief Caches to represent walls not allowed to be passed over.
*/ */
RadiusLayerPolygonCache m_wall_restrictions_cache; RadiusLayerPolygonCache m_wall_restrictions_cache;
// A different cache for min_xy_dist as the maximal safe distance an influence area can be increased(guaranteed overlap of two walls in consecutive layer) // A different cache for min_xy_dist as the maximal safe distance an influence area can be increased(guaranteed overlap of two walls in consecutive layer)
// is much smaller when min_xy_dist is used. This causes the area of the wall restriction to be thinner and as such just using the min_xy_dist wall // is much smaller when min_xy_dist is used. This causes the area of the wall restriction to be thinner and as such just using the min_xy_dist wall
// restriction would be slower. // restriction would be slower.
RadiusLayerPolygonCache m_wall_restrictions_cache_min; RadiusLayerPolygonCache m_wall_restrictions_cache_min;
#ifdef SLIC3R_TREESUPPORTS_PROGRESS #ifdef SLIC3R_TREESUPPORTS_PROGRESS
std::unique_ptr<std::mutex> m_critical_progress { std::make_unique<std::mutex>() }; std::unique_ptr<std::mutex> m_critical_progress { std::make_unique<std::mutex>() };

View File

@ -874,9 +874,9 @@ static std::optional<std::pair<Point, size_t>> polyline_sample_next_point_at_dis
if (area(expoly) <= 0.) if (area(expoly) <= 0.)
::MessageBoxA(nullptr, "TreeSupport infill negative area", "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING); ::MessageBoxA(nullptr, "TreeSupport infill negative area", "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING);
#endif // _WIN32 #endif // _WIN32
assert(intersecting_edges(expoly).empty()); assert(intersecting_edges(to_polygons(expoly)).empty());
#ifdef _WIN32 #ifdef _WIN32
if (! intersecting_edges(expoly).empty()) if (! intersecting_edges(to_polygons(expoly)).empty())
::MessageBoxA(nullptr, "TreeSupport infill self intersections", "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING); ::MessageBoxA(nullptr, "TreeSupport infill self intersections", "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING);
#endif // _WIN32 #endif // _WIN32
Surface surface(stInternal, std::move(expoly)); Surface surface(stInternal, std::move(expoly));
@ -1926,13 +1926,13 @@ void TreeSupport::increaseAreas(std::unordered_map<SupportElement, Polygons>& to
constexpr bool increase_radius = true, no_error = true, use_min_radius = true, move = true; // aliases for better readability constexpr bool increase_radius = true, no_error = true, use_min_radius = true, move = true; // aliases for better readability
// Determine in which order configurations are checked if they result in a valid influence area. Check will stop if a valid area is found // Determine in which order configurations are checked if they result in a valid influence area. Check will stop if a valid area is found
std::deque<AreaIncreaseSettings> order; std::vector<AreaIncreaseSettings> order;
auto insertSetting = [&](AreaIncreaseSettings settings, bool back) { auto insertSetting = [&](AreaIncreaseSettings settings, bool back) {
if (std::find(order.begin(), order.end(), settings) == order.end()) { if (std::find(order.begin(), order.end(), settings) == order.end()) {
if (back) if (back)
order.emplace_back(settings); order.emplace_back(settings);
else else
order.emplace_front(settings); order.insert(order.begin(), settings);
} }
}; };
@ -1973,7 +1973,7 @@ void TreeSupport::increaseAreas(std::unordered_map<SupportElement, Polygons>& to
if (elem.use_min_xy_dist) if (elem.use_min_xy_dist)
{ {
std::deque<AreaIncreaseSettings> new_order; std::vector<AreaIncreaseSettings> new_order;
// if the branch currently has to use min_xy_dist check if the configuration would also be valid with the regular xy_distance before checking with use_min_radius (Only happens when Support Distance priority is z overrides xy ) // if the branch currently has to use min_xy_dist check if the configuration would also be valid with the regular xy_distance before checking with use_min_radius (Only happens when Support Distance priority is z overrides xy )
for (AreaIncreaseSettings settings : order) for (AreaIncreaseSettings settings : order)
{ {
@ -2444,15 +2444,16 @@ void TreeSupport::generateBranchAreas(
// if larger area did not fix the problem, all parts off the nozzle path that do not contain the center point are removed, hoping for the best // if larger area did not fix the problem, all parts off the nozzle path that do not contain the center point are removed, hoping for the best
if (nozzle_path.size() > 1) { if (nozzle_path.size() > 1) {
Polygons polygons_with_correct_center; Polygons polygons_with_correct_center;
for (const ExPolygon &part : nozzle_path) { for (ExPolygon &part : nozzle_path) {
if (part.contains(elem->result_on_layer)) if (part.contains(elem->result_on_layer))
polygons_with_correct_center = union_(polygons_with_correct_center, part); polygons_with_correct_center = union_(polygons_with_correct_center, part);
else { else {
// try a fuzzy inside as sometimes the point should be on the border, but is not because of rounding errors... // try a fuzzy inside as sometimes the point should be on the border, but is not because of rounding errors...
Point from = elem->result_on_layer; Point from = elem->result_on_layer;
moveInside(part, from, 0); Polygons &to = to_polygons(std::move(part));
moveInside(to, from, 0);
if ((elem->result_on_layer - from).cast<double>().norm() < scaled<double>(0.025)) if ((elem->result_on_layer - from).cast<double>().norm() < scaled<double>(0.025))
polygons_with_correct_center = union_(polygons_with_correct_center, part); polygons_with_correct_center = union_(polygons_with_correct_center, to);
} }
} }
// Increase the area again, to ensure the nozzle path when calculated later is very similar to the one assumed above. // Increase the area again, to ensure the nozzle path when calculated later is very similar to the one assumed above.

View File

@ -37,17 +37,16 @@
#define SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL false #define SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL false
#define SUPPORT_TREE_AVOID_SUPPORT_BLOCKER true #define SUPPORT_TREE_AVOID_SUPPORT_BLOCKER true
#define SUPPORT_TREE_USE_EXPONENTIAL_COLLISION_RESOLUTION true
#define SUPPORT_TREE_EXPONENTIAL_THRESHOLD 1000
#define SUPPORT_TREE_EXPONENTIAL_FACTOR 1.5
#define SUPPORT_TREE_PRE_EXPONENTIAL_STEPS 1
#define SUPPORT_TREE_COLLISION_RESOLUTION 500 // Only has an effect if SUPPORT_TREE_USE_EXPONENTIAL_COLLISION_RESOLUTION is false
namespace Slic3r namespace Slic3r
{ {
using LayerIndex = int; using LayerIndex = int;
static constexpr const double SUPPORT_TREE_EXPONENTIAL_FACTOR = 1.5;
static constexpr const coord_t SUPPORT_TREE_EXPONENTIAL_THRESHOLD = scaled<coord_t>(1. * SUPPORT_TREE_EXPONENTIAL_FACTOR);
static constexpr const coord_t SUPPORT_TREE_COLLISION_RESOLUTION = scaled<coord_t>(0.5);
//FIXME //FIXME
class Print; class Print;
class PrintObject; class PrintObject;