multithreading && code cleanup

This commit is contained in:
2026-02-13 05:06:02 +01:00
parent 7ec01f634a
commit e84404a6de
9 changed files with 109 additions and 81 deletions

View File

@@ -6,4 +6,5 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
rand = "*" rand = "0.7.0"
rayon = "1.8"

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,6 @@
use std::io::Write;
use crate::{vec3::*, rtweekend::clamp}; use crate::{vec3::*, rtweekend::clamp};
fn get_scaled_color_components(c: Color, samples_per_pixel: i32) -> (u8, u8, u8) { pub fn get_scaled_color_components(c: Color, samples_per_pixel: i32) -> (u8, u8, u8) {
let r = c.x(); let r = c.x();
let g = c.y(); let g = c.y();
let b = c.z(); let b = c.z();
@@ -18,16 +16,4 @@ fn get_scaled_color_components(c: Color, samples_per_pixel: i32) -> (u8, u8, u8)
(255.0 * clamp(g, 0.0, 1.0)) as u8, (255.0 * clamp(g, 0.0, 1.0)) as u8,
(255.0 * clamp(b, 0.0, 1.0)) as u8, (255.0 * clamp(b, 0.0, 1.0)) as u8,
) )
}
pub fn write_color<T: Write>(writer: &mut T, pixel_color: Color, samples_per_pixel: i32) {
let (r, g, b) = get_scaled_color_components(pixel_color, samples_per_pixel);
write!(writer, "{r} {g} {b} ").expect("Can write pixel data");
}
pub fn write_color_bin<T: Write>(writer: &mut T, pixel_color: Color, samples_per_pixel: i32) {
let (r, g, b) = get_scaled_color_components(pixel_color, samples_per_pixel);
let binary_data = [r, g, b];
writer.write(&binary_data).expect("Can write binary pixel data!");
} }

View File

@@ -26,6 +26,6 @@ impl HitRecord<'_> {
} }
} }
pub trait Hittable { pub trait Hittable: Send + Sync {
fn hit(&mut self, r: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord>; fn hit(&self, r: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord<'_>>;
} }

View File

@@ -19,11 +19,11 @@ impl HittableList {
} }
impl Hittable for HittableList { impl Hittable for HittableList {
fn hit(&mut self, r: &crate::ray::Ray, t_min: f64, t_max: f64) -> Option<HitRecord> { fn hit(&self, r: &crate::ray::Ray, t_min: f64, t_max: f64) -> Option<HitRecord<'_>> {
let mut temp_rec = None; let mut temp_rec = None;
let mut closest_so_far = t_max; let mut closest_so_far = t_max;
for object in &mut self.objects { for object in &self.objects {
if let Some(rec) = object.hit(r, t_min, closest_so_far) { if let Some(rec) = object.hit(r, t_min, closest_so_far) {
closest_so_far = rec.t; closest_so_far = rec.t;
temp_rec = Some(rec); temp_rec = Some(rec);

View File

@@ -1,37 +1,51 @@
pub mod vec3; pub mod camera;
pub mod color; pub mod color;
pub mod ray;
pub mod sphere;
pub mod hittable; pub mod hittable;
pub mod hittable_list; pub mod hittable_list;
pub mod rtweekend;
pub mod camera;
pub mod material; pub mod material;
pub mod ray;
pub mod rtweekend;
pub mod sphere;
pub mod vec3;
use std::{io::{BufWriter, Write, stdout}, fs::File, time::Instant}; use rayon::prelude::*;
use std::{
io::{stdout, Write},
sync::atomic::{AtomicUsize, Ordering},
};
use crate::{ use crate::{
material::*, camera::*,
color::*, color::*,
sphere::*,
rtweekend::*,
hittable_list::*,
vec3::*,
ray::*,
hittable::*, hittable::*,
camera::*, material::{Lambertian, Dielectric}, hittable_list::*,
material::*,
material::{Dielectric, Lambertian},
ray::*,
rtweekend::*,
sphere::*,
vec3::*,
}; };
fn random_scene() -> HittableList { fn random_scene() -> HittableList {
let mut world = HittableList::new_empty(); let mut world = HittableList::new_empty();
static GROUND_MATERIAL: Lambertian = Lambertian::new(Color::new(0.5, 0.5, 0.5)); static GROUND_MATERIAL: Lambertian = Lambertian::new(Color::new(0.5, 0.5, 0.5));
world.add(Box::new(Sphere::new(Point3::new(0.0, -1000.0, 0.0), 1000.0, &GROUND_MATERIAL))); world.add(Box::new(Sphere::new(
Point3::new(0.0, -1000.0, 0.0),
1000.0,
&GROUND_MATERIAL,
)));
for a in 0..12 { for a in 0..12 {
for b in 0..12 { for b in 0..12 {
let choose_mat = random_f64(); let choose_mat = random_f64();
let center = Point3::new(a as f64 + 0.9*random_f64(), 0.2, b as f64 + 0.9*random_f64()); let center = Point3::new(
a as f64 + 0.9 * random_f64(),
0.2,
b as f64 + 0.9 * random_f64(),
);
if (center - Point3::new(4.0, 0.2, 0.0)).length() > 0.9 { if (center - Point3::new(4.0, 0.2, 0.0)).length() > 0.9 {
if choose_mat < 0.8 { if choose_mat < 0.8 {
@@ -50,18 +64,30 @@ fn random_scene() -> HittableList {
} }
static MATERIAL1: Dielectric = Dielectric::new(1.5); static MATERIAL1: Dielectric = Dielectric::new(1.5);
world.add(Box::new(Sphere::new(Point3::new(0.0, 1.0, 0.0), 1.0, &MATERIAL1))); world.add(Box::new(Sphere::new(
Point3::new(0.0, 1.0, 0.0),
1.0,
&MATERIAL1,
)));
static MATERIAL2: Lambertian = Lambertian::new(Color::new(0.4, 0.2, 0.1)); static MATERIAL2: Lambertian = Lambertian::new(Color::new(0.4, 0.2, 0.1));
world.add(Box::new(Sphere::new(Point3::new(-4.0, 1.0, 0.0), 1.0, &MATERIAL2))); world.add(Box::new(Sphere::new(
Point3::new(-4.0, 1.0, 0.0),
1.0,
&MATERIAL2,
)));
static MATERIAL3: Metal = Metal::new(Color::new(0.7, 0.6, 0.5), 0.0); static MATERIAL3: Metal = Metal::new(Color::new(0.7, 0.6, 0.5), 0.0);
world.add(Box::new(Sphere::new(Point3::new(4.0, 1.0, 0.0), 1.0, &MATERIAL3))); world.add(Box::new(Sphere::new(
Point3::new(4.0, 1.0, 0.0),
1.0,
&MATERIAL3,
)));
world world
} }
fn ray_color(r: Ray, world: &mut dyn Hittable, depth: i32) -> Color { fn ray_color(r: Ray, world: &dyn Hittable, depth: i32) -> Color {
// Limit the bounces // Limit the bounces
if depth <= 0 { if depth <= 0 {
return Color::new(0.0, 0.0, 0.0); return Color::new(0.0, 0.0, 0.0);
@@ -71,8 +97,11 @@ fn ray_color(r: Ray, world: &mut dyn Hittable, depth: i32) -> Color {
let mut scattered = Ray::new_empty(); let mut scattered = Ray::new_empty();
let mut attenuation = Color::new_empty(); let mut attenuation = Color::new_empty();
if rec.mat_ptr.scatter(r, rec, &mut attenuation, &mut scattered) { if rec
return attenuation * ray_color(scattered, world, depth - 1); .mat_ptr
.scatter(r, rec, &mut attenuation, &mut scattered)
{
return attenuation * ray_color(scattered, world, depth - 1);
} }
return Color::new_empty(); return Color::new_empty();
} }
@@ -86,19 +115,19 @@ fn ray_color(r: Ray, world: &mut dyn Hittable, depth: i32) -> Color {
fn main() { fn main() {
// Image // Image
let aspect_ratio: f64 = 1.0 / 1.0; let aspect_ratio: f64 = 1.0 / 1.0;
let image_width: i32 = 1000; let image_height: i32 = 1000;
let image_height: i32 = (image_width as f64 / aspect_ratio as f64) as i32; let image_width: i32 = (image_height as f64 / aspect_ratio as f64) as i32;
let samples_per_pixel: i32 = 200; let samples_per_pixel: i32 = 50;
let max_depth: i32 = 4; let max_depth: i32 = 4;
// World // World
let mut world = random_scene(); let world = random_scene();
// Camera // Camera
let lookfrom = Point3::new(-13.0, 2.0, 3.0); let lookfrom = Point3::new(-13.0, 2.0, 3.0);
let lookat = Point3::new(0.0, 0.0, 0.0); let lookat = Point3::new(0.0, 0.0, 0.0);
let vup = Vec3::new(0.0, 1.0, 0.0); let vup = Vec3::new(0.0, 1.0, 0.0);
let dist_to_focus = 10.0;//(lookfrom-lookat).length_squared(); let dist_to_focus = 10.0; //(lookfrom-lookat).length_squared();
let cam = Camera::new( let cam = Camera::new(
lookfrom, lookfrom,
@@ -107,37 +136,51 @@ fn main() {
30.0, 30.0,
aspect_ratio, aspect_ratio,
0.0, 0.0,
dist_to_focus dist_to_focus,
); );
// Render // Render
//let mut rng = rand::thread_rng(); rayon::ThreadPoolBuilder::new()
let now = Instant::now(); .num_threads(11)
let mut file = BufWriter::new(File::create("img.ppm").expect("File creation failed")); .build_global()
.unwrap();
write!(file, "P6\n{image_width} {image_height}\n255\n").expect("Error while writing Magic Byte"); let mut buffer = vec![0u8; (image_width * image_height * 3) as usize];
for j in (0..image_width).rev() { let rows_completed = AtomicUsize::new(0);
for i in 0..image_height { let total_rows = image_height as usize;
let mut pixel_color = Color::new(0.0, 0.0, 0.0);
buffer
for _ in 0..samples_per_pixel { .par_chunks_mut((image_width * 3) as usize)
let u = (i as f64 + random_f64()) / (image_width - 1) as f64; .enumerate()
let v = (j as f64 + random_f64()) / (image_height - 1) as f64; .for_each(|(index, row_slice)| {
let r: Ray = cam.get_ray(u, v); let j = image_height - 1 - index as i32;
pixel_color += ray_color(r, &mut world, max_depth)
for i in 0..image_width {
let mut pixel_color = Color::new(0.0, 0.0, 0.0);
for _ in 0..samples_per_pixel {
let u = (i as f64 + random_f64()) / (image_width - 1) as f64;
let v = (j as f64 + random_f64()) / (image_height - 1) as f64;
let r = cam.get_ray(u, v);
pixel_color += ray_color(r, &world, max_depth);
}
let (ir, ig, ib) = get_scaled_color_components(pixel_color, samples_per_pixel);
row_slice[(i as usize) * 3] = ir;
row_slice[(i as usize) * 3 + 1] = ig;
row_slice[(i as usize) * 3 + 2] = ib;
} }
write_color_bin(&mut file, pixel_color, samples_per_pixel);
}
print!( // progress
"[ {:.2} % ]\r", let completed = rows_completed.fetch_add(1, Ordering::Relaxed) + 1;
(1.0 - ((j + 1) as f64 / image_width as f64)) * 100.0 if completed % 10 == 0 || completed == total_rows {
); print!(
stdout().flush().expect("Can flush stdout"); "\r[ {:.2} % ] Rendering...",
} (completed as f64 / total_rows as f64) * 100.0
println!("Took {:.6}ms", now.elapsed().as_millis()); );
println!("Took {:.6}s", now.elapsed().as_secs_f64()); let _ = stdout().flush();
println!("Took {:.6}m", now.elapsed().as_secs_f64() / 60.0); }
println!("Took {:.6}h", now.elapsed().as_secs_f64() / 3600.0); });
} }

View File

@@ -16,7 +16,7 @@ use crate::vec3::{Color, random_unit_vector, reflect, refract};
*/ */
pub trait Material { pub trait Material: Send + Sync {
fn scatter(&self, r_in: Ray, rec: HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool; fn scatter(&self, r_in: Ray, rec: HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool;
} }

View File

@@ -16,7 +16,7 @@ pub fn random_f64() -> f64 {
// PLEASE CHANGE // PLEASE CHANGE
pub fn random_range_f64(min: f64, max: f64) -> f64 { pub fn random_range_f64(min: f64, max: f64) -> f64 {
rand::thread_rng().gen_range(min..=max) rand::thread_rng().gen_range(min, max)
} }
pub fn clamp(x: f64, min: f64, max: f64) -> f64 { pub fn clamp(x: f64, min: f64, max: f64) -> f64 {

View File

@@ -15,7 +15,7 @@ impl Sphere<'_> {
} }
impl Hittable for Sphere<'_> { impl Hittable for Sphere<'_> {
fn hit(&mut self, r: &crate::ray::Ray, t_min: f64, t_max: f64) -> Option<HitRecord> { fn hit(&self, r: &crate::ray::Ray, t_min: f64, t_max: f64) -> Option<HitRecord<'_>> {
let oc = r.origin() - self.center; let oc = r.origin() - self.center;
let a = r.direction().length_squared(); let a = r.direction().length_squared();
let half_b = oc.dot(&r.direction()); let half_b = oc.dot(&r.direction());