| … | … | @@ -1,4 +1,5 @@ |
| 1 | 1 | #include "dc_generator_mesh_sdf.h" |
| 2 | #include <thread> |
| 2 | 3 | |
| 3 | 4 | #include <fstream> |
| 4 | 5 | #include <sstream> |
| … | … | @@ -223,11 +224,30 @@ void DCGeneratorMeshSDF::build_bvh() { |
| 223 | 224 | tri_indices_.resize(n); |
| 224 | 225 | std::iota(tri_indices_.begin(), tri_indices_.end(), 0); |
| 225 | 226 | |
| 227 | // WORST CASE UP FRONT. A binary tree whose leaves hold at least one |
| 228 | // triangle has at most n leaves and n-1 interior nodes, so 2n-1 covers any |
| 229 | // mesh. Resized rather than reserved because the parallel build hands out |
| 230 | // nodes by atomic bump and never touches the vector's size -- if it could |
| 231 | // grow, a reallocation would move the array under a thread reading it. |
| 232 | // Centroids once, not once per comparison. See the note on tri_centroids_. |
| 233 | tri_centroids_.resize((size_t)n * 3); |
| 234 | for (int i = 0; i < n; i++) { |
| 235 | const Triangle& t = triangles_[i]; |
| 236 | tri_centroids_[(size_t)i * 3 + 0] = (t.v0.x + t.v1.x + t.v2.x) / 3.0f; |
| 237 | tri_centroids_[(size_t)i * 3 + 1] = (t.v0.y + t.v1.y + t.v2.y) / 3.0f; |
| 238 | tri_centroids_[(size_t)i * 3 + 2] = (t.v0.z + t.v1.z + t.v2.z) / 3.0f; |
| 239 | } |
| 240 | |
| 226 | 241 | bvh_nodes_.clear(); |
| 227 | | bvh_nodes_.reserve(2 * n); |
| 242 | bvh_nodes_.resize((size_t)(2 * n > 0 ? 2 * n : 1)); |
| 243 | bvh_alloc_.store(1); |
| 228 | 244 | |
| 229 | | bvh_nodes_.push_back(BVHNode()); |
| 230 | 245 | build_bvh_recursive(0, 0, n, 0); |
| 246 | |
| 247 | // Give back what the split did not need. The bound is generous by design; |
| 248 | // a median split usually lands nearer n/2 nodes than 2n. |
| 249 | bvh_nodes_.resize((size_t)bvh_alloc_.load()); |
| 250 | bvh_nodes_.shrink_to_fit(); |
| 231 | 251 | } |
| 232 | 252 | |
| 233 | 253 | void DCGeneratorMeshSDF::build_bvh_recursive(int node_idx, int start, int count, int depth) { |
| … | … | @@ -270,20 +290,13 @@ void DCGeneratorMeshSDF::build_bvh_recursive(int node_idx, int start, int count, |
| 270 | 290 | if (extent[1] > extent[axis]) axis = 1; |
| 271 | 291 | if (extent[2] > extent[axis]) axis = 2; |
| 272 | 292 | |
| 273 | | auto centroid_component = [&](int tri_idx, int ax) -> float { |
| 274 | | const Triangle& t = triangles_[tri_idx]; |
| 275 | | const float v[3][3] = {{t.v0.x, t.v0.y, t.v0.z}, |
| 276 | | {t.v1.x, t.v1.y, t.v1.z}, |
| 277 | | {t.v2.x, t.v2.y, t.v2.z}}; |
| 278 | | return (v[0][ax] + v[1][ax] + v[2][ax]) / 3.0f; |
| 279 | | }; |
| 280 | | |
| 293 | const float* cent = tri_centroids_.data(); |
| 281 | 294 | int mid = start + count / 2; |
| 282 | 295 | std::nth_element(tri_indices_.begin() + start, |
| 283 | 296 | tri_indices_.begin() + mid, |
| 284 | 297 | tri_indices_.begin() + start + count, |
| 285 | | [&](int a, int b) { |
| 286 | | return centroid_component(a, axis) < centroid_component(b, axis); |
| 298 | [cent, axis](int a, int b) { |
| 299 | return cent[(size_t)a * 3 + axis] < cent[(size_t)b * 3 + axis]; |
| 287 | 300 | }); |
| 288 | 301 | |
| 289 | 302 | int left_count = mid - start; |
| … | … | @@ -293,18 +306,41 @@ void DCGeneratorMeshSDF::build_bvh_recursive(int node_idx, int start, int count, |
| 293 | 306 | return; // Can't split, keep as leaf |
| 294 | 307 | } |
| 295 | 308 | |
| 296 | | int left_idx = (int)bvh_nodes_.size(); |
| 297 | | bvh_nodes_.push_back(BVHNode()); |
| 298 | | int right_idx = (int)bvh_nodes_.size(); |
| 299 | | bvh_nodes_.push_back(BVHNode()); |
| 309 | const int left_idx = bvh_alloc_.fetch_add(2); |
| 310 | const int right_idx = left_idx + 1; |
| 300 | 311 | |
| 301 | | // Re-fetch after potential reallocation |
| 302 | 312 | bvh_nodes_[node_idx].left = left_idx; |
| 303 | 313 | bvh_nodes_[node_idx].right = right_idx; |
| 304 | 314 | bvh_nodes_[node_idx].tri_count = 0; // interior node |
| 305 | 315 | |
| 306 | | build_bvh_recursive(left_idx, start, left_count, depth + 1); |
| 307 | | build_bvh_recursive(right_idx, mid, right_count, depth + 1); |
| 316 | // ── PARALLEL BELOW THE TOP LEVELS ─────────────────────────────────────── |
| 317 | // |
| 318 | // Measured 2026-09-08 on the bridged voronoi mountain: this build is 10.5 s |
| 319 | // of a 12.7 s Mesh to SDF Grid, over 5.16M triangles, on one core. The |
| 320 | // narrow band had already taken the QUERIES to 1.2 s, which is what made |
| 321 | // the build visible as the remaining cost. |
| 322 | // |
| 323 | // Safe because the two subtrees own DISJOINT ranges of tri_indices_ -- |
| 324 | // nth_element has already partitioned [start, start+count) about mid, and |
| 325 | // neither side touches the other's half again -- triangles_ is immutable |
| 326 | // after load_arrays, and nodes come from the atomic bump above into an |
| 327 | // array that cannot move. |
| 328 | // |
| 329 | // Only the first few levels spawn: depth 4 is up to 16 subtrees, which |
| 330 | // matches a typical core count, and below that a thread costs more than |
| 331 | // the subtree does. The count guard keeps small meshes entirely serial. |
| 332 | const int PARALLEL_DEPTH = 4; |
| 333 | const int PARALLEL_MIN_TRIS = 8192; |
| 334 | if (depth < PARALLEL_DEPTH && count > PARALLEL_MIN_TRIS) { |
| 335 | std::thread left_thread([this, left_idx, start, left_count, depth]() { |
| 336 | build_bvh_recursive(left_idx, start, left_count, depth + 1); |
| 337 | }); |
| 338 | build_bvh_recursive(right_idx, mid, right_count, depth + 1); |
| 339 | left_thread.join(); |
| 340 | } else { |
| 341 | build_bvh_recursive(left_idx, start, left_count, depth + 1); |
| 342 | build_bvh_recursive(right_idx, mid, right_count, depth + 1); |
| 343 | } |
| 308 | 344 | } |
| 309 | 345 | |
| 310 | 346 | // --------------------------------------------------------------------------- |