From c82c821b31b84444d2f76f244eb8dfc56f5a67d0 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Sun, 9 Apr 2017 23:33:36 -0700 Subject: [PATCH] BVH and objects now use MemArena. --- src/accel/bvh.rs | 188 ++++++++++++++++++---------------- src/light/rectangle_light.rs | 33 +++--- src/light/sphere_light.rs | 30 +++--- src/parse/psy_assembly.rs | 12 ++- src/parse/psy_light.rs | 12 ++- src/parse/psy_mesh_surface.rs | 8 +- src/scene/assembly.rs | 17 +-- src/surface/triangle_mesh.rs | 34 +++--- 8 files changed, 188 insertions(+), 146 deletions(-) diff --git a/src/accel/bvh.rs b/src/accel/bvh.rs index e8c384e..a0636d9 100644 --- a/src/accel/bvh.rs +++ b/src/accel/bvh.rs @@ -1,5 +1,7 @@ #![allow(dead_code)] +use mem_arena::MemArena; + use algorithm::{partition, merge_slices_append}; use bbox::BBox; use boundable::Boundable; @@ -12,15 +14,14 @@ use super::objects_split::{sah_split, median_split}; const BVH_MAX_DEPTH: usize = 64; -#[derive(Debug)] -pub struct BVH { - nodes: Vec, - bounds: Vec, +#[derive(Copy, Clone, Debug)] +pub struct BVH<'a> { + nodes: &'a [BVHNode], + bounds: &'a [BBox], depth: usize, - bounds_cache: Vec, } -#[derive(Debug)] +#[derive(Copy, Clone, Debug)] enum BVHNode { Internal { bounds_range: (usize, usize), @@ -34,9 +35,102 @@ enum BVHNode { }, } -impl BVH { - pub fn new_empty() -> BVH { +impl<'a> BVH<'a> { + pub fn from_objects<'b, T, F>(arena: &'a MemArena, + objects: &mut [T], + objects_per_leaf: usize, + bounder: F) + -> BVH<'a> + where F: 'b + Fn(&T) -> &'b [BBox] + { + let mut builder = BVHBuilder::new_empty(); + + builder.recursive_build(0, 0, objects_per_leaf, objects, &bounder); + BVH { + nodes: arena.copy_slice(&builder.nodes), + bounds: arena.copy_slice(&builder.bounds), + depth: builder.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]) + { + if self.nodes.len() == 0 { + return; + } + + // +2 of max depth for root and last child + let mut i_stack = [0; 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 self.nodes[i_stack[stack_ptr]] { + BVHNode::Internal { bounds_range: br, second_child_index, split_axis } => { + let part = partition(&mut rays[..ray_i_stack[stack_ptr]], |r| { + (!r.is_done()) && + lerp_slice(&self.bounds[br.0..br.1], r.time).intersect_accel_ray(r) + }); + if part > 0 { + i_stack[stack_ptr] += 1; + i_stack[stack_ptr + 1] = second_child_index; + 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() { + i_stack.swap(stack_ptr, stack_ptr + 1); + } + stack_ptr += 1; + } else { + stack_ptr -= 1; + } + } + + BVHNode::Leaf { bounds_range: br, object_range } => { + let part = partition(&mut rays[..ray_i_stack[stack_ptr]], |r| { + (!r.is_done()) && + lerp_slice(&self.bounds[br.0..br.1], 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; + } + } + } + } +} + +impl<'a> Boundable for BVH<'a> { + fn bounds<'b>(&'b self) -> &'b [BBox] { + match self.nodes[0] { + BVHNode::Internal { bounds_range, .. } => &self.bounds[bounds_range.0..bounds_range.1], + + BVHNode::Leaf { bounds_range, .. } => &self.bounds[bounds_range.0..bounds_range.1], + } + } +} + + +#[derive(Debug)] +struct BVHBuilder { + nodes: Vec, + bounds: Vec, + depth: usize, + bounds_cache: Vec, +} + +impl BVHBuilder { + fn new_empty() -> BVHBuilder { + BVHBuilder { nodes: Vec::new(), bounds: Vec::new(), depth: 0, @@ -44,22 +138,6 @@ impl BVH { } } - pub fn from_objects<'a, T, F>(objects: &mut [T], objects_per_leaf: usize, bounder: F) -> BVH - where F: 'a + Fn(&T) -> &'a [BBox] - { - let mut bvh = BVH::new_empty(); - - bvh.recursive_build(0, 0, objects_per_leaf, objects, &bounder); - bvh.bounds_cache.clear(); - bvh.bounds_cache.shrink_to_fit(); - - bvh - } - - pub fn tree_depth(&self) -> usize { - self.depth - } - fn acc_bounds<'a, T, F>(&mut self, objects: &mut [T], bounder: &F) where F: 'a + Fn(&T) -> &'a [BBox] { @@ -168,66 +246,4 @@ impl BVH { return (me, (bi, self.bounds.len())); } } - - - pub fn traverse(&self, rays: &mut [AccelRay], objects: &[T], mut obj_ray_test: F) - where F: FnMut(&T, &mut [AccelRay]) - { - if self.nodes.len() == 0 { - return; - } - - // +2 of max depth for root and last child - let mut i_stack = [0; 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 self.nodes[i_stack[stack_ptr]] { - BVHNode::Internal { bounds_range: br, second_child_index, split_axis } => { - let part = partition(&mut rays[..ray_i_stack[stack_ptr]], |r| { - (!r.is_done()) && - lerp_slice(&self.bounds[br.0..br.1], r.time).intersect_accel_ray(r) - }); - if part > 0 { - i_stack[stack_ptr] += 1; - i_stack[stack_ptr + 1] = second_child_index; - 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() { - i_stack.swap(stack_ptr, stack_ptr + 1); - } - stack_ptr += 1; - } else { - stack_ptr -= 1; - } - } - - BVHNode::Leaf { bounds_range: br, object_range } => { - let part = partition(&mut rays[..ray_i_stack[stack_ptr]], |r| { - (!r.is_done()) && - lerp_slice(&self.bounds[br.0..br.1], 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; - } - } - } - } -} - - -impl Boundable for BVH { - fn bounds<'a>(&'a self) -> &'a [BBox] { - match self.nodes[0] { - BVHNode::Internal { bounds_range, .. } => &self.bounds[bounds_range.0..bounds_range.1], - - BVHNode::Leaf { bounds_range, .. } => &self.bounds[bounds_range.0..bounds_range.1], - } - } } diff --git a/src/light/rectangle_light.rs b/src/light/rectangle_light.rs index c5dccc6..758bc09 100644 --- a/src/light/rectangle_light.rs +++ b/src/light/rectangle_light.rs @@ -1,3 +1,5 @@ +use mem_arena::MemArena; + use bbox::BBox; use boundable::Boundable; use color::{XYZ, SpectralSample, Color}; @@ -8,16 +10,19 @@ use sampling::{spherical_triangle_solid_angle, uniform_sample_spherical_triangle use super::LightSource; -#[derive(Debug)] -pub struct RectangleLight { - dimensions: Vec<(f32, f32)>, - colors: Vec, - bounds_: Vec, +#[derive(Copy, Clone, Debug)] +pub struct RectangleLight<'a> { + dimensions: &'a [(f32, f32)], + colors: &'a [XYZ], + bounds_: &'a [BBox], } -impl RectangleLight { - pub fn new(dimensions: Vec<(f32, f32)>, colors: Vec) -> RectangleLight { - let bbs = dimensions.iter() +impl<'a> RectangleLight<'a> { + pub fn new<'b>(arena: &'b MemArena, + dimensions: Vec<(f32, f32)>, + colors: Vec) + -> RectangleLight<'b> { + let bbs: Vec<_> = dimensions.iter() .map(|d| { BBox { min: Point::new(d.0 * -0.5, d.1 * -0.5, 0.0), @@ -26,14 +31,14 @@ impl RectangleLight { }) .collect(); RectangleLight { - dimensions: dimensions, - colors: colors, - bounds_: bbs, + dimensions: arena.copy_slice(&dimensions), + colors: arena.copy_slice(&colors), + bounds_: arena.copy_slice(&bbs), } } } -impl LightSource for RectangleLight { +impl<'a> LightSource for RectangleLight<'a> { fn sample(&self, space: &Matrix4x4, arr: Point, @@ -166,8 +171,8 @@ impl LightSource for RectangleLight { } } -impl Boundable for RectangleLight { - fn bounds<'a>(&'a self) -> &'a [BBox] { +impl<'a> Boundable for RectangleLight<'a> { + fn bounds<'b>(&'b self) -> &'b [BBox] { &self.bounds_ } } diff --git a/src/light/sphere_light.rs b/src/light/sphere_light.rs index 243c6da..cd806dd 100644 --- a/src/light/sphere_light.rs +++ b/src/light/sphere_light.rs @@ -1,5 +1,7 @@ use std::f64::consts::PI as PI_64; +use mem_arena::MemArena; + use bbox::BBox; use boundable::Boundable; use color::{XYZ, SpectralSample, Color}; @@ -11,16 +13,16 @@ use super::LightSource; // TODO: handle case where radius = 0.0. -#[derive(Debug)] -pub struct SphereLight { - radii: Vec, - colors: Vec, - bounds_: Vec, +#[derive(Copy, Clone, Debug)] +pub struct SphereLight<'a> { + radii: &'a [f32], + colors: &'a [XYZ], + bounds_: &'a [BBox], } -impl SphereLight { - pub fn new(radii: Vec, colors: Vec) -> SphereLight { - let bbs = radii.iter() +impl<'a> SphereLight<'a> { + pub fn new<'b>(arena: &'b MemArena, radii: Vec, colors: Vec) -> SphereLight<'b> { + let bbs: Vec<_> = radii.iter() .map(|r| { BBox { min: Point::new(-*r, -*r, -*r), @@ -29,14 +31,14 @@ impl SphereLight { }) .collect(); SphereLight { - radii: radii, - colors: colors, - bounds_: bbs, + radii: arena.copy_slice(&radii), + colors: arena.copy_slice(&colors), + bounds_: arena.copy_slice(&bbs), } } } -impl LightSource for SphereLight { +impl<'a> LightSource for SphereLight<'a> { fn sample(&self, space: &Matrix4x4, arr: Point, @@ -170,8 +172,8 @@ impl LightSource for SphereLight { } } -impl Boundable for SphereLight { - fn bounds<'a>(&'a self) -> &'a [BBox] { +impl<'a> Boundable for SphereLight<'a> { + fn bounds<'b>(&'b self) -> &'b [BBox] { &self.bounds_ } } diff --git a/src/parse/psy_assembly.rs b/src/parse/psy_assembly.rs index 95562ef..5e3289f 100644 --- a/src/parse/psy_assembly.rs +++ b/src/parse/psy_assembly.rs @@ -67,7 +67,9 @@ pub fn parse_assembly<'a>(arena: &'a MemArena, "MeshSurface" => { if let &DataTree::Internal { ident: Some(ident), .. } = child { builder.add_object(ident, - Object::Surface(Box::new(parse_mesh_surface(&child)?))); + Object::Surface(arena.alloc( + parse_mesh_surface(arena, &child)? + ))); } else { // TODO: error condition of some kind, because no ident panic!(); @@ -78,7 +80,9 @@ pub fn parse_assembly<'a>(arena: &'a MemArena, "SphereLight" => { if let &DataTree::Internal { ident: Some(ident), .. } = child { builder.add_object(ident, - Object::Light(Box::new(parse_sphere_light(&child)?))); + Object::Light(arena.alloc( + parse_sphere_light(arena, &child)? + ))); } else { // TODO: error condition of some kind, because no ident panic!(); @@ -89,7 +93,9 @@ pub fn parse_assembly<'a>(arena: &'a MemArena, "RectangleLight" => { if let &DataTree::Internal { ident: Some(ident), .. } = child { builder.add_object(ident, - Object::Light(Box::new(parse_rectangle_light(&child)?))); + Object::Light(arena.alloc( + parse_rectangle_light(arena, &child)? + ))); } else { // TODO: error condition of some kind, because no ident panic!(); diff --git a/src/parse/psy_light.rs b/src/parse/psy_light.rs index 34645d4..91b081c 100644 --- a/src/parse/psy_light.rs +++ b/src/parse/psy_light.rs @@ -72,7 +72,9 @@ pub fn parse_distant_disk_light<'a>(arena: &'a MemArena, } -pub fn parse_sphere_light(tree: &DataTree) -> Result { +pub fn parse_sphere_light<'a>(arena: &'a MemArena, + tree: &'a DataTree) + -> Result, PsyParseError> { if let &DataTree::Internal { ref children, .. } = tree { let mut radii = Vec::new(); let mut colors = Vec::new(); @@ -108,13 +110,15 @@ pub fn parse_sphere_light(tree: &DataTree) -> Result } } - return Ok(SphereLight::new(radii, colors)); + return Ok(SphereLight::new(arena, radii, colors)); } else { return Err(PsyParseError::UnknownError); } } -pub fn parse_rectangle_light(tree: &DataTree) -> Result { +pub fn parse_rectangle_light<'a>(arena: &'a MemArena, + tree: &'a DataTree) + -> Result, PsyParseError> { if let &DataTree::Internal { ref children, .. } = tree { let mut dimensions = Vec::new(); let mut colors = Vec::new(); @@ -151,7 +155,7 @@ pub fn parse_rectangle_light(tree: &DataTree) -> Result Result { +pub fn parse_mesh_surface<'a>(arena: &'a MemArena, + tree: &'a DataTree) + -> Result, PsyParseError> { let mut verts = Vec::new(); let mut face_vert_counts = Vec::new(); let mut face_vert_indices = Vec::new(); @@ -100,5 +104,5 @@ pub fn parse_mesh_surface(tree: &DataTree) -> Result { pub xforms: &'a [Matrix4x4], // Object list - pub objects: Vec, + pub objects: Vec>, // Assembly list pub assemblies: Vec>, // Object accel - pub object_accel: BVH, + pub object_accel: BVH<'a>, // Light accel pub light_accel: LightTree, @@ -137,7 +137,7 @@ pub struct AssemblyBuilder<'a> { xforms: Vec, // Object list - objects: Vec, + objects: Vec>, object_map: HashMap, // map Name -> Index // Assembly list @@ -159,7 +159,7 @@ impl<'a> AssemblyBuilder<'a> { } } - pub fn add_object(&mut self, name: &str, obj: Object) { + pub fn add_object(&mut self, name: &str, obj: Object<'a>) { // Make sure the name hasn't already been used. if self.name_exists(name) { panic!("Attempted to add object to assembly with a name that already exists."); @@ -231,7 +231,8 @@ impl<'a> AssemblyBuilder<'a> { let (bis, bbs) = self.instance_bounds(); // Build object accel - let object_accel = BVH::from_objects(&mut self.instances[..], + let object_accel = BVH::from_objects(self.arena, + &mut self.instances[..], 1, |inst| &bbs[bis[inst.id]..bis[inst.id + 1]]); @@ -335,9 +336,9 @@ impl<'a> AssemblyBuilder<'a> { #[derive(Debug)] -pub enum Object { - Surface(Box), - Light(Box), +pub enum Object<'a> { + Surface(&'a Surface), + Light(&'a LightSource), } diff --git a/src/surface/triangle_mesh.rs b/src/surface/triangle_mesh.rs index 90b19e0..e3cc094 100644 --- a/src/surface/triangle_mesh.rs +++ b/src/surface/triangle_mesh.rs @@ -1,5 +1,7 @@ #![allow(dead_code)] +use mem_arena::MemArena; + use accel::BVH; use bbox::BBox; use boundable::Boundable; @@ -13,18 +15,19 @@ use super::{Surface, SurfaceIntersection, SurfaceIntersectionData}; use super::triangle; -#[derive(Debug)] -pub struct TriangleMesh { +#[derive(Copy, Clone, Debug)] +pub struct TriangleMesh<'a> { time_samples: usize, - geo: Vec<(Point, Point, Point)>, - indices: Vec, - accel: BVH, + geo: &'a [(Point, Point, Point)], + indices: &'a [usize], + accel: BVH<'a>, } -impl TriangleMesh { - pub fn from_triangles(time_samples: usize, - triangles: Vec<(Point, Point, Point)>) - -> TriangleMesh { +impl<'a> TriangleMesh<'a> { + pub fn from_triangles<'b>(arena: &'b MemArena, + time_samples: usize, + triangles: Vec<(Point, Point, Point)>) + -> TriangleMesh<'b> { assert!(triangles.len() % time_samples == 0); let mut indices: Vec = (0..(triangles.len() / time_samples)) @@ -41,27 +44,28 @@ impl TriangleMesh { bounds }; - let accel = BVH::from_objects(&mut indices[..], + let accel = BVH::from_objects(arena, + &mut indices[..], 3, |tri_i| &bounds[*tri_i..(*tri_i + time_samples)]); TriangleMesh { time_samples: time_samples, - geo: triangles, - indices: indices, + geo: arena.copy_slice(&triangles), + indices: arena.copy_slice(&indices), accel: accel, } } } -impl Boundable for TriangleMesh { - fn bounds<'a>(&'a self) -> &'a [BBox] { +impl<'a> Boundable for TriangleMesh<'a> { + fn bounds<'b>(&'b self) -> &'b [BBox] { self.accel.bounds() } } -impl Surface for TriangleMesh { +impl<'a> Surface for TriangleMesh<'a> { fn intersect_rays(&self, accel_rays: &mut [AccelRay], wrays: &[Ray],