Completed first attempt at implementing BVH4.

Completely untested, so almost certainly non-functional.  But it
compiles!
This commit is contained in:
Nathan Vegdahl 2017-04-12 23:33:34 -07:00
parent e0c6dfbd76
commit 53a14996c9
4 changed files with 328 additions and 161 deletions

View File

@ -21,6 +21,309 @@ use super::bvh_base::{BVHBase, BVHBaseNode, BVH_MAX_DEPTH};
// TRAVERSAL_TABLE // TRAVERSAL_TABLE
include!("bvh4_table.inc"); 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::<BVH4Node>() };
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<T, F>(&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::<BVH4Node>(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 // Calculates the traversal code for a BVH4 node based on the splits and topology
// of its children. // 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]; 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] 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<T, F>(&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::<BVH4Node>() };
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::<BVH4Node>() };
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,
}
}
}
}
}

View File

@ -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 { impl BVHBase {
fn new() -> BVHBase { fn new() -> BVHBase {
BVHBase { BVHBase {

View File

@ -12,6 +12,10 @@ impl BitStack128 {
BitStack128 { data: (0, 0) } BitStack128 { data: (0, 0) }
} }
pub fn new_with_1() -> BitStack128 {
BitStack128 { data: (1, 0) }
}
/// Push a bit onto the top of the stack. /// Push a bit onto the top of the stack.
pub fn push(&mut self, value: bool) { pub fn push(&mut self, value: bool) {
debug_assert!((self.data.1 >> (size_of::<u64>() - 1)) == 0); // Verify no stack overflow debug_assert!((self.data.1 >> (size_of::<u64>() - 1)) == 0); // Verify no stack overflow
@ -57,6 +61,16 @@ impl BitStack128 {
return b; 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::<BitStack128>()); // 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::<u64>() - n));
self.data.1 >>= n;
return b;
}
/// Read the top bit of the stack without popping it. /// Read the top bit of the stack without popping it.
pub fn peek(&self) -> bool { pub fn peek(&self) -> bool {
(self.data.0 & 1) != 0 (self.data.0 & 1) != 0

View File

@ -17,7 +17,6 @@ pub struct Ray {
pub max_t: f32, pub max_t: f32,
pub time: f32, pub time: f32,
pub flags: u32, pub flags: u32,
pub trav_stack: BitStack128,
} }
impl Ray { impl Ray {
@ -29,7 +28,6 @@ impl Ray {
max_t: std::f32::INFINITY, max_t: std::f32::INFINITY,
time: time, time: time,
flags: 0, flags: 0,
trav_stack: BitStack128::new(),
} }
} else { } else {
Ray { Ray {
@ -38,7 +36,6 @@ impl Ray {
max_t: 1.0, max_t: 1.0,
time: time, time: time,
flags: OCCLUSION_FLAG, flags: OCCLUSION_FLAG,
trav_stack: BitStack128::new(),
} }
} }
} }
@ -62,6 +59,7 @@ pub struct AccelRay {
pub time: f32, pub time: f32,
pub flags: u32, pub flags: u32,
pub id: u32, pub id: u32,
pub trav_stack: BitStack128,
} }
impl AccelRay { impl AccelRay {
@ -73,6 +71,7 @@ impl AccelRay {
time: ray.time, time: ray.time,
flags: ray.flags, flags: ray.flags,
id: id, id: id,
trav_stack: BitStack128::new_with_1(),
} }
} }