summaryrefslogtreecommitdiff
path: root/racer-tracer/src/render.rs
diff options
context:
space:
mode:
authorSakarias Johansson <sakarias.johansson@goodbyekansas.com>2023-02-17 17:48:41 +0100
committerSakarias Johansson <sakariasjohansson@hotmail.com>2023-02-17 18:05:16 +0100
commit971372cf6350533e36db0404afedb1a36817037c (patch)
tree207dac0f9eda28b58d23761e0e384f4bd4d7f137 /racer-tracer/src/render.rs
parent9959f90ea4b7ebd933387e6b33647fc661785d84 (diff)
downloadracer-tracer-971372cf6350533e36db0404afedb1a36817037c.tar.gz
racer-tracer-971372cf6350533e36db0404afedb1a36817037c.tar.xz
racer-tracer-971372cf6350533e36db0404afedb1a36817037c.zip
🧹 Refactor rendering & Cleanup
Refactored how the threading worked. Before it just keept up splitting the screen recursively for x number of times. Felt like using recursion was unnecessary. It's now just done in a loop instead. The user can control the behaviour by setting `num_threads_width` and `num_threads_height` which will split the image x number of times and run a thread for each. Split up the rendering function for one that does scaling and one that doesn't. I just don't want to deal with any kind of scaling when actually rendering the image. The code shouldn't change much so maintaining is going to be ok. - Fixed scaling issues where black bars would appear if the subimage size wasn't divisible by the scale. - Cleaned up a bunch of arcs and other things that wasn't neccesary any more.
Diffstat (limited to 'racer-tracer/src/render.rs')
-rw-r--r--racer-tracer/src/render.rs227
1 files changed, 148 insertions, 79 deletions
diff --git a/racer-tracer/src/render.rs b/racer-tracer/src/render.rs
index 1d6d65d..8e01acb 100644
--- a/racer-tracer/src/render.rs
+++ b/racer-tracer/src/render.rs
@@ -1,8 +1,4 @@
-use std::{
- borrow::Borrow,
- sync::{Arc, RwLock},
- time::Duration,
-};
+use std::{borrow::Borrow, sync::RwLock, time::Duration};
use rayon::prelude::*;
use synchronoise::SignalEvent;
@@ -11,7 +7,7 @@ use crate::{
camera::Camera,
config::RenderData,
geometry::Hittable,
- image::{QuadSplit, SubImage},
+ image::{Image, SubImage},
ray::Ray,
util::random_double,
vec3::{Color, Vec3},
@@ -37,50 +33,27 @@ fn ray_color(scene: &dyn Hittable, ray: &Ray, depth: usize) -> Vec3 {
(1.0 - t) * first_color + t * second_color
}
-pub fn raytrace(
+pub fn raytrace_scaled(
buffer: &RwLock<Vec<u32>>,
- cancel_event: Option<Arc<SignalEvent>>,
+ cancel_event: Option<&SignalEvent>,
scene: &dyn Hittable,
- camera: &Camera,
+ camera: Camera,
image: &SubImage,
data: &RenderData,
+ scale: (usize, usize),
) {
- // TODO: This scale shit doesn't work.
- // Just force power of two or other solutions to avoid this.
- // Can be ok for preview but the actual render could use a different function.
-
- let mut scaled_width = image.width / data.scale;
- let mut scaled_height = image.height / data.scale;
- // In the case where we get an odd one out we patch the widht and
- // height with the esception of the edges of the screen. Without
- // this everything has to be power of 2 which isn't a crazy
- // asumption.
- //
- // Biggest problem is that the width and height we get here is
- // depending on resolution and how many times the image is split
- // up between threads.
- if scaled_width * data.scale != image.width
- && (image.x + scaled_width * data.scale + 1 < image.screen_width)
- {
- scaled_width += 1;
- }
+ let (scale_width, scale_height) = scale;
+ let scaled_width = image.width / scale_width;
+ let scaled_height = image.height / scale_height;
- if scaled_height * data.scale != image.height
- && (image.y + scaled_height * data.scale + 1 < image.screen_height)
- {
- scaled_height += 1;
- }
-
- let scaled_screen_width = image.screen_width / data.scale;
- let scaled_screen_height = image.screen_height / data.scale;
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 / data.scale + column) as f64 + random_double())
- / (scaled_screen_width - 1) as f64;
+ 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 / data.scale + row) as f64 + random_double())
- / (scaled_screen_height - 1) as f64;
+ 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),
@@ -89,12 +62,12 @@ pub fn raytrace(
}
}
- if do_cancel(&cancel_event) {
+ if do_cancel(cancel_event) {
return;
}
}
- if do_cancel(&cancel_event) {
+ if do_cancel(cancel_event) {
return;
}
@@ -103,61 +76,157 @@ pub fn raytrace(
.expect("Failed to get write guard when flushing data.");
let offset = image.y * image.screen_width + image.x;
- for half_row in 0..scaled_height {
- for half_col in 0..scaled_width {
- let color = colors[half_row * scaled_width + half_col]
+ 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 = half_row * data.scale;
- let col = half_col * data.scale;
-
- for scale_x in 0..data.scale {
- for scale_y in 0..data.scale {
- buf[offset + (row + scale_x) * image.screen_width + col + scale_y] = 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;
}
}
}
}
}
-fn do_cancel(cancel_event: &Option<Arc<SignalEvent>>) -> bool {
+pub fn raytrace(
+ buffer: &RwLock<Vec<u32>>,
+ cancel_event: Option<&SignalEvent>,
+ scene: &dyn Hittable,
+ camera: Camera,
+ image: &SubImage,
+ data: &RenderData,
+) {
+ 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;
+ }
+ }
+
+ 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;
+ }
+ }
+}
+
+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 render(
- buffer: Arc<RwLock<Vec<u32>>>,
- camera: Arc<RwLock<Camera>>,
- image: &SubImage,
- scene: Arc<Box<dyn Hittable>>,
- data: Arc<RenderData>,
- split_depth: usize,
- cancel_event: Option<Arc<SignalEvent>>,
+ buffer: &RwLock<Vec<u32>>,
+ camera: &RwLock<Camera>,
+ image: &Image,
+ scene: &dyn Hittable,
+ data: &RenderData,
+ cancel_event: Option<&SignalEvent>,
+ scale: Option<usize>,
) {
- if do_cancel(&cancel_event) {
+ if do_cancel(cancel_event) {
return;
}
-
- if split_depth == 0 {
- let scene: &(dyn Hittable) = (*scene).borrow();
- let camera = { camera.read().expect("TODO").clone() };
- raytrace(&buffer, cancel_event, scene, &camera, image, &data);
- } else {
- // Split into more quads
- let quads = image.quad_split();
- quads.into_par_iter().for_each(|image| {
- render(
- Arc::clone(&buffer),
- Arc::clone(&camera),
- &image,
- Arc::clone(&scene),
- Arc::clone(&data),
- split_depth - 1,
- cancel_event.clone(),
+ 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<SubImage> = (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();
+ subs
+ })
+ .collect::<Vec<SubImage>>()
+ .into_par_iter()
+ .for_each(|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),
+ )
+ },
);
});
- }
}