-
Notifications
You must be signed in to change notification settings - Fork 115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Faster import #1139
Faster import #1139
Changes from all commits
6ee1cba
f3e831f
b0a2468
ee384a2
32290b9
9eff95a
3cd5084
63c3833
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -68,12 +68,20 @@ | |
// two original triangles. | ||
const TriRef ref0 = triRef[edge / 3]; | ||
int current = NextHalfedge(halfedge[edge].pairedHalfedge); | ||
const TriRef ref1 = triRef[current / 3]; | ||
TriRef ref1 = triRef[current / 3]; | ||
bool ref1Updated = !ref0.SameFace(ref1); | ||
while (current != edge) { | ||
current = NextHalfedge(halfedge[current].pairedHalfedge); | ||
int tri = current / 3; | ||
const TriRef ref = triRef[tri]; | ||
if (!ref.SameFace(ref0) && !ref.SameFace(ref1)) return false; | ||
if (!ref.SameFace(ref0) && !ref.SameFace(ref1)) { | ||
if (!ref1Updated) { | ||
ref1 = ref; | ||
ref1Updated = true; | ||
} else { | ||
return false; | ||
} | ||
} | ||
} | ||
return true; | ||
} | ||
|
@@ -109,14 +117,78 @@ | |
} | ||
}; | ||
|
||
struct SortEntry { | ||
int start; | ||
int end; | ||
size_t index; | ||
inline bool operator<(const SortEntry& other) const { | ||
return start == other.start ? end < other.end : start < other.start; | ||
struct FlagStore { | ||
#if MANIFOLD_PAR == 1 | ||
tbb::combinable<std::vector<size_t>> store; | ||
#endif | ||
std::vector<size_t> s; | ||
|
||
template <typename Pred, typename F> | ||
void run_seq(size_t n, Pred pred, F f) { | ||
for (size_t i = 0; i < n; ++i) | ||
if (pred(i)) s.push_back(i); | ||
for (size_t i : s) f(i); | ||
s.clear(); | ||
} | ||
|
||
#if MANIFOLD_PAR == 1 | ||
template <typename Pred, typename F> | ||
void run_par(size_t n, Pred pred, F f) { | ||
// Test pred in parallel, store i into thread-local vectors when pred(i) is | ||
// true. After testing pred, iterate and call f over the indices in | ||
// ascending order by using a heap in a single thread | ||
auto& store = this->store; | ||
tbb::parallel_for(tbb::blocked_range<size_t>(0, n), | ||
[&store, &pred, f](const auto& r) { | ||
auto& local = store.local(); | ||
for (auto i = r.begin(); i < r.end(); ++i) { | ||
if (pred(i)) local.push_back(i); | ||
} | ||
}); | ||
|
||
std::vector<std::vector<size_t>> stores; | ||
// first index: index within the vector | ||
// second index: vector index within stores | ||
using P = std::pair<size_t, size_t>; | ||
auto cmp = [&stores](P a, P b) { | ||
return stores[a.second][a.first] < stores[b.second][b.first]; | ||
}; | ||
std::vector<P> s; | ||
|
||
store.combine_each([&stores, &s, &cmp](std::vector<size_t>& local) { | ||
if (local.empty()) return; | ||
stores.emplace_back(std::move(local)); | ||
s.push_back(std::make_pair(0, stores.size() - 1)); | ||
std::push_heap(s.begin(), s.end(), cmp); | ||
}); | ||
while (!s.empty()) { | ||
std::pop_heap(s.begin(), s.end()); | ||
auto [a, b] = s.back(); | ||
auto i = stores[b][a]; | ||
if (a + 1 == stores[b].size()) { | ||
s.pop_back(); | ||
} else { | ||
s.back().first++; | ||
std::push_heap(s.begin(), s.end(), cmp); | ||
} | ||
f(i); | ||
} | ||
} | ||
#endif | ||
|
||
template <typename Pred, typename F> | ||
void run(size_t n, Pred pred, F f) { | ||
#if MANIFOLD_PAR == 1 | ||
if (n > 1e5) { | ||
run_par(n, pred, f); | ||
} else | ||
#endif | ||
{ | ||
run_seq(n, pred, f); | ||
} | ||
} | ||
}; | ||
|
||
} // namespace | ||
|
||
namespace manifold { | ||
|
@@ -132,30 +204,66 @@ | |
// verts. They must be removed before edge collapse. | ||
SplitPinchedVerts(); | ||
|
||
Vec<int> entries; | ||
FlagStore s; | ||
while (1) { | ||
ZoneScopedN("DedupeEdge"); | ||
|
||
const size_t nbEdges = halfedge_.size(); | ||
size_t numFlagged = 0; | ||
|
||
Vec<SortEntry> entries; | ||
entries.reserve(nbEdges / 2); | ||
for (size_t i = 0; i < nbEdges; ++i) { | ||
if (halfedge_[i].IsForward()) { | ||
entries.push_back({halfedge_[i].startVert, halfedge_[i].endVert, i}); | ||
#if MANIFOLD_PAR == 1 | ||
if (nbEdges > 1e5) { | ||
// Note that this is slightly different from the single thread version | ||
// because we store all indices instead of just indices of forward | ||
// halfedges. Backward halfedges are placed at the end by modifying the | ||
// comparison function. | ||
entries.resize_nofill(nbEdges); | ||
sequence(entries.begin(), entries.end()); | ||
stable_sort(entries.begin(), entries.end(), [&](int a, int b) { | ||
const auto& self = halfedge_[a]; | ||
const auto& other = halfedge_[b]; | ||
// place all backward edges at the end | ||
if (!self.IsForward()) return false; | ||
if (!other.IsForward()) return true; | ||
// dictionary order based on start and end vertices | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we sort differently for parallel and serial? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is now explained in the comment:
|
||
return self.startVert == other.startVert | ||
? self.endVert < other.endVert | ||
: self.startVert < other.startVert; | ||
}); | ||
entries.resize(nbEdges / 2); | ||
} else | ||
#endif | ||
{ | ||
entries.clear(true); | ||
entries.reserve(nbEdges / 2); | ||
for (size_t i = 0; i < nbEdges; ++i) { | ||
if (halfedge_[i].IsForward()) { | ||
entries.push_back(i); | ||
} | ||
} | ||
stable_sort(entries.begin(), entries.end(), [&](int a, int b) { | ||
const auto& self = halfedge_[a]; | ||
const auto& other = halfedge_[b]; | ||
// dictionary order based on start and end vertices | ||
return self.startVert == other.startVert | ||
? self.endVert < other.endVert | ||
: self.startVert < other.startVert; | ||
}); | ||
} | ||
|
||
stable_sort(entries.begin(), entries.end()); | ||
for (size_t i = 0; i < entries.size() - 1; ++i) { | ||
const int h0 = entries[i].index; | ||
const int h1 = entries[i + 1].index; | ||
if (halfedge_[h0].startVert == halfedge_[h1].startVert && | ||
halfedge_[h0].endVert == halfedge_[h1].endVert) { | ||
DedupeEdge(entries[i].index); | ||
numFlagged++; | ||
} | ||
} | ||
s.run( | ||
entries.size() - 1, | ||
[&](int i) { | ||
const int h0 = entries[i]; | ||
const int h1 = entries[i + 1]; | ||
return (halfedge_[h0].startVert == halfedge_[h1].startVert && | ||
halfedge_[h0].endVert == halfedge_[h1].endVert); | ||
}, | ||
[&](int i) { | ||
DedupeEdge(entries[i]); | ||
numFlagged++; | ||
}); | ||
|
||
if (numFlagged == 0) break; | ||
|
||
|
@@ -199,23 +307,20 @@ | |
const size_t nbEdges = halfedge_.size(); | ||
auto policy = autoPolicy(nbEdges, 1e5); | ||
size_t numFlagged = 0; | ||
Vec<uint8_t> bFlags(nbEdges); | ||
|
||
std::vector<int> scratchBuffer; | ||
scratchBuffer.reserve(10); | ||
|
||
FlagStore s; | ||
{ | ||
ZoneScopedN("CollapseShortEdge"); | ||
numFlagged = 0; | ||
ShortEdge se{halfedge_, vertPos_, epsilon_}; | ||
for_each_n(policy, countAt(0_uz), nbEdges, | ||
[&](size_t i) { bFlags[i] = se(i); }); | ||
for (size_t i = 0; i < nbEdges; ++i) { | ||
if (bFlags[i]) { | ||
CollapseEdge(i, scratchBuffer); | ||
scratchBuffer.resize(0); | ||
numFlagged++; | ||
} | ||
} | ||
s.run(nbEdges, se, [&](size_t i) { | ||
CollapseEdge(i, scratchBuffer); | ||
scratchBuffer.resize(0); | ||
numFlagged++; | ||
}); | ||
} | ||
|
||
#ifdef MANIFOLD_DEBUG | ||
|
@@ -229,15 +334,11 @@ | |
ZoneScopedN("CollapseFlaggedEdge"); | ||
numFlagged = 0; | ||
FlagEdge se{halfedge_, meshRelation_.triRef}; | ||
for_each_n(policy, countAt(0_uz), nbEdges, | ||
[&](size_t i) { bFlags[i] = se(i); }); | ||
for (size_t i = 0; i < nbEdges; ++i) { | ||
if (bFlags[i]) { | ||
CollapseEdge(i, scratchBuffer); | ||
scratchBuffer.resize(0); | ||
numFlagged++; | ||
} | ||
} | ||
s.run(nbEdges, se, [&](size_t i) { | ||
CollapseEdge(i, scratchBuffer); | ||
scratchBuffer.resize(0); | ||
numFlagged++; | ||
}); | ||
} | ||
|
||
#ifdef MANIFOLD_DEBUG | ||
|
@@ -251,23 +352,19 @@ | |
ZoneScopedN("RecursiveEdgeSwap"); | ||
numFlagged = 0; | ||
SwappableEdge se{halfedge_, vertPos_, faceNormal_, tolerance_}; | ||
for_each_n(policy, countAt(0_uz), nbEdges, | ||
[&](size_t i) { bFlags[i] = se(i); }); | ||
std::vector<int> edgeSwapStack; | ||
std::vector<int> visited(halfedge_.size(), -1); | ||
int tag = 0; | ||
for (size_t i = 0; i < nbEdges; ++i) { | ||
if (bFlags[i]) { | ||
numFlagged++; | ||
tag++; | ||
RecursiveEdgeSwap(i, tag, visited, edgeSwapStack, scratchBuffer); | ||
while (!edgeSwapStack.empty()) { | ||
int last = edgeSwapStack.back(); | ||
edgeSwapStack.pop_back(); | ||
RecursiveEdgeSwap(last, tag, visited, edgeSwapStack, scratchBuffer); | ||
} | ||
s.run(nbEdges, se, [&](size_t i) { | ||
numFlagged++; | ||
tag++; | ||
RecursiveEdgeSwap(i, tag, visited, edgeSwapStack, scratchBuffer); | ||
while (!edgeSwapStack.empty()) { | ||
int last = edgeSwapStack.back(); | ||
edgeSwapStack.pop_back(); | ||
RecursiveEdgeSwap(last, tag, visited, edgeSwapStack, scratchBuffer); | ||
} | ||
} | ||
}); | ||
} | ||
|
||
#ifdef MANIFOLD_DEBUG | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -297,7 +297,7 @@ void Manifold::Impl::InitializeOriginal(bool keepFaceID) { | |
const int meshID = ReserveIDs(1); | ||
meshRelation_.originalID = meshID; | ||
auto& triRef = meshRelation_.triRef; | ||
triRef.resize(NumTri()); | ||
triRef.resize_nofill(NumTri()); | ||
for_each_n(autoPolicy(NumTri(), 1e5), countAt(0), NumTri(), | ||
[meshID, keepFaceID, &triRef](const int tri) { | ||
triRef[tri] = {meshID, meshID, tri, | ||
|
@@ -390,8 +390,8 @@ void Manifold::Impl::CreateHalfedges(const Vec<ivec3>& triVerts) { | |
const size_t numTri = triVerts.size(); | ||
const int numHalfedge = 3 * numTri; | ||
// drop the old value first to avoid copy | ||
halfedge_.resize(0); | ||
halfedge_.resize(numHalfedge); | ||
halfedge_.clear(true); | ||
halfedge_.resize_nofill(numHalfedge); | ||
Vec<uint64_t> edge(numHalfedge); | ||
Vec<int> ids(numHalfedge); | ||
auto policy = autoPolicy(numTri, 1e5); | ||
|
@@ -470,11 +470,11 @@ void Manifold::Impl::Update() { | |
|
||
void Manifold::Impl::MarkFailure(Error status) { | ||
bBox_ = Box(); | ||
vertPos_.resize(0); | ||
halfedge_.resize(0); | ||
vertNormal_.resize(0); | ||
faceNormal_.resize(0); | ||
halfedgeTangent_.resize(0); | ||
vertPos_.clear(); | ||
halfedge_.clear(); | ||
vertNormal_.clear(); | ||
faceNormal_.clear(); | ||
halfedgeTangent_.clear(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
meshRelation_ = MeshRelationD(); | ||
status_ = status; | ||
} | ||
|
@@ -493,7 +493,7 @@ void Manifold::Impl::WarpBatch(std::function<void(VecView<vec3>)> warpFunc) { | |
return; | ||
} | ||
Update(); | ||
faceNormal_.resize(0); // force recalculation of triNormal | ||
faceNormal_.clear(); // force recalculation of triNormal | ||
CalculateNormals(); | ||
SetEpsilon(); | ||
Finish(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps a comment about what this struct is used for? I don't entirely follow from reading it.