-
Notifications
You must be signed in to change notification settings - Fork 110
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
sdf interpreter #1122
base: master
Are you sure you want to change the base?
sdf interpreter #1122
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1122 +/- ##
==========================================
- Coverage 91.46% 88.77% -2.70%
==========================================
Files 30 37 +7
Lines 5908 6617 +709
==========================================
+ Hits 5404 5874 +470
- Misses 504 743 +239 ☔ View full report in Codecov by Sentry. |
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.
The API, code generation (with simple optimization) and execution should be done. There are limits on the number of variables and registers that can be used in the code gen, but I will lift that in a day or two, hopefully.
The actual SDF to mesh algorithm is not yet written, will probably take more time to do that.
|
||
// not really a precise implementation... | ||
template <typename Domain> | ||
struct Interval { |
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 a pretty general interval arithmetic implementation that covers common math functions. I tried to make things as precise as possible, but things like mod, for example, is hard to make precise and probably subject to changes later.
} | ||
|
||
constexpr Interval mod(double m) const { | ||
// FIXME: cannot deal with negative m right 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.
This is not the typical std::fmod
in c++, this returns something positive. This is nicer to interval arithmetic, and IMO more natural. For negative m
however, I am not sure what I should do with that, maybe give something negative?
while (1) { | ||
OpCode current = static_cast<OpCode>(tape[i]); | ||
// fast binary/ternary operations | ||
if (current >= OpCode::ADD) { |
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 the bytecode interpreter implementation, which is optimized a bit for faster common arithmetic operations, but can still extend to other domains. It uses operator overloading for common arithmetic operations, but fallback to specialization for other functions (because I can't add a sin
method for double, for example).
|
||
enum class OpCode : uint8_t { | ||
NOP, | ||
RETURN, |
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.
Because SDFs typically doesn't have control flow, there is no control flow here as well. Complicated SDFs will require runtime optimization for fast execution and optimize away the unused path, which will be implemented later.
src/sdf/value.cpp
Outdated
std::vector<VO> stack; | ||
if (kind == ValueKind::OPERATION) stack.push_back(std::get<VO>(v)); | ||
|
||
auto getOperand = [&](Value x, std::function<void(Value)> f) { |
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.
Simple recursive algorithm to convert the value AST to IR in context.h
. context.h
handles the IR, performs optimization and eventually output a bytecode tape.
We don't do any optimization here, so it is quite simple (except using an explicit loop instead of recursion).
|
||
struct ValueOperation; | ||
|
||
class Value { |
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.
AST type for users to build their SDF. Intended to be public.
|
||
EvalContext<double> ctxSimple{ | ||
tape.first, VecView(tape.second.data(), tape.second.size())}; | ||
for (double x = -period; x < period; x += period / 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.
We do grid evaluation to check if the SDF is correct.
ok spilling done |
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
// rewrite of https://github.com/p-ranav/small_vector |
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.
Originally I copied it from the repository mentioned, but it turns out it was too buggy and misses some required functions, so I changed it quite a bit.
I am following commits and drooling a bit :) ... pls post some pics when you will have some examples to show :) |
I'm still working on the code generation and tape optimization stuff. I'm more comfortable with that and less familiar with the mesh generation code. @elalish would be great if you can give a level set function that takes two functions, one that outputs an interval and one regular, and I can plug the stuff here in. |
I'm not quite sure what you mean; can you give an example? |
using namespace manifold; | ||
using namespace manifold::sdf; | ||
|
||
int recursive_interval(EvalContext<Interval<double>>& ctx, vec3 start, |
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.
@elalish change NearSurface
into something like this that is recursive and takes a function that outputs an interval.
}; | ||
Value d = Value::Constant(0); | ||
for (const auto& ball : balls) { | ||
auto tmp = smoothstepFn(Value::Constant(-1), Value::Constant(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.
@elalish example here
Re. Soundness of the interval arithmetic implementation, I think there is a succinct argument why this is sound w.r.t. floating point arithmetic (not real arithmetic, though). What we want to argue is:
The argument basically boils down to monotonicity and case analysis. For monotonic functions, we know that For addition, it is also monotonic w.r.t. each of its argument, i.e. For multiplication, it is slightly more complicated because we need to split the intervals and union them back, as well as do case analysis. If Negation may cause issues when the rounding mode is round up/down (whether it is a real issue requires some more thought), but for the common case of round-to-nearest and ties round-to-even, the result should be the same regardless of sign, so the argument should work for us. |
Some initial implementation for sdf interpreter. Names are quite random, I have no idea how I should name these.
Basically, there are four things here:
sdf/interval.h
). Note that it is not robust against things like NaN and rounding errors (yet).sdf/value.h
), and this file is intended to go to the public API later, probably with another name. It is currently using methods, but I am happy to make it use regular functions.sdf/context.h
) that transforms the value into low level opcode, and does some simple optimization. Currently, it only performs constant folding and convert (a * b + c) into FMA, but I plan to do a bit more than these. It is also very limited because I haven't yet added spilling support, and it can only support 256 variables for now.tape.h
) that is pretty fast, and does both pointwise evaluation and interval evaluation. Optimizations related to interval evaluation are not implemented yet, but I have idea about how to do them. I will probably add more opcode later as well, e.g. load store for spilling.And in the test file (
sdf_tape_test.cpp
), I implemented a simple version of manifold dual contouring (the algorithm that uses an octree and interval arithmetic) but without actually constructing the mesh. I do this just to test the number of evaluations needed. Note that I haven't yet fully debugged my implementation, so there may be bugs that can throw off the result...Some initial statistics:
Do note that this is run without parallelization. I haven't yet tried to implement the interval evaluation with parallelization yet.