Implemented basic multi-threaded rendering.

It's a bit inefficient because a thread is spawned for each pixel.
Need to implement bucketing.
This commit is contained in:
Nathan Vegdahl 2016-06-03 21:03:04 -07:00
parent fac46d6db3
commit a681ba461e
8 changed files with 113 additions and 67 deletions

15
Cargo.lock generated
View File

@ -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"

View File

@ -6,4 +6,6 @@ authors = ["Nathan Vegdahl <cessen@cessen.com>"]
[dependencies]
docopt = "0.6"
rustc-serialize = "0.3"
nom = "1.2"
nom = "1.2"
scoped_threadpool = "0.1"
num_cpus = "0.2"

View File

@ -1,6 +1,6 @@
Scene $Scene_fr1 {
Output {
Path ["cube.ppm"]
Path ["test_renders/cube.ppm"]
}
RenderSettings {
Resolution [960 540]

View File

@ -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);
}
}
}

View File

@ -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),

View File

@ -48,15 +48,13 @@ pub fn parse_scene(tree: &DataTree) -> Result<Renderer, PsyParseError> {
// 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()));

View File

@ -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));
}
}
}

View File

@ -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]);
}