summaryrefslogtreecommitdiff
path: root/racer-tracer
diff options
context:
space:
mode:
Diffstat (limited to 'racer-tracer')
-rw-r--r--racer-tracer/src/config.rs6
-rw-r--r--racer-tracer/src/main.rs77
-rw-r--r--racer-tracer/src/render.rs247
-rw-r--r--racer-tracer/src/renderer.rs51
-rw-r--r--racer-tracer/src/renderer/cpu.rs124
-rw-r--r--racer-tracer/src/renderer/cpu_scaled.rs106
6 files changed, 325 insertions, 286 deletions
diff --git a/racer-tracer/src/config.rs b/racer-tracer/src/config.rs
index 4cb0880..3992176 100644
--- a/racer-tracer/src/config.rs
+++ b/racer-tracer/src/config.rs
@@ -13,7 +13,7 @@ pub struct Screen {
}
#[derive(Default, Debug, Deserialize)]
-pub struct RenderData {
+pub struct RenderConfigData {
pub samples: usize,
pub max_depth: usize,
pub num_threads_width: usize,
@@ -108,10 +108,10 @@ impl FromStr for ImageAction {
#[derive(Default, Debug, Deserialize)]
pub struct Config {
#[serde(default)]
- pub preview: RenderData,
+ pub preview: RenderConfigData,
#[serde(default)]
- pub render: RenderData,
+ pub render: RenderConfigData,
#[serde(default)]
pub screen: Screen,
diff --git a/racer-tracer/src/main.rs b/racer-tracer/src/main.rs
index b70a140..f950000 100644
--- a/racer-tracer/src/main.rs
+++ b/racer-tracer/src/main.rs
@@ -8,7 +8,7 @@ mod image_action;
mod key_inputs;
mod material;
mod ray;
-mod render;
+mod renderer;
mod scene;
mod terminal;
mod util;
@@ -36,7 +36,10 @@ use structopt::StructOpt;
use synchronoise::SignalEvent;
use terminal::Terminal;
-use crate::vec3::Vec3;
+use crate::{
+ renderer::{RenderData, Renderer},
+ vec3::Vec3,
+};
use crate::{
camera::Camera,
@@ -44,13 +47,14 @@ use crate::{
error::TracerError,
image_action::ImageAction,
key_inputs::KeyInputs,
- render::render,
+ 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<Vec<u32>> = RwLock::new(vec![0; image.width * image.height]);
let look_from = Vec3::new(13.0, 2.0, 3.0);
@@ -138,42 +142,43 @@ fn run(config: Config, log: Logger, term: Terminal) -> Result<(), TracerError> {
|| {
render_image.reset();
// Render preview
- render(
- &screen_buffer,
- &camera,
- &image,
- &scene,
- &config.preview,
- None,
- Some(config.preview.scale),
- )
+ 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();
- render(
- &screen_buffer,
- &camera,
- &image,
- &scene,
- &config.render,
- Some(&render_image),
- None,
- )
- .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,
- )
- })
+ 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,
+ )
+ })
},
)
});
diff --git a/racer-tracer/src/render.rs b/racer-tracer/src/render.rs
deleted file mode 100644
index 6926091..0000000
--- a/racer-tracer/src/render.rs
+++ /dev/null
@@ -1,247 +0,0 @@
-use std::{sync::RwLock, time::Duration};
-
-use rayon::prelude::*;
-use synchronoise::SignalEvent;
-
-use crate::{
- camera::Camera,
- config::RenderData,
- error::TracerError,
- geometry::Hittable,
- image::{Image, SubImage},
- ray::Ray,
- util::random_double,
- vec3::{Color, Vec3},
-};
-
-fn ray_color(scene: &dyn Hittable, ray: &Ray, depth: usize) -> Vec3 {
- if depth == 0 {
- return Vec3::default();
- }
-
- if let Some(rec) = scene.hit(ray, 0.001, std::f64::INFINITY) {
- if let Some((scattered, attenuation)) = rec.material.scatter(ray, &rec) {
- return attenuation * ray_color(scene, &scattered, depth - 1);
- }
- return Color::default();
- }
-
- // Sky
- let first_color = Vec3::new(1.0, 1.0, 1.0);
- let second_color = Vec3::new(0.5, 0.7, 1.0);
- let unit_direction = ray.direction().unit_vector();
- let t = 0.5 * (unit_direction.y() + 1.0);
- (1.0 - t) * first_color + t * second_color
-}
-
-fn do_cancel(cancel_event: Option<&SignalEvent>) -> bool {
- match cancel_event {
- Some(event) => event.wait_timeout(Duration::from_secs(0)),
- None => false,
- }
-}
-
-fn get_highest_divdable(value: usize, mut div: usize) -> usize {
- // Feels like there could possibly be some other nicer trick to this.
- while (value % div) != 0 {
- div -= 1;
- }
- div
-}
-
-pub fn raytrace_scaled(
- buffer: &RwLock<Vec<u32>>,
- cancel_event: Option<&SignalEvent>,
- scene: &dyn Hittable,
- camera: Camera,
- 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;
-
- let mut colors: Vec<Vec3> = vec![Vec3::default(); scaled_height * scaled_width];
- for row in 0..scaled_height {
- for column in 0..scaled_width {
- let u: f64 = ((image.x + column * scale_width) as f64 + random_double())
- / (image.screen_width - 1) as f64;
- for _ in 0..data.samples {
- let v: f64 = ((image.y + row * scale_height) as f64 + random_double())
- / (image.screen_height - 1) as f64;
- colors[row * scaled_width + column].add(ray_color(
- scene,
- &camera.get_ray(u, v),
- data.max_depth,
- ));
- }
- }
-
- if do_cancel(cancel_event) {
- return Ok(());
- }
- }
-
- (!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(
- buffer: &RwLock<Vec<u32>>,
- cancel_event: Option<&SignalEvent>,
- scene: &dyn Hittable,
- camera: Camera,
- image: &SubImage,
- data: &RenderData,
-) -> Result<(), TracerError> {
- let mut colors: Vec<Vec3> = vec![Vec3::default(); image.height * image.width];
- for row in 0..image.height {
- for column in 0..image.width {
- let u: f64 =
- ((image.x + column) as f64 + random_double()) / (image.screen_width - 1) as f64;
- for _ in 0..data.samples {
- let v: f64 =
- ((image.y + row) as f64 + random_double()) / (image.screen_height - 1) as f64;
- colors[row * image.width + column].add(ray_color(
- scene,
- &camera.get_ray(u, v),
- data.max_depth,
- ));
- }
- }
-
- if do_cancel(cancel_event) {
- return Ok(());
- }
- }
-
- (!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;
- }
- }
- })
-}
-
-pub fn render(
- buffer: &RwLock<Vec<u32>>,
- camera: &RwLock<Camera>,
- image: &Image,
- scene: &dyn Hittable,
- data: &RenderData,
- cancel_event: Option<&SignalEvent>,
- scale: Option<usize>,
-) -> Result<(), TracerError> {
- if do_cancel(cancel_event) {
- return Ok(());
- }
-
- 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));
-
- (!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::<Vec<SubImage>>()
- })
- .collect::<Vec<SubImage>>();
-
- (cam, images)
- })
- .and_then(|(cam, sub_images)| {
- sub_images
- .into_par_iter()
- .map(|image| {
- scale.map_or_else(
- || raytrace(buffer, cancel_event, scene, cam.clone(), &image, data),
- |_| {
- raytrace_scaled(
- buffer,
- cancel_event,
- scene,
- cam.clone(),
- &image,
- data,
- (scaled_width, scaled_height),
- )
- },
- )
- })
- .collect::<Result<(), TracerError>>()
- })
-}
diff --git a/racer-tracer/src/renderer.rs b/racer-tracer/src/renderer.rs
new file mode 100644
index 0000000..162dc4b
--- /dev/null
+++ b/racer-tracer/src/renderer.rs
@@ -0,0 +1,51 @@
+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,
+};
+
+pub mod cpu;
+pub mod cpu_scaled;
+
+fn do_cancel(cancel_event: Option<&SignalEvent>) -> bool {
+ match cancel_event {
+ Some(event) => event.wait_timeout(Duration::from_secs(0)),
+ None => false,
+ }
+}
+
+fn ray_color(scene: &dyn Hittable, ray: &Ray, depth: usize) -> Vec3 {
+ if depth == 0 {
+ return Vec3::default();
+ }
+
+ if let Some(rec) = scene.hit(ray, 0.001, std::f64::INFINITY) {
+ if let Some((scattered, attenuation)) = rec.material.scatter(ray, &rec) {
+ return attenuation * ray_color(scene, &scattered, depth - 1);
+ }
+ return Color::default();
+ }
+
+ // Sky
+ let first_color = Vec3::new(1.0, 1.0, 1.0);
+ let second_color = Vec3::new(0.5, 0.7, 1.0);
+ let unit_direction = ray.direction().unit_vector();
+ let t = 0.5 * (unit_direction.y() + 1.0);
+ (1.0 - t) * first_color + t * second_color
+}
+
+pub struct RenderData<'a> {
+ pub buffer: &'a RwLock<Vec<u32>>,
+ pub camera: &'a RwLock<Camera>,
+ pub image: &'a Image,
+ pub scene: &'a dyn Hittable,
+ pub config: &'a Config,
+ pub cancel_event: Option<&'a SignalEvent>,
+}
+
+pub trait Renderer: Send + Sync {
+ fn render(&self, render_data: RenderData) -> Result<(), TracerError>;
+}
diff --git a/racer-tracer/src/renderer/cpu.rs b/racer-tracer/src/renderer/cpu.rs
new file mode 100644
index 0000000..f97ad9c
--- /dev/null
+++ b/racer-tracer/src/renderer/cpu.rs
@@ -0,0 +1,124 @@
+use rayon::prelude::*;
+
+use crate::{
+ camera::Camera,
+ error::TracerError,
+ image::SubImage,
+ renderer::{do_cancel, ray_color, Renderer},
+ util::random_double,
+ vec3::Vec3,
+};
+
+use super::RenderData;
+
+pub struct CpuRenderer {}
+
+impl CpuRenderer {
+ pub fn raytrace(
+ &self,
+ rd: &RenderData,
+ camera: &Camera,
+ image: &SubImage,
+ ) -> Result<(), TracerError> {
+ let mut colors: Vec<Vec3> = vec![Vec3::default(); image.height * image.width];
+ for row in 0..image.height {
+ for column in 0..image.width {
+ let u: f64 =
+ ((image.x + column) as f64 + random_double()) / (image.screen_width - 1) as f64;
+ for _ in 0..rd.config.render.samples {
+ let v: f64 = ((image.y + row) as f64 + random_double())
+ / (image.screen_height - 1) as f64;
+ colors[row * image.width + column].add(ray_color(
+ rd.scene,
+ &camera.get_ray(u, v),
+ rd.config.render.max_depth,
+ ));
+ }
+ }
+
+ if do_cancel(rd.cancel_event) {
+ return Ok(());
+ }
+ }
+
+ (!do_cancel(rd.cancel_event))
+ .then_some(|| ())
+ .ok_or(TracerError::CancelEvent)
+ .and_then(|_| {
+ rd.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(rd.config.render.samples)
+ .as_color();
+ buf[offset + row * image.screen_width + col] = color;
+ }
+ }
+ })
+ }
+
+ pub fn prepare_threads(rd: &RenderData) -> Result<(Camera, Vec<SubImage>), TracerError> {
+ let width_step = rd.image.width / rd.config.render.num_threads_width;
+ let height_step = rd.image.height / rd.config.render.num_threads_height;
+
+ (!do_cancel(rd.cancel_event))
+ .then_some(|| ())
+ .ok_or(TracerError::CancelEvent)
+ .and_then(|_| {
+ rd.camera
+ .read()
+ .map_err(|e| TracerError::FailedToAcquireLock(e.to_string()))
+ // We make a clone of it as we don't want the
+ // camera to move as we render the scene. It also
+ // ensures we keep the lock to a minimum.
+ .map(|cam| cam.clone())
+ })
+ .map(|cam| {
+ let images = (0..rd.config.render.num_threads_width)
+ .flat_map(|ws| {
+ (0..rd.config.render.num_threads_height)
+ .map(|hs| SubImage {
+ x: width_step * ws,
+ y: height_step * hs,
+ screen_width: rd.image.width,
+ screen_height: rd.image.height,
+
+ // Neccesary in case the threads width is not
+ // evenly divisible by the image width.
+ width: if ws == rd.config.render.num_threads_width - 1 {
+ rd.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 == rd.config.render.num_threads_height - 1 {
+ rd.image.height - height_step * hs
+ } else {
+ height_step
+ },
+ })
+ .collect::<Vec<SubImage>>()
+ })
+ .collect::<Vec<SubImage>>();
+ (cam, images)
+ })
+ }
+}
+
+impl Renderer for CpuRenderer {
+ fn render(&self, rd: RenderData) -> Result<(), TracerError> {
+ CpuRenderer::prepare_threads(&rd).and_then(|(cam, images)| {
+ images
+ .into_par_iter()
+ .map(|image| self.raytrace(&rd, &cam, &image))
+ .collect::<Result<(), TracerError>>()
+ })
+ }
+}
diff --git a/racer-tracer/src/renderer/cpu_scaled.rs b/racer-tracer/src/renderer/cpu_scaled.rs
new file mode 100644
index 0000000..44e73fa
--- /dev/null
+++ b/racer-tracer/src/renderer/cpu_scaled.rs
@@ -0,0 +1,106 @@
+use rayon::prelude::*;
+
+use crate::{
+ camera::Camera,
+ error::TracerError,
+ image::SubImage,
+ renderer::{cpu::CpuRenderer, do_cancel, ray_color, Renderer},
+ util::random_double,
+ vec3::Vec3,
+};
+
+use super::RenderData;
+
+fn get_highest_divdable(value: usize, mut div: usize) -> usize {
+ // Feels like there could possibly be some other nicer trick to this.
+ while (value % div) != 0 {
+ div -= 1;
+ }
+ div
+}
+
+pub struct CpuRendererScaled {}
+
+impl CpuRendererScaled {
+ pub fn raytrace(
+ &self,
+ rd: &RenderData,
+ camera: &Camera,
+ image: &SubImage,
+ 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;
+
+ let mut colors: Vec<Vec3> = vec![Vec3::default(); scaled_height * scaled_width];
+ for row in 0..scaled_height {
+ for column in 0..scaled_width {
+ let u: f64 = ((image.x + column * scale_width) as f64 + random_double())
+ / (image.screen_width - 1) as f64;
+ for _ in 0..rd.config.preview.samples {
+ let v: f64 = ((image.y + row * scale_height) as f64 + random_double())
+ / (image.screen_height - 1) as f64;
+ colors[row * scaled_width + column].add(ray_color(
+ rd.scene,
+ &camera.get_ray(u, v),
+ rd.config.preview.max_depth,
+ ));
+ }
+ }
+
+ if do_cancel(rd.cancel_event) {
+ return Ok(());
+ }
+ }
+
+ (!do_cancel(rd.cancel_event))
+ .then_some(|| ())
+ .ok_or(TracerError::CancelEvent)
+ .and_then(|_| {
+ rd.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(rd.config.preview.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;
+ }
+ }
+ }
+ }
+ })
+ }
+}
+
+impl Renderer for CpuRendererScaled {
+ fn render(&self, rd: RenderData) -> Result<(), crate::error::TracerError> {
+ let scale_width = get_highest_divdable(
+ rd.image.width / rd.config.render.num_threads_width,
+ rd.config.preview.scale,
+ );
+ let scale_height = get_highest_divdable(
+ rd.image.height / rd.config.render.num_threads_height,
+ rd.config.preview.scale,
+ );
+
+ CpuRenderer::prepare_threads(&rd).and_then(|(cam, images)| {
+ images
+ .into_par_iter()
+ .map(|image| self.raytrace(&rd, &cam, &image, (scale_width, scale_height)))
+ .collect::<Result<(), TracerError>>()
+ })
+ }
+}