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