diff --git a/src/libslic3r/TreeModelVolumes.cpp b/src/libslic3r/TreeModelVolumes.cpp index bcd071767..3c7cd8af3 100644 --- a/src/libslic3r/TreeModelVolumes.cpp +++ b/src/libslic3r/TreeModelVolumes.cpp @@ -574,7 +574,8 @@ void TreeModelVolumes::calculateCollisionHolefree(const std::vector(0, max_layer + 1, keys.size()), [&](const tbb::blocked_range &range) { - RadiusLayerPolygonCacheData data; + std::vector> data; + data.reserve(range.size() * keys.size()); for (LayerIndex layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { for (RadiusLayerPair key : keys) if (layer_idx <= key.second) { @@ -585,10 +586,10 @@ void TreeModelVolumes::calculateCollisionHolefree(const std::vector 0); // this union is important as otherwise holes(in form of lines that will increase to holes in a later step) can get unioned onto the area. - data[RadiusLayerPair(radius, layer_idx)] = polygons_simplify( - offset(union_ex(getCollision(m_increase_until_radius, layer_idx, false)), + data.emplace_back(RadiusLayerPair(radius, layer_idx), polygons_simplify( + offset(union_ex(this->getCollision(m_increase_until_radius, layer_idx, false)), 5 - increase_radius_ceil, ClipperLib::jtRound, m_min_resolution), - m_min_resolution); + m_min_resolution)); } } m_collision_cache_holefree.insert(std::move(data)); @@ -832,13 +833,25 @@ coord_t TreeModelVolumes::ceilRadius(const coord_t radius) const return out; } +void TreeModelVolumes::RadiusLayerPolygonCache::allocate_layers(size_t num_layers) +{ + if (num_layers > m_data.size()) { + if (num_layers > m_data.capacity()) + reserve_power_of_2(m_data, num_layers); + m_data.resize(num_layers, {}); + } +} + // For debugging purposes, sorted by layer index, then by radius. std::vector>> TreeModelVolumes::RadiusLayerPolygonCache::sorted() const { std::vector>> out; - for (auto it = this->data.begin(); it != this->data.end(); ++ it) - out.emplace_back(it->first, it->second); - std::sort(out.begin(), out.end(), [](auto &l, auto &r){ return l.first.second < r.first.second || (l.first.second == r.first.second) && l.first.first < r.first.first; }); + for (auto &layer : m_data) { + auto layer_idx = LayerIndex(&layer - m_data.data()); + for (auto &radius_polygons : layer) + out.emplace_back(std::make_pair(radius_polygons.first, layer_idx), radius_polygons.second); + } + assert(std::is_sorted(out.begin(), out.end(), [](auto &l, auto &r){ return l.first.second < r.first.second || (l.first.second == r.first.second) && l.first.first < r.first.first; })); return out; } diff --git a/src/libslic3r/TreeModelVolumes.hpp b/src/libslic3r/TreeModelVolumes.hpp index 99f5797d2..407025e1f 100644 --- a/src/libslic3r/TreeModelVolumes.hpp +++ b/src/libslic3r/TreeModelVolumes.hpp @@ -305,36 +305,36 @@ private: * \brief Convenience typedef for the keys to the caches */ using RadiusLayerPair = std::pair; - using RadiusLayerPolygonCacheData = std::unordered_map>; class RadiusLayerPolygonCache { + // Map from radius to Polygons. Cache of one layer collision regions. + using LayerData = std::map; + // Vector of layers, at each layer map of radius to Polygons. + // Reference to Polygons returned shall be stable to insertion. + using Layers = std::vector; public: RadiusLayerPolygonCache() = default; - RadiusLayerPolygonCache(RadiusLayerPolygonCache &&rhs) : data(std::move(rhs.data)) {} - RadiusLayerPolygonCache& operator=(RadiusLayerPolygonCache &&rhs) { data = std::move(rhs.data); return *this; } + RadiusLayerPolygonCache(RadiusLayerPolygonCache &&rhs) : m_data(std::move(rhs.m_data)) {} + RadiusLayerPolygonCache& operator=(RadiusLayerPolygonCache &&rhs) { m_data = std::move(rhs.m_data); return *this; } RadiusLayerPolygonCache(const RadiusLayerPolygonCache&) = delete; RadiusLayerPolygonCache& operator=(const RadiusLayerPolygonCache&) = delete; - void insert(RadiusLayerPolygonCacheData &&in) { - std::lock_guard guard(this->mutex); - for (auto& d : in) - this->data.emplace(d.first, std::move(d.second)); - } void insert(std::vector> &&in) { - std::lock_guard guard(this->mutex); - for (auto& d : in) - this->data.emplace(d.first, std::move(d.second)); + std::lock_guard guard(m_mutex); + for (auto &d : in) + this->get_allocate_layer_data(d.first.second).emplace(d.first.first, std::move(d.second)); } // by layer void insert(std::vector> &&in, coord_t radius) { - std::lock_guard guard(this->mutex); + std::lock_guard guard(m_mutex); for (auto &d : in) - this->data.emplace(RadiusLayerPair{ radius, d.first }, std::move(d.second)); + this->get_allocate_layer_data(d.first).emplace(radius, std::move(d.second)); } void insert(std::vector &&in, coord_t first_layer_idx, coord_t radius) { - std::lock_guard guard(this->mutex); + std::lock_guard guard(m_mutex); + allocate_layers(first_layer_idx + in.size()); for (auto &d : in) - this->data.emplace(RadiusLayerPair{ radius, first_layer_idx ++ }, std::move(d)); + m_data[first_layer_idx ++].emplace(radius, std::move(d)); } /*! * \brief Checks a cache for a given RadiusLayerPair and returns it if it is found @@ -342,11 +342,30 @@ private: * \return A wrapped optional reference of the requested area (if it was found, an empty optional if nothing was found) */ std::optional> getArea(const TreeModelVolumes::RadiusLayerPair &key) const { - std::lock_guard guard(this->mutex); - const auto it = this->data.find(key); - return it == this->data.end() ? + std::lock_guard guard(m_mutex); + if (key.second >= m_data.size()) + return std::optional>{}; + const auto &layer = m_data[key.second]; + auto it = layer.find(key.first); + return it == layer.end() ? std::optional>{} : std::optional>{ it->second }; } + // Get a collision area at a given layer for a radius that is a lower or equial to the key radius. + std::optional>> get_lower_bound_area(const TreeModelVolumes::RadiusLayerPair &key) const { + std::lock_guard guard(m_mutex); + if (key.second >= m_data.size()) + return {}; + const auto &layer = m_data[key.second]; + if (layer.empty()) + return {}; + auto it = layer.lower_bound(key.first); + if (it == layer.end() || it->first != key.first) { + if (it == layer.begin()) + return {}; + -- it; + } + return std::make_pair(it->first, std::reference_wrapper(it->second)); + } /*! * \brief Get the highest already calculated layer in the cache. * \param radius The radius for which the highest already calculated layer has to be found. @@ -355,22 +374,27 @@ private: * \return A wrapped optional reference of the requested area (if it was found, an empty optional if nothing was found) */ LayerIndex getMaxCalculatedLayer(coord_t radius) const { - std::lock_guard guard(this->mutex); - int max_layer = -1; - // the placeable on model areas do not exist on layer 0, as there can not be model below it. As such it may be possible that layer 1 is available, but layer 0 does not exist. - if (this->data.find({ radius, 1 }) != this->data.end()) - max_layer = 1; - while (this->data.count(TreeModelVolumes::RadiusLayerPair(radius, max_layer + 1)) > 0) - ++ max_layer; - return max_layer; + std::lock_guard guard(m_mutex); + auto layer_idx = LayerIndex(m_data.size()) - 1; + for (; layer_idx > 0; -- layer_idx) + if (const auto &layer = m_data[layer_idx]; layer.find(radius) != layer.end()) + break; + // The placeable on model areas do not exist on layer 0, as there can not be model below it. As such it may be possible that layer 1 is available, but layer 0 does not exist. + return layer_idx == 0 ? -1 : layer_idx; } // For debugging purposes, sorted by layer index, then by radius. [[nodiscard]] std::vector>> sorted() const; private: - RadiusLayerPolygonCacheData data; - mutable std::mutex mutex; + LayerData& get_allocate_layer_data(LayerIndex layer_idx) { + allocate_layers(layer_idx + 1); + return m_data[layer_idx]; + } + void allocate_layers(size_t num_layers); + + Layers m_data; + mutable std::mutex m_mutex; }; diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 00de10fc4..815d6ff55 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -176,6 +176,21 @@ template size_t next_highest_power_of_2(T v, return next_highest_power_of_2(uint32_t(v)); } +template void reserve_power_of_2(VectorType &vector, size_t n) +{ + vector.reserve(next_highest_power_of_2(n)); +} + +template void reserve_more(VectorType &vector, size_t n) +{ + vector.reserve(vector.size() + n); +} + +template void reserve_more_power_of_2(VectorType &vector, size_t n) +{ + vector.reserve(next_highest_power_of_2(vector.size() + n)); +} + template inline INDEX_TYPE prev_idx_modulo(INDEX_TYPE idx, const INDEX_TYPE count) {