OverviewFilesCommitsIssuesDocsReleasesPeers

dc-terrain / tests / test_mesh_sdf_bench.cpp

66 lines · cpp ref:
// ============================================================================
// 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;
}