From 497ea94fbfcf1df8c8e6eafea6b1f9d0c5f8e522 Mon Sep 17 00:00:00 2001 From: Robert Garrett Date: Sat, 24 Jun 2023 16:24:28 -0500 Subject: [PATCH] feat: Thread job dispatching! There are now two threads: The main thread (obv) and a single worker thread to render lines. There's a render context struct to keep track of the many, many arguments to the `render_line()` function. Jobs are submitted through a channel to the thread, and results are returned through another. Next up is to make a bunch of threads and collect across them. --- src/camera.rs | 1 + src/hittable.rs | 1 + src/main.rs | 83 ++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 70 insertions(+), 15 deletions(-) diff --git a/src/camera.rs b/src/camera.rs index 8fcadba..58482bf 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -16,6 +16,7 @@ use crate::degrees_to_radians; use rand::rngs::SmallRng; +#[derive (Clone, Copy)] pub struct Camera { origin: Vec3, lower_left_corner: Vec3, diff --git a/src/hittable.rs b/src/hittable.rs index 9ef5ea7..990e21f 100644 --- a/src/hittable.rs +++ b/src/hittable.rs @@ -18,6 +18,7 @@ impl HitRecord{ } } +#[derive (Clone)] pub enum Hittable { Sphere { center: Vec3, radius: f32, material: Option }, HittableList { hittables: Vec } diff --git a/src/main.rs b/src/main.rs index fe59706..6a8f757 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,13 +3,11 @@ mod vec3; mod ray; mod camera; -mod material; -mod hittable; +mod material; mod hittable; use crate::vec3::Vec3; use crate::ray::Ray; use crate::hittable::Hittable; -use crate::hittable::Hittable::{HittableList, Sphere}; use crate::material::Material; use crate::camera::Camera; @@ -17,6 +15,9 @@ use rand::{Rng, SeedableRng}; use rand::rngs::SmallRng; use rand::distributions::Uniform; +use std::thread; +use std::sync::mpsc; + fn main() { // image let aspect_ratio = 3.0 / 2.0; @@ -30,8 +31,6 @@ fn main() { // random generator let mut small_rng = SmallRng::seed_from_u64(0); - - // world let world = random_scene(&mut small_rng); @@ -51,30 +50,84 @@ fn main() { aperture, dist_to_focus ); + + // thread messaging channels + // Render output pipe endpoints + let (render_tx, render_rx) = mpsc::sync_channel::<(i32, Vec)>(1); // TODO: Figure out good names for the ends of the output pipe + let (job_tx, job_rx) = mpsc::channel::(); + + // Threads exist for the whole duration of the (main function) program. + let thread_handle = thread::spawn(move || { + let mut srng = small_rng.clone(); + while let Ok(job) = job_rx.recv() { + match job { + RenderCommand::Stop => { + break; + } + RenderCommand::Line { line_num, context } => { + let line = render_line(line_num, &mut srng, context); + let result = (line_num, line); + render_tx.send(result).unwrap(); + } + } + } + }); // render + // The render loop should now be a job submission mechanism + // Iterate lines, submitting them as tasks to the thread. println!("P3\n{} {}\n255", image.0, image.1); + let context = RenderContext { + camera: cam, + image, + max_depth, + samples_per_pixel, + world, + }; for y in (0..image.1).rev() { - eprintln!("Scanlines remaining: {}", y); - let line = render_line(y, image, samples_per_pixel, &world, max_depth, &mut small_rng, &cam); - for color in line { + eprintln!("Submitting scanline: {}", y); + let job = RenderCommand::Line { line_num: y, context: context.clone() }; + job_tx.send(job).unwrap(); + } + job_tx.send(RenderCommand::Stop).unwrap(); + + while let Ok(line) = render_rx.recv() { + //TODO: sort results once multiple threads are introduced. + let (linenum, colors) = line; + eprintln!("Received scanline: {}", linenum); + for color in colors { println!("{}", color.print_ppm(samples_per_pixel)); } } + thread_handle.join().unwrap(); eprintln!("Done!"); } -fn render_line(y: i32, image: (i32, i32), samples_per_pixel: u32, world: &Hittable, max_depth: u32, small_rng: &mut SmallRng, cam: &camera::Camera ) -> Vec { +#[derive (Clone)] +struct RenderContext{ + image: (i32, i32), + samples_per_pixel: u32, + max_depth: u32, + world: Hittable, + camera: Camera, +} + +enum RenderCommand{ + Stop, + Line { line_num: i32, context: RenderContext }, +} + +fn render_line(y: i32, small_rng: &mut SmallRng, context: RenderContext ) -> Vec { let distrib_zero_one = Uniform::new(0.0, 1.0); let distrib_plusminus_one = Uniform::new(-1.0, 1.0); let mut line = Vec::::new(); - for x in 0..image.0 { + for x in 0..context.image.0 { let mut color = Vec3::zero(); - for _ in 0..samples_per_pixel { - let u = ((x as f32) + small_rng.sample(distrib_zero_one)) / ((image.0 - 1) as f32); - let v = ((y as f32) + small_rng.sample(distrib_zero_one)) / ((image.1 - 1) as f32); - let ray = cam.get_ray(u, v, small_rng); - color+= ray_color(ray, world, max_depth, small_rng, distrib_plusminus_one); + for _ in 0..context.samples_per_pixel { + let u = ((x as f32) + small_rng.sample(distrib_zero_one)) / ((context.image.0 - 1) as f32); + let v = ((y as f32) + small_rng.sample(distrib_zero_one)) / ((context.image.1 - 1) as f32); + let ray = context.camera.get_ray(u, v, small_rng); + color+= ray_color(ray, &context.world, context.max_depth, small_rng, distrib_plusminus_one); } line.push(color); }