Blender V5.0
build.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
2 * SPDX-FileCopyrightText: 2011-2022 Blender Foundation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Adapted code from NVIDIA Corporation. */
7
8#include "bvh/build.h"
9
10#include "bvh/binning.h"
11#include "bvh/node.h"
12#include "bvh/params.h"
13#include "bvh/split.h"
14
15#include "scene/curves.h"
16#include "scene/hair.h"
17#include "scene/mesh.h"
18#include "scene/object.h"
19#include "scene/pointcloud.h"
20#include "scene/scene.h"
21
22#include "util/algorithm.h"
23
24#include "util/log.h"
25#include "util/progress.h"
27#include "util/time.h"
28
30
31/* Constructor / Destructor */
32
34 array<int> &prim_type_,
35 array<int> &prim_index_,
36 array<int> &prim_object_,
37 array<float2> &prim_time_,
38 const BVHParams &params_,
39 Progress &progress_)
40 : objects(objects_),
41 prim_type(prim_type_),
42 prim_index(prim_index_),
43 prim_object(prim_object_),
44 prim_time(prim_time_),
45 params(params_),
46 progress(progress_),
48 unaligned_heuristic(objects_)
49{
51}
52
53BVHBuild::~BVHBuild() = default;
54
55/* Adding References */
56
58 BoundBox &center,
59 Mesh *mesh,
60 const int object_index)
61{
62 const PrimitiveType primitive_type = mesh->primitive_type();
63 const Attribute *attr_mP = nullptr;
64 if (mesh->has_motion_blur()) {
66 }
67 const size_t num_triangles = mesh->num_triangles();
68 for (uint j = 0; j < num_triangles; j++) {
69 const Mesh::Triangle t = mesh->get_triangle(j);
70 const float3 *verts = mesh->verts.data();
71 if (attr_mP == nullptr) {
74 if (bounds.valid() && t.valid(verts)) {
75 references.push_back(BVHReference(bounds, j, object_index, primitive_type));
76 root.grow(bounds);
77 center.grow(bounds.center2());
78 }
79 }
80 else if (params.num_motion_triangle_steps == 0 || params.use_spatial_split) {
81 /* Motion triangles, simple case: single node for the whole
82 * primitive. Lowest memory footprint and faster BVH build but
83 * least optimal ray-tracing.
84 */
85 /* TODO(sergey): Support motion steps for spatially split BVH. */
86 const size_t num_verts = mesh->verts.size();
87 const size_t num_steps = mesh->motion_steps;
88 const float3 *vert_steps = attr_mP->data_float3();
91 for (size_t step = 0; step < num_steps - 1; step++) {
92 t.bounds_grow(vert_steps + step * num_verts, bounds);
93 }
94 if (bounds.valid()) {
95 references.push_back(BVHReference(bounds, j, object_index, primitive_type));
96 root.grow(bounds);
97 center.grow(bounds.center2());
98 }
99 }
100 else {
101 /* Motion triangles, trace optimized case: we split triangle
102 * primitives into separate nodes for each of the time steps.
103 * This way we minimize overlap of neighbor triangle primitives.
104 */
105 const int num_bvh_steps = params.num_motion_triangle_steps * 2 + 1;
106 const float num_bvh_steps_inv_1 = 1.0f / (num_bvh_steps - 1);
107 const size_t num_verts = mesh->verts.size();
108 const size_t num_steps = mesh->motion_steps;
109 const float3 *vert_steps = attr_mP->data_float3();
110 /* Calculate bounding box of the previous time step.
111 * Will be reused later to avoid duplicated work on
112 * calculating BVH time step boundbox.
113 */
114 float3 prev_verts[3];
115 t.motion_verts(verts, vert_steps, num_verts, num_steps, 0.0f, prev_verts);
116 BoundBox prev_bounds = BoundBox::empty;
117 prev_bounds.grow(prev_verts[0]);
118 prev_bounds.grow(prev_verts[1]);
119 prev_bounds.grow(prev_verts[2]);
120 /* Create all primitive time steps, */
121 for (int bvh_step = 1; bvh_step < num_bvh_steps; ++bvh_step) {
122 const float curr_time = (float)(bvh_step)*num_bvh_steps_inv_1;
123 float3 curr_verts[3];
124 t.motion_verts(verts, vert_steps, num_verts, num_steps, curr_time, curr_verts);
125 BoundBox curr_bounds = BoundBox::empty;
126 curr_bounds.grow(curr_verts[0]);
127 curr_bounds.grow(curr_verts[1]);
128 curr_bounds.grow(curr_verts[2]);
129 BoundBox bounds = prev_bounds;
130 bounds.grow(curr_bounds);
131 if (bounds.valid()) {
132 const float prev_time = (float)(bvh_step - 1) * num_bvh_steps_inv_1;
133 references.push_back(
134 BVHReference(bounds, j, object_index, primitive_type, prev_time, curr_time));
135 root.grow(bounds);
136 center.grow(bounds.center2());
137 }
138 /* Current time boundbox becomes previous one for the
139 * next time step.
140 */
141 prev_bounds = curr_bounds;
142 }
143 }
144 }
145}
146
148 BoundBox &center,
149 Hair *hair,
150 const int object_index)
151{
152 const Attribute *curve_attr_mP = nullptr;
153 if (hair->has_motion_blur()) {
154 curve_attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
155 }
156
157 const PrimitiveType primitive_type = hair->primitive_type();
158
159 const size_t num_curves = hair->num_curves();
160 for (uint j = 0; j < num_curves; j++) {
161 const Hair::Curve curve = hair->get_curve(j);
162 const float *curve_radius = hair->get_curve_radius().data();
163 for (int k = 0; k < curve.num_keys - 1; k++) {
164 if (curve_attr_mP == nullptr) {
165 /* Really simple logic for static hair. */
167 curve.bounds_grow(k, hair->get_curve_keys().data(), curve_radius, bounds);
168 if (bounds.valid()) {
169 const int packed_type = PRIMITIVE_PACK_SEGMENT(primitive_type, k);
170 references.push_back(BVHReference(bounds, j, object_index, packed_type));
171 root.grow(bounds);
172 center.grow(bounds.center2());
173 }
174 }
175 else if (params.num_motion_curve_steps == 0 || params.use_spatial_split) {
176 /* Simple case of motion curves: single node for the while
177 * shutter time. Lowest memory usage but less optimal
178 * rendering.
179 */
180 /* TODO(sergey): Support motion steps for spatially split BVH. */
182 curve.bounds_grow(k, hair->get_curve_keys().data(), curve_radius, bounds);
183 const size_t num_keys = hair->get_curve_keys().size();
184 const size_t num_steps = hair->get_motion_steps();
185 const float4 *key_steps = curve_attr_mP->data_float4();
186 for (size_t step = 0; step < num_steps - 1; step++) {
187 curve.bounds_grow(k, key_steps + step * num_keys, bounds);
188 }
189 if (bounds.valid()) {
190 const int packed_type = PRIMITIVE_PACK_SEGMENT(primitive_type, k);
191 references.push_back(BVHReference(bounds, j, object_index, packed_type));
192 root.grow(bounds);
193 center.grow(bounds.center2());
194 }
195 }
196 else {
197 /* Motion curves, trace optimized case: we split curve keys
198 * primitives into separate nodes for each of the time steps.
199 * This way we minimize overlap of neighbor curve primitives.
200 */
201 const int num_bvh_steps = params.num_motion_curve_steps * 2 + 1;
202 const float num_bvh_steps_inv_1 = 1.0f / (num_bvh_steps - 1);
203 const size_t num_steps = hair->get_motion_steps();
204 const float3 *curve_keys = hair->get_curve_keys().data();
205 const float4 *key_steps = curve_attr_mP->data_float4();
206 const size_t num_keys = hair->get_curve_keys().size();
207 /* Calculate bounding box of the previous time step.
208 * Will be reused later to avoid duplicated work on
209 * calculating BVH time step boundbox.
210 */
211 float4 prev_keys[4];
212 curve.cardinal_motion_keys(curve_keys,
213 curve_radius,
214 key_steps,
215 num_keys,
216 num_steps,
217 0.0f,
218 k - 1,
219 k,
220 k + 1,
221 k + 2,
222 prev_keys);
223 BoundBox prev_bounds = BoundBox::empty;
224 curve.bounds_grow(prev_keys, prev_bounds);
225 /* Create all primitive time steps, */
226 for (int bvh_step = 1; bvh_step < num_bvh_steps; ++bvh_step) {
227 const float curr_time = (float)(bvh_step)*num_bvh_steps_inv_1;
228 float4 curr_keys[4];
229 curve.cardinal_motion_keys(curve_keys,
230 curve_radius,
231 key_steps,
232 num_keys,
233 num_steps,
234 curr_time,
235 k - 1,
236 k,
237 k + 1,
238 k + 2,
239 curr_keys);
240 BoundBox curr_bounds = BoundBox::empty;
241 curve.bounds_grow(curr_keys, curr_bounds);
242 BoundBox bounds = prev_bounds;
243 bounds.grow(curr_bounds);
244 if (bounds.valid()) {
245 const float prev_time = (float)(bvh_step - 1) * num_bvh_steps_inv_1;
246 const int packed_type = PRIMITIVE_PACK_SEGMENT(primitive_type, k);
247 references.push_back(
248 BVHReference(bounds, j, object_index, packed_type, prev_time, curr_time));
249 root.grow(bounds);
250 center.grow(bounds.center2());
251 }
252 /* Current time boundbox becomes previous one for the
253 * next time step.
254 */
255 prev_bounds = curr_bounds;
256 }
257 }
258 }
259 }
260}
261
263 BoundBox &center,
264 PointCloud *pointcloud,
265 const int i)
266{
267 const Attribute *point_attr_mP = nullptr;
268 if (pointcloud->has_motion_blur()) {
269 point_attr_mP = pointcloud->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
270 }
271
272 const float3 *points_data = pointcloud->points.data();
273 const float *radius_data = pointcloud->radius.data();
274 const size_t num_points = pointcloud->num_points();
275 const float4 *motion_data = (point_attr_mP) ? point_attr_mP->data_float4() : nullptr;
276 const size_t num_steps = pointcloud->get_motion_steps();
277
278 if (point_attr_mP == nullptr) {
279 /* Really simple logic for static points. */
280 for (uint j = 0; j < num_points; j++) {
281 const PointCloud::Point point = pointcloud->get_point(j);
283 point.bounds_grow(points_data, radius_data, bounds);
284 if (bounds.valid()) {
286 root.grow(bounds);
287 center.grow(bounds.center2());
288 }
289 }
290 }
291 else if (params.num_motion_point_steps == 0 || params.use_spatial_split) {
292 /* Simple case of motion points: single node for the whole
293 * shutter time. Lowest memory usage but less optimal
294 * rendering.
295 */
296 /* TODO(sergey): Support motion steps for spatially split BVH. */
297 for (uint j = 0; j < num_points; j++) {
298 const PointCloud::Point point = pointcloud->get_point(j);
300 point.bounds_grow(points_data, radius_data, bounds);
301 for (size_t step = 0; step < num_steps - 1; step++) {
302 point.bounds_grow(motion_data[step * num_points + j], bounds);
303 }
304 if (bounds.valid()) {
306 root.grow(bounds);
307 center.grow(bounds.center2());
308 }
309 }
310 }
311 else {
312 /* Motion points, trace optimized case: we split point
313 * primitives into separate nodes for each of the time steps.
314 * This way we minimize overlap of neighbor point primitives.
315 */
316 const int num_bvh_steps = params.num_motion_point_steps * 2 + 1;
317 const float num_bvh_steps_inv_1 = 1.0f / (num_bvh_steps - 1);
318
319 for (uint j = 0; j < num_points; j++) {
320 const PointCloud::Point point = pointcloud->get_point(j);
321 const size_t num_steps = pointcloud->get_motion_steps();
322 const float4 *point_steps = point_attr_mP->data_float4();
323
324 /* Calculate bounding box of the previous time step.
325 * Will be reused later to avoid duplicated work on
326 * calculating BVH time step boundbox.
327 */
328 const float4 prev_key = point.motion_key(
329 points_data, radius_data, point_steps, num_points, num_steps, 0.0f, j);
330 BoundBox prev_bounds = BoundBox::empty;
331 point.bounds_grow(prev_key, prev_bounds);
332 /* Create all primitive time steps, */
333 for (int bvh_step = 1; bvh_step < num_bvh_steps; ++bvh_step) {
334 const float curr_time = (float)(bvh_step)*num_bvh_steps_inv_1;
335 const float4 curr_key = point.motion_key(
336 points_data, radius_data, point_steps, num_points, num_steps, curr_time, j);
337 BoundBox curr_bounds = BoundBox::empty;
338 point.bounds_grow(curr_key, curr_bounds);
339 BoundBox bounds = prev_bounds;
340 bounds.grow(curr_bounds);
341 if (bounds.valid()) {
342 const float prev_time = (float)(bvh_step - 1) * num_bvh_steps_inv_1;
343 references.push_back(
344 BVHReference(bounds, j, i, PRIMITIVE_MOTION_POINT, prev_time, curr_time));
345 root.grow(bounds);
346 center.grow(bounds.center2());
347 }
348 /* Current time boundbox becomes previous one for the
349 * next time step.
350 */
351 prev_bounds = curr_bounds;
352 }
353 }
354 }
355}
356
358 BoundBox &center,
359 Geometry *geom,
360 const int object_index)
361{
362 if (geom->is_mesh() || geom->is_volume()) {
363 Mesh *mesh = static_cast<Mesh *>(geom);
364 add_reference_triangles(root, center, mesh, object_index);
365 }
366 else if (geom->is_hair()) {
367 Hair *hair = static_cast<Hair *>(geom);
368 add_reference_curves(root, center, hair, object_index);
369 }
370 else if (geom->is_pointcloud()) {
371 PointCloud *pointcloud = static_cast<PointCloud *>(geom);
372 add_reference_points(root, center, pointcloud, object_index);
373 }
374}
375
376void BVHBuild::add_reference_object(BoundBox &root, BoundBox &center, Object *ob, const int i)
377{
378 references.push_back(BVHReference(ob->bounds, -1, i, 0));
379 root.grow(ob->bounds);
380 center.grow(ob->bounds.center2());
381}
382
383static size_t count_curve_segments(Hair *hair)
384{
385 size_t num = 0;
386 const size_t num_curves = hair->num_curves();
387
388 for (size_t i = 0; i < num_curves; i++) {
389 num += hair->get_curve(i).num_keys - 1;
390 }
391
392 return num;
393}
394
395static size_t count_primitives(Geometry *geom)
396{
397 if (geom->is_mesh() || geom->is_volume()) {
398 Mesh *mesh = static_cast<Mesh *>(geom);
399 return mesh->num_triangles();
400 }
401 if (geom->is_hair()) {
402 Hair *hair = static_cast<Hair *>(geom);
403 return count_curve_segments(hair);
404 }
405 if (geom->is_pointcloud()) {
406 PointCloud *pointcloud = static_cast<PointCloud *>(geom);
407 return pointcloud->num_points();
408 }
409
410 return 0;
411}
412
414{
415 /* reserve space for references */
416 size_t num_alloc_references = 0;
417
418 for (Object *ob : objects) {
419 if (params.top_level) {
420 if (!ob->is_traceable()) {
421 continue;
422 }
423 if (!ob->get_geometry()->is_instanced()) {
424 num_alloc_references += count_primitives(ob->get_geometry());
425 }
426 else {
427 num_alloc_references++;
428 }
429 }
430 else {
431 num_alloc_references += count_primitives(ob->get_geometry());
432 }
433 }
434
435 references.reserve(num_alloc_references);
436
437 /* add references from objects */
439 BoundBox center = BoundBox::empty;
440 int i = 0;
441
442 for (Object *ob : objects) {
443 if (params.top_level) {
444 if (!ob->is_traceable()) {
445 ++i;
446 continue;
447 }
448 if (!ob->get_geometry()->is_instanced()) {
449 add_reference_geometry(bounds, center, ob->get_geometry(), i);
450 }
451 else {
452 add_reference_object(bounds, center, ob, i);
453 }
454 }
455 else {
456 add_reference_geometry(bounds, center, ob->get_geometry(), i);
457 }
458
459 i++;
460
461 if (progress.get_cancel()) {
462 return;
463 }
464 }
465
466 /* happens mostly on empty meshes */
467 if (!bounds.valid()) {
468 bounds.grow(zero_float3());
469 }
470
471 root = BVHRange(bounds, center, 0, references.size());
472}
473
474/* Build */
475
477{
478 BVHRange root;
479
480 /* add references */
481 add_references(root);
482
483 if (progress.get_cancel()) {
484 return nullptr;
485 }
486
487 /* init spatial splits */
488 if (params.top_level) {
489 /* NOTE: Technically it is supported by the builder but it's not really
490 * optimized for speed yet and not really clear yet if it has measurable
491 * improvement on render time. Needs some extra investigation before
492 * enabling spatial split for top level BVH.
493 */
494 params.use_spatial_split = false;
495 }
496
497 if (!params.top_level && params.use_spatial_split && params.use_unaligned_nodes &&
498 !references.empty())
499 {
500 /* Spatial splitting for curve splitting leads to very long build times.
501 *
502 * There are also some issues (and, possibly, bugs in the split builder w.r.t how the alignment
503 * space is interpreted. It would be good to fix those issues, but due to performance, and the
504 * BVH becoming more obsolete with time it might not be the most optimal development time
505 * investment.
506 *
507 * The check is a bit implicit here, it relies on the fact that spatial splits are disabled on
508 * the top level, and that an object has primitives of the same type. */
510 params.use_spatial_split = false;
511 }
512 }
513
514 spatial_min_overlap = root.bounds().safe_area() * params.spatial_split_alpha;
516
517 need_prim_time = params.use_motion_steps();
518
519 /* init progress updates */
520 double build_start_time;
521 build_start_time = progress_start_time = time_dt();
522 progress_count = 0;
523 progress_total = references.size();
525
526 prim_type.resize(references.size());
527 prim_index.resize(references.size());
528 prim_object.resize(references.size());
529 if (need_prim_time) {
530 prim_time.resize(references.size());
531 }
532 else {
533 prim_time.resize(0);
534 }
535
536 /* build recursively */
537 unique_ptr<BVHNode> rootnode;
538
539 if (params.use_spatial_split) {
540 /* Perform multithreaded spatial split build. */
541 BVHSpatialStorage *local_storage = &spatial_storage.local();
542 rootnode = build_node(root, references, 0, local_storage);
543 task_pool.wait_work();
544 }
545 else {
546 /* Perform multithreaded binning build. */
547 const BVHObjectBinning rootbin(root, (!references.empty()) ? references.data() : nullptr);
548 rootnode = build_node(rootbin, 0);
549 task_pool.wait_work();
550 }
551
552 /* clean up temporary memory usage by threads */
553 spatial_storage.clear();
554
555 /* delete if we canceled */
556 if (rootnode) {
557 if (progress.get_cancel()) {
558 rootnode.reset();
559 LOG_DEBUG << "BVH build canceled.";
560 }
561 else {
562 /*rotate(rootnode, 4, 5);*/
563 rootnode->update_visibility();
564 rootnode->update_time();
565 }
566 if (rootnode != nullptr) {
567 LOG_DEBUG << "BVH build statistics:"
568 << " Build time: " << time_dt() - build_start_time << "\n"
569 << " Total number of nodes: "
570 << string_human_readable_number(rootnode->getSubtreeSize(BVH_STAT_NODE_COUNT))
571 << "\n"
572 << " Number of inner nodes: "
573 << string_human_readable_number(rootnode->getSubtreeSize(BVH_STAT_INNER_COUNT))
574 << "\n"
575 << " Number of leaf nodes: "
576 << string_human_readable_number(rootnode->getSubtreeSize(BVH_STAT_LEAF_COUNT))
577 << "\n"
578 << " Number of unaligned nodes: "
579 << string_human_readable_number(rootnode->getSubtreeSize(BVH_STAT_UNALIGNED_COUNT))
580 << "\n"
581 << " Allocation slop factor: "
582 << ((prim_type.capacity() != 0) ? (float)prim_type.size() / prim_type.capacity() :
583 1.0f)
584 << "\n"
585 << " Maximum depth: "
586 << string_human_readable_number(rootnode->getSubtreeSize(BVH_STAT_DEPTH));
587 }
588 }
589
590 return rootnode;
591}
592
594{
595 if (time_dt() - progress_start_time < 0.25) {
596 return;
597 }
598
599 const double progress_start = (double)progress_count / (double)progress_total;
600 const double duplicates = (double)(progress_total - progress_original_total) /
601 (double)progress_total;
602
603 const string msg = string_printf(
604 "Building BVH %.0f%%, duplicates %.0f%%", progress_start * 100.0, duplicates * 100.0);
605
606 progress.set_substatus(msg);
608}
609
611 const int child,
612 const BVHObjectBinning &range,
613 const int level)
614{
615 if (progress.get_cancel()) {
616 return;
617 }
618
619 /* build nodes */
620 unique_ptr<BVHNode> node = build_node(range, level);
621
622 /* set child in inner node */
623 inner->children[child] = std::move(node);
624
625 /* update progress */
626 if (range.size() < THREAD_TASK_SIZE) {
627 /*rotate(node, INT_MAX, 5);*/
628
630
631 progress_count += range.size();
633 }
634}
635
637 const int child,
638 const BVHRange &range,
640 const int level)
641{
642 if (progress.get_cancel()) {
643 return;
644 }
645
646 /* Get per-thread memory for spatial split. */
647 BVHSpatialStorage *local_storage = &spatial_storage.local();
648
649 /* build nodes */
650 unique_ptr<BVHNode> node = build_node(range, references, level, local_storage);
651
652 /* set child in inner node */
653 inner->children[child] = std::move(node);
654}
655
657 const vector<BVHReference> &references) const
658{
659 const size_t size = range.size();
660 const size_t max_leaf_size = max(max(params.max_triangle_leaf_size, params.max_curve_leaf_size),
661 params.max_point_leaf_size);
662
663 if (size > max_leaf_size) {
664 return false;
665 }
666
667 size_t num_triangles = 0;
668 size_t num_motion_triangles = 0;
669 size_t num_curves = 0;
670 size_t num_motion_curves = 0;
671 size_t num_points = 0;
672 size_t num_motion_points = 0;
673
674 for (int i = 0; i < size; i++) {
675 const BVHReference &ref = references[range.start() + i];
676
677 if (ref.prim_type() & PRIMITIVE_CURVE) {
678 if (ref.prim_type() & PRIMITIVE_MOTION) {
679 num_motion_curves++;
680 }
681 else {
682 num_curves++;
683 }
684 }
685 else if (ref.prim_type() & PRIMITIVE_TRIANGLE) {
686 if (ref.prim_type() & PRIMITIVE_MOTION) {
687 num_motion_triangles++;
688 }
689 else {
690 num_triangles++;
691 }
692 }
693 else if (ref.prim_type() & PRIMITIVE_POINT) {
694 if (ref.prim_type() & PRIMITIVE_MOTION) {
695 num_motion_points++;
696 }
697 else {
698 num_points++;
699 }
700 }
701 }
702
703 return (num_triangles <= params.max_triangle_leaf_size) &&
704 (num_motion_triangles <= params.max_motion_triangle_leaf_size) &&
705 (num_curves <= params.max_curve_leaf_size) &&
706 (num_motion_curves <= params.max_motion_curve_leaf_size) &&
707 (num_points <= params.max_point_leaf_size) &&
708 (num_motion_points <= params.max_motion_point_leaf_size);
709}
710
711/* multithreaded binning builder */
713{
714 const size_t size = range.size();
715 const float leafSAH = params.sah_primitive_cost * range.leafSAH;
716 const float splitSAH = params.sah_node_cost * range.bounds().half_area() +
717 params.sah_primitive_cost * range.splitSAH;
718
719 /* Have at least one inner node on top level, for performance and correct
720 * visibility tests, since object instances do not check visibility flag.
721 */
722 if (!(range.size() > 0 && params.top_level && level == 0)) {
723 /* Make leaf node when threshold reached or SAH tells us. */
724 if ((params.small_enough_for_leaf(size, level)) ||
725 (range_within_max_leaf_size(range, references) && leafSAH < splitSAH))
726 {
727 return create_leaf_node(range, references);
728 }
729 }
730
731 BVHObjectBinning unaligned_range;
732 float unalignedSplitSAH = FLT_MAX;
733 float unalignedLeafSAH = FLT_MAX;
734 Transform aligned_space;
735 bool do_unalinged_split = false;
736 if (params.use_unaligned_nodes && splitSAH > params.unaligned_split_threshold * leafSAH) {
737 aligned_space = unaligned_heuristic.compute_aligned_space(range, references.data());
738 unaligned_range = BVHObjectBinning(
739 range, references.data(), &unaligned_heuristic, &aligned_space);
740 unalignedSplitSAH = params.sah_node_cost * unaligned_range.unaligned_bounds().half_area() +
741 params.sah_primitive_cost * unaligned_range.splitSAH;
742 unalignedLeafSAH = params.sah_primitive_cost * unaligned_range.leafSAH;
743 if (!(range.size() > 0 && params.top_level && level == 0)) {
744 if (unalignedLeafSAH < unalignedSplitSAH && unalignedSplitSAH < splitSAH &&
746 {
747 return create_leaf_node(range, references);
748 }
749 }
750 /* Check whether unaligned split is better than the regular one. */
751 if (unalignedSplitSAH < splitSAH) {
752 do_unalinged_split = true;
753 }
754 }
755
756 /* Perform split. */
758 BVHObjectBinning right;
759 if (do_unalinged_split) {
760 unaligned_range.split(references.data(), left, right);
761 }
762 else {
763 range.split(references.data(), left, right);
764 }
765
767 if (do_unalinged_split) {
768 bounds = unaligned_heuristic.compute_aligned_boundbox(range, references.data(), aligned_space);
769 }
770 else {
771 bounds = range.bounds();
772 }
773
774 /* Create inner node. */
776 if (range.size() < THREAD_TASK_SIZE) {
777 /* local build */
778 unique_ptr<BVHNode> leftnode = build_node(left, level + 1);
779 unique_ptr<BVHNode> rightnode = build_node(right, level + 1);
780
781 inner = make_unique<InnerNode>(bounds, std::move(leftnode), std::move(rightnode));
782 }
783 else {
784 /* Threaded build */
785 inner = make_unique<InnerNode>(bounds);
786 InnerNode *inner_ptr = inner.get();
787
788 task_pool.push(
789 [this, inner_ptr, left, level] { thread_build_node(inner_ptr, 0, left, level + 1); });
790 task_pool.push(
791 [this, inner_ptr, right, level] { thread_build_node(inner_ptr, 1, right, level + 1); });
792 }
793
794 if (do_unalinged_split) {
795 inner->set_aligned_space(aligned_space);
796 }
797
798 return inner;
799}
800
801/* multithreaded spatial split builder */
804 const int level,
805 BVHSpatialStorage *storage)
806{
807 /* Update progress.
808 *
809 * TODO(sergey): Currently it matches old behavior, but we can move it to the
810 * task thread (which will mimic non=split builder) and save some CPU ticks
811 * on checking cancel status.
812 */
814 if (progress.get_cancel()) {
815 return nullptr;
816 }
817
818 /* Small enough or too deep => create leaf. */
819 if (!(range.size() > 0 && params.top_level && level == 0)) {
820 if (params.small_enough_for_leaf(range.size(), level)) {
821 progress_count += range.size();
822 return create_leaf_node(range, references);
823 }
824 }
825
826 /* Perform splitting test. */
827 BVHMixedSplit split(this, storage, range, references, level);
828
829 if (!(range.size() > 0 && params.top_level && level == 0)) {
830 if (split.no_split) {
831 progress_count += range.size();
832 return create_leaf_node(range, references);
833 }
834 }
835 const float leafSAH = params.sah_primitive_cost * split.leafSAH;
836 const float splitSAH = params.sah_node_cost * range.bounds().half_area() +
837 params.sah_primitive_cost * split.nodeSAH;
838
839 BVHMixedSplit unaligned_split;
840 float unalignedSplitSAH = FLT_MAX;
841 // float unalignedLeafSAH = FLT_MAX;
842 Transform aligned_space;
843 bool do_unalinged_split = false;
844 if (params.use_unaligned_nodes && splitSAH > params.unaligned_split_threshold * leafSAH) {
845 aligned_space = unaligned_heuristic.compute_aligned_space(range, &references.at(0));
846 unaligned_split = BVHMixedSplit(
847 this, storage, range, references, level, &unaligned_heuristic, &aligned_space);
848 // unalignedLeafSAH = params.sah_primitive_cost * split.leafSAH;
849 unalignedSplitSAH = params.sah_node_cost * unaligned_split.bounds.half_area() +
850 params.sah_primitive_cost * unaligned_split.nodeSAH;
851 /* TODO(sergey): Check we can create leaf already. */
852 /* Check whether unaligned split is better than the regular one. */
853 if (unalignedSplitSAH < splitSAH) {
854 do_unalinged_split = true;
855 }
856 }
857
858 /* Do split. */
860 BVHRange right;
861 if (do_unalinged_split) {
862 unaligned_split.split(this, left, right, range);
863 }
864 else {
865 split.split(this, left, right, range);
866 }
867
868 progress_total += left.size() + right.size() - range.size();
869
871 if (do_unalinged_split) {
872 bounds = unaligned_heuristic.compute_aligned_boundbox(range, &references.at(0), aligned_space);
873 }
874 else {
875 bounds = range.bounds();
876 }
877
878 /* Create inner node. */
880 if (range.size() < THREAD_TASK_SIZE) {
881 /* Local build. */
882
883 /* Build left node. */
884 vector<BVHReference> right_references(references.begin() + right.start(),
885 references.begin() + right.end());
886 right.set_start(0);
887
888 unique_ptr<BVHNode> leftnode = build_node(left, references, level + 1, storage);
889
890 /* Build right node. */
891 unique_ptr<BVHNode> rightnode = build_node(right, right_references, level + 1, storage);
892
893 inner = make_unique<InnerNode>(bounds, std::move(leftnode), std::move(rightnode));
894 }
895 else {
896 /* Threaded build. */
897 inner = make_unique<InnerNode>(bounds);
898
899 vector<BVHReference> left_references(references.begin() + left.start(),
900 references.begin() + left.end());
901 vector<BVHReference> right_references(references.begin() + right.start(),
902 references.begin() + right.end());
903 right.set_start(0);
904
905 InnerNode *inner_ptr = inner.get();
906
907 /* Create tasks for left and right nodes, using copy for most arguments and
908 * move for reference to avoid memory copies. */
909 task_pool.push([this, inner_ptr, left, level, refs = std::move(left_references)]() mutable {
910 thread_build_spatial_split_node(inner_ptr, 0, left, refs, level + 1);
911 });
912 task_pool.push([this, inner_ptr, right, level, refs = std::move(right_references)]() mutable {
913 thread_build_spatial_split_node(inner_ptr, 1, right, refs, level + 1);
914 });
915 }
916
917 if (do_unalinged_split) {
918 inner->set_aligned_space(aligned_space);
919 }
920
921 return inner;
922}
923
924/* Create Nodes */
925
927 const int start,
928 const int num)
929{
930 if (num == 0) {
932 return make_unique<LeafNode>(bounds, 0, 0, 0);
933 }
934 if (num == 1) {
935 assert(start < prim_type.size());
936 prim_type[start] = ref->prim_type();
937 prim_index[start] = ref->prim_index();
938 prim_object[start] = ref->prim_object();
939 if (need_prim_time) {
940 prim_time[start] = make_float2(ref->time_from(), ref->time_to());
941 }
942
943 const uint visibility = objects[ref->prim_object()]->visibility_for_tracing();
944 unique_ptr<BVHNode> leaf_node = make_unique<LeafNode>(
945 ref->bounds(), visibility, start, start + 1);
946 leaf_node->time_from = ref->time_from();
947 leaf_node->time_to = ref->time_to();
948 return leaf_node;
949 }
950
951 const int mid = num / 2;
952 unique_ptr<BVHNode> leaf0 = create_object_leaf_nodes(ref, start, mid);
953 unique_ptr<BVHNode> leaf1 = create_object_leaf_nodes(ref + mid, start + mid, num - mid);
954
956 bounds.grow(leaf0->bounds);
957 bounds.grow(leaf1->bounds);
958
959 const float time_from = min(leaf0->time_from, leaf1->time_from);
960 const float time_to = max(leaf0->time_to, leaf1->time_to);
961
962 unique_ptr<BVHNode> inner_node = make_unique<InnerNode>(
963 bounds, std::move(leaf0), std::move(leaf1));
964 inner_node->time_from = time_from;
965 inner_node->time_to = time_to;
966 return inner_node;
967}
968
971{
972 /* This is a bit over-allocating here (considering leaf size into account),
973 * but chunk-based re-allocation in vector makes it difficult to use small
974 * size of stack storage here. Some tweaks are possible tho.
975 *
976 * NOTES:
977 * - If the size is too big, we'll have inefficient stack usage,
978 * and lots of cache misses.
979 * - If the size is too small, then we can run out of memory
980 * allowed to be used by vector.
981 * In practice it wouldn't mean crash, just allocator will fall back
982 * to heap which is slower.
983 * - Optimistic re-allocation in STL could jump us out of stack usage
984 * because re-allocation happens in chunks and size of those chunks we
985 * can not control.
986 */
987 using LeafStackAllocator = StackAllocator<256, int>;
988 using LeafTimeStackAllocator = StackAllocator<256, float2>;
989 using LeafReferenceStackAllocator = StackAllocator<256, BVHReference>;
990
996
997 /* TODO(sergey): In theory we should be able to store references. */
999
1000 uint visibility[PRIMITIVE_NUM] = {0};
1001 /* NOTE: Keep initialization in sync with actual number of primitives. */
1004 int ob_num = 0;
1005 int num_new_prims = 0;
1006 /* Fill in per-type type/index array. */
1007 for (int i = 0; i < range.size(); i++) {
1008 const BVHReference &ref = references[range.start() + i];
1009 if (ref.prim_index() != -1) {
1010 const uint32_t type_index = PRIMITIVE_INDEX(ref.prim_type() & PRIMITIVE_ALL);
1011 p_ref[type_index].push_back(ref);
1012 p_type[type_index].push_back(ref.prim_type());
1013 p_index[type_index].push_back(ref.prim_index());
1014 p_object[type_index].push_back(ref.prim_object());
1015 p_time[type_index].push_back(make_float2(ref.time_from(), ref.time_to()));
1016
1017 bounds[type_index].grow(ref.bounds());
1018 visibility[type_index] |= objects[ref.prim_object()]->visibility_for_tracing();
1019 ++num_new_prims;
1020 }
1021 else {
1022 object_references.push_back(ref);
1023 ++ob_num;
1024 }
1025 }
1026
1027 /* Create leaf nodes for every existing primitive.
1028 *
1029 * Here we write primitive types, indices and objects to a temporary array.
1030 * This way we keep all the heavy memory allocation code outside of the
1031 * thread lock in the case of spatial split building.
1032 *
1033 * TODO(sergey): With some pointer trickery we can write directly to the
1034 * destination buffers for the non-spatial split BVH.
1035 */
1037 int num_leaves = 0;
1038 size_t start_index = 0;
1039 vector<int, LeafStackAllocator> local_prim_type;
1040 vector<int, LeafStackAllocator> local_prim_index;
1041 vector<int, LeafStackAllocator> local_prim_object;
1043 local_prim_type.resize(num_new_prims);
1044 local_prim_index.resize(num_new_prims);
1045 local_prim_object.resize(num_new_prims);
1046 if (need_prim_time) {
1047 local_prim_time.resize(num_new_prims);
1048 }
1049 for (int i = 0; i < PRIMITIVE_NUM; ++i) {
1050 const int num = (int)p_type[i].size();
1051 if (num != 0) {
1052 assert(p_type[i].size() == p_index[i].size());
1053 assert(p_type[i].size() == p_object[i].size());
1054 Transform aligned_space;
1055 bool alignment_found = false;
1056 for (int j = 0; j < num; ++j) {
1057 const int index = start_index + j;
1058 local_prim_type[index] = p_type[i][j];
1059 local_prim_index[index] = p_index[i][j];
1060 local_prim_object[index] = p_object[i][j];
1061 if (need_prim_time) {
1062 local_prim_time[index] = p_time[i][j];
1063 }
1064 if (params.use_unaligned_nodes && !alignment_found) {
1065 alignment_found = unaligned_heuristic.compute_aligned_space(p_ref[i][j], &aligned_space);
1066 }
1067 }
1068 unique_ptr<LeafNode> leaf_node = make_unique<LeafNode>(
1069 bounds[i], visibility[i], start_index, start_index + num);
1070 if (true) {
1071 float time_from = 1.0f;
1072 float time_to = 0.0f;
1073 for (int j = 0; j < num; ++j) {
1074 const BVHReference &ref = p_ref[i][j];
1075 time_from = min(time_from, ref.time_from());
1076 time_to = max(time_to, ref.time_to());
1077 }
1078 leaf_node->time_from = time_from;
1079 leaf_node->time_to = time_to;
1080 }
1081 if (alignment_found) {
1082 /* Need to recalculate leaf bounds with new alignment. */
1083 leaf_node->bounds = BoundBox::empty;
1084 for (int j = 0; j < num; ++j) {
1085 const BVHReference &ref = p_ref[i][j];
1086 const BoundBox ref_bounds = unaligned_heuristic.compute_aligned_prim_boundbox(
1087 ref, aligned_space);
1088 leaf_node->bounds.grow(ref_bounds);
1089 }
1090 /* Set alignment space. */
1091 leaf_node->set_aligned_space(aligned_space);
1092 }
1093 leaves[num_leaves++] = std::move(leaf_node);
1094 start_index += num;
1095 }
1096 }
1097 /* Get size of new data to be copied to the packed arrays. */
1098 const int num_new_leaf_data = start_index;
1099 const size_t new_leaf_data_size = sizeof(int) * num_new_leaf_data;
1100 /* Copy actual data to the packed array. */
1101 if (params.use_spatial_split) {
1102 spatial_spin_lock.lock();
1103 /* We use first free index in the packed arrays and mode pointer to the
1104 * end of the current range.
1105 *
1106 * This doesn't give deterministic packed arrays, but it shouldn't really
1107 * matter because order of children in BVH is deterministic.
1108 */
1109 start_index = spatial_free_index;
1110 spatial_free_index += range.size();
1111 /* Extend an array when needed. */
1112 const size_t range_end = start_index + range.size();
1113 if (prim_type.size() < range_end) {
1114 /* Avoid extra re-allocations by pre-allocating bigger array in an
1115 * advance.
1116 */
1117 if (range_end >= prim_type.capacity()) {
1118 const float progress = (float)progress_count / (float)progress_total;
1119 const float factor = (1.0f - progress);
1120 const size_t reserve = (size_t)(range_end + (float)range_end * factor);
1121 prim_type.reserve(reserve);
1122 prim_index.reserve(reserve);
1123 prim_object.reserve(reserve);
1124 if (need_prim_time) {
1125 prim_time.reserve(reserve);
1126 }
1127 }
1128
1129 prim_type.resize(range_end);
1130 prim_index.resize(range_end);
1131 prim_object.resize(range_end);
1132 if (need_prim_time) {
1133 prim_time.resize(range_end);
1134 }
1135 }
1136 /* Perform actual data copy. */
1137 if (new_leaf_data_size > 0) {
1138 memcpy(&prim_type[start_index], local_prim_type.data(), new_leaf_data_size);
1139 memcpy(&prim_index[start_index], local_prim_index.data(), new_leaf_data_size);
1140 memcpy(&prim_object[start_index], local_prim_object.data(), new_leaf_data_size);
1141 if (need_prim_time) {
1142 memcpy(
1143 &prim_time[start_index], local_prim_time.data(), sizeof(float2) * num_new_leaf_data);
1144 }
1145 }
1146 spatial_spin_lock.unlock();
1147 }
1148 else {
1149 /* For the regular BVH builder we simply copy new data starting at the
1150 * range start. This is totally thread-safe, all threads are living
1151 * inside of their own range.
1152 */
1153 start_index = range.start();
1154 if (new_leaf_data_size > 0) {
1155 memcpy(&prim_type[start_index], local_prim_type.data(), new_leaf_data_size);
1156 memcpy(&prim_index[start_index], local_prim_index.data(), new_leaf_data_size);
1157 memcpy(&prim_object[start_index], local_prim_object.data(), new_leaf_data_size);
1158 if (need_prim_time) {
1159 memcpy(
1160 &prim_time[start_index], local_prim_time.data(), sizeof(float2) * num_new_leaf_data);
1161 }
1162 }
1163 }
1164
1165 /* So far leaves were created with the zero-based index in an arrays,
1166 * here we modify the indices to correspond to actual packed array start
1167 * index.
1168 */
1169 for (int i = 0; i < num_leaves; ++i) {
1170 LeafNode *leaf = (LeafNode *)leaves[i].get();
1171 leaf->lo += start_index;
1172 leaf->hi += start_index;
1173 }
1174
1175 /* Create leaf node for object. */
1176 if (num_leaves == 0 || ob_num) {
1177 /* Only create object leaf nodes if there are objects or no other
1178 * nodes created.
1179 */
1180 const BVHReference *ref = (ob_num) ? object_references.data() : nullptr;
1181 leaves[num_leaves] = create_object_leaf_nodes(ref, start_index + num_new_leaf_data, ob_num);
1182 ++num_leaves;
1183 }
1184
1185 /* TODO(sergey): Need to take care of alignment when number of leaves
1186 * is more than 1.
1187 */
1188 if (num_leaves == 1) {
1189 /* Simplest case: single leaf, just return it.
1190 * In all the rest cases we'll be creating intermediate inner node with
1191 * an appropriate bounding box.
1192 */
1193 return std::move(leaves[0]);
1194 }
1195 if (num_leaves == 2) {
1196 return make_unique<InnerNode>(range.bounds(), std::move(leaves[0]), std::move(leaves[1]));
1197 }
1198 if (num_leaves == 3) {
1199 const BoundBox inner_bounds = merge(leaves[1]->bounds, leaves[2]->bounds);
1200 unique_ptr<BVHNode> inner = make_unique<InnerNode>(
1201 inner_bounds, std::move(leaves[1]), std::move(leaves[2]));
1202 return make_unique<InnerNode>(range.bounds(), std::move(leaves[0]), std::move(inner));
1203 }
1204
1205 /* Should be doing more branches if more primitive types added. */
1206 assert(num_leaves <= 5);
1207 const BoundBox inner_bounds_a = merge(leaves[0]->bounds, leaves[1]->bounds);
1208 const BoundBox inner_bounds_b = merge(leaves[2]->bounds, leaves[3]->bounds);
1209 unique_ptr<BVHNode> inner_a = make_unique<InnerNode>(
1210 inner_bounds_a, std::move(leaves[0]), std::move(leaves[1]));
1211 unique_ptr<BVHNode> inner_b = make_unique<InnerNode>(
1212 inner_bounds_b, std::move(leaves[2]), std::move(leaves[3]));
1213 const BoundBox inner_bounds_c = merge(inner_a->bounds, inner_b->bounds);
1214 unique_ptr<BVHNode> inner_c = make_unique<InnerNode>(
1215 inner_bounds_c, std::move(inner_a), std::move(inner_b));
1216 if (num_leaves == 5) {
1217 return make_unique<InnerNode>(range.bounds(), std::move(inner_c), std::move(leaves[4]));
1218 }
1219 return inner_c;
1220
1221#undef MAX_ITEMS_PER_LEAF
1222}
1223
1224/* Tree Rotations */
1225
1226void BVHBuild::rotate(BVHNode *node, const int max_depth, const int iterations)
1227{
1228 /* In tested scenes, this resulted in slightly slower ray-tracing, so disabled
1229 * it for now. could be implementation bug, or depend on the scene. */
1230 if (node) {
1231 for (int i = 0; i < iterations; i++) {
1232 rotate(node, max_depth);
1233 }
1234 }
1235}
1236
1237void BVHBuild::rotate(BVHNode *node, const int max_depth)
1238{
1239 /* nothing to rotate if we reached a leaf node. */
1240 if (node->is_leaf() || max_depth < 0) {
1241 return;
1242 }
1243
1244 InnerNode *parent = (InnerNode *)node;
1245
1246 /* rotate all children first */
1247 for (size_t c = 0; c < 2; c++) {
1248 rotate(parent->children[c].get(), max_depth - 1);
1249 }
1250
1251 /* compute current area of all children */
1252 const BoundBox bounds0 = parent->children[0]->bounds;
1253 const BoundBox bounds1 = parent->children[1]->bounds;
1254
1255 const float area0 = bounds0.half_area();
1256 const float area1 = bounds1.half_area();
1257 float4 child_area = make_float4(area0, area1, 0.0f, 0.0f);
1258
1259 /* find best rotation. we pick a target child of a first child, and swap
1260 * this with an other child. we perform the best such swap. */
1261 float best_cost = FLT_MAX;
1262 int best_child = -1;
1263 int best_target = -1;
1264 int best_other = -1;
1265
1266 for (size_t c = 0; c < 2; c++) {
1267 /* ignore leaf nodes as we cannot descent into */
1268 if (parent->children[c]->is_leaf()) {
1269 continue;
1270 }
1271
1272 InnerNode *child = (InnerNode *)parent->children[c].get();
1273 const BoundBox &other = (c == 0) ? bounds1 : bounds0;
1274
1275 /* transpose child bounds */
1276 const BoundBox target0 = child->children[0]->bounds;
1277 const BoundBox target1 = child->children[1]->bounds;
1278
1279 /* compute cost for both possible swaps */
1280 const float cost0 = merge(other, target1).half_area() - child_area[c];
1281 const float cost1 = merge(target0, other).half_area() - child_area[c];
1282
1283 if (min(cost0, cost1) < best_cost) {
1284 best_child = (int)c;
1285 best_other = (int)(1 - c);
1286
1287 if (cost0 < cost1) {
1288 best_cost = cost0;
1289 best_target = 0;
1290 }
1291 else {
1292 best_cost = cost0;
1293 best_target = 1;
1294 }
1295 }
1296 }
1297
1298 /* if we did not find a swap that improves the SAH then do nothing */
1299 if (best_cost >= 0) {
1300 return;
1301 }
1302
1303 assert(best_child == 0 || best_child == 1);
1304 assert(best_target != -1);
1305
1306 /* perform the best found tree rotation */
1307 InnerNode *child = (InnerNode *)parent->children[best_child].get();
1308
1309 swap(parent->children[best_other], child->children[best_target]);
1310 child->bounds = merge(child->children[0]->bounds, child->children[1]->bounds);
1311}
1312
ATTR_WARN_UNUSED_RESULT const size_t num
unsigned int uint
static void split(const char *text, const char *seps, char ***str, int *count)
volatile int lock
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition btDbvt.cpp:299
static size_t count_primitives(Geometry *geom)
Definition build.cpp:395
static size_t count_curve_segments(Hair *hair)
Definition build.cpp:383
@ BVH_STAT_NODE_COUNT
Definition bvh/node.h:17
@ BVH_STAT_DEPTH
Definition bvh/node.h:28
@ BVH_STAT_UNALIGNED_COUNT
Definition bvh/node.h:23
@ BVH_STAT_INNER_COUNT
Definition bvh/node.h:18
@ BVH_STAT_LEAF_COUNT
Definition bvh/node.h:19
Attribute * find(ustring name) const
void add_reference_triangles(BoundBox &root, BoundBox &center, Mesh *mesh, const int object_index)
Definition build.cpp:57
size_t progress_count
Definition build.h:127
BVHBuild(const vector< Object * > &objects, array< int > &prim_type, array< int > &prim_index, array< int > &prim_object, array< float2 > &prim_time, const BVHParams &params, Progress &progress)
Definition build.cpp:33
void thread_build_spatial_split_node(InnerNode *inner, const int child, const BVHRange &range, vector< BVHReference > &references, int level)
Definition build.cpp:636
unique_ptr< BVHNode > run()
Definition build.cpp:476
void rotate(BVHNode *node, const int max_depth)
Definition build.cpp:1237
bool range_within_max_leaf_size(const BVHRange &range, const vector< BVHReference > &references) const
Definition build.cpp:656
thread_spin_lock spatial_spin_lock
Definition build.h:135
thread_mutex build_mutex
Definition build.h:99
void add_references(BVHRange &root)
Definition build.cpp:413
void add_reference_object(BoundBox &root, BoundBox &center, Object *ob, const int i)
Definition build.cpp:376
unique_ptr< BVHNode > create_leaf_node(const BVHRange &range, const vector< BVHReference > &references)
Definition build.cpp:969
bool need_prim_time
Definition build.h:119
size_t spatial_free_index
Definition build.h:134
void add_reference_curves(BoundBox &root, BoundBox &center, Hair *hair, const int object_index)
Definition build.cpp:147
@ THREAD_TASK_SIZE
Definition build.h:89
BVHUnaligned unaligned_heuristic
Definition build.h:141
TaskPool task_pool
Definition build.h:138
friend class BVHObjectBinning
Definition build.h:57
unique_ptr< BVHNode > create_object_leaf_nodes(const BVHReference *ref, const int start, const int num)
Definition build.cpp:926
Progress & progress
Definition build.h:125
array< float2 > & prim_time
Definition build.h:117
vector< Object * > objects
Definition build.h:109
unique_ptr< BVHNode > build_node(const BVHRange &range, vector< BVHReference > &references, const int level, BVHSpatialStorage *storage)
Definition build.cpp:802
array< int > & prim_index
Definition build.h:115
enumerable_thread_specific< BVHSpatialStorage > spatial_storage
Definition build.h:133
size_t progress_total
Definition build.h:128
size_t progress_original_total
Definition build.h:129
BVHParams params
Definition build.h:122
float spatial_min_overlap
Definition build.h:132
void thread_build_node(InnerNode *inner, const int child, const BVHObjectBinning &range, const int level)
Definition build.cpp:610
array< int > & prim_type
Definition build.h:114
double progress_start_time
Definition build.h:126
array< int > & prim_object
Definition build.h:116
void progress_update()
Definition build.cpp:593
friend class BVHMixedSplit
Definition build.h:52
vector< BVHReference > references
Definition build.h:110
void add_reference_points(BoundBox &root, BoundBox &center, PointCloud *pointcloud, const int i)
Definition build.cpp:262
void add_reference_geometry(BoundBox &root, BoundBox &center, Geometry *geom, const int object_index)
Definition build.cpp:357
__forceinline void split(BVHBuild *builder, BVHRange &left, BVHRange &right, const BVHRange &range)
Definition bvh/split.h:220
BoundBox bounds
Definition bvh/split.h:177
void split(BVHReference *prims, BVHObjectBinning &left_o, BVHObjectBinning &right_o) const
Definition binning.cpp:212
__forceinline const BoundBox & unaligned_bounds()
Definition binning.h:37
__forceinline int size() const
Definition params.h:281
__forceinline int start() const
Definition params.h:277
__forceinline const BoundBox & bounds() const
Definition params.h:269
__forceinline void set_start(const int start_)
Definition params.h:264
__forceinline int end() const
Definition params.h:285
__forceinline int prim_type() const
Definition params.h:216
__forceinline int prim_object() const
Definition params.h:212
__forceinline float time_from() const
Definition params.h:220
__forceinline const BoundBox & bounds() const
Definition params.h:204
__forceinline float time_to() const
Definition params.h:224
__forceinline int prim_index() const
Definition params.h:208
bool is_volume() const
bool is_pointcloud() const
bool is_hair() const
virtual bool has_motion_blur() const
AttributeSet attributes
bool is_mesh() const
Definition hair.h:13
Curve get_curve(const size_t i) const
Definition hair.h:111
size_t num_curves() const
Definition hair.h:126
PrimitiveType primitive_type() const override
Definition hair.cpp:529
unique_ptr< BVHNode > children[kNumMaxChildren]
Definition bvh/node.h:166
nullptr float
#define PRIMITIVE_PACK_SEGMENT(type, segment)
#define PRIMITIVE_INDEX(type)
#define CCL_NAMESPACE_END
static float verts[][3]
#define assert(assertion)
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
PrimitiveType
@ PRIMITIVE_ALL
@ PRIMITIVE_MOTION
@ PRIMITIVE_NUM
@ PRIMITIVE_CURVE
@ PRIMITIVE_TRIANGLE
@ PRIMITIVE_MOTION_POINT
@ PRIMITIVE_POINT
@ ATTR_STD_MOTION_VERTEX_POSITION
OrientationBounds merge(const OrientationBounds &cone_a, const OrientationBounds &cone_b)
#define LOG_DEBUG
Definition log.h:107
CCL_NAMESPACE_BEGIN ccl_device_inline float3 zero_float3()
Definition math_float3.h:17
static int left
#define make_float2
#define make_float4
#define swap(a, b)
Definition sort.cc:59
#define min(a, b)
Definition sort.cc:36
#define FLT_MAX
Definition stdcycles.h:14
string string_human_readable_number(size_t num)
Definition string.cpp:276
CCL_NAMESPACE_BEGIN string string_printf(const char *format,...)
Definition string.cpp:23
float3 * data_float3()
float4 * data_float4()
virtual bool is_leaf() const =0
BoundBox bounds
Definition bvh/node.h:89
__forceinline float half_area() const
Definition boundbox.h:106
__forceinline float safe_area() const
Definition boundbox.h:92
__forceinline void grow(const float3 &pt)
Definition boundbox.h:35
__forceinline float3 center2() const
Definition boundbox.h:117
void bounds_grow(const int k, const float3 *curve_keys, const float *curve_radius, BoundBox &bounds) const
Definition hair.cpp:44
void cardinal_motion_keys(const float3 *curve_keys, const float *curve_radius, const float4 *key_steps, const size_t num_curve_keys, const size_t num_steps, const float time, size_t k0, size_t k1, size_t k2, size_t k3, float4 r_keys[4]) const
Definition hair.cpp:148
int num_keys
Definition hair.h:20
bool valid(const float3 *verts) const
void bounds_grow(const float3 *verts, BoundBox &bounds) const
void motion_verts(const float3 *verts, const float3 *vert_steps, const size_t num_verts, const size_t num_steps, const float time, float3 r_verts[3]) const
bool has_motion_blur() const override
size_t num_triangles() const
Definition scene/mesh.h:77
Triangle get_triangle(const size_t i) const
Definition scene/mesh.h:71
PrimitiveType primitive_type() const override
NODE_DECLARE BoundBox bounds
void bounds_grow(const float3 *points, const float *radius, BoundBox &bounds) const
float4 motion_key(const float3 *points, const float *radius, const float4 *point_steps, const size_t num_points, const size_t num_steps, const float time, size_t p) const
Point get_point(const int i) const
size_t num_points() const
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
std::unique_lock< std::mutex > thread_scoped_lock
Definition thread.h:28
CCL_NAMESPACE_BEGIN double time_dt()
Definition time.cpp:47