From e1de889d3ad058805a8a27873a37f079a53c7ca7 Mon Sep 17 00:00:00 2001 From: Matthew Gordon Date: Sat, 21 Dec 2019 09:55:34 -0500 Subject: [PATCH] Quick-and-dirty multithreading Not the best multithreading scheme and needs error handling, but it works. --- src/camera.rs | 36 ++++++++++------------- src/main.rs | 75 +++++++++++++++++++++++++++++++---------------- src/materials.rs | 5 ++-- src/mesh.rs | 32 ++++++++++---------- src/raycasting.rs | 34 ++++++++++----------- 5 files changed, 101 insertions(+), 81 deletions(-) diff --git a/src/camera.rs b/src/camera.rs index 2ad527a..043bc2f 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -7,6 +7,8 @@ use super::raycasting::Ray; use super::sampler::Sampler; use super::scene::Scene; +use std::sync::{Arc, Mutex}; + struct ImageSampler { image_height_pixels: u32, image_width_pixels: u32, @@ -62,30 +64,23 @@ impl ImageSampler { } } -pub fn render_scene(output_image: &mut ImageRgbF, scene: &Scene) { - partial_render_scene( - output_image, - scene, - 0, - output_image.get_height(), - 0, - output_image.get_width(), - ) +pub fn render_scene(output_image: Arc>>, scene: Arc>) { + let height = output_image.lock().unwrap().get_height(); + let width = output_image.lock().unwrap().get_width(); + partial_render_scene(output_image, scene, 0, height, 0, width, height, width) } pub fn partial_render_scene( - output_image: &mut ImageRgbF, - scene: &Scene, + output_image: Arc>>, + scene: Arc>, row_start: u32, row_end: u32, column_start: u32, column_end: u32, + height: u32, + width: u32, ) { - let image_sampler = ImageSampler::new( - output_image.get_width(), - output_image.get_height(), - scene.camera_location, - ); + let image_sampler = ImageSampler::new(width, height, scene.camera_location); let ambient_intensity: T = convert(0.0); let directional_intensity1: T = convert(7.0); let directional_intensity2: T = convert(3.0); @@ -107,7 +102,7 @@ pub fn partial_render_scene( }, ], }; - let sampler = Sampler { scene }; + let sampler = Sampler { scene: &scene }; for column in column_start..column_end { for row in row_start..row_end { let ray = image_sampler.ray_for_pixel(row, column); @@ -116,7 +111,8 @@ pub fn partial_render_scene( None => ColourRgbF::from_named(NamedColour::Black), Some(intersection_info) => integrator.integrate(&sampler, &intersection_info), }; - output_image.set_colour(row, column, colour); + let mut locked_image = output_image.lock().unwrap(); + locked_image.set_colour(row, column, colour); } } } @@ -126,7 +122,7 @@ mod tests { use super::*; use crate::materials::LambertianMaterial; use crate::raycasting::{Intersect, IntersectionInfo, Plane}; - use std::rc::Rc; + use std::sync::Arc; #[cfg(test)] mod imagesampler { @@ -151,7 +147,7 @@ mod tests { let film_plane = Plane::new( Vector3::new(0.0, 0.0, 1.0), target.film_distance, - Rc::new(LambertianMaterial::::new_dummy()), + Arc::new(LambertianMaterial::::new_dummy()), ); let point_on_film_plane = match film_plane.intersect(&ray) { Some(IntersectionInfo { diff --git a/src/main.rs b/src/main.rs index d48cadb..cd76d52 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,16 +7,18 @@ use std::time::Duration; use nalgebra::{Point3, Vector3}; +use itertools::iproduct; use std::cmp::min; -use std::rc::Rc; use std::path::Path; +use std::sync::{Arc, Mutex}; +use std::thread; use vanrijn::camera::partial_render_scene; use vanrijn::colour::{ColourRgbF, NamedColour}; use vanrijn::image::{ClampingToneMapper, ImageRgbF, ImageRgbU8, ToneMapper}; use vanrijn::materials::{LambertianMaterial, PhongMaterial, ReflectiveMaterial}; -use vanrijn::mesh::{load_obj, Triangle}; +use vanrijn::mesh::load_obj; use vanrijn::raycasting::{Intersect, Plane, Sphere}; use vanrijn::scene::Scene; @@ -59,7 +61,7 @@ pub fn main() -> Result<(), Box> { image_width as u32, image_height as u32, )?; - let mut output_image = ImageRgbF::::new(image_width, image_height); + let output_image = Arc::new(Mutex::new(ImageRgbF::::new(image_width, image_height))); let scene = Arc::new(Scene { camera_location: Point3::new(-2.0, 1.0, -5.0), @@ -78,7 +80,7 @@ pub fn main() -> Result<(), Box> { Box::new(Plane::new( Vector3::new(0.0, 1.0, 0.0), -2.0, - Rc::new(LambertianMaterial { + Arc::new(LambertianMaterial { colour: ColourRgbF::new(0.55, 0.27, 0.04), diffuse_strength: 0.1, }), @@ -94,7 +96,7 @@ pub fn main() -> Result<(), Box> { Box::new(Sphere::new( Point3::new(-4.25, -0.5, 2.0), 1.0, - Rc::new(ReflectiveMaterial { + Arc::new(ReflectiveMaterial { colour: ColourRgbF::from_named(NamedColour::Blue), diffuse_strength: 0.01, reflection_strength: 0.99, @@ -103,7 +105,7 @@ pub fn main() -> Result<(), Box> { Box::new(Sphere::new( Point3::new(-5.0, 1.5, 1.0), 1.0, - Rc::new(PhongMaterial { + Arc::new(PhongMaterial { colour: ColourRgbF::from_named(NamedColour::Red), diffuse_strength: 0.05, smoothness: 100.0, @@ -117,26 +119,49 @@ pub fn main() -> Result<(), Box> { let mut event_pump = sdl_context.event_pump()?; let mut i = 0; 'running: loop { - let tile_size = 256; - for tile_row in 0..=(output_image.get_height() + 1) / tile_size { - for tile_column in 0..=(output_image.get_width() + 1) / tile_size { - let row_start = tile_row * tile_size; - let row_end = min(tile_row * tile_size + tile_size, output_image.get_height()); - let column_start = tile_column * tile_size; - let column_end = min( - tile_column * tile_size + tile_size, - output_image.get_width(), - ); - partial_render_scene( - &mut output_image, - &scene, - row_start, - row_end, - column_start, - column_end, - ); + let subtile_size = 16; + let tile_divisions = 4; + let tile_size = subtile_size * tile_divisions; + for tile_row in 0..=(image_height + 1) / tile_size { + for tile_column in 0..=(image_width + 1) / tile_size { + //let row_start = tile_row * tile_size; + //let row_end = min(tile_row * tile_size + tile_size, image_height); + //let column_start = tile_column * tile_size; + //let column_end = min(tile_column * tile_size + tile_size, image_width); + let join_handles: Vec<_> = iproduct!(0..tile_divisions, 0..tile_divisions) + .map(|(tile_i, tile_j)| { + let start_i = tile_row * tile_size + tile_i * subtile_size; + let start_j = tile_column * tile_size + tile_j * subtile_size; + ( + start_i, + min(start_i + subtile_size, image_height), + start_j, + min(start_j + subtile_size, image_width), + ) + }) + .map(|(i_min, i_max, j_min, j_max)| { + let image_ptr = output_image.clone(); + let scene_ptr = scene.clone(); + thread::spawn(move || { + partial_render_scene( + image_ptr, + scene_ptr, + i_min, + i_max, + j_min, + j_max, + image_height, + image_width, + ); + }) + }) + .collect(); + for h in join_handles { + h.join(); + } + let locked_image = output_image.lock().unwrap(); let mut output_image_rgbu8 = ImageRgbU8::new(image_width, image_height); - ClampingToneMapper {}.apply_tone_mapping(&output_image, &mut output_image_rgbu8); + ClampingToneMapper {}.apply_tone_mapping(&locked_image, &mut output_image_rgbu8); update_texture(&output_image_rgbu8, &mut rendered_image_texture); canvas.copy(&rendered_image_texture, None, None)?; canvas.present(); diff --git a/src/materials.rs b/src/materials.rs index a7b554a..1f103b6 100644 --- a/src/materials.rs +++ b/src/materials.rs @@ -4,10 +4,9 @@ use super::colour::{ColourRgbF, NamedColour}; use std::fmt::Debug; -type Bsdf<'a, T> = - Box, Vector3, ColourRgbF) -> ColourRgbF + 'a>; +type Bsdf<'a, T> = Box, Vector3, ColourRgbF) -> ColourRgbF + 'a>; -pub trait Material: Debug { +pub trait Material: Debug + Sync + Send { fn bsdf<'a>(&'a self) -> Bsdf<'a, T>; fn sample(&self, _w_o: &Vector3) -> Vec> { diff --git a/src/mesh.rs b/src/mesh.rs index 21db21a..e9334aa 100644 --- a/src/mesh.rs +++ b/src/mesh.rs @@ -4,16 +4,16 @@ use obj::{IndexTuple, Obj, SimplePolygon}; use super::materials::Material; use super::raycasting::{Intersect, IntersectionInfo, Ray}; -use std::rc::Rc; use alga::general::SupersetOf; use std::io::Result; use std::path::Path; +use std::sync::Arc; #[derive(Debug)] pub struct Triangle { pub vertices: [Point3; 3], pub normals: [Vector3; 3], - pub material: Rc>, + pub material: Arc >, } impl Intersect for Triangle { @@ -65,7 +65,7 @@ impl Intersect for Triangle { .normalize(); let tangent = cotangent.cross(&normal).normalize(); let retro = (ray.origin - location).normalize(); - let material = Rc::clone(&self.material); + let material = Arc::clone(&self.material); Some(IntersectionInfo { distance, location, @@ -373,7 +373,7 @@ mod tests { Point3::new(-1.0, -1.0, 1.0), ], normals: [Vector3::zeros(); 3], - material: Rc::new(LambertianMaterial::new_dummy()), + material: Arc::new(LambertianMaterial::new_dummy()), }; let target_ray = Ray::new(Point3::new(0.0, 0.0, 0.0), Vector3::new(0.0, 0.0, 1.0)); if let None = target_triangle.intersect(&target_ray) { @@ -390,7 +390,7 @@ mod tests { Point3::new(1.0, -1.0, 1.0), ], normals: [Vector3::zeros(); 3], - material: Rc::new(LambertianMaterial::new_dummy()), + material: Arc::new(LambertianMaterial::new_dummy()), }; let target_ray = Ray::new(Point3::new(0.0, 0.0, 0.0), Vector3::new(0.0, 0.0, 1.0)); if let None = target_triangle.intersect(&target_ray) { @@ -407,7 +407,7 @@ mod tests { Point3::new(-1.0, -1.0, -1.0), ], normals: [Vector3::zeros(); 3], - material: Rc::new(LambertianMaterial::new_dummy()), + material: Arc::new(LambertianMaterial::new_dummy()), }; let target_ray = Ray::new(Point3::new(0.0, 0.0, 0.0), Vector3::new(0.0, 0.0, -1.0)); if let None = target_triangle.intersect(&target_ray) { @@ -424,7 +424,7 @@ mod tests { Point3::new(1.0, -1.0, -1.0), ], normals: [Vector3::zeros(); 3], - material: Rc::new(LambertianMaterial::new_dummy()), + material: Arc::new(LambertianMaterial::new_dummy()), }; let target_ray = Ray::new(Point3::new(0.0, 0.0, 0.0), Vector3::new(0.0, 0.0, -1.0)); if let None = target_triangle.intersect(&target_ray) { @@ -441,7 +441,7 @@ mod tests { Point3::new(4.0, 4.0, 6.0), ], normals: [Vector3::zeros(); 3], - material: Rc::new(LambertianMaterial::new_dummy()), + material: Arc::new(LambertianMaterial::new_dummy()), }; let target_ray = Ray::new(Point3::new(5.0, 5.0, 5.0), Vector3::new(0.0, 0.0, 1.0)); if let None = target_triangle.intersect(&target_ray) { @@ -458,7 +458,7 @@ mod tests { Point3::new(5.0, 4.5, 6.0), ], normals: [Vector3::zeros(); 3], - material: Rc::new(LambertianMaterial::new_dummy()), + material: Arc::new(LambertianMaterial::new_dummy()), }; let target_ray = Ray::new(Point3::new(5.0, 5.0, 5.0), Vector3::new(1.0, 0.5, 1.0)); if let None = target_triangle.intersect(&target_ray) { @@ -492,7 +492,7 @@ mod tests { Point3::from(vertex2), ], normals: [normal; 3], - material: Rc::new(LambertianMaterial::new_dummy()), + material: Arc::new(LambertianMaterial::new_dummy()), }; let ray = Ray::new(ray_origin, ray_direction); @@ -523,7 +523,7 @@ mod tests { Point3::from(vertex2), ], normals: [normal; 3], - material: Rc::new(LambertianMaterial::new_dummy()), + material: Arc::new(LambertianMaterial::new_dummy()), }; let ray = Ray::new(ray_origin, ray_direction); @@ -668,7 +668,7 @@ mod tests { Point3::from(vertex2), ], normals: [normal; 3], - material: Rc::new(LambertianMaterial::new_dummy()), + material: Arc::new(LambertianMaterial::new_dummy()), }; let ray = Ray::new(ray_origin, ray_direction); @@ -795,7 +795,7 @@ mod tests { let triangle = Triangle { vertices: [vertex0, vertex1, vertex2], normals: [Vector3::zeros(); 3], - material: Rc::new(LambertianMaterial::new_dummy()), + material: Arc::new(LambertianMaterial::new_dummy()), }; match triangle.intersect(&ray) { Some(_) => false, @@ -823,7 +823,7 @@ mod tests { let triangle = Triangle { vertices: [vertex0, vertex1, vertex2], normals: [Vector3::zeros(); 3], - material: Rc::new(LambertianMaterial::new_dummy()), + material: Arc::new(LambertianMaterial::new_dummy()), }; match triangle.intersect(&ray) { Some(_) => false, @@ -851,7 +851,7 @@ mod tests { let triangle = Triangle { vertices: [vertex0, vertex1, vertex2], normals: [Vector3::zeros(); 3], - material: Rc::new(LambertianMaterial::new_dummy()), + material: Arc::new(LambertianMaterial::new_dummy()), }; match triangle.intersect(&ray) { Some(_) => false, @@ -877,7 +877,7 @@ mod tests { let triangle = Triangle { vertices: [vertex0, vertex1, vertex2], normals: [Vector3::zeros(); 3], - material: Rc::new(LambertianMaterial::new_dummy()), + material: Arc::new(LambertianMaterial::new_dummy()), }; match triangle.intersect(&ray) { Some(_) => false, diff --git a/src/raycasting.rs b/src/raycasting.rs index 81fce73..a8c4937 100644 --- a/src/raycasting.rs +++ b/src/raycasting.rs @@ -2,7 +2,7 @@ use nalgebra::{convert, Point3, RealField, Vector3}; use super::materials::Material; -use std::rc::Rc; +use std::sync::Arc; #[derive(Clone, Debug)] pub struct Ray { @@ -35,21 +35,21 @@ pub struct IntersectionInfo { pub tangent: Vector3, pub cotangent: Vector3, pub retro: Vector3, - pub material: Rc>, + pub material: Arc>, } -pub trait Intersect { +pub trait Intersect: Send + Sync { fn intersect<'a>(&'a self, ray: &Ray) -> Option>; } pub struct Sphere { centre: Point3, radius: T, - material: Rc>, + material: Arc>, } impl Sphere { - pub fn new(centre: Point3, radius: T, material: Rc>) -> Sphere { + pub fn new(centre: Point3, radius: T, material: Arc>) -> Sphere { Sphere { centre, radius, @@ -142,7 +142,7 @@ impl Intersect for Sphere { tangent, cotangent, retro, - material: Rc::clone(&self.material), + material: Arc::clone(&self.material), }) } } @@ -154,14 +154,14 @@ pub struct Plane { tangent: Vector3, cotangent: Vector3, distance_from_origin: T, - material: Rc>, + material: Arc>, } impl Plane { pub fn new( normal: Vector3, distance_from_origin: T, - material: Rc>, + material: Arc>, ) -> Plane { normal.normalize(); let mut axis_closest_to_tangent = Vector3::zeros(); @@ -202,7 +202,7 @@ impl Intersect for Plane { tangent: self.tangent, cotangent: self.cotangent, retro: -ray.direction, - material: Rc::clone(&self.material), + material: Arc::clone(&self.material), }) } } @@ -266,7 +266,7 @@ mod tests { let s = Sphere::new( Point3::new(1.5, 1.5, 15.0), 5.0, - Rc::new(LambertianMaterial::new_dummy()), + Arc::new(LambertianMaterial::new_dummy()), ); assert_matches!(s.intersect(&r), Some(_)); } @@ -277,7 +277,7 @@ mod tests { let s = Sphere::new( Point3::new(-5.0, 1.5, 15.0), 5.0, - Rc::new(LambertianMaterial::new_dummy()), + Arc::new(LambertianMaterial::new_dummy()), ); assert_matches!(s.intersect(&r), None); } @@ -288,7 +288,7 @@ mod tests { let s = Sphere::new( Point3::new(1.5, 1.5, -15.0), 5.0, - Rc::new(LambertianMaterial::new_dummy()), + Arc::new(LambertianMaterial::new_dummy()), ); assert_matches!(s.intersect(&r), None); } @@ -299,7 +299,7 @@ mod tests { let s = Sphere::new( Point3::new(1.5, 1.5, 2.0), 5.0, - Rc::new(LambertianMaterial::new_dummy()), + Arc::new(LambertianMaterial::new_dummy()), ); assert_matches!(s.intersect(&r), Some(_)); } @@ -316,7 +316,7 @@ mod tests { let sphere = Sphere::new( sphere_centre, radius, - Rc::new(LambertianMaterial::new_dummy()), + Arc::new(LambertianMaterial::new_dummy()), ); let ray = Ray::new(ray_origin, sphere_centre - ray_origin); let info = sphere.intersect(&ray).unwrap(); @@ -332,7 +332,7 @@ mod tests { let p = Plane::new( Vector3::new(1.0, 0.0, 0.0), -5.0, - Rc::new(LambertianMaterial::new_dummy()), + Arc::new(LambertianMaterial::new_dummy()), ); assert_matches!(p.intersect(&r), Some(_)); } @@ -343,7 +343,7 @@ mod tests { let p = Plane::new( Vector3::new(1.0, 0.0, 0.0), -5.0, - Rc::new(LambertianMaterial::new_dummy()), + Arc::new(LambertianMaterial::new_dummy()), ); assert_matches!(p.intersect(&r), None); } @@ -354,7 +354,7 @@ mod tests { let p = Plane::new( Vector3::new(1.0, 0.0, 0.0), -5.0, - Rc::new(LambertianMaterial::new_dummy()), + Arc::new(LambertianMaterial::new_dummy()), ); match p.intersect(&r) { Some(IntersectionInfo {