From a681ba461e1b63e20e396f46d7ec4330801d4636 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Fri, 3 Jun 2016 21:03:04 -0700 Subject: [PATCH] Implemented basic multi-threaded rendering. It's a bit inefficient because a thread is spawned for each pixel. Need to implement bucketing. --- Cargo.lock | 15 +++++ Cargo.toml | 4 +- example_scenes/cube.psy | 2 +- src/main.rs | 4 +- src/parse/data_tree.rs | 3 +- src/parse/psy.rs | 12 ++-- src/renderer.rs | 138 ++++++++++++++++++++++++---------------- src/surface/mod.rs | 2 +- 8 files changed, 113 insertions(+), 67 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 14921f3..dbb5948 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,9 @@ version = "0.1.0" dependencies = [ "docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)", "nom 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", + "scoped_threadpool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -52,6 +54,14 @@ name = "nom" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "num_cpus" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "regex" version = "0.1.71" @@ -74,6 +84,11 @@ name = "rustc-serialize" version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "scoped_threadpool" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "strsim" version = "0.3.0" diff --git a/Cargo.toml b/Cargo.toml index 75bfd32..de51fa8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,6 @@ authors = ["Nathan Vegdahl "] [dependencies] docopt = "0.6" rustc-serialize = "0.3" -nom = "1.2" \ No newline at end of file +nom = "1.2" +scoped_threadpool = "0.1" +num_cpus = "0.2" \ No newline at end of file diff --git a/example_scenes/cube.psy b/example_scenes/cube.psy index e3cc55c..dbef271 100644 --- a/example_scenes/cube.psy +++ b/example_scenes/cube.psy @@ -1,6 +1,6 @@ Scene $Scene_fr1 { Output { - Path ["cube.ppm"] + Path ["test_renders/cube.ppm"] } RenderSettings { Resolution [960 540] diff --git a/src/main.rs b/src/main.rs index f73ea18..3d752d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,7 @@ extern crate rustc_serialize; extern crate docopt; +extern crate scoped_threadpool; +extern crate num_cpus; #[macro_use] extern crate nom; @@ -99,7 +101,7 @@ fn main() { } println!("Rendering scene..."); - r.render(); + r.render(num_cpus::get() as u32); } } } diff --git a/src/parse/data_tree.rs b/src/parse/data_tree.rs index 17624b7..305996d 100644 --- a/src/parse/data_tree.rs +++ b/src/parse/data_tree.rs @@ -606,8 +606,7 @@ mod tests { let (token8, input9) = next_token(input8); assert_eq!((token1, input2), - (Token::TypeName("Thing"), - (5, " $yar { # A comment\n\tThing2 []\n}"))); + (Token::TypeName("Thing"), (5, " $yar { # A comment\n\tThing2 []\n}"))); assert_eq!((token2, input3), (Token::Ident("$yar"), (10, " { # A comment\n\tThing2 []\n}"))); assert_eq!((token3, input4), diff --git a/src/parse/psy.rs b/src/parse/psy.rs index 90cbfb8..69b3705 100644 --- a/src/parse/psy.rs +++ b/src/parse/psy.rs @@ -48,15 +48,13 @@ pub fn parse_scene(tree: &DataTree) -> Result { // Parse output info let output_info = try!(parse_output_info(tree.iter_children_with_type("Output") - .nth(0) - .unwrap())); + .nth(0) + .unwrap())); // Parse render settings - let render_settings = try!(parse_render_settings(tree.iter_children_with_type("Rende\ - rSett\ - ings") - .nth(0) - .unwrap())); + let render_settings = try!(parse_render_settings(tree.iter_children_with_type("RenderSettings") + .nth(0) + .unwrap())); // Parse camera let camera = try!(parse_camera(tree.iter_children_with_type("Camera").nth(0).unwrap())); diff --git a/src/renderer.rs b/src/renderer.rs index 31f1f81..2705c9d 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,6 +1,9 @@ #![allow(dead_code)] use std::path::Path; +use std::sync::Mutex; +use std::cell::RefCell; +use scoped_threadpool::Pool; use tracer::Tracer; use halton; @@ -18,10 +21,11 @@ pub struct Renderer { } impl Renderer { - pub fn render(&self) { - let mut rays = Vec::new(); - let mut tracer = Tracer::from_assembly(&self.scene.root); - let mut img = Image::new(self.resolution.0, self.resolution.1); + pub fn render(&self, thread_count: u32) { + let mut tpool = Pool::new(thread_count); + + let img = Mutex::new(RefCell::new(Image::new(self.resolution.0, self.resolution.1))); + // Pre-calculate some useful values related to the image plane let cmpx = 1.0 / self.resolution.0 as f32; @@ -35,62 +39,88 @@ impl Renderer { // Render - for y in 0..img.height() { - for x in 0..img.width() { - let offset = hash_u32(((x as u32) << 16) ^ (y as u32), 0); + tpool.scoped(|scope| { + let (img_width, img_height) = { + let i = img.lock().unwrap(); + let w = i.borrow().width(); + let h = i.borrow().height(); + (w, h) + }; + for y in 0..img_height { + for x in 0..img_width { + let img = &img; + scope.execute(move || { + let mut rays = Vec::new(); + let mut tracer = Tracer::from_assembly(&self.scene.root); - // Generate rays - rays.clear(); - for si in 0..self.spp { - let mut ray = { - let filter_x = fast_logit(halton::sample(3, offset + si as u32), 1.5) + 0.5; - let filter_y = fast_logit(halton::sample(4, offset + si as u32), 1.5) + 0.5; - let samp_x = (filter_x + x as f32) * cmpx; - let samp_y = (filter_y + y as f32) * cmpy; + let offset = hash_u32(((x as u32) << 16) ^ (y as u32), 0); - self.scene.camera.generate_ray((samp_x - 0.5) * x_extent, - (0.5 - samp_y) * y_extent, - halton::sample(0, offset + si as u32), - halton::sample(1, offset + si as u32), - halton::sample(2, offset + si as u32)) - }; - ray.id = si as u32; - rays.push(ray); + // Generate rays + rays.clear(); + for si in 0..self.spp { + let mut ray = { + let filter_x = + fast_logit(halton::sample(3, offset + si as u32), 1.5) + 0.5; + let filter_y = + fast_logit(halton::sample(4, offset + si as u32), 1.5) + 0.5; + let samp_x = (filter_x + x as f32) * cmpx; + let samp_y = (filter_y + y as f32) * cmpy; + + self.scene.camera.generate_ray((samp_x - 0.5) * x_extent, + (0.5 - samp_y) * y_extent, + halton::sample(0, + offset + si as u32), + halton::sample(1, + offset + si as u32), + halton::sample(2, + offset + si as u32)) + }; + ray.id = si as u32; + rays.push(ray); + } + + // Test rays against scene + let isects = tracer.trace(&rays); + + // Calculate color based on ray hits + let mut r = 0.0; + let mut g = 0.0; + let mut b = 0.0; + for isect in isects.iter() { + if let &surface::SurfaceIntersection::Hit { t: _, + pos: _, + nor: _, + space: _, + uv } = isect { + r += uv.0; + g += uv.1; + b += (1.0 - uv.0 - uv.1).max(0.0); + } else { + r += 0.02; + g += 0.02; + b += 0.02; + } + } + r = 255.0 * srgb_gamma(r / self.spp as f32); + g = 255.0 * srgb_gamma(g / self.spp as f32); + b = 255.0 * srgb_gamma(b / self.spp as f32); + + // Set pixel color + let img = img.lock().unwrap(); + img.borrow_mut().set(x, y, (r as u8, g as u8, b as u8)); + }); } - - // Test rays against scene - let isects = tracer.trace(&rays); - - // Calculate color based on ray hits - let mut r = 0.0; - let mut g = 0.0; - let mut b = 0.0; - for isect in isects.iter() { - if let &surface::SurfaceIntersection::Hit { t: _, - pos: _, - nor: _, - space: _, - uv } = isect { - r += uv.0; - g += uv.1; - b += (1.0 - uv.0 - uv.1).max(0.0); - } else { - r += 0.02; - g += 0.02; - b += 0.02; - } - } - r = 255.0 * srgb_gamma(r / self.spp as f32); - g = 255.0 * srgb_gamma(g / self.spp as f32); - b = 255.0 * srgb_gamma(b / self.spp as f32); - - // Set pixel color - img.set(x, y, (r as u8, g as u8, b as u8)); } - } + // scope.defer(|| println!("Exiting scope")); + // scope.spawn(|| println!("Running child thread in scope")) + }); + // Write rendered image to disk - let _ = img.write_binary_ppm(Path::new(&self.output_file)); + { + let img = &img.lock().unwrap(); + let _ = img.borrow().write_binary_ppm(Path::new(&self.output_file)); + } } } diff --git a/src/surface/mod.rs b/src/surface/mod.rs index 41672b4..a0993fd 100644 --- a/src/surface/mod.rs +++ b/src/surface/mod.rs @@ -22,6 +22,6 @@ pub enum SurfaceIntersection { }, } -pub trait Surface: Boundable + Debug { +pub trait Surface: Boundable + Debug + Sync { fn intersect_rays(&self, rays: &mut [Ray], isects: &mut [SurfaceIntersection]); }