// ============================================================================
// MESH SDF COST — is a 563k-triangle shell too big to subtract? #25 §2.
//
// Written because I asserted 563544 triangles was "likely impractical" without
// measuring it, which was speculation dressed as a finding. The BVH is
// logarithmic and the measurement says so:
//
// csg4 shell 3605 tris build 21 ms 3420 ns/sample
// voxel boundary 563544 tris build 1449 ms 4587 ns/sample
//
// 156x the triangles for 1.34x the cost — log2(156) = 7.3 extra levels of
// descent. TRIANGLE COUNT IS NOT THE CONSTRAINT. Build time (one-off at import,
// roughly doubling the level's own 1320 ms) and ~100 MB of GPU buffers at
// 128 B/tri + 32 B/node are the real costs, and memory is the one that matters.
//
// CAVEAT, and it is not small: this is CPU and single-threaded. The density
// shader runs the same traversal on the GPU across thousands of threads and is
// more sensitive to divergence, so the ABSOLUTE figures here do not transfer.
// What transfers is the RATIO — that is a property of the tree, not the machine.
//
// Input: /tmp/outer_shell.obj and /tmp/shell_voxel.obj (tests/test_outer_shell.cpp)
// Build: g++ -O2 -std=c++17 -I libdc/include -o /tmp/sdfbench \
// tests/test_mesh_sdf_bench.cpp libdc/src/generators/dc_generator_mesh_sdf.cpp
// ============================================================================
#include "dc_generator_mesh_sdf.h"
#include <cstdio>
#include <chrono>
#include <random>
using clk = std::chrono::high_resolution_clock;
static void bench(const char* path, const char* label) {
DCGeneratorMeshSDF sdf;
auto t0 = clk::now();
if (!sdf.load_obj(path)) { printf("%s: LOAD FAILED\n", label); return; }
auto t1 = clk::now();
double build_ms = std::chrono::duration<double, std::milli>(t1 - t0).count();
float lo[3], hi[3];
sdf.get_bounds(lo, hi);
std::mt19937 rng(12345);
std::uniform_real_distribution<float> dx(lo[0], hi[0]), dy(lo[1], hi[1]), dz(lo[2], hi[2]);
const int N = 200000;
std::vector<float> px(N), py(N), pz(N);
for (int i = 0; i < N; i++) { px[i]=dx(rng); py[i]=dy(rng); pz[i]=dz(rng); }
auto t2 = clk::now();
double acc = 0;
for (int i = 0; i < N; i++) acc += sdf.evaluate(px[i], py[i], pz[i], 0.224f);
auto t3 = clk::now();
double eval_ms = std::chrono::duration<double, std::milli>(t3 - t2).count();
double ns = eval_ms * 1e6 / N;
printf("%-16s tris %8d build %8.1f ms %10.0f ns/sample (acc %.1f)\n",
label, sdf.triangle_count(), build_ms, ns, acc);
// A 32^3 chunk = 32768 samples.
printf("%-16s -> %.1f ms per 32^3 chunk, %.1f ms per 64^3 chunk\n",
"", ns * 32768.0 / 1e6, ns * 262144.0 / 1e6);
}
int main() {
bench("/tmp/outer_shell.obj", "csg4 shell");
bench("/tmp/shell_voxel.obj", "voxel boundary");
return 0;
}