From 0ae6ca062f5936ae6f595f45ca0a78ed049452bc Mon Sep 17 00:00:00 2001 From: Sakarias Johansson Date: Sun, 9 Apr 2023 19:11:39 +0200 Subject: =?UTF-8?q?=E2=9C=A8=20Add=20scene=20controller?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wanted to be able to for example move around freely in the scene but also have something that would for example follow key frames and render a gif. Abstracted it with a scene controller. You can hook up keybinds and other thigns for it as well. Right now there is only the interactive scene controller which keep the behaviours previous to this change. Now I could possibly switch it out with something that uses key frames to render several images to create for example a gif. List of other Misc changes: - Add configuration setting for scene controller (`scene_controller`) - Add configuration setting for renderer - Add configuration setting for preview renderer (`preview_renderer`) - Add clone to Config. - Add from implementation for Renderer to be created from the config object. - Add cancel event to image action. An action could be blocking when the application wants to exit. Actions can now listen to the cancel event to exit early and not block. - Fixed bug where WaitForSignal action would block after application tries to exit. - Add method to KeyInputs to be able to take a list of callbacks instead of manually registering every callback one at the time. --- racer-tracer/src/config.rs | 32 +++- racer-tracer/src/image_action.rs | 7 +- racer-tracer/src/image_action/png.rs | 1 + racer-tracer/src/image_action/wait_for_signal.rs | 5 +- racer-tracer/src/key_inputs.rs | 67 ++++++-- racer-tracer/src/main.rs | 177 ++++++------------- racer-tracer/src/renderer.rs | 22 ++- racer-tracer/src/renderer/cpu.rs | 3 + racer-tracer/src/renderer/cpu_scaled.rs | 3 + racer-tracer/src/scene_controller.rs | 37 ++++ racer-tracer/src/scene_controller/interactive.rs | 209 +++++++++++++++++++++++ 11 files changed, 411 insertions(+), 152 deletions(-) create mode 100644 racer-tracer/src/scene_controller.rs create mode 100644 racer-tracer/src/scene_controller/interactive.rs (limited to 'racer-tracer/src') diff --git a/racer-tracer/src/config.rs b/racer-tracer/src/config.rs index 3992176..7d28ebe 100644 --- a/racer-tracer/src/config.rs +++ b/racer-tracer/src/config.rs @@ -6,13 +6,13 @@ use structopt::StructOpt; use crate::error::TracerError; -#[derive(Default, Debug, Deserialize)] +#[derive(Default, Clone, Debug, Deserialize)] pub struct Screen { pub height: usize, pub width: usize, } -#[derive(Default, Debug, Deserialize)] +#[derive(Default, Clone, Debug, Deserialize)] pub struct RenderConfigData { pub samples: usize, pub max_depth: usize, @@ -93,6 +93,23 @@ pub enum ImageAction { SavePng, } +#[derive(StructOpt, Debug, Clone, Deserialize, Default)] +pub enum ConfigSceneController { + #[default] + Interactive, +} + +#[derive(StructOpt, Debug, Clone, Deserialize, Default)] +pub enum Renderer { + #[default] + Cpu, + CpuPreview, +} + +fn default_preview_renderer() -> Renderer { + Renderer::CpuPreview +} + impl FromStr for ImageAction { type Err = TracerError; @@ -105,7 +122,7 @@ impl FromStr for ImageAction { } } -#[derive(Default, Debug, Deserialize)] +#[derive(Default, Clone, Debug, Deserialize)] pub struct Config { #[serde(default)] pub preview: RenderConfigData, @@ -124,6 +141,15 @@ pub struct Config { #[serde(default)] pub image_output_dir: Option, + + #[serde(default)] + pub scene_controller: ConfigSceneController, + + #[serde(default)] + pub renderer: Renderer, + + #[serde(default = "default_preview_renderer")] + pub preview_renderer: Renderer, } impl Config { diff --git a/racer-tracer/src/image_action.rs b/racer-tracer/src/image_action.rs index 3f984a1..9156b6d 100644 --- a/racer-tracer/src/image_action.rs +++ b/racer-tracer/src/image_action.rs @@ -18,6 +18,7 @@ pub trait ImageAction: Send + Sync { fn action( &self, screen_buffer: &RwLock>, + cancel_event: &SignalEvent, event: &SignalEvent, config: &Config, log: Logger, @@ -25,11 +26,11 @@ pub trait ImageAction: Send + Sync { ) -> Result<(), TracerError>; } -impl From<&CImageAction> for Box { +impl From<&CImageAction> for &dyn ImageAction { fn from(image_action: &CImageAction) -> Self { match image_action { - CImageAction::WaitForSignal => Box::new(WaitForSignal {}), - CImageAction::SavePng => Box::new(SavePng {}), + CImageAction::WaitForSignal => &WaitForSignal {} as &dyn ImageAction, + CImageAction::SavePng => &SavePng {} as &dyn ImageAction, } } } diff --git a/racer-tracer/src/image_action/png.rs b/racer-tracer/src/image_action/png.rs index dbed285..5b2bf1a 100644 --- a/racer-tracer/src/image_action/png.rs +++ b/racer-tracer/src/image_action/png.rs @@ -14,6 +14,7 @@ impl ImageAction for SavePng { fn action( &self, screen_buffer: &RwLock>, + _cancel_event: &SignalEvent, event: &SignalEvent, config: &Config, log: Logger, diff --git a/racer-tracer/src/image_action/wait_for_signal.rs b/racer-tracer/src/image_action/wait_for_signal.rs index 0ffe5e7..1e31200 100644 --- a/racer-tracer/src/image_action/wait_for_signal.rs +++ b/racer-tracer/src/image_action/wait_for_signal.rs @@ -1,4 +1,4 @@ -use std::sync::RwLock; +use std::{sync::RwLock, time::Duration}; use slog::Logger; use synchronoise::SignalEvent; @@ -17,6 +17,7 @@ impl ImageAction for WaitForSignal { fn action( &self, _screen_buffer: &RwLock>, + cancel_event: &SignalEvent, event: &SignalEvent, _config: &Config, _log: Logger, @@ -24,7 +25,7 @@ impl ImageAction for WaitForSignal { ) -> Result<(), TracerError> { if !event.status() { write_term!(term, "Press R to resume."); - event.wait(); + while !event.wait_timeout(Duration::from_secs(1)) && !cancel_event.status() {} } event.reset(); Ok(()) diff --git a/racer-tracer/src/key_inputs.rs b/racer-tracer/src/key_inputs.rs index 1e3505e..9a7bde5 100644 --- a/racer-tracer/src/key_inputs.rs +++ b/racer-tracer/src/key_inputs.rs @@ -5,15 +5,20 @@ use slog::Logger; use crate::error::TracerError; -pub type KeyClosure<'a> = &'a (dyn Fn(f64) -> Result<(), TracerError> + Send + Sync); +pub enum KeyEvent { + Release, + Down, +} -pub struct KeyInputs<'a> { - is_down_callbacks: HashMap>>, - is_released_callbacks: HashMap>>, +type Callback<'func> = Box Result<(), TracerError> + Send + Sync + 'func>; +pub type KeyCallback<'func> = (KeyEvent, Key, Callback<'func>); +pub struct KeyInputs<'func> { + is_down_callbacks: HashMap>>, + is_released_callbacks: HashMap>>, log: Logger, } -impl<'a> KeyInputs<'a> { +impl<'func, 'window> KeyInputs<'func> { pub fn new(log: Logger) -> Self { KeyInputs { is_down_callbacks: HashMap::new(), @@ -22,35 +27,69 @@ impl<'a> KeyInputs<'a> { } } - pub fn release(&mut self, key: Key, closure: KeyClosure<'a>) { + pub fn input( + event: KeyEvent, + key: Key, + callback: impl Fn(f64) -> Result<(), TracerError> + Send + Sync + 'func, + ) -> KeyCallback<'func> { + (event, key, Box::new(callback)) + } + + pub fn register_inputs(&mut self, inputs: Vec>) { + inputs.into_iter().for_each(|(ev, key, closure)| match ev { + KeyEvent::Release => { + let callbacks = self + .is_released_callbacks + .entry(key) + .or_insert_with(Vec::new); + callbacks.push(closure); + } + KeyEvent::Down => { + let callbacks = self.is_down_callbacks.entry(key).or_insert_with(Vec::new); + callbacks.push(closure); + } + }) + } + + #[allow(dead_code)] + pub fn release( + &mut self, + key: Key, + closure: impl Fn(f64) -> Result<(), TracerError> + Send + Sync + 'func, + ) { let callbacks = self .is_released_callbacks .entry(key) .or_insert_with(Vec::new); - callbacks.push(closure); + callbacks.push(Box::new(closure)); } - pub fn down(&mut self, key: Key, closure: KeyClosure<'a>) { + #[allow(dead_code)] + pub fn down( + &mut self, + key: Key, + closure: impl Fn(f64) -> Result<(), TracerError> + Send + Sync + 'func, + ) { let callbacks = self.is_down_callbacks.entry(key).or_insert_with(Vec::new); - callbacks.push(closure); + callbacks.push(Box::new(closure)); } - pub fn update(&mut self, window: &Window, dt: f64) { + pub fn update(&self, window: &'window Window, dt: f64) { self.is_down_callbacks - .iter_mut() + .iter() .filter(|(key, _callbacks)| window.is_key_down(**key)) .for_each(|(_key, callbacks)| { - callbacks.iter_mut().for_each(|callback| { + callbacks.iter().for_each(|callback| { if let Err(e) = callback(dt) { error!(self.log, "Key callback error: {}", e); } }) }); self.is_released_callbacks - .iter_mut() + .iter() .filter(|(key, _callbacks)| window.is_key_released(**key)) .for_each(|(_key, callbacks)| { - callbacks.iter_mut().for_each(|callback| { + callbacks.iter().for_each(|callback| { if let Err(e) = callback(dt) { error!(self.log, "Key callback error: {}", e); } diff --git a/racer-tracer/src/main.rs b/racer-tracer/src/main.rs index f950000..c31f09b 100644 --- a/racer-tracer/src/main.rs +++ b/racer-tracer/src/main.rs @@ -10,6 +10,7 @@ mod material; mod ray; mod renderer; mod scene; +mod scene_controller; mod terminal; mod util; mod vec3; @@ -25,9 +26,7 @@ use std::{ convert::TryFrom, fs::OpenOptions, path::Path, - sync::RwLock, time::{Duration, Instant}, - vec::Vec, }; use minifb::{Key, Window, WindowOptions}; @@ -37,7 +36,7 @@ use synchronoise::SignalEvent; use terminal::Terminal; use crate::{ - renderer::{RenderData, Renderer}, + scene_controller::{interactive::InteractiveScene, SceneController, SceneData}, vec3::Vec3, }; @@ -45,22 +44,16 @@ use crate::{ camera::Camera, config::{Args, Config}, error::TracerError, - image_action::ImageAction, key_inputs::KeyInputs, - renderer::{cpu::CpuRenderer, cpu_scaled::CpuRendererScaled}, scene::Scene, }; fn run(config: Config, log: Logger, term: Terminal) -> Result<(), TracerError> { info!(log, "Starting racer-tracer {}", env!("CARGO_PKG_VERSION")); - let renderer: &dyn Renderer = &CpuRenderer {} as &dyn Renderer; - let renderer_preview: &dyn Renderer = &CpuRendererScaled {} as &dyn Renderer; let image = image::Image::new(config.screen.width, config.screen.height); - let screen_buffer: RwLock> = RwLock::new(vec![0; image.width * image.height]); let look_from = Vec3::new(13.0, 2.0, 3.0); let look_at = Vec3::new(0.0, 0.0, 0.0); - let camera_speed = 2.0; - let camera = RwLock::new(Camera::new( + let camera = Camera::new( look_from, look_at, Vec3::new(0.0, 1.0, 0.0), @@ -68,120 +61,42 @@ fn run(config: Config, log: Logger, term: Terminal) -> Result<(), TracerError> { &image, 0.1, 10.0, - )); - + ); let scene = Scene::try_new(&config.loader)?; - let mut window_res: Result<(), TracerError> = Ok(()); let mut render_res: Result<(), TracerError> = Ok(()); - - let render_image = SignalEvent::manual(false); let exit = SignalEvent::manual(false); - let image_action: Box = (&config.image_action).into(); - - // Setting up controls - let mut key_inputs = KeyInputs::new(log.new(o!("scope" => "key-intputs"))); - let render_image_fn = |_| { - render_image.signal(); - Ok(()) - }; - key_inputs.release(Key::R, &render_image_fn); - - let go_forward = |dt: f64| { - camera - .write() - .map_err(|e| TracerError::KeyError(e.to_string())) - .map(|mut cam| { - cam.go_forward(-dt * camera_speed); - }) - }; - key_inputs.down(Key::W, &go_forward); - - let go_back = |dt: f64| { - camera - .write() - .map_err(|e| TracerError::KeyError(e.to_string())) - .map(|mut cam| { - cam.go_forward(dt * camera_speed); - }) + let scene_controller = { + match &config.scene_controller { + config::ConfigSceneController::Interactive => { + let camera_speed = 0.000002; + InteractiveScene::new( + SceneData { + log: log.new(o!("scope" => "scene-controller")), + term, + config: config.clone(), + scene, + camera, + image: image.clone(), + }, + camera_speed, + ) + } + } }; - key_inputs.down(Key::S, &go_back); - let go_left = |dt: f64| { - camera - .write() - .map_err(|e| TracerError::KeyError(e.to_string())) - .map(|mut cam| { - cam.go_right(-dt * camera_speed); - }) - }; - key_inputs.down(Key::A, &go_left); - - let go_right = |dt: f64| { - camera - .write() - .map_err(|e| TracerError::KeyError(e.to_string())) - .map(|mut cam| { - cam.go_right(dt * camera_speed); - }) - }; - key_inputs.down(Key::D, &go_right); + let mut inputs = KeyInputs::new(log.new(o!("scope" => "key-inputs"))); + inputs.register_inputs(scene_controller.get_inputs()); rayon::scope(|s| { + // Render s.spawn(|_| { 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_image.reset(); - // Render preview - renderer_preview.render(RenderData { - buffer: &screen_buffer, - camera: &camera, - image: &image, - scene: &scene, - config: &config, - cancel_event: None, - }) - }, - |_| { - render_image.reset(); - let render_time = Instant::now(); - renderer - .render(RenderData { - buffer: &screen_buffer, - camera: &camera, - image: &image, - scene: &scene, - config: &config, - cancel_event: Some(&render_image), - }) - .and_then(|_| { - info!( - log, - "It took {} seconds to render the image.", - Instant::now() - .duration_since(render_time) - .as_secs() - ); - image_action.action( - &screen_buffer, - &render_image, - &config, - log.new(o!("scope" => "image-action")), - &term, - ) - }) - }, - ) - }); + .and_then(|_| scene_controller.render()); } if render_res.is_err() { @@ -189,6 +104,7 @@ fn run(config: Config, log: Logger, term: Terminal) -> Result<(), TracerError> { } }); + // Update s.spawn(|_| { window_res = Window::new( "racer-tracer", @@ -201,27 +117,32 @@ fn run(config: Config, log: Logger, term: Terminal) -> Result<(), TracerError> { window.limit_update_rate(Some(std::time::Duration::from_micros(16600))); window }) - .and_then(|mut window| { + .map(|mut window| { let mut t = Instant::now(); - while window.is_open() && !window.is_key_down(Key::Escape) && !exit.status() { - let dt = t.elapsed().as_micros() as f64 / 1000000.0; + while window_res.is_ok() + && window.is_open() + && !window.is_key_down(Key::Escape) + && !exit.status() + { + let dt = t.elapsed().as_micros() as f64; t = Instant::now(); - key_inputs.update(&window, dt); - // Sleep a bit to not hog the lock on the buffer all the time. - std::thread::sleep(std::time::Duration::from_millis(1)); - - screen_buffer - .read() - .map_err(|e| TracerError::FailedToUpdateWindow(e.to_string())) - .and_then(|buf| { - window - .update_with_buffer(&buf, image.width, image.height) - .map_err(|e| TracerError::FailedToUpdateWindow(e.to_string())) - })? + inputs.update(&window, dt); + + window_res = + scene_controller + .get_buffer() + .and_then(|maybe_buf| match maybe_buf { + Some(buf) => window + .update_with_buffer(&buf, image.width, image.height) + .map_err(|e| TracerError::FailedToUpdateWindow(e.to_string())), + None => { + window.update(); + Ok(()) + } + }); } exit.signal(); - render_image.signal(); - Ok(()) + scene_controller.stop() }); }); }); @@ -265,7 +186,7 @@ fn main() { Err(TracerError::ExitEvent) => {} Ok(_) => {} Err(e) => { - error!(log, "{}", e); + error!(log, "Error: {}", e); let exit_code = i32::from(e); error!(log, "Exiting with: {}", exit_code); std::process::exit(exit_code) diff --git a/racer-tracer/src/renderer.rs b/racer-tracer/src/renderer.rs index 162dc4b..0c351c9 100644 --- a/racer-tracer/src/renderer.rs +++ b/racer-tracer/src/renderer.rs @@ -3,10 +3,18 @@ use std::{sync::RwLock, time::Duration}; use synchronoise::SignalEvent; use crate::{ - camera::Camera, config::Config, error::TracerError, geometry::Hittable, image::Image, ray::Ray, - vec3::Color, vec3::Vec3, + camera::Camera, + config::{Config, Renderer as ConfigRenderer}, + error::TracerError, + geometry::Hittable, + image::Image, + ray::Ray, + vec3::Color, + vec3::Vec3, }; +use self::{cpu::CpuRenderer, cpu_scaled::CpuRendererScaled}; + pub mod cpu; pub mod cpu_scaled; @@ -44,8 +52,18 @@ pub struct RenderData<'a> { pub scene: &'a dyn Hittable, pub config: &'a Config, pub cancel_event: Option<&'a SignalEvent>, + pub buffer_updated: Option<&'a SignalEvent>, } pub trait Renderer: Send + Sync { fn render(&self, render_data: RenderData) -> Result<(), TracerError>; } + +impl From<&ConfigRenderer> for &dyn Renderer { + fn from(r: &ConfigRenderer) -> Self { + match r { + ConfigRenderer::Cpu => &CpuRenderer {} as &dyn Renderer, + ConfigRenderer::CpuPreview => &CpuRendererScaled {} as &dyn Renderer, + } + } +} diff --git a/racer-tracer/src/renderer/cpu.rs b/racer-tracer/src/renderer/cpu.rs index f97ad9c..48e9528 100644 --- a/racer-tracer/src/renderer/cpu.rs +++ b/racer-tracer/src/renderer/cpu.rs @@ -59,6 +59,9 @@ impl CpuRenderer { buf[offset + row * image.screen_width + col] = color; } } + if let Some(updated) = rd.buffer_updated { + updated.signal() + } }) } diff --git a/racer-tracer/src/renderer/cpu_scaled.rs b/racer-tracer/src/renderer/cpu_scaled.rs index 44e73fa..3cbbaab 100644 --- a/racer-tracer/src/renderer/cpu_scaled.rs +++ b/racer-tracer/src/renderer/cpu_scaled.rs @@ -81,6 +81,9 @@ impl CpuRendererScaled { } } } + if let Some(updated) = rd.buffer_updated { + updated.signal() + } }) } } diff --git a/racer-tracer/src/scene_controller.rs b/racer-tracer/src/scene_controller.rs new file mode 100644 index 0000000..4a867f5 --- /dev/null +++ b/racer-tracer/src/scene_controller.rs @@ -0,0 +1,37 @@ +pub mod interactive; + +use slog::Logger; + +use crate::{ + camera::Camera, config::Config, error::TracerError, image::Image, key_inputs::KeyCallback, + scene::Scene, terminal::Terminal, +}; + +pub fn create_screen_buffer(image: &Image) -> Vec { + vec![0; image.width * image.height] +} + +pub struct SceneData { + pub log: Logger, + pub term: Terminal, + pub config: Config, + pub scene: Scene, + pub camera: Camera, + pub image: Image, +} + +pub trait SceneController: Send + Sync { + // Return a vector of key callbacks. The provided closure will be + // called when the corresponding key is release/pressed. + fn get_inputs(&self) -> Vec; + + // Render function + fn render(&self) -> Result<(), TracerError>; + + // Returns the screen buffer produced by the scene controller. + // Returns None if no new buffer is available + fn get_buffer(&self) -> Result>, TracerError>; + + // Called when the application wants to exit. + fn stop(&self); +} diff --git a/racer-tracer/src/scene_controller/interactive.rs b/racer-tracer/src/scene_controller/interactive.rs new file mode 100644 index 0000000..4624ba1 --- /dev/null +++ b/racer-tracer/src/scene_controller/interactive.rs @@ -0,0 +1,209 @@ +use std::{ + sync::RwLock, + time::{Duration, Instant}, +}; + +use minifb::Key; +use slog::Logger; +use synchronoise::SignalEvent; + +use crate::{ + camera::Camera, + config::Config, + error::TracerError, + image::Image, + image_action::ImageAction, + key_inputs::{KeyCallback, KeyEvent, KeyInputs}, + renderer::{RenderData, Renderer}, + scene::Scene, + terminal::Terminal, +}; + +use super::{create_screen_buffer, SceneController, SceneData}; + +pub struct InteractiveScene<'renderer, 'action> { + screen_buffer: RwLock>, + preview_buffer: RwLock>, + camera_speed: f64, + render_image_event: SignalEvent, + buffer_updated: SignalEvent, + stop_event: SignalEvent, + + log: Logger, + term: Terminal, + image_action: &'action dyn ImageAction, + config: Config, + scene: Scene, + camera: RwLock, + image: Image, + renderer: &'renderer dyn Renderer, + renderer_preview: &'renderer dyn Renderer, +} + +impl<'renderer, 'action> InteractiveScene<'renderer, 'action> { + pub fn new(scene_data: SceneData, camera_speed: f64) -> Self { + Self { + screen_buffer: RwLock::new(create_screen_buffer(&scene_data.image)), + preview_buffer: RwLock::new(create_screen_buffer(&scene_data.image)), + camera_speed, + render_image_event: SignalEvent::manual(false), + buffer_updated: SignalEvent::manual(false), + stop_event: SignalEvent::manual(false), + + log: scene_data.log, + term: scene_data.term, + image_action: (&scene_data.config.image_action).into(), + scene: scene_data.scene, + camera: RwLock::new(scene_data.camera), + image: scene_data.image, + renderer: (&scene_data.config.renderer).into(), + renderer_preview: (&scene_data.config.preview_renderer).into(), + config: scene_data.config, + } + } +} + +impl<'renderer, 'action> SceneController for InteractiveScene<'renderer, 'action> { + fn get_inputs(&self) -> Vec { + vec![ + KeyInputs::input(KeyEvent::Release, Key::R, |_| { + self.render_image_event.signal(); + Ok(()) + }), + KeyInputs::input(KeyEvent::Down, Key::W, |dt| { + self.camera + .write() + .map_err(|e| TracerError::KeyError(e.to_string())) + .map(|mut cam| { + cam.go_forward(-dt * self.camera_speed); + }) + }), + KeyInputs::input(KeyEvent::Down, Key::S, |dt| { + self.camera + .write() + .map_err(|e| TracerError::KeyError(e.to_string())) + .map(|mut cam| { + cam.go_forward(dt * self.camera_speed); + }) + }), + KeyInputs::input(KeyEvent::Down, Key::A, |dt| { + self.camera + .write() + .map_err(|e| TracerError::KeyError(e.to_string())) + .map(|mut cam| { + cam.go_right(-dt * self.camera_speed); + }) + }), + KeyInputs::input(KeyEvent::Down, Key::D, |dt| { + self.camera + .write() + .map_err(|e| TracerError::KeyError(e.to_string())) + .map(|mut cam| { + cam.go_right(dt * self.camera_speed); + }) + }), + ] + } + + fn get_buffer(&self) -> Result>, TracerError> { + self.buffer_updated + .wait_timeout(Duration::from_secs(0)) + .then_some(|| ()) + .map_or(Ok(None), |_| { + self.screen_buffer + .read() + .map_err(|e| TracerError::FailedToAcquireLock(e.to_string())) + .map(|v| Some(v.to_owned())) + }) + } + + fn render(&self) -> Result<(), TracerError> { + self.render_image_event + .wait_timeout(Duration::from_secs(0)) + .then_some(|| ()) + .map_or_else( + || { + // Render preview + self.render_image_event.reset(); + + // We do not want partial screen updates for the preview. + // Which is why we send in a different buffer. + self.renderer_preview + .render(RenderData { + buffer: &self.preview_buffer, + camera: &self.camera, + image: &self.image, + scene: &self.scene, + config: &self.config, + cancel_event: None, + buffer_updated: None, + }) + .and_then(|_| { + self.screen_buffer + .write() + .map_err(|e| TracerError::FailedToAcquireLock(e.to_string())) + }) + .and_then(|w_buffer| { + self.preview_buffer + .read() + .map_err(|e| TracerError::FailedToAcquireLock(e.to_string())) + .map(|r_buffer| (r_buffer, w_buffer)) + }) + .map(|(r_buffer, mut w_buffer)| { + // For the preview we want the complete image + // result before signaling the image is done. + *w_buffer = r_buffer.to_owned(); + self.buffer_updated.signal(); + }) + }, + |_| { + let render_time = Instant::now(); + self.render_image_event.reset(); + + // When we render the final image we want partial + // updates to the screen buffer. We send in our + // original screen_buffer and the buffer_updated + // signal. This will ensure that the window will + // get updated with a new buffer as soon as a + // thread finishes writing a block and we get + // partial updates of the rendered image. + self.renderer + .render(RenderData { + buffer: &self.screen_buffer, + camera: &self.camera, + image: &self.image, + scene: &self.scene, + config: &self.config, + cancel_event: Some(&self.render_image_event), + buffer_updated: Some(&self.buffer_updated), + }) + .and_then(|_| { + if !self.render_image_event.status() { + info!( + self.log, + "It took {} seconds to render the image.", + Instant::now().duration_since(render_time).as_secs() + ); + } else { + info!(self.log, "Image render cancelled."); + } + + self.image_action.action( + &self.screen_buffer, + &self.stop_event, + &self.render_image_event, + &self.config, + self.log.new(o!("scope" => "image-action")), + &self.term, + ) + }) + }, + ) + } + + fn stop(&self) { + // If we are currently rendering anything we try to cancel it + self.render_image_event.signal(); + self.stop_event.signal(); + } +} -- cgit v1.2.3