This commit is contained in:
Bill Thiede 2019-10-12 15:11:33 -07:00
commit ddb0bd893d
11 changed files with 78 additions and 22 deletions

View File

@ -119,6 +119,9 @@ pub struct Opt {
/// Image height /// Image height
#[structopt(short = "h", long = "height", default_value = "1024")] #[structopt(short = "h", long = "height", default_value = "1024")]
pub height: usize, pub height: usize,
/// Number of threads
#[structopt(short = "t", long = "num_threads")]
pub num_threads: Option<usize>,
/// Sub-samples per pixel /// Sub-samples per pixel
#[structopt(short = "s", long = "subsample", default_value = "8")] #[structopt(short = "s", long = "subsample", default_value = "8")]
pub subsamples: usize, pub subsamples: usize,
@ -139,6 +142,7 @@ pub struct Opt {
} }
pub fn opt_hash(opt: &Opt) -> String { pub fn opt_hash(opt: &Opt) -> String {
// TODO(wathiede): add threads.
format!( format!(
"w:{}-h:{}-s:{}-pprof:{}-model:{}-use_accel:{}-{}", "w:{}-h:{}-s:{}-pprof:{}-model:{}-use_accel:{}-{}",
opt.width, opt.width,
@ -155,6 +159,7 @@ pub struct Scene {
pub world: Box<Hit>, pub world: Box<Hit>,
pub camera: Camera, pub camera: Camera,
pub subsamples: usize, pub subsamples: usize,
pub num_threads: Option<usize>,
pub width: usize, pub width: usize,
pub height: usize, pub height: usize,
pub global_illumination: bool, pub global_illumination: bool,
@ -236,6 +241,16 @@ enum Response {
pixels: Vec<Vec3>, pixels: Vec<Vec3>,
}, },
} }
fn render_pixel(scene: &Scene, x: usize, y: usize) -> Vec3 {
let mut pixel: Vec3 = Default::default();
for _ in 0..scene.subsamples {
pixel = pixel + trace_pixel(x, y, scene);
}
pixel = pixel / scene.subsamples as f32;
// Gamma correct, use gamma 2 correction, which is 1/gamma where gamma=2 which is 1/2 or
// sqrt.
Vec3::new(pixel[0].sqrt(), pixel[1].sqrt(), pixel[2].sqrt())
}
fn render_worker( fn render_worker(
tid: usize, tid: usize,
@ -245,32 +260,27 @@ fn render_worker(
) { ) {
for req in input_chan { for req in input_chan {
match req { match req {
Request::Line { width, y } => {
let pixels = (0..width).map(|x| render_pixel(scene, x, y)).collect();
output_chan.send(Response::Line { width, y, pixels });
}
Request::Pixel { x, y } => { Request::Pixel { x, y } => {
let mut pixel: Vec3 = Default::default(); let pixel = render_pixel(scene, x, y);
for _ in 0..scene.subsamples {
pixel = pixel + trace_pixel(x, y, scene);
}
pixel = pixel / scene.subsamples as f32;
// Gamma correct, use gamma 2 correction, which is 1/gamma where gamma=2 which is 1/2 or
// sqrt.
let pixel = Vec3::new(pixel[0].sqrt(), pixel[1].sqrt(), pixel[2].sqrt());
output_chan.send(Response::Pixel { x, y, pixel }); output_chan.send(Response::Pixel { x, y, pixel });
} }
_ => unimplemented!("Only Pixel requests are implemented"),
} }
} }
trace!(target: "renderer", "Shutting down worker {}", tid); trace!(target: "renderer", "Shutting down worker {}", tid);
} }
pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::io::Error> { pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::io::Error> {
let cpus = num_cpus::get(); let num_threads = scene.num_threads.unwrap_or_else(num_cpus::get);
let (pixel_req_tx, pixel_req_rx) = channel::bounded(2 * cpus); let (pixel_req_tx, pixel_req_rx) = channel::bounded(2 * num_threads);
let (pixel_resp_tx, pixel_resp_rx) = channel::bounded(2 * cpus); let (pixel_resp_tx, pixel_resp_rx) = channel::bounded(2 * num_threads);
let scene = sync::Arc::new(scene); let scene = sync::Arc::new(scene);
println!("Creating {} render threads", cpus); println!("Creating {} render threads", num_threads);
for i in 0..cpus { for i in 0..num_threads {
let s = sync::Arc::clone(&scene); let s = sync::Arc::clone(&scene);
let pixel_req_rx = pixel_req_rx.clone(); let pixel_req_rx = pixel_req_rx.clone();
let pixel_resp_tx = pixel_resp_tx.clone(); let pixel_resp_tx = pixel_resp_tx.clone();
@ -283,10 +293,16 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
let (w, h) = (scene.width, scene.height); let (w, h) = (scene.width, scene.height);
thread::spawn(move || { thread::spawn(move || {
// TODO(wathiede): handle sending Line requests for optimization. let batch_by_line = true;
for y in 0..w { if batch_by_line {
for x in 0..h { for y in 0..h {
pixel_req_tx.send(Request::Pixel { x, y }); pixel_req_tx.send(Request::Line { width: w, y });
}
} else {
for y in 0..h {
for x in 0..w {
pixel_req_tx.send(Request::Pixel { x, y });
}
} }
} }
drop(pixel_req_tx); drop(pixel_req_tx);
@ -295,8 +311,9 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
println!("Rendering with {} subsamples", scene.subsamples); println!("Rendering with {} subsamples", scene.subsamples);
let mut img = RgbImage::new(scene.width as u32, scene.height as u32); let mut img = RgbImage::new(scene.width as u32, scene.height as u32);
let total = scene.width * scene.height; let total = scene.width * scene.height;
let mut cur_pixel = 0;
let mut last_progress = 1000; let mut last_progress = 1000;
for (i, resp) in pixel_resp_rx.iter().enumerate() { for resp in pixel_resp_rx {
match resp { match resp {
Response::Pixel { x, y, pixel } => { Response::Pixel { x, y, pixel } => {
let y_inv = scene.height - y - 1; let y_inv = scene.height - y - 1;
@ -309,7 +326,7 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
(pixel[2] * 255.).min(255.) as u8, (pixel[2] * 255.).min(255.) as u8,
]), ]),
); );
let progress = 100 * i / total; let progress = 100 * cur_pixel / total;
if progress != last_progress { if progress != last_progress {
last_progress = progress; last_progress = progress;
if progress % 10 == 0 { if progress % 10 == 0 {
@ -319,8 +336,37 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
} }
io::stdout().flush().unwrap(); io::stdout().flush().unwrap();
} }
cur_pixel += 1;
}
Response::Line {
width: _,
y,
pixels,
} => {
for (x, pixel) in pixels.iter().enumerate() {
let y_inv = scene.height - y - 1;
img.put_pixel(
x as u32,
y_inv as u32,
image::Rgb([
(pixel[0] * 255.).min(255.) as u8,
(pixel[1] * 255.).min(255.) as u8,
(pixel[2] * 255.).min(255.) as u8,
]),
);
let progress = 100 * cur_pixel / total;
if progress != last_progress {
last_progress = progress;
if progress % 10 == 0 {
print!("{}%", progress);
} else {
print!(".");
}
io::stdout().flush().unwrap();
}
cur_pixel += 1;
}
} }
_ => unimplemented!("Only Pixel responses are implemented"),
} }
} }
println!(); println!();

View File

@ -69,6 +69,7 @@ pub fn new(opt: &Opt) -> Scene {
camera, camera,
world, world,
subsamples: opt.subsamples, subsamples: opt.subsamples,
num_threads: opt.num_threads,
width: opt.width, width: opt.width,
height: opt.height, height: opt.height,
global_illumination: true, global_illumination: true,

View File

@ -51,6 +51,7 @@ pub fn new(opt: &Opt) -> Scene {
camera, camera,
world, world,
subsamples: opt.subsamples, subsamples: opt.subsamples,
num_threads: opt.num_threads,
width: opt.width, width: opt.width,
height: opt.height, height: opt.height,
global_illumination: true, global_illumination: true,

View File

@ -63,6 +63,7 @@ pub fn new(opt: &Opt) -> Scene {
camera, camera,
world, world,
subsamples: opt.subsamples, subsamples: opt.subsamples,
num_threads: opt.num_threads,
width: opt.width, width: opt.width,
height: opt.height, height: opt.height,
global_illumination: true, global_illumination: true,

View File

@ -130,6 +130,7 @@ pub fn new(opt: &Opt) -> Scene {
camera, camera,
world, world,
subsamples: opt.subsamples, subsamples: opt.subsamples,
num_threads: opt.num_threads,
width: opt.width, width: opt.width,
height: opt.height, height: opt.height,
global_illumination: false, global_illumination: false,

View File

@ -116,6 +116,7 @@ pub fn new(opt: &Opt) -> Scene {
camera, camera,
world, world,
subsamples: opt.subsamples, subsamples: opt.subsamples,
num_threads: opt.num_threads,
width: opt.width, width: opt.width,
height: opt.height, height: opt.height,
global_illumination: false, global_illumination: false,

View File

@ -169,6 +169,7 @@ pub fn new(opt: &Opt) -> Scene {
camera, camera,
world: Box::new(HitableList::new(list)), world: Box::new(HitableList::new(list)),
subsamples: opt.subsamples, subsamples: opt.subsamples,
num_threads: opt.num_threads,
width: opt.width, width: opt.width,
height: opt.height, height: opt.height,
global_illumination: false, global_illumination: false,

View File

@ -149,6 +149,7 @@ pub fn new(opt: &Opt) -> Scene {
camera, camera,
world, world,
subsamples: opt.subsamples, subsamples: opt.subsamples,
num_threads: opt.num_threads,
width: opt.width, width: opt.width,
height: opt.height, height: opt.height,
global_illumination: false, global_illumination: false,

View File

@ -65,6 +65,7 @@ pub fn new(opt: &Opt) -> Scene {
camera, camera,
world, world,
subsamples: opt.subsamples, subsamples: opt.subsamples,
num_threads: opt.num_threads,
width: opt.width, width: opt.width,
height: opt.height, height: opt.height,
global_illumination: true, global_illumination: true,

View File

@ -142,6 +142,7 @@ pub fn new(opt: &Opt) -> Scene {
camera, camera,
world, world,
subsamples: opt.subsamples, subsamples: opt.subsamples,
num_threads: opt.num_threads,
width: opt.width, width: opt.width,
height: opt.height, height: opt.height,
global_illumination: false, global_illumination: false,

View File

@ -70,6 +70,7 @@ pub fn new(opt: &Opt) -> Scene {
camera, camera,
world, world,
subsamples: opt.subsamples, subsamples: opt.subsamples,
num_threads: opt.num_threads,
width: opt.width, width: opt.width,
height: opt.height, height: opt.height,
global_illumination: true, global_illumination: true,