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_),
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()) {
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 }
82 else if (params.num_motion_triangle_steps == 0 || params.use_spatial_split) {
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 }
174 else if (params.num_motion_curve_steps == 0 || params.use_spatial_split) {
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 }
290 else if (params.num_motion_point_steps == 0 || params.use_spatial_split) {
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 */
491 params.use_spatial_split = false;
492 }
493
494 spatial_min_overlap = root.bounds().safe_area() * params.spatial_split_alpha;
496
497 need_prim_time = params.use_motion_steps();
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
506 prim_type.resize(references.size());
507 prim_index.resize(references.size());
508 prim_object.resize(references.size());
509 if (need_prim_time) {
510 prim_time.resize(references.size());
511 }
512 else {
513 prim_time.resize(0);
514 }
515
516 /* build recursively */
517 BVHNode *rootnode;
518
519 if (params.use_spatial_split) {
520 /* Perform multithreaded spatial split build. */
521 BVHSpatialStorage *local_storage = &spatial_storage.local();
522 rootnode = build_node(root, references, 0, local_storage);
523 task_pool.wait_work();
524 }
525 else {
526 /* Perform multithreaded binning build. */
527 BVHObjectBinning rootbin(root, (references.size()) ? &references[0] : NULL);
528 rootnode = build_node(rootbin, 0);
529 task_pool.wait_work();
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
586 progress.set_substatus(msg);
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,
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();
640 size_t max_leaf_size = max(max(params.max_triangle_leaf_size, params.max_curve_leaf_size),
641 params.max_point_leaf_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) {
747 bounds = unaligned_heuristic.compute_aligned_boundbox(range, &references[0], aligned_space);
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 */
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];
1048 BoundBox ref_bounds = unaligned_heuristic.compute_aligned_prim_boundbox(ref,
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. */
1063 if (params.use_spatial_split) {
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)
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
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
vector< Object * > objects
Definition build.h:97
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
__forceinline int size() const
Definition params.h:291
__forceinline int start() const
Definition params.h:287
__forceinline const BoundBox & bounds() const
Definition params.h:279
__forceinline void set_start(int start_)
Definition params.h:274
__forceinline int end() const
Definition params.h:295
__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
Type geometry_type
bool has_motion_blur() const
AttributeSet attributes
Definition hair.h:14
Curve get_curve(size_t i) const
Definition hair.h:112
size_t num_curves() const
Definition hair.h:126
PrimitiveType primitive_type() const override
Definition hair.cpp:548
BVHNode * children[kNumMaxChildren]
Definition bvh/node.h:197
#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]
#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:266
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
void cardinal_motion_keys(const float3 *curve_keys, const float *curve_radius, const float4 *key_steps, size_t num_curve_keys, size_t num_steps, float time, size_t k0, size_t k1, size_t k2, size_t k3, float4 r_keys[4]) const
Definition hair.cpp:146
void bounds_grow(const int k, const float3 *curve_keys, const float *curve_radius, BoundBox &bounds) const
Definition hair.cpp:42
int num_keys
Definition hair.h:21
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
Triangle get_triangle(size_t i) const
Definition scene/mesh.h:74
size_t num_triangles() const
Definition scene/mesh.h:80
PrimitiveType primitive_type() const override
NODE_DECLARE BoundBox bounds
bool is_traceable() const
Point get_point(int i) const
size_t num_points() const
VecBase< float, 4 > float4
std::unique_lock< std::mutex > thread_scoped_lock
Definition thread.h:30
CCL_NAMESPACE_BEGIN double time_dt()
Definition time.cpp:36
float max