From 53a14996c985cdc52feb2ca371f3c3aaaf87f12a Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Wed, 12 Apr 2017 23:33:34 -0700 Subject: [PATCH] Completed first attempt at implementing BVH4. Completely untested, so almost certainly non-functional. But it compiles! --- src/accel/bvh4.rs | 461 +++++++++++++++++++++++++++--------------- src/accel/bvh_base.rs | 9 + src/bitstack.rs | 14 ++ src/ray.rs | 5 +- 4 files changed, 328 insertions(+), 161 deletions(-) diff --git a/src/accel/bvh4.rs b/src/accel/bvh4.rs index 9a5fa40..b2c45ae 100644 --- a/src/accel/bvh4.rs +++ b/src/accel/bvh4.rs @@ -21,6 +21,309 @@ use super::bvh_base::{BVHBase, BVHBaseNode, BVH_MAX_DEPTH}; // TRAVERSAL_TABLE include!("bvh4_table.inc"); +#[derive(Copy, Clone, Debug)] +pub struct BVH4<'a> { + root: Option<&'a BVH4Node<'a>>, + depth: usize, + _bounds: Option<&'a [BBox]>, +} + +#[derive(Copy, Clone, Debug)] +enum BVH4Node<'a> { + Internal { + bounds: &'a [BBox4], + children: &'a [BVH4Node<'a>], + traversal_code: u8, + }, + + Leaf { object_range: (usize, usize) }, +} + +impl<'a> BVH4<'a> { + pub fn from_objects<'b, T, F>(arena: &'a MemArena, + objects: &mut [T], + objects_per_leaf: usize, + bounder: F) + -> BVH4<'a> + where F: 'b + Fn(&T) -> &'b [BBox] + { + if objects.len() == 0 { + BVH4 { + root: None, + depth: 0, + _bounds: None, + } + } else { + let base = BVHBase::from_objects(objects, objects_per_leaf, bounder); + + let mut fill_node = unsafe { arena.alloc_uninitialized::() }; + BVH4::construct_from_base(arena, &base, &base.nodes[base.root_node_index()], fill_node); + + BVH4 { + root: Some(fill_node), + depth: (base.depth / 2) + 1, + _bounds: { + let range = base.nodes[base.root_node_index()].bounds_range(); + Some(arena.copy_slice(&base.bounds[range.0..range.1])) + }, + } + } + } + + pub fn tree_depth(&self) -> usize { + self.depth + } + + pub fn traverse(&self, rays: &mut [AccelRay], objects: &[T], mut obj_ray_test: F) + where F: FnMut(&T, &mut [AccelRay]) + { + match self.root { + None => {} + + Some(root) => { + // +2 of max depth for root and last child + let mut node_stack = [Some(root); BVH_MAX_DEPTH + 2]; + let mut ray_i_stack = [rays.len(); BVH_MAX_DEPTH + 2]; + let mut stack_ptr = 1; + let mut first_loop = true; + + let ray_code = (rays[0].dir_inv.x().is_sign_negative() as u8) | + ((rays[0].dir_inv.y().is_sign_negative() as u8) << 1) | + ((rays[0].dir_inv.z().is_sign_negative() as u8) << 2); + + while stack_ptr > 0 { + match node_stack[stack_ptr] { + Some(&BVH4Node::Internal { bounds, children, traversal_code }) => { + let node_order_code = { + TRAVERSAL_TABLE[ray_code as usize][traversal_code as usize] + }; + + let mut all_hits = 0; // Accumulate + let part = partition(&mut rays[..ray_i_stack[stack_ptr]], |r| { + if (!r.is_done()) && (first_loop || r.trav_stack.pop()) { + let hits = lerp_slice(bounds, r.time) + .intersect_accel_ray(r) + .to_bitmask(); + all_hits |= hits; + + // Push hit bits onto ray's traversal stack + let mut shuffled_hits = 0; + for i in 0..3 { + let ii = (node_order_code >> (i * 2)) & 3; + shuffled_hits |= ((hits >> ii) & 1) << i; + } + r.trav_stack.push_3(shuffled_hits); + + true + } else { + false + } + }); + if part > 0 { + node_stack[stack_ptr] = { + Some(&children[(node_order_code & 3) as usize]) + }; + node_stack[stack_ptr + 1] = { + Some(&children[((node_order_code >> 2) & 3) as usize]) + }; + node_stack[stack_ptr + 2] = if children.len() > 2 { + Some(&children[((node_order_code >> 4) & 3) as usize]) + } else { + None + }; + node_stack[stack_ptr + 3] = if children.len() > 3 { + Some(&children[((node_order_code >> 6) & 3) as usize]) + } else { + None + }; + + ray_i_stack[stack_ptr] = part; + ray_i_stack[stack_ptr + 1] = part; + stack_ptr += children.len() - 1; + } else { + stack_ptr -= 1; + } + } + + Some(&BVH4Node::Leaf { object_range }) => { + let part = if !first_loop { + partition(&mut rays[..ray_i_stack[stack_ptr]], + |r| r.trav_stack.pop()) + } else { + ray_i_stack[stack_ptr] + }; + + for obj in &objects[object_range.0..object_range.1] { + obj_ray_test(obj, &mut rays[..part]); + } + + stack_ptr -= 1; + } + + None => { + if !first_loop { + for r in (&mut rays[..ray_i_stack[stack_ptr]]).iter_mut() { + r.trav_stack.pop(); + } + } + stack_ptr -= 1; + } + } + + first_loop = false; + } + } + } + } + + fn construct_from_base(arena: &'a MemArena, + base: &BVHBase, + node: &BVHBaseNode, + fill_node: &mut BVH4Node<'a>) { + match node { + // Create internal node + &BVHBaseNode::Internal { bounds_range: _, children_indices, split_axis } => { + let child_l = &base.nodes[children_indices.0]; + let child_r = &base.nodes[children_indices.1]; + + // Prepare convenient access to the stuff we need. + let child_count; + let children; // [Optional, Optional, Optional, Optional] + let split_axis_l; // Optional + let split_axis_r; // Optional + match child_l { + &BVHBaseNode::Internal { children_indices: i_l, split_axis: s_l, .. } => { + match child_r { + &BVHBaseNode::Internal { children_indices: i_r, + split_axis: s_r, + .. } => { + // Four nodes + child_count = 4; + children = [Some(&base.nodes[i_l.0]), + Some(&base.nodes[i_l.1]), + Some(&base.nodes[i_r.0]), + Some(&base.nodes[i_r.1])]; + split_axis_l = Some(s_l); + split_axis_r = Some(s_r); + } + &BVHBaseNode::Leaf { .. } => { + // Three nodes with left split + child_count = 3; + children = [Some(&base.nodes[i_l.0]), + Some(&base.nodes[i_l.1]), + Some(child_r), + None]; + split_axis_l = Some(s_l); + split_axis_r = None; + } + } + } + &BVHBaseNode::Leaf { .. } => { + match child_r { + &BVHBaseNode::Internal { children_indices: i_r, + split_axis: s_r, + .. } => { + // Three nodes with right split + child_count = 3; + children = [Some(child_l), + Some(&base.nodes[i_r.0]), + Some(&base.nodes[i_r.1]), + None]; + split_axis_l = None; + split_axis_r = Some(s_r); + } + &BVHBaseNode::Leaf { .. } => { + // Two nodes + child_count = 2; + children = [Some(child_l), Some(child_r), None, None]; + split_axis_l = None; + split_axis_r = None; + } + } + } + } + + // Construct bounds + let bounds = { + let bounds_len = children.iter() + .map(|c| if let &Some(n) = c { + n.bounds_range().1 - n.bounds_range().0 + } else { + 0 + }) + .max() + .unwrap(); + let mut bounds = unsafe { arena.alloc_array_uninitialized(bounds_len) }; + for (i, b) in bounds.iter_mut().enumerate() { + let time = i as f32 / (bounds_len - 1) as f32; + + let b1 = children[0].map_or(BBox::new(), |c| { + let (x, y) = c.bounds_range(); + lerp_slice(&base.bounds[x..y], time) + }); + let b2 = children[1].map_or(BBox::new(), |c| { + let (x, y) = c.bounds_range(); + lerp_slice(&base.bounds[x..y], time) + }); + let b3 = children[2].map_or(BBox::new(), |c| { + let (x, y) = c.bounds_range(); + lerp_slice(&base.bounds[x..y], time) + }); + let b4 = children[3].map_or(BBox::new(), |c| { + let (x, y) = c.bounds_range(); + lerp_slice(&base.bounds[x..y], time) + }); + *b = BBox4::from_bboxes(b1, b2, b3, b4); + } + bounds + }; + + // Construct child nodes + let mut child_nodes = + unsafe { arena.alloc_array_uninitialized::(child_count) }; + for (i, c) in children[0..child_count].iter().enumerate() { + BVH4::construct_from_base(arena, base, c.unwrap(), &mut child_nodes[i]); + } + + // Build this node + let traversal_code = { + let topology_code = if child_count == 4 { + 0 + } else if child_count == 2 { + 3 + } else if split_axis_l.is_some() { + 1 + } else { + 2 + }; + calc_traversal_code(split_axis, + split_axis_l.unwrap_or(split_axis_r.unwrap_or(0)), + split_axis_r.unwrap_or(0), + topology_code) + }; + *fill_node = BVH4Node::Internal { + bounds: bounds, + children: child_nodes, + traversal_code: traversal_code, + }; + } + + // Create internal node + &BVHBaseNode::Leaf { object_range, .. } => { + *fill_node = BVH4Node::Leaf { object_range: object_range }; + } + } + } +} + + +impl<'a> Boundable for BVH4<'a> { + fn bounds<'b>(&'b self) -> &'b [BBox] { + self._bounds.unwrap_or(&[]) + } +} + + // Calculates the traversal code for a BVH4 node based on the splits and topology // of its children. // @@ -44,161 +347,3 @@ fn calc_traversal_code(split_1: u8, split_2: u8, split_3: u8, topology: u8) -> u static T_TABLE: [u8; 4] = [0, 27, 27 + 9, 27 + 9 + 9]; split_1 + (split_2 * 3) + (split_3 * 9) + T_TABLE[topology as usize] } - -#[derive(Copy, Clone, Debug)] -pub struct BVH4<'a> { - root: Option<&'a BVH4Node<'a>>, - depth: usize, -} - -#[derive(Copy, Clone, Debug)] -enum BVH4Node<'a> { - // Internal { - // bounds: &'a [BBox4], - // children: [&'a BVH4Node<'a>; 4], - // children_count: u8, - // traversal_code: u8, - // }, - Internal { - bounds: &'a [BBox], - children: (&'a BVH4Node<'a>, &'a BVH4Node<'a>), - split_axis: u8, - }, - - Leaf { - bounds: &'a [BBox], - object_range: (usize, usize), - }, -} - -impl<'a> BVH4<'a> { - pub fn from_objects<'b, T, F>(arena: &'a MemArena, - objects: &mut [T], - objects_per_leaf: usize, - bounder: F) - -> BVH4<'a> - where F: 'b + Fn(&T) -> &'b [BBox] - { - if objects.len() == 0 { - BVH4 { - root: None, - depth: 0, - } - } else { - let base = BVHBase::from_objects(objects, objects_per_leaf, bounder); - - BVH4 { - root: Some(BVH4::construct_from_base(arena, &base, base.root_node_index())), - depth: base.depth, - } - } - } - - pub fn tree_depth(&self) -> usize { - self.depth - } - - pub fn traverse(&self, rays: &mut [AccelRay], objects: &[T], mut obj_ray_test: F) - where F: FnMut(&T, &mut [AccelRay]) - { - match self.root { - None => {} - - Some(root) => { - // +2 of max depth for root and last child - let mut node_stack = [root; BVH_MAX_DEPTH + 2]; - let mut ray_i_stack = [rays.len(); BVH_MAX_DEPTH + 2]; - let mut stack_ptr = 1; - - while stack_ptr > 0 { - match node_stack[stack_ptr] { - &BVH4Node::Internal { bounds, children, split_axis } => { - let part = partition(&mut rays[..ray_i_stack[stack_ptr]], |r| { - (!r.is_done()) && lerp_slice(bounds, r.time).intersect_accel_ray(r) - }); - if part > 0 { - node_stack[stack_ptr] = children.0; - node_stack[stack_ptr + 1] = children.1; - ray_i_stack[stack_ptr] = part; - ray_i_stack[stack_ptr + 1] = part; - if rays[0].dir_inv.get_n(split_axis as usize).is_sign_positive() { - node_stack.swap(stack_ptr, stack_ptr + 1); - } - stack_ptr += 1; - } else { - stack_ptr -= 1; - } - } - - &BVH4Node::Leaf { bounds, object_range } => { - let part = partition(&mut rays[..ray_i_stack[stack_ptr]], |r| { - (!r.is_done()) && lerp_slice(bounds, r.time).intersect_accel_ray(r) - }); - if part > 0 { - for obj in &objects[object_range.0..object_range.1] { - obj_ray_test(obj, &mut rays[..part]); - } - } - - stack_ptr -= 1; - } - } - } - } - } - } - - fn construct_from_base(arena: &'a MemArena, - base: &BVHBase, - node_index: usize) - -> &'a mut BVH4Node<'a> { - match &base.nodes[node_index] { - &BVHBaseNode::Internal { bounds_range, children_indices, split_axis } => { - let mut node = unsafe { arena.alloc_uninitialized::() }; - - let bounds = arena.copy_slice(&base.bounds[bounds_range.0..bounds_range.1]); - let child1 = BVH4::construct_from_base(arena, base, children_indices.0); - let child2 = BVH4::construct_from_base(arena, base, children_indices.1); - - *node = BVH4Node::Internal { - bounds: bounds, - children: (child1, child2), - split_axis: split_axis, - }; - - return node; - } - - &BVHBaseNode::Leaf { bounds_range, object_range } => { - let mut node = unsafe { arena.alloc_uninitialized::() }; - let bounds = arena.copy_slice(&base.bounds[bounds_range.0..bounds_range.1]); - - *node = BVH4Node::Leaf { - bounds: bounds, - object_range: object_range, - }; - - return node; - } - } - } -} - -lazy_static! { - static ref DEGENERATE_BOUNDS: [BBox; 1] = [BBox::new()]; -} - -impl<'a> Boundable for BVH4<'a> { - fn bounds<'b>(&'b self) -> &'b [BBox] { - match self.root { - None => &DEGENERATE_BOUNDS[..], - Some(root) => { - match root { - &BVH4Node::Internal { bounds, .. } => bounds, - - &BVH4Node::Leaf { bounds, .. } => bounds, - } - } - } - } -} diff --git a/src/accel/bvh_base.rs b/src/accel/bvh_base.rs index a6c61c7..58e8b0a 100644 --- a/src/accel/bvh_base.rs +++ b/src/accel/bvh_base.rs @@ -33,6 +33,15 @@ pub enum BVHBaseNode { }, } +impl BVHBaseNode { + pub fn bounds_range(&self) -> (usize, usize) { + match self { + &BVHBaseNode::Internal { bounds_range, .. } => bounds_range, + &BVHBaseNode::Leaf { bounds_range, .. } => bounds_range, + } + } +} + impl BVHBase { fn new() -> BVHBase { BVHBase { diff --git a/src/bitstack.rs b/src/bitstack.rs index 2e96788..afe28e4 100644 --- a/src/bitstack.rs +++ b/src/bitstack.rs @@ -12,6 +12,10 @@ impl BitStack128 { BitStack128 { data: (0, 0) } } + pub fn new_with_1() -> BitStack128 { + BitStack128 { data: (1, 0) } + } + /// Push a bit onto the top of the stack. pub fn push(&mut self, value: bool) { debug_assert!((self.data.1 >> (size_of::() - 1)) == 0); // Verify no stack overflow @@ -57,6 +61,16 @@ impl BitStack128 { return b; } + /// Pop the top n bits off the stack, returning only the first + /// one. + pub fn pop_1_remove_n(&mut self, n: usize) -> bool { + debug_assert!(n < size_of::()); // Can't pop more than we have + let b = (self.data.0 & 1) != 0; + self.data.0 = (self.data.0 >> n) | (self.data.1 << (size_of::() - n)); + self.data.1 >>= n; + return b; + } + /// Read the top bit of the stack without popping it. pub fn peek(&self) -> bool { (self.data.0 & 1) != 0 diff --git a/src/ray.rs b/src/ray.rs index d3717cb..3980a78 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -17,7 +17,6 @@ pub struct Ray { pub max_t: f32, pub time: f32, pub flags: u32, - pub trav_stack: BitStack128, } impl Ray { @@ -29,7 +28,6 @@ impl Ray { max_t: std::f32::INFINITY, time: time, flags: 0, - trav_stack: BitStack128::new(), } } else { Ray { @@ -38,7 +36,6 @@ impl Ray { max_t: 1.0, time: time, flags: OCCLUSION_FLAG, - trav_stack: BitStack128::new(), } } } @@ -62,6 +59,7 @@ pub struct AccelRay { pub time: f32, pub flags: u32, pub id: u32, + pub trav_stack: BitStack128, } impl AccelRay { @@ -73,6 +71,7 @@ impl AccelRay { time: ray.time, flags: ray.flags, id: id, + trav_stack: BitStack128::new_with_1(), } }