From 1687077f4a87022f6a8fd4a7c5060fb56eb33ad6 Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Fri, 11 Oct 2019 11:14:52 -0700 Subject: [PATCH 1/2] Render whole lines at a time. --- rtiow/src/renderer.rs | 75 +++++++++++++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 17 deletions(-) diff --git a/rtiow/src/renderer.rs b/rtiow/src/renderer.rs index c697b33..72ac4e9 100644 --- a/rtiow/src/renderer.rs +++ b/rtiow/src/renderer.rs @@ -236,6 +236,16 @@ enum Response { pixels: Vec, }, } +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( tid: usize, @@ -245,19 +255,14 @@ fn render_worker( ) { for req in input_chan { 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 } => { - 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. - let pixel = Vec3::new(pixel[0].sqrt(), pixel[1].sqrt(), pixel[2].sqrt()); - + let pixel = render_pixel(scene, x, y); output_chan.send(Response::Pixel { x, y, pixel }); } - _ => unimplemented!("Only Pixel requests are implemented"), } } trace!(target: "renderer", "Shutting down worker {}", tid); @@ -283,10 +288,16 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i let (w, h) = (scene.width, scene.height); thread::spawn(move || { - // TODO(wathiede): handle sending Line requests for optimization. - for y in 0..w { - for x in 0..h { - pixel_req_tx.send(Request::Pixel { x, y }); + let batch_by_line = true; + if batch_by_line { + for y in 0..h { + 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); @@ -295,8 +306,9 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i println!("Rendering with {} subsamples", scene.subsamples); let mut img = RgbImage::new(scene.width as u32, scene.height as u32); let total = scene.width * scene.height; + let mut cur_pixel = 0; let mut last_progress = 1000; - for (i, resp) in pixel_resp_rx.iter().enumerate() { + for resp in pixel_resp_rx { match resp { Response::Pixel { x, y, pixel } => { let y_inv = scene.height - y - 1; @@ -309,7 +321,7 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i (pixel[2] * 255.).min(255.) as u8, ]), ); - let progress = 100 * i / total; + let progress = 100 * cur_pixel / total; if progress != last_progress { last_progress = progress; if progress % 10 == 0 { @@ -319,8 +331,37 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i } 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!(); From 235a9d12043be5da7618c7c0dfe9566eab34a869 Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Sat, 12 Oct 2019 14:29:36 -0700 Subject: [PATCH 2/2] Add flag to set number of threads. --- rtiow/src/renderer.rs | 15 ++++++++++----- rtiow/src/scenes/bench.rs | 1 + rtiow/src/scenes/book.rs | 1 + rtiow/src/scenes/bvh.rs | 1 + rtiow/src/scenes/cornell_box.rs | 1 + rtiow/src/scenes/cornell_smoke.rs | 1 + rtiow/src/scenes/final_scene.rs | 1 + rtiow/src/scenes/mandelbrot.rs | 1 + rtiow/src/scenes/perlin_debug.rs | 1 + rtiow/src/scenes/test.rs | 1 + rtiow/src/scenes/tutorial.rs | 1 + 11 files changed, 20 insertions(+), 5 deletions(-) diff --git a/rtiow/src/renderer.rs b/rtiow/src/renderer.rs index 72ac4e9..a1c7af0 100644 --- a/rtiow/src/renderer.rs +++ b/rtiow/src/renderer.rs @@ -119,6 +119,9 @@ pub struct Opt { /// Image height #[structopt(short = "h", long = "height", default_value = "1024")] pub height: usize, + /// Number of threads + #[structopt(short = "t", long = "num_threads")] + pub num_threads: Option, /// Sub-samples per pixel #[structopt(short = "s", long = "subsample", default_value = "8")] pub subsamples: usize, @@ -139,6 +142,7 @@ pub struct Opt { } pub fn opt_hash(opt: &Opt) -> String { + // TODO(wathiede): add threads. format!( "w:{}-h:{}-s:{}-pprof:{}-model:{}-use_accel:{}-{}", opt.width, @@ -155,6 +159,7 @@ pub struct Scene { pub world: Box, pub camera: Camera, pub subsamples: usize, + pub num_threads: Option, pub width: usize, pub height: usize, pub global_illumination: bool, @@ -269,13 +274,13 @@ fn render_worker( } pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::io::Error> { - let cpus = num_cpus::get(); - let (pixel_req_tx, pixel_req_rx) = channel::bounded(2 * cpus); - let (pixel_resp_tx, pixel_resp_rx) = channel::bounded(2 * cpus); + let num_threads = scene.num_threads.unwrap_or_else(num_cpus::get); + let (pixel_req_tx, pixel_req_rx) = channel::bounded(2 * num_threads); + let (pixel_resp_tx, pixel_resp_rx) = channel::bounded(2 * num_threads); let scene = sync::Arc::new(scene); - println!("Creating {} render threads", cpus); - for i in 0..cpus { + println!("Creating {} render threads", num_threads); + for i in 0..num_threads { let s = sync::Arc::clone(&scene); let pixel_req_rx = pixel_req_rx.clone(); let pixel_resp_tx = pixel_resp_tx.clone(); diff --git a/rtiow/src/scenes/bench.rs b/rtiow/src/scenes/bench.rs index 50475f2..4f2cd80 100644 --- a/rtiow/src/scenes/bench.rs +++ b/rtiow/src/scenes/bench.rs @@ -69,6 +69,7 @@ pub fn new(opt: &Opt) -> Scene { camera, world, subsamples: opt.subsamples, + num_threads: opt.num_threads, width: opt.width, height: opt.height, global_illumination: true, diff --git a/rtiow/src/scenes/book.rs b/rtiow/src/scenes/book.rs index 6c5f912..db5ce51 100644 --- a/rtiow/src/scenes/book.rs +++ b/rtiow/src/scenes/book.rs @@ -51,6 +51,7 @@ pub fn new(opt: &Opt) -> Scene { camera, world, subsamples: opt.subsamples, + num_threads: opt.num_threads, width: opt.width, height: opt.height, global_illumination: true, diff --git a/rtiow/src/scenes/bvh.rs b/rtiow/src/scenes/bvh.rs index 8ffc228..b23cec5 100644 --- a/rtiow/src/scenes/bvh.rs +++ b/rtiow/src/scenes/bvh.rs @@ -63,6 +63,7 @@ pub fn new(opt: &Opt) -> Scene { camera, world, subsamples: opt.subsamples, + num_threads: opt.num_threads, width: opt.width, height: opt.height, global_illumination: true, diff --git a/rtiow/src/scenes/cornell_box.rs b/rtiow/src/scenes/cornell_box.rs index 2b574ac..42fd2f2 100644 --- a/rtiow/src/scenes/cornell_box.rs +++ b/rtiow/src/scenes/cornell_box.rs @@ -130,6 +130,7 @@ pub fn new(opt: &Opt) -> Scene { camera, world, subsamples: opt.subsamples, + num_threads: opt.num_threads, width: opt.width, height: opt.height, global_illumination: false, diff --git a/rtiow/src/scenes/cornell_smoke.rs b/rtiow/src/scenes/cornell_smoke.rs index a54e9f2..f7f0765 100644 --- a/rtiow/src/scenes/cornell_smoke.rs +++ b/rtiow/src/scenes/cornell_smoke.rs @@ -115,6 +115,7 @@ pub fn new(opt: &Opt) -> Scene { camera, world, subsamples: opt.subsamples, + num_threads: opt.num_threads, width: opt.width, height: opt.height, global_illumination: false, diff --git a/rtiow/src/scenes/final_scene.rs b/rtiow/src/scenes/final_scene.rs index e7d8c02..e3e5d33 100644 --- a/rtiow/src/scenes/final_scene.rs +++ b/rtiow/src/scenes/final_scene.rs @@ -166,6 +166,7 @@ pub fn new(opt: &Opt) -> Scene { camera, world: Box::new(HitableList::new(list)), subsamples: opt.subsamples, + num_threads: opt.num_threads, width: opt.width, height: opt.height, global_illumination: false, diff --git a/rtiow/src/scenes/mandelbrot.rs b/rtiow/src/scenes/mandelbrot.rs index 7237222..1783b6d 100644 --- a/rtiow/src/scenes/mandelbrot.rs +++ b/rtiow/src/scenes/mandelbrot.rs @@ -144,6 +144,7 @@ pub fn new(opt: &Opt) -> Scene { camera, world, subsamples: opt.subsamples, + num_threads: opt.num_threads, width: opt.width, height: opt.height, global_illumination: false, diff --git a/rtiow/src/scenes/perlin_debug.rs b/rtiow/src/scenes/perlin_debug.rs index 32091e7..b5b789e 100644 --- a/rtiow/src/scenes/perlin_debug.rs +++ b/rtiow/src/scenes/perlin_debug.rs @@ -65,6 +65,7 @@ pub fn new(opt: &Opt) -> Scene { camera, world, subsamples: opt.subsamples, + num_threads: opt.num_threads, width: opt.width, height: opt.height, global_illumination: true, diff --git a/rtiow/src/scenes/test.rs b/rtiow/src/scenes/test.rs index 143dcd6..461e7b1 100644 --- a/rtiow/src/scenes/test.rs +++ b/rtiow/src/scenes/test.rs @@ -142,6 +142,7 @@ pub fn new(opt: &Opt) -> Scene { camera, world, subsamples: opt.subsamples, + num_threads: opt.num_threads, width: opt.width, height: opt.height, global_illumination: false, diff --git a/rtiow/src/scenes/tutorial.rs b/rtiow/src/scenes/tutorial.rs index caefd56..bf34ea9 100644 --- a/rtiow/src/scenes/tutorial.rs +++ b/rtiow/src/scenes/tutorial.rs @@ -70,6 +70,7 @@ pub fn new(opt: &Opt) -> Scene { camera, world, subsamples: opt.subsamples, + num_threads: opt.num_threads, width: opt.width, height: opt.height, global_illumination: true,