From 4b8b556ea378da95337cbda98e02dbdc22ea8e74 Mon Sep 17 00:00:00 2001 From: Max Nuding Date: Sat, 2 Jul 2022 18:17:03 +0200 Subject: [PATCH] Implement multithreading --- Cargo.lock | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/main.rs | 62 ++++++++++++++++---------- src/ray.rs | 4 +- 4 files changed, 164 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 151d364..e02f247 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,69 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "crossbeam-channel" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "once_cell", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "either" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" + [[package]] name = "getrandom" version = "0.2.7" @@ -19,12 +76,46 @@ dependencies = [ "wasi", ] +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "libc" version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" + [[package]] name = "ppv-lite86" version = "0.2.16" @@ -61,13 +152,44 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rayon" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + [[package]] name = "rustracer" version = "0.1.0" dependencies = [ "rand", + "rayon", ] +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 7f9d3d0..d5af266 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" [dependencies] rand = "0.8.5" +rayon = "1.5.3" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index d396cc8..371e6f2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,13 @@ +use std::sync::Mutex; +use std::time::Instant; + use crate::camera::Camera; use crate::hittable::{Hittable, Sphere}; use crate::output::{Output, P3}; use crate::ray::Ray; use crate::vec3::{Color, Point3, Vec3}; use rand::distributions::{Distribution, Uniform}; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; use crate::material::{Dielectric, Lambertian, Material, Metal}; mod vec3; @@ -13,8 +17,8 @@ mod material; mod camera; mod output; -fn random_scene() -> Vec> { - let mut world:Vec> = Vec::new(); +fn random_scene() -> Vec> { + let mut world:Vec> = Vec::new(); let material_ground = Material::Lambertian(Lambertian::new(Color::new(0.5, 0.5, 0.5))); let ground = Sphere { @@ -82,8 +86,9 @@ fn main() { const ASPECT_RATIO: f64 = 3.0 / 2.0; const IMAGE_WIDTH: usize = 1200; const IMAGE_HEIGHT: usize = (IMAGE_WIDTH as f64 / ASPECT_RATIO) as usize; - const SAMPLES_PER_PIXEL: i32 = 100; + const SAMPLES_PER_PIXEL: i32 = 500; const MAX_DEPTH: i32 = 50; + let hh = IMAGE_HEIGHT; let look_from = Point3::new(13.0, 2.0, 3.0); let look_at = Point3::new(0.0, 0.0, 0.0); @@ -101,27 +106,38 @@ fn main() { // World let world= random_scene(); - //println!("P3\n{} {}\n255", IMAGE_WIDTH, IMAGE_HEIGHT); - let between = Uniform::from(0.0..1.0); - let mut pixels = Vec::::new(); - let mut rng = rand::thread_rng(); - for j in (0..IMAGE_HEIGHT).rev() { - eprint!("\rScanlines remaining: {} ", j); - for i in 0..IMAGE_WIDTH { - let mut color = Color::default(); - (0..SAMPLES_PER_PIXEL).for_each(|_s| { - let random_number = between.sample(&mut rng); - let u = (i as f64 + random_number) / (IMAGE_WIDTH - 1) as f64; - let v = (j as f64 + random_number) / (IMAGE_HEIGHT - 1) as f64; - let ray = cam.get_ray(u, v); - color += ray.pixel_color(&world, MAX_DEPTH); - }); - pixels.append(&mut color.into_bytes(SAMPLES_PER_PIXEL)); - //color.write_color(SAMPLES_PER_PIXEL); + let start = Instant::now(); + let mut pixels = vec![0; IMAGE_WIDTH * IMAGE_HEIGHT * 3]; + let bands: Vec<(usize, &mut [u8])> = pixels.chunks_mut(3).enumerate().collect(); + let count = Mutex::new(0); + bands.into_par_iter().for_each(|(i, pixel)| { + let row = IMAGE_HEIGHT - (i / IMAGE_WIDTH) - 1; + let col = i % IMAGE_WIDTH; + let mut rng = rand::thread_rng(); + let mut color = Color::default(); + (0..SAMPLES_PER_PIXEL).for_each(|_s| { + let random_number = between.sample(&mut rng); + let u = (col as f64 + random_number) / (IMAGE_WIDTH - 1) as f64; + let v = (row as f64 + random_number) / (IMAGE_HEIGHT - 1) as f64; + let ray = cam.get_ray(u, v); + color += ray.pixel_color(&world, MAX_DEPTH); + }); + let bytes = color.into_bytes(SAMPLES_PER_PIXEL); + pixel[0] = bytes[0]; + pixel[1] = bytes[1]; + pixel[2] = bytes[2]; + if i % 100 == 0 { + let mut rem = count.lock().unwrap(); + let percent_done_before = 100 * *rem / (IMAGE_WIDTH * IMAGE_HEIGHT); + *rem += 100; + let percent_done_after = 100 * *rem / (IMAGE_WIDTH * IMAGE_HEIGHT); + if percent_done_before != percent_done_after { + eprint!("\rProgress: {}% ", percent_done_after); + } } - } - P3::write("i.ppm", &pixels, IMAGE_WIDTH, IMAGE_HEIGHT).expect("Error writing image: {}"); - eprintln!("\nDone"); + }); + P3::write("imc.ppm", &pixels, IMAGE_WIDTH, IMAGE_HEIGHT).expect("Error writing image: {}"); + eprintln!("\nDone. Time: {}ms", start.elapsed().as_millis()); } diff --git a/src/ray.rs b/src/ray.rs index ecf6684..497e772 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -23,7 +23,7 @@ impl Ray { } pub fn direction(&self) -> Vec3 { self.direction } pub fn origin(&self) -> Point3 { self.origin } - pub fn pixel_color(&self, world: &Vec>, depth: i32) -> Color { + pub fn pixel_color(&self, world: &Vec>, depth: i32) -> Color { if depth <= 0 { return Color::default(); } @@ -50,7 +50,7 @@ impl Ray { } fn hit_world<'material>( &self, - world: &'material Vec>, + world: &'material Vec>, t_min: f64, t_max: f64, ) -> Option> {