Skip to content
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

Merged
merged 8 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/boolean_result.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ std::tuple<Vec<int>, Vec<int>> SizeOutput(
int numFaceR = facePQ2R.back();
facePQ2R.resize(inP.NumTri() + inQ.NumTri());

outR.faceNormal_.resize(numFaceR);
outR.faceNormal_.resize_nofill(numFaceR);

Vec<size_t> tmpBuffer(outR.faceNormal_.size());
auto faceIds = TransformIterator(countAt(0_uz), [&sidesPerFacePQ](size_t i) {
Expand Down Expand Up @@ -563,7 +563,7 @@ void CreateProperties(Manifold::Impl &outR, const Manifold::Impl &inP,
if (numProp == 0) return;

const int numTri = outR.NumTri();
outR.meshRelation_.triProperties.resize(numTri);
outR.meshRelation_.triProperties.resize_nofill(numTri);

Vec<vec3> bary(outR.halfedge_.size());
for_each_n(autoPolicy(numTri, 1e4), countAt(0), numTri,
Expand Down Expand Up @@ -746,7 +746,7 @@ Manifold::Impl Boolean3::Result(OpType op) const {
outR.epsilon_ = std::max(inP_.epsilon_, inQ_.epsilon_);
outR.tolerance_ = std::max(inP_.tolerance_, inQ_.tolerance_);

outR.vertPos_.resize(numVertR);
outR.vertPos_.resize_nofill(numVertR);
// Add vertices, duplicating for inclusion numbers not in [-1, 1].
// Retained vertices from P and Q:
for_each_n(autoPolicy(inP_.NumVert(), 1e4), countAt(0), inP_.NumVert(),
Expand Down
2 changes: 1 addition & 1 deletion src/collider.h
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ class Collider {
"vectors must be the same length");
int num_nodes = 2 * leafBB.size() - 1;
// assign and allocate members
nodeBBox_.resize(num_nodes);
nodeBBox_.resize_nofill(num_nodes);
nodeParent_.resize(num_nodes, -1);
internalChildren_.resize(leafBB.size() - 1, std::make_pair(-1, -1));
// organize tree
Expand Down
10 changes: 5 additions & 5 deletions src/csg_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,15 +206,15 @@ std::shared_ptr<CsgLeafNode> CsgLeafNode::Compose(
Manifold::Impl combined;
combined.epsilon_ = epsilon;
combined.tolerance_ = tolerance;
combined.vertPos_.resize(numVert);
combined.halfedge_.resize(2 * numEdge);
combined.faceNormal_.resize(numTri);
combined.vertPos_.resize_nofill(numVert);
combined.halfedge_.resize_nofill(2 * numEdge);
combined.faceNormal_.resize_nofill(numTri);
combined.halfedgeTangent_.resize(2 * numEdge);
combined.meshRelation_.triRef.resize(numTri);
combined.meshRelation_.triRef.resize_nofill(numTri);
if (numPropOut > 0) {
combined.meshRelation_.numProp = numPropOut;
combined.meshRelation_.properties.resize(numPropOut * numPropVert, 0);
combined.meshRelation_.triProperties.resize(numTri);
combined.meshRelation_.triProperties.resize_nofill(numTri);
}
auto policy = autoPolicy(numTri);

Expand Down
207 changes: 152 additions & 55 deletions src/edge_op.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 {
Copy link
Owner

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.

#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 {
Expand All @@ -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
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we sort differently for parallel and serial?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now explained in the comment:

      // 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.

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;

Check warning on line 251 in src/edge_op.cpp

View check run for this annotation

Codecov / codecov/patch

src/edge_op.cpp#L251

Added line #L251 was not covered by tests
});
}

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;

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/face_op.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ void Manifold::Impl::Face2Tri(const Vec<int>& faceEdge,
Vec<ivec3> triVerts;
Vec<vec3> triNormal;
Vec<TriRef>& triRef = meshRelation_.triRef;
triRef.resize(0);
triRef.clear();
auto processFace = [&](GeneralTriangulation general, AddTriangle addTri,
int face) {
const int firstEdge = faceEdge[face];
Expand Down
18 changes: 9 additions & 9 deletions src/impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

meshRelation_ = MeshRelationD();
status_ = status;
}
Expand All @@ -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();
Expand Down
Loading
Loading