Perform per-pixel parallelism instead of per frame.
This sets up the renderer to have other sampling algorithms. Progress bar is now percentage based.
This commit is contained in:
parent
bc8b79a3da
commit
bf9e226899
@ -177,21 +177,38 @@ fn trace_pixel(x: usize, y: usize, scene: &Scene) -> Vec3 {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PixelRequest {
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PixelResponse {
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
pixel: Vec3,
|
||||||
|
}
|
||||||
|
|
||||||
fn render_worker(
|
fn render_worker(
|
||||||
tid: usize,
|
tid: usize,
|
||||||
scene: &Scene,
|
scene: &Scene,
|
||||||
input_chan: channel::Receiver<usize>,
|
input_chan: channel::Receiver<PixelRequest>,
|
||||||
output_chan: &channel::Sender<(usize, Vec<Vec3>)>,
|
output_chan: &channel::Sender<PixelResponse>,
|
||||||
) {
|
) {
|
||||||
for subsample in input_chan {
|
for req in input_chan {
|
||||||
let mut pixel_data: Vec<Vec3> = Vec::with_capacity(scene.width * scene.height);
|
let mut pixel: Vec3 = Default::default();
|
||||||
for y in 0..scene.height {
|
for _ in 0..scene.subsamples {
|
||||||
for x in 0..scene.width {
|
pixel = pixel + trace_pixel(req.x, req.y, scene);
|
||||||
let p = trace_pixel(x, y, scene);
|
|
||||||
pixel_data.push(p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
output_chan.send((subsample, pixel_data));
|
pixel = pixel / scene.subsamples as f32;
|
||||||
|
// Gamma correct, use gamma 2 correction, which is 1/gamma where gamma=2 which is 1/2 or
|
||||||
|
// sqrt.
|
||||||
|
let pixel = Vec3::new(pixel[0].sqrt(), pixel[1].sqrt(), pixel[2].sqrt());
|
||||||
|
|
||||||
|
output_chan.send(PixelResponse {
|
||||||
|
x: req.x,
|
||||||
|
y: req.y,
|
||||||
|
pixel,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
trace!(target: "renderer", "Shutting down worker {}", tid);
|
trace!(target: "renderer", "Shutting down worker {}", tid);
|
||||||
}
|
}
|
||||||
@ -201,65 +218,52 @@ pub fn render(
|
|||||||
output_dir: &Path,
|
output_dir: &Path,
|
||||||
store_intermediate: bool,
|
store_intermediate: bool,
|
||||||
) -> std::result::Result<(), std::io::Error> {
|
) -> std::result::Result<(), std::io::Error> {
|
||||||
let (seq_tx, seq_rx) = channel::unbounded();
|
let (pixel_req_tx, pixel_req_rx) = channel::unbounded();
|
||||||
let (pixel_data_tx, pixel_data_rx) = channel::unbounded();
|
let (pixel_resp_tx, pixel_resp_rx) = channel::unbounded();
|
||||||
|
|
||||||
let scene = sync::Arc::new(scene);
|
let scene = sync::Arc::new(scene);
|
||||||
for i in 0..num_cpus::get() {
|
for i in 0..num_cpus::get() {
|
||||||
let s = sync::Arc::clone(&scene);
|
let s = sync::Arc::clone(&scene);
|
||||||
let seq_rx = seq_rx.clone();
|
let pixel_req_rx = pixel_req_rx.clone();
|
||||||
let pixel_data_tx = pixel_data_tx.clone();
|
let pixel_resp_tx = pixel_resp_tx.clone();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
render_worker(i, &s, seq_rx, &pixel_data_tx);
|
render_worker(i, &s, pixel_req_rx, &pixel_resp_tx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
drop(seq_rx);
|
drop(pixel_req_rx);
|
||||||
drop(pixel_data_tx);
|
drop(pixel_resp_tx);
|
||||||
|
|
||||||
(1..=scene.subsamples).for_each(|idx| seq_tx.send(idx));
|
for y in 0..scene.height {
|
||||||
drop(seq_tx);
|
for x in 0..scene.width {
|
||||||
|
pixel_req_tx.send(PixelRequest { x, y });
|
||||||
let mut acc_count = 0;
|
}
|
||||||
let mut acc: Vec<Vec3> = Vec::with_capacity(scene.width * scene.height);
|
|
||||||
for _ in 0..(scene.width * scene.height) {
|
|
||||||
acc.push(Default::default());
|
|
||||||
}
|
}
|
||||||
|
drop(pixel_req_tx);
|
||||||
|
|
||||||
println!("Rendering with {} subsamples", scene.subsamples);
|
println!("Rendering with {} subsamples", scene.subsamples);
|
||||||
let mut img = RgbImage::new(scene.width as u32, scene.height as u32);
|
let mut img = RgbImage::new(scene.width as u32, scene.height as u32);
|
||||||
for (_subsample, pixel_data) in pixel_data_rx {
|
let total = scene.width * scene.height;
|
||||||
acc_count += 1;
|
let mut last_progress = 1000;
|
||||||
pixel_data.iter().enumerate().for_each(|(idx, p)| {
|
for (i, resp) in pixel_resp_rx.enumerate() {
|
||||||
let x = idx % scene.width;
|
let y_inv = scene.height - resp.y - 1;
|
||||||
let y = idx / scene.width;
|
img.put_pixel(
|
||||||
let y_inv = scene.height - y - 1;
|
resp.x as u32,
|
||||||
acc[idx] = acc[idx] + *p;
|
y_inv as u32,
|
||||||
|
image::Rgb([
|
||||||
// Gamma correct, use gamma 2 correction, which is 1/gamma where gamma=2 which is 1/2 or
|
(resp.pixel[0] * 255.).min(255.) as u8,
|
||||||
// sqrt.
|
(resp.pixel[1] * 255.).min(255.) as u8,
|
||||||
let col = acc[idx] / acc_count as f32;
|
(resp.pixel[2] * 255.).min(255.) as u8,
|
||||||
let col = Vec3::new(col[0].sqrt(), col[1].sqrt(), col[2].sqrt());
|
]),
|
||||||
img.put_pixel(
|
);
|
||||||
x as u32,
|
let progress = 100 * i / total;
|
||||||
y_inv as u32,
|
if progress != last_progress {
|
||||||
image::Rgb([
|
last_progress = progress;
|
||||||
(col[0] * 255.).min(255.) as u8,
|
if progress % 10 == 0 {
|
||||||
(col[1] * 255.).min(255.) as u8,
|
print!("{}%", progress);
|
||||||
(col[2] * 255.).min(255.) as u8,
|
} else {
|
||||||
]),
|
print!(".");
|
||||||
);
|
}
|
||||||
});
|
io::stdout().flush().unwrap();
|
||||||
if acc_count % 10 == 0 {
|
|
||||||
print!("{}", acc_count);
|
|
||||||
} else {
|
|
||||||
print!(".");
|
|
||||||
}
|
|
||||||
io::stdout().flush().unwrap();
|
|
||||||
if store_intermediate {
|
|
||||||
let path = output_dir.join(format!("iteration{:05}.png", acc_count));
|
|
||||||
trace!(target: "renderer", "Saving {}", path.display());
|
|
||||||
img.save(&path)
|
|
||||||
.unwrap_or_else(|_| panic!("Failed save {}", path.display()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user