summaryrefslogtreecommitdiff
path: root/racer-tracer/src/main.rs
diff options
context:
space:
mode:
authorSakarias Johansson <sakarias.johansson@goodbyekansas.com>2023-01-10 17:54:39 +0100
committerSakarias Johansson <sakarias.johansson@goodbyekansas.com>2023-01-11 19:36:38 +0100
commitbbecf67545c2bd6822b0680673aa850c5ddef9f3 (patch)
tree741b2fc03797a8fedcfe67e34687b4443fcf41f7 /racer-tracer/src/main.rs
parent899f81eed6c221dce22333ad03704b12d7634a54 (diff)
downloadracer-tracer-bbecf67545c2bd6822b0680673aa850c5ddef9f3.tar.gz
racer-tracer-bbecf67545c2bd6822b0680673aa850c5ddef9f3.tar.xz
racer-tracer-bbecf67545c2bd6822b0680673aa850c5ddef9f3.zip
🔨 Refactors & Use rayon
- All data shared between threads are now Arcs since the data is immutable. - Remove tokio - Rustified main
Diffstat (limited to 'racer-tracer/src/main.rs')
-rw-r--r--racer-tracer/src/main.rs177
1 files changed, 102 insertions, 75 deletions
diff --git a/racer-tracer/src/main.rs b/racer-tracer/src/main.rs
index 9d45762..c9ed169 100644
--- a/racer-tracer/src/main.rs
+++ b/racer-tracer/src/main.rs
@@ -8,13 +8,18 @@ mod scene;
mod util;
mod vec3;
-use std::vec::Vec;
+use std::{
+ borrow::Borrow,
+ sync::{Arc, Mutex, RwLock},
+ vec::Vec,
+};
-use futures::{select, stream::FuturesUnordered, stream::StreamExt};
use geometry::Hittable;
use minifb::{Key, Window, WindowOptions};
+use rayon::prelude::*;
use crate::camera::Camera;
+use crate::error::TracerError;
use crate::geometry::sphere::Sphere;
use crate::image::Image;
use crate::ray::Ray;
@@ -22,41 +27,66 @@ use crate::scene::Scene;
use crate::util::random_double;
use crate::vec3::Vec3;
-fn ray_color(scene: &Scene, ray: &Ray) -> Vec3 {
+fn ray_color(scene: &dyn Hittable, ray: &Ray) -> Vec3 {
if let Some(hit_record) = scene.hit(ray, 0.0, std::f64::INFINITY) {
//return hit_record.color;
return 0.5 * (hit_record.normal + Vec3::new(1.0, 1.0, 1.0));
}
+ // TODO: make sky part of scene.
// Sky
let unit_direction = ray.direction().unit_vector();
let t = 0.5 * (unit_direction.y() + 1.0);
(1.0 - t) * Vec3::new(1.0, 1.0, 1.0) + t * Vec3::new(0.5, 0.7, 1.0)
}
-// TODO: Rustify
-async fn raytrace(
- scene: Scene,
- camera: Camera,
- image: Image,
- row: usize,
-) -> Result<(usize, Vec<u32>), error::TracerError> {
- let mut buffer: Vec<u32> = vec![0; image.width as usize];
+fn raytrace(scene: &dyn Hittable, camera: &Camera, image: &Image, row: usize) -> Vec<u32> {
let mut colors: Vec<Vec3> = vec![Vec3::default(); image.width as usize];
-
- for i in 0..buffer.len() {
+ colors.iter_mut().enumerate().for_each(|(i, col)| {
for _ in 0..image.samples_per_pixel {
let u: f64 = (i as f64 + random_double()) / (image.width - 1) as f64;
let v: f64 = (row as f64 + random_double()) / (image.height - 1) as f64;
- colors[i] += ray_color(&scene, &camera.get_ray(u, v));
+ *col += ray_color(scene, &camera.get_ray(u, v));
}
- }
+ });
+ // TODO: Could do rolling average
+ let mut buffer: Vec<u32> = vec![0; image.width as usize];
for i in 0..image.width {
buffer[i] = (colors[i] / image.samples_per_pixel as f64).as_color();
}
- Ok((row, buffer))
+ buffer
+}
+
+type Data = (Arc<RwLock<Vec<u32>>>, Arc<Camera>, Arc<Image>, usize);
+
+fn render(
+ buffer: Arc<RwLock<Vec<u32>>>,
+ camera: Arc<Camera>,
+ image: Arc<Image>,
+ scene: Box<dyn Hittable + std::marker::Sync>,
+) {
+ let scene: &(dyn Hittable + Sync) = scene.borrow();
+
+ let v: Vec<Data> = (0..image.height)
+ .map(|row| {
+ (
+ Arc::clone(&buffer),
+ Arc::clone(&camera),
+ Arc::clone(&image),
+ row,
+ )
+ })
+ .collect();
+
+ v.par_iter().for_each(|(buf, camera, image, row)| {
+ let start = row * image.width;
+ let end = start + image.width;
+ let col_buf = raytrace(scene.borrow(), camera, image, *row);
+ let mut buf = buf.write().expect("Failed to get screen buffer lock."); // TODO: No except
+ buf[start..end].copy_from_slice(col_buf.as_slice());
+ });
}
fn create_scene() -> Scene {
@@ -72,70 +102,67 @@ fn create_scene() -> Scene {
scene
}
-async fn run(
- rows_per_update: u32,
- aspect_ratio: f64,
- screen_width: usize,
- samples: usize,
-) -> Result<(), error::TracerError> {
- let image = image::Image::new(aspect_ratio, screen_width, samples);
- let camera = camera::Camera::new(&image, 2.0, 1.0);
- let scene = create_scene();
-
- let mut screen_buffer: Vec<u32> = vec![0; image.width * image.height];
- let mut window = Window::new(
- "racer-tracer",
- image.width,
- image.height,
- WindowOptions::default(),
- )
- .expect("Unable to create window");
- window.limit_update_rate(Some(std::time::Duration::from_micros(16600)));
-
- let mut futs = FuturesUnordered::new();
- // One future per row is a bit high.
- // Could do something less spammy.
- for h in 0..image.height {
- // TODO: Either clone all or lock em all.
- futs.push(raytrace(scene.clone(), camera.clone(), image.clone(), h));
- }
-
- // TODO: use rayon
- // Since it's cooperative multitasking this is not really helpful at the moment.
- // You will get pretty much get the same result without the tokio asyncness.
- // using rayon with threads is a different matter.
- let mut complete = false;
- while window.is_open() && !window.is_key_down(Key::Escape) {
- if !complete {
- for _ in 0..rows_per_update {
- select! {
- res = futs.select_next_some() => {
- let row_buffer = res.expect("Expected to get data");
- let start = row_buffer.0 * image.width;
- let end = start + image.width;
- screen_buffer[start..end].copy_from_slice(row_buffer.1.as_slice());
- },
- complete => {
- if !complete {
- println!("Completed!");
- }
- complete = true;
- },
+fn run(aspect_ratio: f64, screen_width: usize, samples: usize) -> Result<(), TracerError> {
+ let image = Arc::new(image::Image::new(aspect_ratio, screen_width, samples));
+ let camera = Arc::new(camera::Camera::new(&image, 2.0, 1.0));
+ let scene: Box<dyn Hittable + Sync + Send> = Box::new(create_scene());
+ let screen_buffer: Arc<RwLock<Vec<u32>>> =
+ Arc::new(RwLock::new(vec![0; image.width * image.height]));
+
+ let window_res: Arc<Mutex<Result<(), TracerError>>> = Arc::new(Mutex::new(Ok(())));
+
+ rayon::scope(|s| {
+ s.spawn(|_| {
+ render(
+ Arc::clone(&screen_buffer),
+ camera,
+ Arc::clone(&image),
+ scene,
+ )
+ });
+ s.spawn(|_| {
+ let result = Window::new(
+ "racer-tracer",
+ image.width,
+ image.height,
+ WindowOptions::default(),
+ )
+ .map_err(|e| TracerError::FailedToCreateWindow(e.to_string()))
+ .map(|mut window| {
+ window.limit_update_rate(Some(std::time::Duration::from_micros(16600)));
+ window
+ })
+ .and_then(|mut window| {
+ // TODO: Only re-render window then buffer is changed
+ while window.is_open() && !window.is_key_down(Key::Escape) {
+ // Sleep a bit to not hog the lock on the buffer all the time.
+ std::thread::sleep(std::time::Duration::from_secs(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()))
+ })?
}
- }
- }
+ Ok(())
+ });
- window
- .update_with_buffer(&screen_buffer, image.width, image.height)
- .map_err(|e| error::TracerError::FailedToUpdateWindow(e.to_string()))?;
- }
+ if result.is_err() {
+ let mut a = window_res.lock().expect("Failed to get result lock.");
+ *a = result;
+ }
+ });
+ });
- Ok(())
+ let res = (window_res.lock().expect("Failed to get result lock.")).clone();
+ res
}
-#[tokio::main]
-async fn main() {
- if let Err(e) = run(100, 16.0 / 9.0, 1200, 100).await {
+fn main() {
+ if let Err(e) = run(16.0 / 9.0, 1200, 10) {
eprintln!("{}", e);
std::process::exit(e.into())
}