1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
use rayon::prelude::*;
use crate::{
camera::Camera,
error::TracerError,
image::SubImage,
renderer::{do_cancel, ray_color, Renderer},
util::random_double,
vec3::Vec3,
};
use super::RenderData;
pub struct CpuRenderer {}
impl CpuRenderer {
pub fn raytrace(
&self,
rd: &RenderData,
camera: &Camera,
image: &SubImage,
) -> Result<(), TracerError> {
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..rd.config.render.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(
rd.scene,
&camera.get_ray(u, v),
rd.config.render.max_depth,
));
}
}
if do_cancel(rd.cancel_event) {
return Ok(());
}
}
(!do_cancel(rd.cancel_event))
.then_some(|| ())
.ok_or(TracerError::CancelEvent)
.and_then(|_| {
rd.buffer
.write()
.map_err(|e| TracerError::FailedToAcquireLock(e.to_string()))
})
.map(|mut buf| {
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(rd.config.render.samples)
.as_color();
buf[offset + row * image.screen_width + col] = color;
}
}
})
}
pub fn prepare_threads(rd: &RenderData) -> Result<(Camera, Vec<SubImage>), TracerError> {
let width_step = rd.image.width / rd.config.render.num_threads_width;
let height_step = rd.image.height / rd.config.render.num_threads_height;
(!do_cancel(rd.cancel_event))
.then_some(|| ())
.ok_or(TracerError::CancelEvent)
.and_then(|_| {
rd.camera
.read()
.map_err(|e| TracerError::FailedToAcquireLock(e.to_string()))
// We make a clone of it as we don't want the
// camera to move as we render the scene. It also
// ensures we keep the lock to a minimum.
.map(|cam| cam.clone())
})
.map(|cam| {
let images = (0..rd.config.render.num_threads_width)
.flat_map(|ws| {
(0..rd.config.render.num_threads_height)
.map(|hs| SubImage {
x: width_step * ws,
y: height_step * hs,
screen_width: rd.image.width,
screen_height: rd.image.height,
// Neccesary in case the threads width is not
// evenly divisible by the image width.
width: if ws == rd.config.render.num_threads_width - 1 {
rd.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 == rd.config.render.num_threads_height - 1 {
rd.image.height - height_step * hs
} else {
height_step
},
})
.collect::<Vec<SubImage>>()
})
.collect::<Vec<SubImage>>();
(cam, images)
})
}
}
impl Renderer for CpuRenderer {
fn render(&self, rd: RenderData) -> Result<(), TracerError> {
CpuRenderer::prepare_threads(&rd).and_then(|(cam, images)| {
images
.into_par_iter()
.map(|image| self.raytrace(&rd, &cam, &image))
.collect::<Result<(), TracerError>>()
})
}
}
|