summaryrefslogtreecommitdiff
path: root/racer-tracer/src/renderer
diff options
context:
space:
mode:
authorSakarias Johansson <sakarias.johansson@goodbyekansas.com>2023-04-05 21:38:33 +0200
committerSakarias Johansson <sakariasjohansson@hotmail.com>2023-04-05 22:06:03 +0200
commit5f8faa17457426c4ca8c54bf67b5aa96eb7a52ea (patch)
treefd2972858804a93294e4ad95057b19c17340acbb /racer-tracer/src/renderer
parent71ea29e48bdfc500a4e958cc1f37eddbd1d7bd2c (diff)
downloadracer-tracer-5f8faa17457426c4ca8c54bf67b5aa96eb7a52ea.tar.gz
racer-tracer-5f8faa17457426c4ca8c54bf67b5aa96eb7a52ea.tar.xz
racer-tracer-5f8faa17457426c4ca8c54bf67b5aa96eb7a52ea.zip
🎨 Move render to rendering interface
Removed render.rs and added a renderer trait. Planning on making several implementations so it makes sense to move it to a trait so you can change implementations.
Diffstat (limited to 'racer-tracer/src/renderer')
-rw-r--r--racer-tracer/src/renderer/cpu.rs124
-rw-r--r--racer-tracer/src/renderer/cpu_scaled.rs106
2 files changed, 230 insertions, 0 deletions
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>>()
+ })
+ }
+}