From bba7712f7daa45808071dddfb66b9dd6b278c941 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Sat, 5 Oct 2024 13:10:27 +0800 Subject: [PATCH 01/12] vec changes --- include/manifold/vec_view.h | 41 ++++++++++++++++++++++++++++++-- src/vec.h | 47 ++++++------------------------------- 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/include/manifold/vec_view.h b/include/manifold/vec_view.h index 12440eb52..4dc5f7344 100644 --- a/include/manifold/vec_view.h +++ b/include/manifold/vec_view.h @@ -15,6 +15,9 @@ #pragma once #include +#include +#include +#include #include "manifold/optional_assert.h" @@ -31,8 +34,13 @@ class VecView { using Iter = T *; using IterC = const T *; + VecView() : ptr_(nullptr), size_(0) {} + VecView(T *ptr, size_t size) : ptr_(ptr), size_(size) {} + VecView(const std::vector> &v) + : ptr_(v.data()), size_(v.size()) {} + VecView(const VecView &other) { ptr_ = other.ptr_; size_ = other.size_; @@ -94,6 +102,37 @@ class VecView { bool empty() const { return size_ == 0; } + VecView view(size_t offset = 0, + size_t length = std::numeric_limits::max()) { + if (length == std::numeric_limits::max()) + length = this->size_ - offset; + ASSERT(length >= 0, std::out_of_range("Vec::view out of range")); + ASSERT(offset + length <= this->size_ && offset >= 0, + std::out_of_range("Vec::view out of range")); + return VecView(this->ptr_ + offset, length); + } + + VecView cview( + size_t offset = 0, + size_t length = std::numeric_limits::max()) const { + if (length == std::numeric_limits::max()) + length = this->size_ - offset; + ASSERT(length >= 0, std::out_of_range("Vec::cview out of range")); + ASSERT(offset + length <= this->size_ && offset >= 0, + std::out_of_range("Vec::cview out of range")); + return VecView(this->ptr_ + offset, length); + } + + VecView view( + size_t offset = 0, + size_t length = std::numeric_limits::max()) const { + return cview(offset, length); + } + + T *data() { return this->ptr_; } + + const T *data() const { return this->ptr_; } + #ifdef MANIFOLD_DEBUG void Dump() const { std::cout << "Vec = " << std::endl; @@ -107,8 +146,6 @@ class VecView { protected: T *ptr_ = nullptr; size_t size_ = 0; - - VecView() = default; }; } // namespace manifold diff --git a/src/vec.h b/src/vec.h index f90b97cb3..d9fa70f18 100644 --- a/src/vec.h +++ b/src/vec.h @@ -19,7 +19,6 @@ #define TracyAllocS(ptr, size, n) (void)0 #define TracyFreeS(ptr, n) (void)0 #endif -#include #include #include "manifold/parallel.h" @@ -112,12 +111,11 @@ class Vec : public VecView { } this->size_ = other.size_; capacity_ = other.size_; - auto policy = autoPolicy(this->size_); if (this->size_ != 0) { this->ptr_ = reinterpret_cast(malloc(this->size_ * sizeof(T))); ASSERT(this->ptr_ != nullptr, std::bad_alloc()); TracyAllocS(this->ptr_, this->size_ * sizeof(T), 3); - copy(policy, other.begin(), other.end(), this->ptr_); + manifold::copy(other.begin(), other.end(), this->ptr_); } return *this; } @@ -145,25 +143,25 @@ class Vec : public VecView { std::swap(capacity_, other.capacity_); } - inline void push_back(const T &val) { + inline void push_back(const T &val, bool seq = false) { if (this->size_ >= capacity_) { // avoid dangling pointer in case val is a reference of our array T val_copy = val; - reserve(capacity_ == 0 ? 128 : capacity_ * 2); + reserve(capacity_ == 0 ? 128 : capacity_ * 2, seq); this->ptr_[this->size_++] = val_copy; return; } this->ptr_[this->size_++] = val; } - void reserve(size_t n) { + void reserve(size_t n, bool seq = false) { if (n > capacity_) { T *newBuffer = reinterpret_cast(malloc(n * sizeof(T))); ASSERT(newBuffer != nullptr, std::bad_alloc()); TracyAllocS(newBuffer, n * sizeof(T), 3); if (this->size_ > 0) - copy(autoPolicy(this->size_), this->ptr_, this->ptr_ + this->size_, - newBuffer); + manifold::copy(seq ? ExecutionPolicy::Seq : autoPolicy(this->size_), + this->ptr_, this->ptr_ + this->size_, newBuffer); if (this->ptr_ != nullptr) { TracyFreeS(this->ptr_, 3); free(this->ptr_); @@ -194,8 +192,7 @@ class Vec : public VecView { newBuffer = reinterpret_cast(malloc(this->size_ * sizeof(T))); ASSERT(newBuffer != nullptr, std::bad_alloc()); TracyAllocS(newBuffer, this->size_ * sizeof(T), 3); - copy(autoPolicy(this->size_), this->ptr_, this->ptr_ + this->size_, - newBuffer); + manifold::copy(this->ptr_, this->ptr_ + this->size_, newBuffer); } if (this->ptr_ != nullptr) { TracyFreeS(this->ptr_, 3); @@ -205,36 +202,6 @@ class Vec : public VecView { capacity_ = this->size_; } - VecView view(size_t offset = 0, - size_t length = std::numeric_limits::max()) { - if (length == std::numeric_limits::max()) - length = this->size_ - offset; - ASSERT(length >= 0, std::out_of_range("Vec::view out of range")); - ASSERT(offset + length <= this->size_ && offset >= 0, - std::out_of_range("Vec::view out of range")); - return VecView(this->ptr_ + offset, length); - } - - VecView cview( - size_t offset = 0, - size_t length = std::numeric_limits::max()) const { - if (length == std::numeric_limits::max()) - length = this->size_ - offset; - ASSERT(length >= 0, std::out_of_range("Vec::cview out of range")); - ASSERT(offset + length <= this->size_ && offset >= 0, - std::out_of_range("Vec::cview out of range")); - return VecView(this->ptr_ + offset, length); - } - - VecView view( - size_t offset = 0, - size_t length = std::numeric_limits::max()) const { - return cview(offset, length); - } - - T *data() { return this->ptr_; } - const T *data() const { return this->ptr_; } - size_t capacity() const { return capacity_; } private: From 22a470e1c32a890ba150110a731e011954fc4db9 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Sat, 5 Oct 2024 13:10:33 +0800 Subject: [PATCH 02/12] collider optimization --- src/collider.h | 108 ++++++++++++++++++++----------------------------- src/sparse.h | 17 +++++++- 2 files changed, 58 insertions(+), 67 deletions(-) diff --git a/src/collider.h b/src/collider.h index b88a322cd..1d26e20de 100644 --- a/src/collider.h +++ b/src/collider.h @@ -23,6 +23,10 @@ #include #endif +#ifdef MANIFOLD_PAR +#include +#endif + namespace manifold { namespace collider_internal { @@ -155,18 +159,18 @@ struct CreateRadixTree { }; template -struct FindCollisions { +struct FindCollision { VecView queries; VecView nodeBBox_; VecView> internalChildren_; Recorder recorder; - int RecordCollision(int node, const int queryIdx) { + int RecordCollision(int node, const int queryIdx, SparseIndices& ind) { bool overlaps = nodeBBox_[node].DoesOverlap(queries[queryIdx]); if (overlaps && IsLeaf(node)) { int leafIdx = Node2Leaf(node); if (!selfCollision || leafIdx != queryIdx) { - recorder.record(queryIdx, leafIdx); + recorder.record(queryIdx, leafIdx, ind); } } return overlaps && IsInternal(node); // Should traverse into node @@ -179,15 +183,14 @@ struct FindCollisions { int top = -1; // Depth-first search int node = kRoot; - // same implies that this query do not have any collision - if (recorder.earlyexit(queryIdx)) return; + SparseIndices& ind = recorder.local(); while (1) { int internal = Node2Internal(node); int child1 = internalChildren_[internal].first; int child2 = internalChildren_[internal].second; - int traverse1 = RecordCollision(child1, queryIdx); - int traverse2 = RecordCollision(child2, queryIdx); + int traverse1 = RecordCollision(child1, queryIdx, ind); + int traverse2 = RecordCollision(child2, queryIdx, ind); if (!traverse1 && !traverse2) { if (top < 0) break; // done @@ -199,48 +202,37 @@ struct FindCollisions { } } } - recorder.end(queryIdx); - } -}; - -struct CountCollisions { - VecView counts; - VecView empty; - void record(int queryIdx, int _leafIdx) { counts[queryIdx]++; } - bool earlyexit(int _queryIdx) { return false; } - void end(int queryIdx) { - if (counts[queryIdx] == 0) empty[queryIdx] = 1; } }; template struct SeqCollisionRecorder { SparseIndices& queryTri_; - void record(int queryIdx, int leafIdx) const { + inline void record(int queryIdx, int leafIdx, SparseIndices& ind) const { if (inverted) - queryTri_.Add(leafIdx, queryIdx); + ind.Add(leafIdx, queryIdx); else - queryTri_.Add(queryIdx, leafIdx); + ind.Add(queryIdx, leafIdx); } - bool earlyexit(int queryIdx) const { return false; } - void end(int queryIdx) const {} + SparseIndices& local() { return queryTri_; } }; +#ifdef MANIFOLD_PAR template struct ParCollisionRecorder { - SparseIndices& queryTri; - VecView counts; - VecView empty; - void record(int queryIdx, int leafIdx) { - int pos = counts[queryIdx]++; + tbb::combinable& store; + inline void record(int queryIdx, int leafIdx, SparseIndices& ind) const { + // Add may invoke something in parallel, and it may return in + // another thread, making thread local unsafe + // we need to explicitly forbid parallelization by passing a flag if (inverted) - queryTri.Set(pos, leafIdx, queryIdx); + ind.Add(leafIdx, queryIdx, true); else - queryTri.Set(pos, queryIdx, leafIdx); + ind.Add(queryIdx, leafIdx, true); } - bool earlyexit(int queryIdx) const { return empty[queryIdx] == 1; } - void end(int queryIdx) const {} + SparseIndices& local() { return store.local(); } }; +#endif struct BuildInternalBoxes { VecView nodeBBox_; @@ -333,42 +325,28 @@ class Collider { typename T> SparseIndices Collisions(const VecView& queriesIn) const { ZoneScoped; - using collider_internal::FindCollisions; - // note that the length is 1 larger than the number of queries so the last - // element can store the sum when using exclusive scan - if (queriesIn.size() < collider_internal::kSequentialThreshold) { - SparseIndices queryTri; - for_each_n( - ExecutionPolicy::Seq, countAt(0), queriesIn.size(), - FindCollisions>{ - queriesIn, nodeBBox_, internalChildren_, {queryTri}}); - return queryTri; - } else { - // compute the number of collisions to determine the size for allocation - // and offset, this avoids the need for atomic - Vec counts(queriesIn.size() + 1, 0); - Vec empty(queriesIn.size(), 0); + using collider_internal::FindCollision; +#ifdef MANIFOLD_PAR + if (queriesIn.size() > collider_internal::kSequentialThreshold) { + tbb::combinable store; for_each_n( ExecutionPolicy::Par, countAt(0), queriesIn.size(), - FindCollisions{ - queriesIn, nodeBBox_, internalChildren_, {counts, empty}}); - // compute start index for each query and total count - manifold::exclusive_scan(counts.begin(), counts.end(), counts.begin(), 0, - std::plus()); - if (counts.back() == 0) return SparseIndices(0); - SparseIndices queryTri(counts.back()); - // actually recording collisions - for_each_n( - ExecutionPolicy::Par, countAt(0), queriesIn.size(), - FindCollisions>{ - queriesIn, - nodeBBox_, - internalChildren_, - {queryTri, counts, empty}}); - return queryTri; + FindCollision>{ + queriesIn, nodeBBox_, internalChildren_, {store}}); + + std::vector tmp; + store.combine_each( + [&](SparseIndices& ind) { tmp.emplace_back(std::move(ind)); }); + return SparseIndices(tmp); } +#endif + SparseIndices queryTri; + for_each_n(ExecutionPolicy::Seq, countAt(0), queriesIn.size(), + FindCollision>{ + queriesIn, nodeBBox_, internalChildren_, {queryTri}}); + return queryTri; } static uint32_t MortonCode(vec3 position, Box bBox) { diff --git a/src/sparse.h b/src/sparse.h index 222b284c1..5d01acdb8 100644 --- a/src/sparse.h +++ b/src/sparse.h @@ -59,6 +59,19 @@ class SparseIndices { SparseIndices() = default; SparseIndices(size_t size) { data_ = Vec(size * sizeof(int64_t)); } + SparseIndices(const std::vector& indices) { + std::vector sizes; + size_t total_size = 0; + for (const auto& ind : indices) { + sizes.push_back(total_size); + total_size += ind.data_.size(); + } + data_ = Vec(total_size); + for_each_n(ExecutionPolicy::Par, countAt(0), indices.size(), [&](size_t i) { + std::copy(indices[i].data_.begin(), indices[i].data_.end(), + data_.begin() + sizes[i]); + }); + } size_t size() const { return data_.size() / sizeof(int64_t); } @@ -130,8 +143,8 @@ class SparseIndices { data_.size() / sizeof(int32_t)); } - inline void Add(int p, int q) { - for (unsigned int i = 0; i < sizeof(int64_t); ++i) data_.push_back(-1); + inline void Add(int p, int q, bool seq = false) { + for (unsigned int i = 0; i < sizeof(int64_t); ++i) data_.push_back(-1, seq); Set(size() - 1, p, q); } From e9fefb8bf203ec88802eb3ea5757e18f29df94cf Mon Sep 17 00:00:00 2001 From: pca006132 Date: Sat, 5 Oct 2024 14:02:35 +0800 Subject: [PATCH 03/12] fix tracy --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a8eb91328..2bbbc60e0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -141,5 +141,5 @@ if(TRACY_ENABLE) GIT_PROGRESS TRUE ) FetchContent_MakeAvailable(tracy) - target_link_libraries(manifold INTERFACE TracyClient) + target_link_libraries(manifold PUBLIC TracyClient) endif() From 03067c41030a42f80bce8e63b9916af577317b79 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Sat, 5 Oct 2024 14:02:42 +0800 Subject: [PATCH 04/12] slight optimization for polygon --- src/collider.h | 18 +++++++++++++----- src/polygon.cpp | 19 +++++++++++-------- src/sparse.h | 7 +++++-- src/vec.h | 24 +++++++++++++++++++++++- 4 files changed, 52 insertions(+), 16 deletions(-) diff --git a/src/collider.h b/src/collider.h index 1d26e20de..526878841 100644 --- a/src/collider.h +++ b/src/collider.h @@ -165,7 +165,7 @@ struct FindCollision { VecView> internalChildren_; Recorder recorder; - int RecordCollision(int node, const int queryIdx, SparseIndices& ind) { + inline int RecordCollision(int node, const int queryIdx, SparseIndices& ind) { bool overlaps = nodeBBox_[node].DoesOverlap(queries[queryIdx]); if (overlaps && IsLeaf(node)) { int leafIdx = Node2Leaf(node); @@ -323,7 +323,8 @@ class Collider { template - SparseIndices Collisions(const VecView& queriesIn) const { + void Collisions(const VecView& queriesIn, + SparseIndices& queryTri) const { ZoneScoped; using collider_internal::FindCollision; #ifdef MANIFOLD_PAR @@ -338,15 +339,22 @@ class Collider { std::vector tmp; store.combine_each( [&](SparseIndices& ind) { tmp.emplace_back(std::move(ind)); }); - return SparseIndices(tmp); + queryTri.FromIndices(tmp); + return; } #endif - SparseIndices queryTri; for_each_n(ExecutionPolicy::Seq, countAt(0), queriesIn.size(), FindCollision>{ queriesIn, nodeBBox_, internalChildren_, {queryTri}}); - return queryTri; + } + + template + SparseIndices Collisions(const VecView& queriesIn) const { + SparseIndices result; + Collisions(queriesIn, result); + return result; } static uint32_t MortonCode(vec3 position, Box bBox) { diff --git a/src/polygon.cpp b/src/polygon.cpp index b2904f70d..bbfc02b0b 100644 --- a/src/polygon.cpp +++ b/src/polygon.cpp @@ -489,7 +489,8 @@ class EarClip { // values < -precision so they will never affect validity. The first // totalCost is designed to give priority to sharper angles. Any cost < (-1 // - precision) has satisfied the Delaunay condition. - double EarCost(double precision, const IdxCollider &collider) const { + double EarCost(double precision, const IdxCollider &collider, + SparseIndices &toTest) const { vec2 openSide = left->pos - right->pos; const vec2 center = 0.5 * (left->pos + right->pos); const double scale = 4 / glm::dot(openSide, openSide); @@ -506,7 +507,7 @@ class EarClip { earBox.push_back({vec3(center.x - radius, center.y - radius, 0), vec3(center.x + radius, center.y + radius, 0)}); earBox.back().Union(vec3(pos, 0)); - const SparseIndices toTest = collider.collider.Collisions(earBox.cview()); + collider.collider.Collisions(earBox.cview(), toTest); const int lid = left->mesh_idx; const int rid = right->mesh_idx; @@ -522,7 +523,7 @@ class EarClip { totalCost = std::max(totalCost, cost); } } - + toTest.Clear(); return totalCost; } @@ -799,7 +800,8 @@ class EarClip { // Recalculate the cost of the Vert v ear, updating it in the queue by // removing and reinserting it. - void ProcessEar(VertItr v, const IdxCollider &collider) { + void ProcessEar(VertItr v, const IdxCollider &collider, + SparseIndices &buffer) { if (v->ear != earsQueue_.end()) { earsQueue_.erase(v->ear); v->ear = earsQueue_.end(); @@ -808,7 +810,7 @@ class EarClip { v->cost = kBest; v->ear = earsQueue_.insert(v); } else if (v->IsConvex(2 * precision_)) { - v->cost = v->EarCost(precision_, collider); + v->cost = v->EarCost(precision_, collider, buffer); v->ear = earsQueue_.insert(v); } else { v->cost = 1; // not used, but marks reflex verts for debug @@ -866,8 +868,9 @@ class EarClip { int numTri = -2; earsQueue_.clear(); + SparseIndices buffer; auto QueueVert = [&](VertItr v) { - ProcessEar(v, vertCollider); + ProcessEar(v, vertCollider, buffer); ++numTri; v->PrintVert(); }; @@ -890,8 +893,8 @@ class EarClip { ClipEar(v); --numTri; - ProcessEar(v->left, vertCollider); - ProcessEar(v->right, vertCollider); + ProcessEar(v->left, vertCollider, buffer); + ProcessEar(v->right, vertCollider, buffer); // This is a backup vert that is used if the queue is empty (geometrically // invalid polygon), to ensure manifoldness. v = v->right; diff --git a/src/sparse.h b/src/sparse.h index 5d01acdb8..9eedb16a3 100644 --- a/src/sparse.h +++ b/src/sparse.h @@ -59,7 +59,10 @@ class SparseIndices { SparseIndices() = default; SparseIndices(size_t size) { data_ = Vec(size * sizeof(int64_t)); } - SparseIndices(const std::vector& indices) { + + void Clear() { data_.clear(false); } + + void FromIndices(const std::vector& indices) { std::vector sizes; size_t total_size = 0; for (const auto& ind : indices) { @@ -144,7 +147,7 @@ class SparseIndices { } inline void Add(int p, int q, bool seq = false) { - for (unsigned int i = 0; i < sizeof(int64_t); ++i) data_.push_back(-1, seq); + data_.extend(sizeof(int64_t), seq); Set(size() - 1, p, q); } diff --git a/src/vec.h b/src/vec.h index d9fa70f18..f27ba6b76 100644 --- a/src/vec.h +++ b/src/vec.h @@ -154,6 +154,25 @@ class Vec : public VecView { this->ptr_[this->size_++] = val; } + inline void extend(size_t n, bool seq = false) { + if (this->size_ + n >= capacity_) { + size_t targetCap = capacity_ == 0 ? 128 : capacity_ * 2; + T *newBuffer = reinterpret_cast(malloc(targetCap * sizeof(T))); + ASSERT(newBuffer != nullptr, std::bad_alloc()); + TracyAllocS(newBuffer, targetCap * sizeof(T), 3); + if (this->size_ > 0) + manifold::copy(seq ? ExecutionPolicy::Seq : autoPolicy(this->size_), + this->ptr_, this->ptr_ + this->size_, newBuffer); + if (this->ptr_ != nullptr) { + TracyFreeS(this->ptr_, 3); + free(this->ptr_); + } + this->ptr_ = newBuffer; + capacity_ = targetCap; + } + this->size_ += n; + } + void reserve(size_t n, bool seq = false) { if (n > capacity_) { T *newBuffer = reinterpret_cast(malloc(n * sizeof(T))); @@ -184,7 +203,10 @@ class Vec : public VecView { void pop_back() { resize(this->size_ - 1); } - void clear() { resize(0); } + void clear(bool shrink = true) { + this->size_ = 0; + if (shrink) shrink_to_fit(); + } void shrink_to_fit() { T *newBuffer = nullptr; From 5229d3b8932c35184bbe1fddd6896c347734f987 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Sat, 5 Oct 2024 14:04:34 +0800 Subject: [PATCH 05/12] cleanup --- src/vec.h | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/vec.h b/src/vec.h index f27ba6b76..51cbad107 100644 --- a/src/vec.h +++ b/src/vec.h @@ -155,21 +155,8 @@ class Vec : public VecView { } inline void extend(size_t n, bool seq = false) { - if (this->size_ + n >= capacity_) { - size_t targetCap = capacity_ == 0 ? 128 : capacity_ * 2; - T *newBuffer = reinterpret_cast(malloc(targetCap * sizeof(T))); - ASSERT(newBuffer != nullptr, std::bad_alloc()); - TracyAllocS(newBuffer, targetCap * sizeof(T), 3); - if (this->size_ > 0) - manifold::copy(seq ? ExecutionPolicy::Seq : autoPolicy(this->size_), - this->ptr_, this->ptr_ + this->size_, newBuffer); - if (this->ptr_ != nullptr) { - TracyFreeS(this->ptr_, 3); - free(this->ptr_); - } - this->ptr_ = newBuffer; - capacity_ = targetCap; - } + if (this->size_ + n >= capacity_) + reserve(capacity_ == 0 ? 128 : capacity_ * 2, seq); this->size_ += n; } From eca5bbc4273312b9ee6d09b77f9ca820b7e0fa8b Mon Sep 17 00:00:00 2001 From: pca006132 Date: Sun, 6 Oct 2024 19:15:13 +0800 Subject: [PATCH 06/12] cleanup --- src/polygon.cpp | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/polygon.cpp b/src/polygon.cpp index bbfc02b0b..806f28096 100644 --- a/src/polygon.cpp +++ b/src/polygon.cpp @@ -308,6 +308,7 @@ class EarClip { struct IdxCollider { Collider collider; std::vector itr; + SparseIndices ind; }; // A circularly-linked list representing the polygon(s) that still need to be @@ -489,8 +490,7 @@ class EarClip { // values < -precision so they will never affect validity. The first // totalCost is designed to give priority to sharper angles. Any cost < (-1 // - precision) has satisfied the Delaunay condition. - double EarCost(double precision, const IdxCollider &collider, - SparseIndices &toTest) const { + double EarCost(double precision, IdxCollider &collider) const { vec2 openSide = left->pos - right->pos; const vec2 center = 0.5 * (left->pos + right->pos); const double scale = 4 / glm::dot(openSide, openSide); @@ -507,12 +507,12 @@ class EarClip { earBox.push_back({vec3(center.x - radius, center.y - radius, 0), vec3(center.x + radius, center.y + radius, 0)}); earBox.back().Union(vec3(pos, 0)); - collider.collider.Collisions(earBox.cview(), toTest); + collider.collider.Collisions(earBox.cview(), collider.ind); const int lid = left->mesh_idx; const int rid = right->mesh_idx; - for (size_t i = 0; i < toTest.size(); ++i) { - const VertItr test = collider.itr[toTest.Get(i, true)]; + for (size_t i = 0; i < collider.ind.size(); ++i) { + const VertItr test = collider.itr[collider.ind.Get(i, true)]; if (!Clipped(test) && test->mesh_idx != mesh_idx && test->mesh_idx != lid && test->mesh_idx != rid) { // Skip duplicated verts @@ -523,7 +523,7 @@ class EarClip { totalCost = std::max(totalCost, cost); } } - toTest.Clear(); + collider.ind.Clear(); return totalCost; } @@ -800,8 +800,7 @@ class EarClip { // Recalculate the cost of the Vert v ear, updating it in the queue by // removing and reinserting it. - void ProcessEar(VertItr v, const IdxCollider &collider, - SparseIndices &buffer) { + void ProcessEar(VertItr v, IdxCollider &collider) { if (v->ear != earsQueue_.end()) { earsQueue_.erase(v->ear); v->ear = earsQueue_.end(); @@ -810,7 +809,7 @@ class EarClip { v->cost = kBest; v->ear = earsQueue_.insert(v); } else if (v->IsConvex(2 * precision_)) { - v->cost = v->EarCost(precision_, collider, buffer); + v->cost = v->EarCost(precision_, collider); v->ear = earsQueue_.insert(v); } else { v->cost = 1; // not used, but marks reflex verts for debug @@ -857,7 +856,7 @@ class EarClip { void TriangulatePoly(VertItr start) { ZoneScoped; - const IdxCollider vertCollider = VertCollider(start); + IdxCollider vertCollider = VertCollider(start); if (vertCollider.itr.empty()) { PRINT("Empty poly"); @@ -868,9 +867,8 @@ class EarClip { int numTri = -2; earsQueue_.clear(); - SparseIndices buffer; auto QueueVert = [&](VertItr v) { - ProcessEar(v, vertCollider, buffer); + ProcessEar(v, vertCollider); ++numTri; v->PrintVert(); }; @@ -893,8 +891,8 @@ class EarClip { ClipEar(v); --numTri; - ProcessEar(v->left, vertCollider, buffer); - ProcessEar(v->right, vertCollider, buffer); + ProcessEar(v->left, vertCollider); + ProcessEar(v->right, vertCollider); // This is a backup vert that is used if the queue is empty (geometrically // invalid polygon), to ensure manifoldness. v = v->right; From de58f27644e88a641087622eff030e8b8ac34fd5 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Sun, 6 Oct 2024 19:46:15 +0800 Subject: [PATCH 07/12] CMAKE_CXX_FLAGS should also be a string --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index feccf217c..e62f28dae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,7 +109,7 @@ if(MANIFOLD_FUZZ) # enable fuzztest fuzzing mode set(FUZZTEST_FUZZING_MODE ON) # address sanitizer required - list(APPEND CMAKE_CXX_FLAGS -fsanitize=address) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") endif() if(TRACY_ENABLE) @@ -118,7 +118,7 @@ if(TRACY_ENABLE) list(APPEND MANIFOLD_FLAGS -DTRACY_MEMORY_USAGE) endif() if(NOT MSVC) - list(APPEND CMAKE_CXX_FLAGS -fno-omit-frame-pointer) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer") endif() else() option(CMAKE_BUILD_TYPE "Build type" Release) From 733ec0b9863ed4995daa60a9b6f63f9a98433e00 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Mon, 7 Oct 2024 02:48:09 +0800 Subject: [PATCH 08/12] slightly optimize polygon --- src/polygon.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/polygon.cpp b/src/polygon.cpp index 806f28096..03df9833b 100644 --- a/src/polygon.cpp +++ b/src/polygon.cpp @@ -21,6 +21,7 @@ #include "./collider.h" #include "./utils.h" #include "manifold/optional_assert.h" +#include "manifold/parallel.h" namespace { using namespace manifold; @@ -503,15 +504,16 @@ class EarClip { return totalCost; } - Vec earBox; - earBox.push_back({vec3(center.x - radius, center.y - radius, 0), - vec3(center.x + radius, center.y + radius, 0)}); - earBox.back().Union(vec3(pos, 0)); - collider.collider.Collisions(earBox.cview(), collider.ind); + Box earBox = Box{vec3(center.x - radius, center.y - radius, 0), + vec3(center.x + radius, center.y + radius, 0)}; + earBox.Union(vec3(pos, 0)); + collider.collider.Collisions(VecView(&earBox, 1), + collider.ind); const int lid = left->mesh_idx; const int rid = right->mesh_idx; - for (size_t i = 0; i < collider.ind.size(); ++i) { + + auto f = [&](size_t i) { const VertItr test = collider.itr[collider.ind.Get(i, true)]; if (!Clipped(test) && test->mesh_idx != mesh_idx && test->mesh_idx != lid && @@ -520,9 +522,13 @@ class EarClip { if (cost < -precision) { cost = DelaunayCost(test->pos - center, scale, precision); } - totalCost = std::max(totalCost, cost); + return cost; } - } + return std::numeric_limits::min(); + }; + totalCost = transform_reduce( + countAt(0), countAt(collider.ind.size()), totalCost, + [](double a, double b) { return std::max(a, b); }, f); collider.ind.Clear(); return totalCost; } From f68b01f4330c29629f1c300afe560a7a522b77c2 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Mon, 7 Oct 2024 11:51:39 +0800 Subject: [PATCH 09/12] should be lowest instead of min --- src/polygon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/polygon.cpp b/src/polygon.cpp index 03df9833b..41ef25fbd 100644 --- a/src/polygon.cpp +++ b/src/polygon.cpp @@ -524,7 +524,7 @@ class EarClip { } return cost; } - return std::numeric_limits::min(); + return std::numeric_limits::lowest(); }; totalCost = transform_reduce( countAt(0), countAt(collider.ind.size()), totalCost, From f6321777acb4d0c082907256b8fb16696d616d5f Mon Sep 17 00:00:00 2001 From: pca006132 Date: Tue, 8 Oct 2024 13:08:33 +0800 Subject: [PATCH 10/12] address comments --- src/polygon.cpp | 28 ++++++++++++++-------------- src/vec.h | 3 ++- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/polygon.cpp b/src/polygon.cpp index 41ef25fbd..66dc8e8ff 100644 --- a/src/polygon.cpp +++ b/src/polygon.cpp @@ -513,22 +513,22 @@ class EarClip { const int lid = left->mesh_idx; const int rid = right->mesh_idx; - auto f = [&](size_t i) { - const VertItr test = collider.itr[collider.ind.Get(i, true)]; - if (!Clipped(test) && test->mesh_idx != mesh_idx && - test->mesh_idx != lid && - test->mesh_idx != rid) { // Skip duplicated verts - double cost = Cost(test, openSide, precision); - if (cost < -precision) { - cost = DelaunayCost(test->pos - center, scale, precision); - } - return cost; - } - return std::numeric_limits::lowest(); - }; totalCost = transform_reduce( countAt(0), countAt(collider.ind.size()), totalCost, - [](double a, double b) { return std::max(a, b); }, f); + [](double a, double b) { return std::max(a, b); }, + [&](size_t i) { + const VertItr test = collider.itr[collider.ind.Get(i, true)]; + if (!Clipped(test) && test->mesh_idx != mesh_idx && + test->mesh_idx != lid && + test->mesh_idx != rid) { // Skip duplicated verts + double cost = Cost(test, openSide, precision); + if (cost < -precision) { + cost = DelaunayCost(test->pos - center, scale, precision); + } + return cost; + } + return std::numeric_limits::lowest(); + }); collider.ind.Clear(); return totalCost; } diff --git a/src/vec.h b/src/vec.h index 51cbad107..9d8273503 100644 --- a/src/vec.h +++ b/src/vec.h @@ -156,7 +156,8 @@ class Vec : public VecView { inline void extend(size_t n, bool seq = false) { if (this->size_ + n >= capacity_) - reserve(capacity_ == 0 ? 128 : capacity_ * 2, seq); + reserve(capacity_ == 0 ? 128 : std::max(capacity_ * 2, this->size_ + n), + seq); this->size_ += n; } From 027c0e0001497d3fdee6e7cf8b8b22a121785fb7 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Wed, 9 Oct 2024 15:40:15 +0800 Subject: [PATCH 11/12] update MANIFOLD_PAR guard --- src/collider.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/collider.h b/src/collider.h index 526878841..5cc570408 100644 --- a/src/collider.h +++ b/src/collider.h @@ -23,7 +23,7 @@ #include #endif -#ifdef MANIFOLD_PAR +#if (MANIFOLD_PAR == 1) #include #endif @@ -217,7 +217,7 @@ struct SeqCollisionRecorder { SparseIndices& local() { return queryTri_; } }; -#ifdef MANIFOLD_PAR +#if (MANIFOLD_PAR == 1) template struct ParCollisionRecorder { tbb::combinable& store; @@ -327,7 +327,7 @@ class Collider { SparseIndices& queryTri) const { ZoneScoped; using collider_internal::FindCollision; -#ifdef MANIFOLD_PAR +#if (MANIFOLD_PAR == 1) if (queriesIn.size() > collider_internal::kSequentialThreshold) { tbb::combinable store; for_each_n( From 7ccf97f4131432229a610c9adde706cabebc3bb6 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Wed, 9 Oct 2024 20:43:09 +0800 Subject: [PATCH 12/12] try to fix black CI --- .github/workflows/check_format.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check_format.yml b/.github/workflows/check_format.yml index bb82889e3..bf56a2fce 100644 --- a/.github/workflows/check_format.yml +++ b/.github/workflows/check_format.yml @@ -18,14 +18,14 @@ jobs: exclude: '*/third_party' extensions: 'h,cpp,js,ts,html' clangFormatVersion: 18 - - uses: psf/black@stable - with: - options: "--check --verbose" - src: "./bindings/python/examples" - uses: actions/setup-python@v5 with: python-version: '3.12' cache: 'pip' + - uses: psf/black@stable + with: + options: "--check --verbose" + src: "./bindings/python/examples" - name: "gersemi cmake check" run: | pip3 install gersemi