From 548011ba6316e83c95b327768581d7d53d49a63e Mon Sep 17 00:00:00 2001 From: Sakarias Johansson Date: Fri, 3 Mar 2023 15:56:26 +0100 Subject: =?UTF-8?q?=F0=9F=9B=A0=20Cleanup=20error=20handling=20&=20functio?= =?UTF-8?q?nalize?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- racer-tracer/src/error.rs | 14 ++- racer-tracer/src/main.rs | 127 +++++++++++++++----------- racer-tracer/src/render.rs | 220 +++++++++++++++++++++++++-------------------- 3 files changed, 209 insertions(+), 152 deletions(-) diff --git a/racer-tracer/src/error.rs b/racer-tracer/src/error.rs index f32dac9..6e1b305 100644 --- a/racer-tracer/src/error.rs +++ b/racer-tracer/src/error.rs @@ -1,6 +1,6 @@ use thiserror::Error; -#[derive(Clone, Error, Debug)] +#[derive(Clone, Error, Debug, PartialEq, Eq)] pub enum TracerError { #[error("Unknown error: {message}")] Unknown { message: String, exit_code: i32 }, @@ -22,6 +22,15 @@ pub enum TracerError { #[error("No scene supplied.")] NoScene(), + + #[error("Failed to acquire lock \"{0}\"")] + FailedToAcquireLock(String), + + #[error("Exit event")] + ExitEvent, + + #[error("Cancel event")] + CancelEvent, } impl From for i32 { @@ -37,6 +46,9 @@ impl From for i32 { TracerError::Configuration(_, _) => 5, TracerError::UnknownMaterial(_) => 6, TracerError::NoScene() => 7, + TracerError::FailedToAcquireLock(_) => 8, + TracerError::ExitEvent => 9, + TracerError::CancelEvent => 10, } } } diff --git a/racer-tracer/src/main.rs b/racer-tracer/src/main.rs index b441dd0..7d7510f 100644 --- a/racer-tracer/src/main.rs +++ b/racer-tracer/src/main.rs @@ -30,18 +30,16 @@ use crate::{ }; fn run(config: Config) -> Result<(), TracerError> { - let preview_render_data = config.preview; - let render_data = config.render; let image = image::Image::new(config.screen.width, config.screen.height); + let screen_buffer: RwLock> = RwLock::new(vec![0; image.width * image.height]); let camera = RwLock::new(Camera::new(&image, 2.0, 1.0)); let scene: Scene = config .scene .ok_or(TracerError::NoScene()) .and_then(Scene::from_file)?; - let screen_buffer: RwLock> = RwLock::new(vec![0; image.width * image.height]); let mut window_res: Result<(), TracerError> = Ok(()); - let move_camera = &camera; + let mut render_res: Result<(), TracerError> = Ok(()); let render_image = SignalEvent::manual(false); let cancel_render = SignalEvent::manual(false); @@ -49,41 +47,54 @@ fn run(config: Config) -> Result<(), TracerError> { rayon::scope(|s| { s.spawn(|_| { - loop { - if exit.wait_timeout(Duration::from_secs(0)) { - return; - } + while render_res.is_ok() { + render_res = (!exit.wait_timeout(Duration::from_secs(0))) + .then_some(|| ()) + .ok_or(TracerError::ExitEvent) + .and_then(|_| { + render_image + .wait_timeout(Duration::from_secs(0)) + .then_some(|| ()) + .map_or_else( + || { + // Render preview + render( + &screen_buffer, + &camera, + &image, + &scene, + &config.preview, + None, + Some(config.preview.scale), + ) + }, + |_| { + let render_time = Instant::now(); + let res = render( + &screen_buffer, + &camera, + &image, + &scene, + &config.render, + Some(&cancel_render), + None, + ); + render_image.reset(); - if render_image.wait_timeout(Duration::from_secs(0)) && render_image.status() { - let render_time = Instant::now(); - render( - &screen_buffer, - &camera, - &image, - &scene, - &render_data, - Some(&cancel_render), - None, - ); - - println!( - "It took {} seconds to render the image.", - Instant::now().duration_since(render_time).as_millis() - ); - } else { - // Render preview - render( - &screen_buffer, - &camera, - &image, - &scene, - &preview_render_data, - None, - Some(preview_render_data.scale), - ); - } + println!( + "It took {} seconds to render the image.", + Instant::now().duration_since(render_time).as_secs() + ); + + // TODO: Output the image + + res + }, + ) + }); } }); + s.spawn(|_| { window_res = Window::new( "racer-tracer", @@ -106,28 +117,32 @@ fn run(config: Config) -> Result<(), TracerError> { if window.is_key_released(Key::R) { if render_image.status() { + // Signal cancel cancel_render.signal(); render_image.reset(); } else { + // Signal render render_image.signal(); cancel_render.reset(); } } - { - let mut cam = move_camera.write().expect("TODO"); - if window.is_key_down(Key::W) { - cam.go_forward(-dt); - } else if window.is_key_down(Key::S) { - cam.go_forward(dt); - } + camera + .write() + .map_err(|e| TracerError::FailedToAcquireLock(e.to_string())) + .map(|mut cam| { + if window.is_key_down(Key::W) { + cam.go_forward(-dt); + } else if window.is_key_down(Key::S) { + cam.go_forward(dt); + } - if window.is_key_down(Key::A) { - cam.go_right(-dt); - } else if window.is_key_down(Key::D) { - cam.go_right(dt); - } - } + if window.is_key_down(Key::A) { + cam.go_right(-dt); + } else if window.is_key_down(Key::D) { + cam.go_right(dt); + } + })?; screen_buffer .read() @@ -139,16 +154,22 @@ fn run(config: Config) -> Result<(), TracerError> { })? } exit.signal(); + cancel_render.signal(); Ok(()) }); }); }); - window_res + + window_res.and(render_res) } use structopt::StructOpt; fn main() { - if let Err(e) = Config::try_from(Args::from_args()).and_then(run) { - eprintln!("{}", e); - std::process::exit(e.into()) + match Config::try_from(Args::from_args()).and_then(run) { + Err(TracerError::ExitEvent) => {} + Ok(_) => {} + Err(e) => { + eprintln!("{}", e); + std::process::exit(e.into()) + } } } diff --git a/racer-tracer/src/render.rs b/racer-tracer/src/render.rs index 8e01acb..fefafce 100644 --- a/racer-tracer/src/render.rs +++ b/racer-tracer/src/render.rs @@ -6,6 +6,7 @@ use synchronoise::SignalEvent; use crate::{ camera::Camera, config::RenderData, + error::TracerError, geometry::Hittable, image::{Image, SubImage}, ray::Ray, @@ -41,7 +42,7 @@ pub fn raytrace_scaled( image: &SubImage, data: &RenderData, scale: (usize, usize), -) { +) -> Result<(), TracerError> { let (scale_width, scale_height) = scale; let scaled_width = image.width / scale_width; let scaled_height = image.height / scale_height; @@ -63,33 +64,36 @@ pub fn raytrace_scaled( } if do_cancel(cancel_event) { - return; + return Ok(()); } } - if do_cancel(cancel_event) { - return; - } - - let mut buf = buffer - .write() - .expect("Failed to get write guard when flushing data."); - - let offset = image.y * image.screen_width + image.x; - for scaled_row in 0..scaled_height { - for scaled_col in 0..scaled_width { - let color = colors[scaled_row * scaled_width + scaled_col] - .scale_sqrt(data.samples) - .as_color(); - let row = scaled_row * scale_height; - let col = scaled_col * scale_width; - for scale_h in 0..scale_height { - for scale_w in 0..scale_width { - buf[offset + (row + scale_h) * image.screen_width + col + scale_w] = color; + (!do_cancel(cancel_event)) + .then_some(|| ()) + .ok_or(TracerError::CancelEvent) + .and_then(|_| { + buffer + .write() + .map_err(|e| TracerError::FailedToAcquireLock(e.to_string())) + }) + .map(|mut buf| { + let offset = image.y * image.screen_width + image.x; + for scaled_row in 0..scaled_height { + for scaled_col in 0..scaled_width { + let color = colors[scaled_row * scaled_width + scaled_col] + .scale_sqrt(data.samples) + .as_color(); + let row = scaled_row * scale_height; + let col = scaled_col * scale_width; + for scale_h in 0..scale_height { + for scale_w in 0..scale_width { + buf[offset + (row + scale_h) * image.screen_width + col + scale_w] = + color; + } + } } } - } - } + }) } pub fn raytrace( @@ -99,7 +103,7 @@ pub fn raytrace( camera: Camera, image: &SubImage, data: &RenderData, -) { +) -> Result<(), TracerError> { let mut colors: Vec = vec![Vec3::default(); image.height * image.width]; for row in 0..image.height { for column in 0..image.width { @@ -117,27 +121,29 @@ pub fn raytrace( } if do_cancel(cancel_event) { - return; + return Ok(()); } } - if do_cancel(cancel_event) { - return; - } - - let mut buf = buffer - .write() - .expect("Failed to get write guard when flushing data."); - - let offset = image.y * image.screen_width + image.x; - for row in 0..image.height { - for col in 0..image.width { - let color = colors[row * image.width + col] - .scale_sqrt(data.samples) - .as_color(); - buf[offset + row * image.screen_width + col] = color; - } - } + (!do_cancel(cancel_event)) + .then_some(|| ()) + .ok_or(TracerError::CancelEvent) + .and_then(|_| { + buffer + .write() + .map_err(|e| TracerError::FailedToAcquireLock(e.to_string())) + }) + .map(|mut buf| { + let offset = image.y * image.screen_width + image.x; + for row in 0..image.height { + for col in 0..image.width { + let color = colors[row * image.width + col] + .scale_sqrt(data.samples) + .as_color(); + buf[offset + row * image.screen_width + col] = color; + } + } + }) } fn do_cancel(cancel_event: Option<&SignalEvent>) -> bool { @@ -163,70 +169,88 @@ pub fn render( data: &RenderData, cancel_event: Option<&SignalEvent>, scale: Option, -) { +) -> Result<(), TracerError> { if do_cancel(cancel_event) { - return; + return Ok(()); } - let cam = camera.read().expect("TODO").clone(); + let width_step = image.width / data.num_threads_width; let height_step = image.height / data.num_threads_height; - let scaled_width = scale.map_or(1, |s| get_highest_divdable(width_step, s)); let scaled_height = scale.map_or(1, |s| get_highest_divdable(height_step, s)); - (0..data.num_threads_width) - .flat_map(|ws| { - let subs: Vec = (0..data.num_threads_height) - .map(|hs| SubImage { - x: width_step * ws, - y: height_step * hs, - screen_width: image.width, - screen_height: image.height, - - // Neccesary in case the threads width is not - // evenly divisible by the image width. - width: if ws == data.num_threads_width - 1 { - image.width - width_step * ws - } else { - width_step - }, - - // Neccesary in case the threads height is not - // evenly divisible by the image height. - height: if hs == data.num_threads_height - 1 { - image.height - height_step * hs - } else { - height_step - }, + (!do_cancel(cancel_event)) + .then_some(|| ()) + .ok_or(TracerError::CancelEvent) + .and_then(|_| { + camera + .read() + .map_err(|e| TracerError::FailedToAcquireLock(e.to_string())) + // We make a clone of it as it's not very important to + // have the latest camera angle etc. Better to keep + // the lock to a minimum. + .map(|cam| cam.clone()) + }) + .map(|cam| { + let images = (0..data.num_threads_width) + .flat_map(|ws| { + (0..data.num_threads_height) + .map(|hs| SubImage { + x: width_step * ws, + y: height_step * hs, + screen_width: image.width, + screen_height: image.height, + + // Neccesary in case the threads width is not + // evenly divisible by the image width. + width: if ws == data.num_threads_width - 1 { + image.width - width_step * ws + } else { + width_step + }, + + // Neccesary in case the threads height is not + // evenly divisible by the image height. + height: if hs == data.num_threads_height - 1 { + image.height - height_step * hs + } else { + height_step + }, + }) + .collect::>() }) - .collect(); - subs + .collect::>(); + + (cam, images) }) - .collect::>() - .into_par_iter() - .for_each(|image| { - scale.map_or_else( - || { - raytrace( - buffer, - cancel_event, - (*scene).borrow(), - cam.clone(), - &image, - data, + .and_then(|(cam, sub_images)| { + sub_images + .into_par_iter() + .map(|image| { + scale.map_or_else( + || { + raytrace( + buffer, + cancel_event, + (*scene).borrow(), + cam.clone(), + &image, + data, + ) + }, + |_| { + raytrace_scaled( + buffer, + cancel_event, + (*scene).borrow(), + cam.clone(), + &image, + data, + (scaled_width, scaled_height), + ) + }, ) - }, - |_| { - raytrace_scaled( - buffer, - cancel_event, - (*scene).borrow(), - cam.clone(), - &image, - data, - (scaled_width, scaled_height), - ) - }, - ); - }); + }) + .collect::>() + }) } -- cgit v1.2.3