1pub mod builder;
4pub mod insertion_removal;
5pub mod leaf_collapser;
6
7pub mod node;
8pub mod reinsertion;
9
10use bytemuck::zeroed_vec;
11use glam::Vec3A;
12use node::Bvh2Node;
13
14use std::{
15 collections::{HashMap, HashSet},
16 fmt,
17};
18
19use reinsertion::find_reinsertion;
20
21use crate::{
22 Boundable, INVALID,
23 aabb::Aabb,
24 fast_stack,
25 faststack::FastStack,
26 ray::{Ray, RayHit},
27};
28
29#[derive(Clone)]
31pub struct Bvh2 {
32 pub nodes: Vec<Bvh2Node>,
37
38 pub primitive_indices: Vec<u32>,
45
46 pub primitive_indices_freelist: Vec<u32>,
51
52 pub primitives_to_nodes: Vec<u32>,
62
63 pub parents: Vec<u32>,
68
69 pub children_are_ordered_after_parents: bool,
72
73 pub max_depth: usize,
77
78 pub uses_spatial_splits: bool,
86}
87
88pub const DEFAULT_MAX_STACK_DEPTH: usize = 96;
89
90impl Default for Bvh2 {
91 fn default() -> Self {
92 Self {
93 nodes: Default::default(),
94 primitive_indices: Default::default(),
95 primitive_indices_freelist: Default::default(),
96 primitives_to_nodes: Default::default(),
97 parents: Default::default(),
98 children_are_ordered_after_parents: Default::default(),
99 max_depth: DEFAULT_MAX_STACK_DEPTH,
100 uses_spatial_splits: Default::default(),
101 }
102 }
103}
104
105impl Bvh2 {
106 pub fn reset_for_reuse(&mut self, prim_count: usize, indices: Option<Vec<u32>>) {
108 let nodes_count = (2 * prim_count as i64 - 1).max(0) as usize;
109 self.nodes.resize(nodes_count, Default::default());
110 if let Some(indices) = indices {
111 self.primitive_indices = indices;
112 } else {
113 self.primitive_indices
114 .resize(prim_count, Default::default());
115 }
116 self.primitive_indices_freelist.clear();
117 self.primitives_to_nodes.clear();
118 self.parents.clear();
119 self.children_are_ordered_after_parents = Default::default();
120 self.max_depth = DEFAULT_MAX_STACK_DEPTH;
121 self.uses_spatial_splits = Default::default();
122 }
123
124 pub fn zeroed(prim_count: usize) -> Self {
125 let nodes_count = (2 * prim_count as i64 - 1).max(0) as usize;
126 Self {
127 nodes: zeroed_vec(nodes_count),
128 primitive_indices: zeroed_vec(prim_count),
129 primitive_indices_freelist: Default::default(),
130 primitives_to_nodes: Default::default(),
131 parents: Default::default(),
132 children_are_ordered_after_parents: Default::default(),
133 max_depth: DEFAULT_MAX_STACK_DEPTH,
134 uses_spatial_splits: Default::default(),
135 }
136 }
137
138 #[inline(always)]
149 pub fn ray_traverse<F: FnMut(&Ray, usize) -> f32>(
150 &self,
151 ray: Ray,
152 hit: &mut RayHit,
153 mut intersection_fn: F,
154 ) -> bool {
155 let mut intersect_prims = |node: &Bvh2Node, ray: &mut Ray, hit: &mut RayHit| {
156 (node.first_index..node.first_index + node.prim_count).for_each(|primitive_id| {
157 let t = intersection_fn(ray, primitive_id as usize);
158 if t < ray.tmax {
159 hit.primitive_id = primitive_id;
160 hit.t = t;
161 ray.tmax = t;
162 }
163 });
164 true
165 };
166
167 fast_stack!(u32, (96, 192), self.max_depth, stack, {
168 Bvh2::ray_traverse_dynamic(self, &mut stack, ray, hit, &mut intersect_prims)
169 });
170
171 hit.t < ray.tmax }
173
174 #[inline(always)]
185 pub fn ray_traverse_miss<F: FnMut(&Ray, usize) -> f32>(
186 &self,
187 ray: Ray,
188 mut intersection_fn: F,
189 ) -> bool {
190 let mut miss = true;
191 let mut intersect_prims = |node: &Bvh2Node, ray: &mut Ray, _hit: &mut RayHit| {
192 for primitive_id in node.first_index..node.first_index + node.prim_count {
193 let t = intersection_fn(ray, primitive_id as usize);
194 if t < ray.tmax {
195 miss = false;
196 return false;
197 }
198 }
199 true
200 };
201
202 fast_stack!(u32, (96, 192), self.max_depth, stack, {
203 Bvh2::ray_traverse_dynamic(
204 self,
205 &mut stack,
206 ray,
207 &mut RayHit::none(),
208 &mut intersect_prims,
209 )
210 });
211
212 miss
213 }
214
215 #[inline(always)]
226 pub fn ray_traverse_anyhit<F: FnMut(&Ray, usize)>(&self, ray: Ray, mut intersection_fn: F) {
227 let mut intersect_prims = |node: &Bvh2Node, ray: &mut Ray, _hit: &mut RayHit| {
228 for primitive_id in node.first_index..node.first_index + node.prim_count {
229 intersection_fn(ray, primitive_id as usize);
230 }
231 true
232 };
233
234 let mut hit = RayHit::none();
235 fast_stack!(u32, (96, 192), self.max_depth, stack, {
236 self.ray_traverse_dynamic(&mut stack, ray, &mut hit, &mut intersect_prims)
237 });
238 }
239
240 #[inline(always)]
258 pub fn ray_traverse_dynamic<
259 F: FnMut(&Bvh2Node, &mut Ray, &mut RayHit) -> bool,
260 Stack: FastStack<u32>,
261 >(
262 &self,
263 stack: &mut Stack,
264 mut ray: Ray,
265 hit: &mut RayHit,
266 mut intersection_fn: F,
267 ) {
268 if self.nodes.is_empty() {
269 return;
270 }
271
272 let root_node = &self.nodes[0];
273 let hit_root = root_node.aabb().intersect_ray(&ray) < ray.tmax;
274 if !hit_root {
275 return;
276 } else if root_node.is_leaf() {
277 intersection_fn(root_node, &mut ray, hit);
278 return;
279 };
280
281 let mut current_node_index = root_node.first_index;
282 loop {
283 let right_index = current_node_index as usize + 1;
284 assert!(right_index < self.nodes.len());
285 let mut left_node = unsafe { self.nodes.get_unchecked(current_node_index as usize) };
286 let mut right_node = unsafe { self.nodes.get_unchecked(right_index) };
287
288 let mut left_t = left_node.aabb().intersect_ray(&ray);
290 let mut right_t = right_node.aabb().intersect_ray(&ray);
291
292 if left_t > right_t {
293 core::mem::swap(&mut left_t, &mut right_t);
294 core::mem::swap(&mut left_node, &mut right_node);
295 }
296
297 let hit_left = left_t < ray.tmax;
298
299 let go_left = if hit_left && left_node.is_leaf() {
300 if !intersection_fn(left_node, &mut ray, hit) {
301 return;
302 }
303 false
304 } else {
305 hit_left
306 };
307
308 let hit_right = right_t < ray.tmax;
309
310 let go_right = if hit_right && right_node.is_leaf() {
311 if !intersection_fn(right_node, &mut ray, hit) {
312 return;
313 }
314 false
315 } else {
316 hit_right
317 };
318
319 match (go_left, go_right) {
320 (true, true) => {
321 current_node_index = left_node.first_index;
322 stack.push(right_node.first_index);
323 }
324 (true, false) => current_node_index = left_node.first_index,
325 (false, true) => current_node_index = right_node.first_index,
326 (false, false) => {
327 let Some(next) = stack.pop() else {
328 hit.t = ray.tmax;
329 return;
330 };
331 current_node_index = next;
332 }
333 }
334 }
335 }
336
337 pub fn ray_traverse_recursive(
342 &self,
343 ray: &Ray,
344 node_index: usize,
345 leaf_indices: &mut Vec<usize>,
346 ) {
347 if self.nodes.is_empty() {
348 return;
349 }
350 let node = &self.nodes[node_index];
351 if node.aabb().intersect_ray(ray) < f32::INFINITY {
352 if node.is_leaf() {
353 leaf_indices.push(node_index);
354 } else {
355 self.ray_traverse_recursive(ray, node.first_index as usize, leaf_indices);
356 self.ray_traverse_recursive(ray, node.first_index as usize + 1, leaf_indices);
357 }
358 }
359 }
360
361 pub fn aabb_traverse<F: FnMut(&Self, u32) -> bool>(&self, aabb: Aabb, mut eval: F) {
367 if self.nodes.is_empty() {
368 return;
369 }
370
371 let root_node = &self.nodes[0];
372 if root_node.is_leaf() {
373 if root_node.aabb().intersect_aabb(&aabb) {
374 eval(self, 0);
375 }
376 return;
377 }
378
379 fast_stack!(u32, (96, 192), self.max_depth, stack, {
380 stack.push(root_node.first_index);
381 while let Some(node_index) = stack.pop() {
382 let node = &self.nodes[node_index as usize];
384 if node.aabb().intersect_aabb(&aabb) {
385 if node.is_leaf() {
386 if !eval(self, node_index) {
387 return;
388 }
389 } else {
390 stack.push(node.first_index);
391 }
392 }
393
394 let node_index = node_index + 1;
396 let node = &self.nodes[node_index as usize];
397 if node.aabb().intersect_aabb(&aabb) {
398 if node.is_leaf() {
399 if !eval(self, node_index) {
400 return;
401 }
402 } else {
403 stack.push(node.first_index);
404 }
405 }
406 }
407 });
408 }
409
410 pub fn point_traverse<F: FnMut(&Self, u32) -> bool>(&self, point: Vec3A, mut eval: F) {
416 if self.nodes.is_empty() {
417 return;
418 }
419
420 let root_node = &self.nodes[0];
421 if root_node.is_leaf() {
422 if root_node.aabb().contains_point(point) {
423 eval(self, 0);
424 }
425 return;
426 }
427
428 fast_stack!(u32, (96, 192), self.max_depth, stack, {
429 stack.push(root_node.first_index);
430 while let Some(node_index) = stack.pop() {
431 let node = &self.nodes[node_index as usize];
433 if node.aabb().contains_point(point) {
434 if node.is_leaf() {
435 if !eval(self, node_index) {
436 return;
437 }
438 } else {
439 stack.push(node.first_index);
440 }
441 }
442
443 let node_index = node_index + 1;
445 let node = &self.nodes[node_index as usize];
446 if node.aabb().contains_point(point) {
447 if node.is_leaf() {
448 if !eval(self, node_index) {
449 return;
450 }
451 } else {
452 stack.push(node.first_index);
453 }
454 }
455 }
456 });
457 }
458
459 pub fn reorder_in_stack_traversal_order(&mut self) {
464 if self.nodes.len() < 2 {
465 return;
466 }
467 let mut new_nodes: Vec<Bvh2Node> = Vec::with_capacity(self.nodes.len());
468 let mut mapping = vec![0; self.nodes.len()]; let mut stack = Vec::new();
470 stack.push(self.nodes[0].first_index);
471 new_nodes.push(self.nodes[0]);
472 mapping[0] = 0;
473 while let Some(current_node_index) = stack.pop() {
474 let node_a = &self.nodes[current_node_index as usize];
475 let node_b = &self.nodes[current_node_index as usize + 1];
476 if !node_a.is_leaf() {
477 stack.push(node_a.first_index);
478 }
479 if !node_b.is_leaf() {
480 stack.push(node_b.first_index);
481 }
482 let new_node_idx = new_nodes.len() as u32;
483 mapping[current_node_index as usize] = new_node_idx;
484 mapping[current_node_index as usize + 1] = new_node_idx + 1;
485 new_nodes.push(*node_a);
486 new_nodes.push(*node_b);
487 }
488 for n in &mut new_nodes {
489 if !n.is_leaf() {
490 n.first_index = mapping[n.first_index as usize];
491 }
492 }
493 self.nodes = new_nodes;
494 if !self.parents.is_empty() {
495 self.update_parents();
496 }
497 if !self.primitives_to_nodes.is_empty() {
498 self.update_primitives_to_nodes();
499 }
500 self.children_are_ordered_after_parents = true;
501 }
502
503 pub fn refit_all(&mut self) {
529 if self.nodes.is_empty() {
530 return;
531 }
532 if self.children_are_ordered_after_parents {
533 for node_id in (0..self.nodes.len()).rev() {
536 let node = &self.nodes[node_id];
537 if !node.is_leaf() {
538 let first_child_bbox = *self.nodes[node.first_index as usize].aabb();
539 let second_child_bbox = *self.nodes[node.first_index as usize + 1].aabb();
540 self.nodes[node_id].set_aabb(first_child_bbox.union(&second_child_bbox));
541 }
542 }
543 } else {
544 fast_stack!(u32, (96, 192), self.max_depth, stack, {
547 let mut reverse_stack = Vec::with_capacity(self.nodes.len());
548 stack.push(0);
549 reverse_stack.push(0);
550 while let Some(current_node_index) = stack.pop() {
551 let node = &self.nodes[current_node_index as usize];
552 if !node.is_leaf() {
553 reverse_stack.push(node.first_index);
554 reverse_stack.push(node.first_index + 1);
555 stack.push(node.first_index);
556 stack.push(node.first_index + 1);
557 }
558 }
559 for node_id in reverse_stack.iter().rev() {
560 let node = &self.nodes[*node_id as usize];
561 if !node.is_leaf() {
562 let first_child_bbox = *self.nodes[node.first_index as usize].aabb();
563 let second_child_bbox = *self.nodes[node.first_index as usize + 1].aabb();
564 self.nodes[*node_id as usize]
565 .set_aabb(first_child_bbox.union(&second_child_bbox));
566 }
567 }
568 });
569 }
570 }
571
572 pub fn init_parents_if_uninit(&mut self) {
574 if self.parents.is_empty() {
575 self.update_parents();
576 }
577 }
578
579 pub fn update_parents(&mut self) {
582 Bvh2::compute_parents(&self.nodes, &mut self.parents);
583 }
584
585 pub fn compute_parents(nodes: &[Bvh2Node], parents: &mut Vec<u32>) {
588 parents.resize(nodes.len(), 0);
589
590 if nodes.is_empty() {
591 return;
592 }
593
594 parents[0] = 0;
595
596 #[cfg(not(feature = "parallel"))]
597 {
598 nodes.iter().enumerate().for_each(|(i, node)| {
599 if !node.is_leaf() {
600 parents[node.first_index as usize] = i as u32;
601 parents[node.first_index as usize + 1] = i as u32;
602 }
603 });
604 }
605 #[cfg(feature = "parallel")]
608 {
609 use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
610 use std::sync::atomic::Ordering;
611
612 let parents = crate::as_slice_of_atomic_u32(parents);
613 nodes.par_iter().enumerate().for_each(|(i, node)| {
614 if !node.is_leaf() {
615 parents[node.first_index as usize].store(i as u32, Ordering::Relaxed);
616 parents[node.first_index as usize + 1].store(i as u32, Ordering::Relaxed);
617 }
618 });
619 }
620 }
621
622 pub fn init_primitives_to_nodes_if_uninit(&mut self) {
625 if self.primitives_to_nodes.is_empty() {
626 self.update_primitives_to_nodes();
627 }
628 }
629
630 pub fn update_primitives_to_nodes(&mut self) {
633 if self.uses_spatial_splits {
634 log::warn!(
635 "Calculating primitives_to_nodes while using spatial splits is currently unsupported as it would \
636 require a mapping from one primitive to multiple nodes in `Bvh2::primitives_to_nodes`."
637 );
638 }
639
640 Bvh2::compute_primitives_to_nodes(
641 &self.nodes,
642 &self.primitive_indices,
643 &mut self.primitives_to_nodes,
644 );
645 }
646
647 pub fn compute_primitives_to_nodes(
649 nodes: &[Bvh2Node],
650 primitive_indices: &[u32],
651 primitives_to_nodes: &mut Vec<u32>,
652 ) {
653 primitives_to_nodes.clear();
654 primitives_to_nodes.resize(primitive_indices.len(), INVALID);
655 for (node_id, node) in nodes.iter().enumerate() {
656 if node.is_leaf() {
657 let start = node.first_index;
658 let end = node.first_index + node.prim_count;
659 for node_prim_id in start..end {
660 let prim_id = primitive_indices[node_prim_id as usize];
662 primitives_to_nodes[prim_id as usize] = node_id as u32;
663 }
664 }
665 }
666 }
667
668 pub fn validate_parents(&self) {
669 self.nodes.iter().enumerate().for_each(|(i, node)| {
670 if !node.is_leaf() {
671 assert_eq!(self.parents[node.first_index as usize], i as u32);
672 assert_eq!(self.parents[node.first_index as usize + 1], i as u32);
673 }
674 });
675 }
676
677 pub fn validate_primitives_to_nodes(&self) {
678 self.primitives_to_nodes
679 .iter()
680 .enumerate()
681 .for_each(|(prim_id, node_id)| {
682 if *node_id != INVALID {
683 let prim_id = prim_id as u32;
684 let node = &self.nodes[*node_id as usize];
685 assert!(node.is_leaf());
686 let start = node.first_index;
687 let end = node.first_index + node.prim_count;
688 let mut found = false;
689 for node_prim_id in start..end {
690 if prim_id == self.primitive_indices[node_prim_id as usize] {
691 found = true;
692 break;
693 }
694 }
695 assert!(found, "prim_id {prim_id} not found")
696 }
697 });
698 }
699
700 pub fn refit_from(&mut self, mut index: usize) {
704 self.init_parents_if_uninit();
705 loop {
706 let node = &self.nodes[index];
707 if !node.is_leaf() {
708 let first_child_bbox = *self.nodes[node.first_index as usize].aabb();
709 let second_child_bbox = *self.nodes[node.first_index as usize + 1].aabb();
710 self.nodes[index].set_aabb(first_child_bbox.union(&second_child_bbox));
711 }
712 if index == 0 {
713 break;
714 }
715 index = self.parents[index] as usize;
716 }
717 }
718
719 pub fn refit_from_fast(&mut self, mut index: usize) {
724 self.init_parents_if_uninit();
725 let mut same_count = 0;
726 loop {
727 let node = &self.nodes[index];
728 if !node.is_leaf() {
729 let first_child_bbox = self.nodes[node.first_index as usize].aabb();
730 let second_child_bbox = self.nodes[node.first_index as usize + 1].aabb();
731 let new_aabb = first_child_bbox.union(second_child_bbox);
732 let node = &mut self.nodes[index];
733 if node.aabb() == &new_aabb {
734 same_count += 1;
735 #[cfg(not(debug_assertions))]
736 if same_count == 2 {
737 return;
738 }
739 } else {
740 debug_assert!(
741 same_count < 2,
742 "Some parents still needed refitting. Unideal fitting is occurring somewhere."
743 );
744 }
745 node.set_aabb(new_aabb);
746 }
747 if index == 0 {
748 break;
749 }
750 index = self.parents[index] as usize;
751 }
752 }
753
754 #[inline]
759 pub fn relink(&mut self, node_id: usize) {
760 let node = self.nodes[node_id];
761 self.relink_node(&node, node_id);
762 }
763
764 #[inline]
769 pub fn relink_node(&mut self, node: &Bvh2Node, node_id: usize) {
770 debug_assert_eq!(node.first_index, self.nodes[node_id].first_index);
771 debug_assert_eq!(node.prim_count, self.nodes[node_id].prim_count);
772 if node.is_leaf() {
773 update_primitives_to_nodes_for_node(
775 node,
776 node_id,
777 &self.primitive_indices,
778 &mut self.primitives_to_nodes,
779 )
780 } else {
781 self.parents[node.first_index as usize] = node_id as u32;
783 self.parents[node.first_index as usize + 1] = node_id as u32;
784 }
785 }
786
787 #[inline]
789 pub fn resize_node(&mut self, node_id: usize, aabb: Aabb) {
790 self.nodes[node_id].set_aabb(aabb);
791 self.refit_from_fast(node_id);
792 }
793
794 #[inline]
797 pub fn reinsert_node(&mut self, node_id: usize) {
798 if node_id == 0 {
799 return;
800 }
801 let reinsertion = find_reinsertion(self, node_id);
802 if reinsertion.area_diff > 0.0 {
803 reinsertion::reinsert_node(self, reinsertion.from as usize, reinsertion.to as usize);
804 self.children_are_ordered_after_parents = false;
805 }
806 }
807
808 #[inline(always)]
812 pub fn active_primitive_indices_count(&self) -> usize {
813 self.primitive_indices.len() - self.primitive_indices_freelist.len()
814 }
815
816 pub fn validate<T: Boundable>(
821 &self,
822 primitives: &[T],
823 direct_layout: bool,
824 tight_fit: bool,
825 ) -> Bvh2ValidationResult {
826 let mut result = Bvh2ValidationResult {
827 direct_layout,
828 require_tight_fit: tight_fit,
829 ..Default::default()
830 };
831
832 if self.nodes.is_empty() {
833 assert!(self.parents.is_empty());
834 assert!(self.primitives_to_nodes.is_empty());
835 return result;
836 }
837
838 if !self.primitives_to_nodes.is_empty() {
839 self.validate_primitives_to_nodes();
840 }
841
842 if !self.parents.is_empty() {
843 self.validate_parents();
844 }
845
846 if !self.nodes.is_empty() {
847 self.validate_impl::<T>(primitives, &mut result, 0, 0, 0);
848 }
849 assert_eq!(result.discovered_nodes.len(), self.nodes.len());
850 assert_eq!(result.node_count, self.nodes.len());
851
852 if !direct_layout {
854 if result.discovered_primitives.is_empty() {
855 assert!(self.active_primitive_indices_count() == 0)
856 } else {
857 if !self.uses_spatial_splits {
858 let active_indices_count = self.active_primitive_indices_count();
861 assert_eq!(result.discovered_primitives.len(), active_indices_count);
862 assert_eq!(result.prim_count, active_indices_count);
863 }
864 let primitive_indices_freeset: HashSet<&u32> =
867 HashSet::from_iter(&self.primitive_indices_freelist);
868 for (slot, index) in self.primitive_indices.iter().enumerate() {
869 let slot = slot as u32;
870 if !primitive_indices_freeset.contains(&slot) {
871 assert!(result.discovered_primitives.contains(index));
872 }
873 }
874 let primitive_indices_set: HashSet<&u32> =
875 HashSet::from_iter(self.primitive_indices.iter().filter(|i| **i != INVALID));
876 for discovered_prim_id in &result.discovered_primitives {
877 assert!(primitive_indices_set.contains(discovered_prim_id))
878 }
879 }
880 }
881 assert!(
882 result.max_depth < self.max_depth as u32,
883 "result.max_depth ({}) must be less than self.max_depth ({})",
884 result.max_depth,
885 self.max_depth as u32
886 );
887 if result.max_depth > DEFAULT_MAX_STACK_DEPTH as u32 {
888 log::warn!(
889 "bvh depth is: {}, a depth beyond {} may be indicative of something pathological in the scene (like thousands of instances perfectly overlapping geometry) that will result in a BVH that is very slow to traverse.",
890 result.max_depth,
891 DEFAULT_MAX_STACK_DEPTH
892 );
893 }
894
895 if self.children_are_ordered_after_parents {
896 let mut temp_parents = vec![];
898 let parents = if self.parents.is_empty() {
899 Bvh2::compute_parents(&self.nodes, &mut temp_parents);
900 &temp_parents
901 } else {
902 &self.parents
903 };
904
905 for node_id in (1..self.nodes.len()).rev() {
906 assert!(parents[node_id] < node_id as u32);
907 }
908 }
909
910 result
911 }
912
913 pub fn validate_impl<T: Boundable>(
914 &self,
915 primitives: &[T],
916 result: &mut Bvh2ValidationResult,
917 node_index: u32,
918 parent_index: u32,
919 current_depth: u32,
920 ) {
921 result.max_depth = result.max_depth.max(current_depth);
922 let parent_aabb = self.nodes[parent_index as usize].aabb();
923 result.discovered_nodes.insert(node_index);
924 let node = &self.nodes[node_index as usize];
925 result.node_count += 1;
926
927 if let Some(count) = result.nodes_at_depth.get(¤t_depth) {
928 result.nodes_at_depth.insert(current_depth, count + 1);
929 } else {
930 result.nodes_at_depth.insert(current_depth, 1);
931 }
932
933 assert!(
934 node.aabb().min.cmpge(parent_aabb.min).all()
935 && node.aabb().max.cmple(parent_aabb.max).all(),
936 "Child {} does not fit in parent {}:\nchild: {:?}\nparent: {:?}",
937 node_index,
938 parent_index,
939 node.aabb(),
940 parent_aabb
941 );
942
943 if node.is_leaf() {
944 result.leaf_count += 1;
945 if let Some(count) = result.leaves_at_depth.get(¤t_depth) {
946 result.leaves_at_depth.insert(current_depth, count + 1);
947 } else {
948 result.leaves_at_depth.insert(current_depth, 1);
949 }
950 let mut temp_aabb = Aabb::empty();
951 for i in 0..node.prim_count {
952 result.prim_count += 1;
953 let mut prim_index = (node.first_index + i) as usize;
954 if result.direct_layout {
955 result.discovered_primitives.insert(prim_index as u32);
956 } else {
957 result
958 .discovered_primitives
959 .insert(self.primitive_indices[prim_index]);
960 }
961 if !self.uses_spatial_splits {
963 if !result.direct_layout {
964 prim_index = self.primitive_indices[prim_index] as usize
965 }
966 let prim_aabb = primitives[prim_index].aabb();
967 temp_aabb = temp_aabb.union(&prim_aabb);
968 assert!(
969 prim_aabb.min.cmpge(node.aabb().min).all()
970 && prim_aabb.max.cmple(node.aabb().max).all(),
971 "Primitive {} does not fit in parent {}:\nprimitive: {:?}\nparent: {:?}",
972 prim_index,
973 parent_index,
974 prim_aabb,
975 node.aabb()
976 );
977 }
978 }
979 if result.require_tight_fit && !self.uses_spatial_splits {
980 assert_eq!(
981 temp_aabb,
982 *node.aabb(),
983 "Primitive do not fit in tightly in parent {node_index}",
984 );
985 }
986 } else {
987 if result.require_tight_fit {
988 let left_id = node.first_index as usize;
989 let right_id = node.first_index as usize + 1;
990 let left_child_aabb = &self.nodes[left_id];
991 let right_child_aabb = &self.nodes[right_id];
992
993 assert_eq!(
994 left_child_aabb.aabb().union(right_child_aabb.aabb()),
995 *node.aabb(),
996 "Children {left_id} & {right_id} do not fit in tightly in parent {node_index}",
997 );
998 }
999
1000 self.validate_impl::<T>(
1001 primitives,
1002 result,
1003 node.first_index,
1004 parent_index,
1005 current_depth + 1,
1006 );
1007 self.validate_impl::<T>(
1008 primitives,
1009 result,
1010 node.first_index + 1,
1011 parent_index,
1012 current_depth + 1,
1013 );
1014 }
1015 }
1016
1017 pub fn print_bvh(&self, node_index: usize, depth: usize) {
1019 let node = &self.nodes[node_index];
1020 if node.is_leaf() {
1021 println!(
1022 "{}{} leaf > {}",
1023 " ".repeat(depth),
1024 node_index,
1025 node.first_index
1026 )
1027 } else {
1028 println!(
1029 "{}{} inner > {}, {}",
1030 " ".repeat(depth),
1031 node_index,
1032 node.first_index,
1033 node.first_index + 1
1034 );
1035 self.print_bvh(node.first_index as usize, depth + 1);
1036 self.print_bvh(node.first_index as usize + 1, depth + 1);
1037 }
1038 }
1039
1040 pub fn depth(&self, node_index: usize) -> usize {
1042 let node = &self.nodes[node_index];
1043 if node.is_leaf() {
1044 1
1045 } else {
1046 1 + self
1047 .depth(node.first_index as usize)
1048 .max(self.depth((node.first_index + 1) as usize))
1049 }
1050 }
1051}
1052
1053#[inline]
1058fn update_primitives_to_nodes_for_node(
1059 node: &Bvh2Node,
1060 node_id: usize,
1061 primitive_indices: &[u32],
1062 primitives_to_nodes: &mut [u32],
1063) {
1064 if !primitives_to_nodes.is_empty() {
1065 let start = node.first_index;
1066 let end = start + node.prim_count;
1067 for node_prim_id in start..end {
1068 let direct_prim_id = primitive_indices[node_prim_id as usize];
1069 primitives_to_nodes[direct_prim_id as usize] = node_id as u32;
1070 }
1071 }
1072}
1073
1074#[derive(Default)]
1076pub struct Bvh2ValidationResult {
1077 pub direct_layout: bool,
1079 pub require_tight_fit: bool,
1081 pub discovered_primitives: HashSet<u32>,
1083 pub discovered_nodes: HashSet<u32>,
1085 pub node_count: usize,
1087 pub leaf_count: usize,
1089 pub prim_count: usize,
1091 pub max_depth: u32,
1093 pub nodes_at_depth: HashMap<u32, u32>,
1095 pub leaves_at_depth: HashMap<u32, u32>,
1097}
1098
1099impl fmt::Display for Bvh2ValidationResult {
1100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1101 writeln!(
1102 f,
1103 "GPU BVH Avg primitives/leaf: {:.3}",
1104 self.prim_count as f64 / self.leaf_count as f64
1105 )?;
1106
1107 writeln!(
1108 f,
1109 "\
1110node_count: {}
1111prim_count: {}
1112leaf_count: {}",
1113 self.node_count, self.prim_count, self.leaf_count
1114 )?;
1115
1116 writeln!(f, "Node & Leaf counts for each depth")?;
1117 for i in 0..=self.max_depth {
1118 writeln!(
1119 f,
1120 "{:<3} {:<10} {:<10}",
1121 i,
1122 self.nodes_at_depth.get(&i).unwrap_or(&0),
1123 self.leaves_at_depth.get(&i).unwrap_or(&0)
1124 )?;
1125 }
1126
1127 Ok(())
1128 }
1129}
1130
1131#[cfg(test)]
1132mod tests {
1133
1134 use glam::*;
1135
1136 use crate::{
1137 BvhBuildParams, Transformable,
1138 ploc::{PlocBuilder, PlocSearchDistance, SortPrecision},
1139 test_util::geometry::demoscene,
1140 };
1141
1142 use super::builder::build_bvh2_from_tris;
1143
1144 #[test]
1145 fn test_refit_all() {
1146 let mut tris = demoscene(32, 0);
1147 let mut aabbs = Vec::with_capacity(tris.len());
1148 let mut indices = Vec::with_capacity(tris.len());
1149 for (i, primitive) in tris.iter().enumerate() {
1150 indices.push(i as u32);
1151 aabbs.push(primitive.aabb());
1152 }
1153
1154 let mut bvh = PlocBuilder::new().build(
1156 PlocSearchDistance::VeryLow,
1157 &aabbs,
1158 indices.clone(),
1159 SortPrecision::U64,
1160 1,
1161 );
1162
1163 bvh.init_primitives_to_nodes_if_uninit();
1164 tris.transform(&Mat4::from_scale_rotation_translation(
1165 Vec3::splat(1.3),
1166 Quat::from_rotation_y(0.1),
1167 vec3(0.33, 0.3, 0.37),
1168 ));
1169 for (prim_id, tri) in tris.iter().enumerate() {
1170 bvh.nodes[bvh.primitives_to_nodes[prim_id] as usize].set_aabb(tri.aabb());
1171 }
1172
1173 bvh.refit_all();
1174
1175 bvh.validate(&tris, false, true);
1176 }
1177
1178 #[test]
1179 fn test_reinsert_node() {
1180 let tris = demoscene(32, 0);
1181
1182 let mut bvh = build_bvh2_from_tris(
1183 &tris,
1184 BvhBuildParams::fastest_build(),
1185 &mut Default::default(),
1186 );
1187
1188 bvh.init_primitives_to_nodes_if_uninit();
1189 bvh.init_parents_if_uninit();
1190
1191 for node_id in 1..bvh.nodes.len() {
1192 bvh.reinsert_node(node_id);
1193 }
1194
1195 bvh.validate(&tris, false, false);
1196 }
1197}