-
Notifications
You must be signed in to change notification settings - Fork 114
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
Fix sparseindices #742
Fix sparseindices #742
Conversation
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## master #742 +/- ##
==========================================
- Coverage 91.67% 91.50% -0.18%
==========================================
Files 37 37
Lines 4722 4766 +44
==========================================
+ Hits 4329 4361 +32
- Misses 393 405 +12 ☔ View full report in Codecov by Sentry. |
OK it is due to undefined behavior: |
@pca006132 That sounds right - the original inputs were indeed long, super-thin cylinders. |
Yes - exporting the mesh and visually inspecting it, it appears to be correct. (For the record, our input meshes were rather messed up in the first place, but technically they were valid manifolds.) |
@elalish btw not sure if we should use the generic |
Good question. I think I only defined custom exceptions for things that are unique to manifold. Out of range sounds reasonable for using the standard. |
one issue though: std::out_of_range is usually what you get when there is a bug, so it is generally hard to recover. However, in our case it means the operation on large meshes is not supported. |
Fair, though I'm not really sure how you'd recover from a too-large mesh either, besides just cancel everything and post a message to the user. |
The way I view this is similar to rust: if it is a software bug, e.g. the normal index out of bound thing, we should just abort (although we are not that strict now). Otherwise, it is an user error and user can cancel the operation. |
src/manifold/src/boolean3.cpp
Outdated
for_each_n(autoPolicy(p2q1.size()), zip(countAt(p1q2.size()), countAt(0)), | ||
for_each_n( | ||
autoPolicy(p1q2.size()), | ||
zip(countAt(static_cast<size_t>(0)), countAt(static_cast<size_t>(0))), |
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.
Can we just tweak the countAt
declaration somehow so we don't need all these static_cast
?
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.
Yes if we want to change everything into using size_t
. Thinking about it, we can also use 0ll
to replace the static_cast
. I used to use 0l
but it errors on Windows, because long
is 32-bits on Windows and we need long long
, so I just switched everything into static_cast
...
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.
ah, nevermind, https://en.cppreference.com/w/cpp/language/integer_literal size_t suffix is only supported on c++23, we can use something user defined though.
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.
now we can use 0_z
where the _z
suffix specifies the number is a size_t
.
triVerts.resize(triCount.back()); | ||
triNormal.resize(triCount.back()); | ||
triRef.resize(triCount.back()); | ||
|
||
auto processFace2 = std::bind( | ||
processFace, [&](int face) { return std::move(results[face]); }, | ||
processFace, [&](size_t face) { return std::move(results[face]); }, |
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.
If we aren't allowing more than MAX_INT faces, why do we need to index them with size_t
now?
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.
Ah this was to please the compiler, because we count with size_t
below. Can change to int
and do a cast below.
|
||
inline int& Get(int i, bool use_q) { | ||
inline int& Get(size_t i, bool use_q) { |
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.
These look like the critical changes to fix the bug, right?
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.
yes, but not only here because we need to make sure that code in boolean3.cpp
handles this correctly.
src/manifold/src/boolean3.cpp
Outdated
@@ -578,6 +584,14 @@ Boolean3::Boolean3(const Manifold::Impl &inP, const Manifold::Impl &inQ, | |||
Intersect12(inQ, inP, s20, p2q0, s11, p1q1, z20, xyzz11, p2q1_, false); | |||
PRINT("x21 size = " << x21_.size()); | |||
|
|||
if (p0q2.size() >= std::numeric_limits<int>::max() || | |||
p2q0.size() >= std::numeric_limits<int>::max() || |
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.
Why should 0-2 (vertices shadowing triangles) have to be fewer than INT_MAX? That's still worst case an O(n^2) combination problem. The other four below this are actually contributing verts, so the check makes more sense (though shouldn't it be their sum?).
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.
Indeed, I was thinking that p0
/q0
have the same size as p0q2
/p2q0
, and thought that they affect the size of w03_
and w30_
. Will address this.
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.
fixed, need to check if the current one is correct.
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.
Thanks, looks like we're close now.
src/manifold/src/boolean3.cpp
Outdated
for_each_n(autoPolicy(p0q2.size()), zip(countAt(0), s02.begin(), z02.begin()), | ||
p0q2.size(), | ||
for_each_n(autoPolicy(p0q2.size()), | ||
zip(countAt(0ll), s02.begin(), z02.begin()), p0q2.size(), |
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.
Is there a reason this one isn't 0_z
?
src/manifold/src/boolean3.cpp
Outdated
@@ -578,6 +578,10 @@ Boolean3::Boolean3(const Manifold::Impl &inP, const Manifold::Impl &inQ, | |||
Intersect12(inQ, inP, s20, p2q0, s11, p1q1, z20, xyzz11, p2q1_, false); | |||
PRINT("x21 size = " << x21_.size()); | |||
|
|||
if (x12_.size() + x21_.size() >= std::numeric_limits<int>::max() || | |||
w03_.size() + w30_.size() >= std::numeric_limits<int>::max()) |
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.
Thinking about this, we can probably remove w03 and w30 too: they always just have size of P.NumVert() and Q.NumVert() respectively.
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.
ah, make sense
src/sdf/src/sdf.cpp
Outdated
2 * maxMorton, static_cast<Uint64>(10 * glm::pow(maxMorton, 0.667))); | ||
HashTable<GridVert, identity> gridVerts(tableSize); | ||
Vec<glm::vec3> vertPos(gridVerts.Size() * 7); | ||
|
||
while (1) { | ||
Vec<int> index(1, 0); | ||
for_each_n(pol, countAt(0), maxMorton + 1, | ||
for_each_n(pol, countAt(0ul), maxMorton + 1, |
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.
0_z
?
src/utilities/include/public.h
Outdated
@@ -31,6 +31,8 @@ | |||
#include <sstream> | |||
#endif | |||
|
|||
constexpr std::size_t operator"" _z(unsigned long long n) { return n; } |
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.
This is cool, but it looks like emscripten doesn't like it?
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.
Thanks!
* accept size_t for Vec * Prepare a test case for "SparseIndices too large" core dump * fix python docstring deps * fix compilation errors * limit sdf hashtable size * another fix to hashtable * log2(0) is undefined * fix formatting * Revert "fix compilation errors" This reverts commit adfb86c. * fix size_t stuff * remove processOverlap setting in test * fix hashtable * vertex number limit * address comments
The changes are everywhere... Basically this PR does two things:
size_t
forVec
indices when possible.Vec
that are too large (>= INT32_MAX
) so other parts that rely on int32 indices will not break.1 is easy, 2 is hard, and I am pretty sure that there are missing cases or cases that can be relaxed a bit, but still it is better than the status quo.
resolves #720, resolves #738, resolves #740.
Btw @starseeker can you verify that the result is correct? The result is like 3 really long rods that are connected at the end or something, the mesh is extremely slim making it hard to screenshot...