rtiow: lockless non-global stats keeping.
This commit is contained in:
parent
27ca936264
commit
848e9879cb
@ -1,11 +1,10 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::ops::AddAssign;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::sync;
|
use std::sync;
|
||||||
use std::sync::atomic::AtomicUsize;
|
|
||||||
use std::sync::atomic::Ordering;
|
|
||||||
use std::sync::mpsc::sync_channel;
|
use std::sync::mpsc::sync_channel;
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::Receiver;
|
||||||
use std::sync::mpsc::SyncSender;
|
use std::sync::mpsc::SyncSender;
|
||||||
@ -49,7 +48,6 @@ impl MockPrometheus {
|
|||||||
fn inc(&self) {}
|
fn inc(&self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
static RAY_COUNT: AtomicUsize = AtomicUsize::new(0);
|
|
||||||
#[cfg(not(feature = "prom"))]
|
#[cfg(not(feature = "prom"))]
|
||||||
static RAY_COUNTER: MockPrometheus = MockPrometheus {};
|
static RAY_COUNTER: MockPrometheus = MockPrometheus {};
|
||||||
|
|
||||||
@ -240,40 +238,41 @@ fn color(
|
|||||||
depth: usize,
|
depth: usize,
|
||||||
global_illumination: bool,
|
global_illumination: bool,
|
||||||
env_map: &Option<EnvMap>,
|
env_map: &Option<EnvMap>,
|
||||||
) -> Vec3 {
|
) -> (Vec3, usize) {
|
||||||
RAY_COUNT.fetch_add(1, Ordering::SeqCst);
|
|
||||||
RAY_COUNTER.with_label_values(&[&depth.to_string()]).inc();
|
RAY_COUNTER.with_label_values(&[&depth.to_string()]).inc();
|
||||||
if let Some(rec) = world.hit(r, 0.001, std::f32::MAX) {
|
if let Some(rec) = world.hit(r, 0.001, std::f32::MAX) {
|
||||||
let (u, v) = rec.uv;
|
let (u, v) = rec.uv;
|
||||||
let emitted = rec.material.emitted(u, v, rec.p);
|
let emitted = rec.material.emitted(u, v, rec.p);
|
||||||
let scatter_response = rec.material.scatter(&r, &rec);
|
let scatter_response = rec.material.scatter(&r, &rec);
|
||||||
if depth < 50 && scatter_response.reflected {
|
if depth < 50 && scatter_response.reflected {
|
||||||
return emitted
|
let (c, rays) = color(
|
||||||
+ scatter_response.attenutation
|
scatter_response.scattered,
|
||||||
* color(
|
world,
|
||||||
scatter_response.scattered,
|
depth + 1,
|
||||||
world,
|
global_illumination,
|
||||||
depth + 1,
|
env_map,
|
||||||
global_illumination,
|
);
|
||||||
env_map,
|
return (emitted + scatter_response.attenutation * c, rays + 1);
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
return emitted;
|
return (emitted, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if global_illumination {
|
if global_illumination {
|
||||||
return match env_map {
|
return match env_map {
|
||||||
Some(env_map) => env_map.color(r.direction.unit_vector()),
|
Some(env_map) => (env_map.color(r.direction.unit_vector()), 1),
|
||||||
None => {
|
None => {
|
||||||
let unit_direction = r.direction.unit_vector();
|
let unit_direction = r.direction.unit_vector();
|
||||||
// No hit, choose color from background.
|
// No hit, choose color from background.
|
||||||
let t = 0.5 * (unit_direction.y + 1.);
|
let t = 0.5 * (unit_direction.y + 1.);
|
||||||
Vec3::new(1., 1., 1.) * (1. - t) + Vec3::new(0.5, 0.7, 1.) * t
|
(
|
||||||
|
Vec3::new(1., 1., 1.) * (1. - t) + Vec3::new(0.5, 0.7, 1.) * t,
|
||||||
|
1,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// No global illumination, so background is black.
|
// No global illumination, so background is black.
|
||||||
Vec3::new(0., 0., 0.)
|
(Vec3::new(0., 0., 0.), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_ADAPTIVE_DEPTH: usize = 10;
|
const MAX_ADAPTIVE_DEPTH: usize = 10;
|
||||||
@ -285,13 +284,13 @@ fn trace_pixel_adaptive(
|
|||||||
x_range: Range<f32>,
|
x_range: Range<f32>,
|
||||||
y_range: Range<f32>,
|
y_range: Range<f32>,
|
||||||
scene: &Scene,
|
scene: &Scene,
|
||||||
) -> Vec3 {
|
) -> (Vec3, usize) {
|
||||||
let w = scene.width as f32;
|
let w = scene.width as f32;
|
||||||
let h = scene.height as f32;
|
let h = scene.height as f32;
|
||||||
let x_mid = x_range.start + ((x_range.end - x_range.start) / 2.);
|
let x_mid = x_range.start + ((x_range.end - x_range.start) / 2.);
|
||||||
let y_mid = y_range.start + ((y_range.end - y_range.start) / 2.);
|
let y_mid = y_range.start + ((y_range.end - y_range.start) / 2.);
|
||||||
let mc = ((x_mid + x as f32) / w, (y_mid + y as f32) / h);
|
let mc = ((x_mid + x as f32) / w, (y_mid + y as f32) / h);
|
||||||
let center = color(
|
let (center, rays) = color(
|
||||||
scene.camera.get_ray(mc.0, mc.1),
|
scene.camera.get_ray(mc.0, mc.1),
|
||||||
scene.world.as_ref(),
|
scene.world.as_ref(),
|
||||||
0,
|
0,
|
||||||
@ -300,7 +299,7 @@ fn trace_pixel_adaptive(
|
|||||||
);
|
);
|
||||||
if depth == 0 {
|
if depth == 0 {
|
||||||
output::set_pixel(output::ADAPTIVE_DEPTH, x, y, [1., 0., 0.].into());
|
output::set_pixel(output::ADAPTIVE_DEPTH, x, y, [1., 0., 0.].into());
|
||||||
return center;
|
return (center, rays);
|
||||||
}
|
}
|
||||||
// t = top
|
// t = top
|
||||||
// m = middle
|
// m = middle
|
||||||
@ -316,7 +315,7 @@ fn trace_pixel_adaptive(
|
|||||||
let bl = ((x_range.start + x as f32) / w, (y_range.end + y as f32) / h);
|
let bl = ((x_range.start + x as f32) / w, (y_range.end + y as f32) / h);
|
||||||
let br = ((x_range.end + x as f32) / w, (y_range.end + y as f32) / h);
|
let br = ((x_range.end + x as f32) / w, (y_range.end + y as f32) / h);
|
||||||
|
|
||||||
let corners = [tl, tr, mc, bl, br]
|
let (corners, rays) = [tl, tr, mc, bl, br]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(u, v)| {
|
.map(|(u, v)| {
|
||||||
color(
|
color(
|
||||||
@ -327,10 +326,13 @@ fn trace_pixel_adaptive(
|
|||||||
&scene.env_map,
|
&scene.env_map,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.fold([0., 0., 0.].into(), |a: Vec3, b: Vec3| a + b)
|
.fold(
|
||||||
/ 5.;
|
([0., 0., 0.].into(), 0),
|
||||||
|
|(p1, r1): (Vec3, usize), (p2, r2): (Vec3, usize)| ((p1 + p2), (r1 + r2)),
|
||||||
|
);
|
||||||
|
let corners = corners / 5.;
|
||||||
if (corners - center).length() > threshold {
|
if (corners - center).length() > threshold {
|
||||||
let pixel = (trace_pixel_adaptive(
|
let tl = trace_pixel_adaptive(
|
||||||
depth - 1,
|
depth - 1,
|
||||||
threshold,
|
threshold,
|
||||||
x,
|
x,
|
||||||
@ -338,7 +340,8 @@ fn trace_pixel_adaptive(
|
|||||||
x_range.start..x_mid,
|
x_range.start..x_mid,
|
||||||
y_range.start..y_mid,
|
y_range.start..y_mid,
|
||||||
scene,
|
scene,
|
||||||
) + trace_pixel_adaptive(
|
);
|
||||||
|
let tr = trace_pixel_adaptive(
|
||||||
depth - 1,
|
depth - 1,
|
||||||
threshold,
|
threshold,
|
||||||
x,
|
x,
|
||||||
@ -346,7 +349,8 @@ fn trace_pixel_adaptive(
|
|||||||
x_mid..x_range.end,
|
x_mid..x_range.end,
|
||||||
y_range.start..y_mid,
|
y_range.start..y_mid,
|
||||||
scene,
|
scene,
|
||||||
) + trace_pixel_adaptive(
|
);
|
||||||
|
let bl = trace_pixel_adaptive(
|
||||||
depth - 1,
|
depth - 1,
|
||||||
threshold,
|
threshold,
|
||||||
x,
|
x,
|
||||||
@ -354,7 +358,8 @@ fn trace_pixel_adaptive(
|
|||||||
x_range.start..x_mid,
|
x_range.start..x_mid,
|
||||||
y_mid..y_range.end,
|
y_mid..y_range.end,
|
||||||
scene,
|
scene,
|
||||||
) + trace_pixel_adaptive(
|
);
|
||||||
|
let br = trace_pixel_adaptive(
|
||||||
depth - 1,
|
depth - 1,
|
||||||
threshold,
|
threshold,
|
||||||
x,
|
x,
|
||||||
@ -362,17 +367,19 @@ fn trace_pixel_adaptive(
|
|||||||
x_mid..x_range.end,
|
x_mid..x_range.end,
|
||||||
y_mid..y_range.end,
|
y_mid..y_range.end,
|
||||||
scene,
|
scene,
|
||||||
)) / 4.;
|
);
|
||||||
pixel
|
let pixel = (tl.0 + tr.0 + bl.0 + br.0) / 4.;
|
||||||
|
let rays = tl.1 + tr.1 + bl.1 + br.1;
|
||||||
|
(pixel, rays)
|
||||||
} else {
|
} else {
|
||||||
if depth == MAX_ADAPTIVE_DEPTH {
|
if depth == MAX_ADAPTIVE_DEPTH {
|
||||||
output::set_pixel(output::ADAPTIVE_DEPTH, x, y, [0., 1., 0.].into());
|
output::set_pixel(output::ADAPTIVE_DEPTH, x, y, [0., 1., 0.].into());
|
||||||
}
|
}
|
||||||
corners
|
(corners, rays)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trace_pixel_random(x: usize, y: usize, scene: &Scene) -> Vec3 {
|
fn trace_pixel_random(x: usize, y: usize, scene: &Scene) -> (Vec3, usize) {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let u = (rng.gen_range::<f32>(0., 1.) + x as f32) / scene.width as f32;
|
let u = (rng.gen_range::<f32>(0., 1.) + x as f32) / scene.width as f32;
|
||||||
let v = (rng.gen_range::<f32>(0., 1.) + y as f32) / scene.height as f32;
|
let v = (rng.gen_range::<f32>(0., 1.) + y as f32) / scene.height as f32;
|
||||||
@ -386,21 +393,67 @@ fn trace_pixel_random(x: usize, y: usize, scene: &Scene) -> Vec3 {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct RenderStats {
|
||||||
|
rays: usize,
|
||||||
|
pixels: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddAssign for RenderStats {
|
||||||
|
fn add_assign(&mut self, other: Self) {
|
||||||
|
*self = Self {
|
||||||
|
rays: self.rays + other.rays,
|
||||||
|
pixels: self.pixels + other.pixels,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn progress(
|
||||||
|
last_stat: &RenderStats,
|
||||||
|
current_stat: &RenderStats,
|
||||||
|
time_diff: time::Duration,
|
||||||
|
pixel_total: usize,
|
||||||
|
) -> String {
|
||||||
|
let human = human::Formatter::new();
|
||||||
|
let pixel_diff = current_stat.pixels - last_stat.pixels;
|
||||||
|
let ray_diff = current_stat.rays - last_stat.rays;
|
||||||
|
format!(
|
||||||
|
"{:7} / {:7}pixels ({:2}%) {:7}pixels/s {:7}rays/s",
|
||||||
|
human.format(current_stat.pixels as f64),
|
||||||
|
human.format(pixel_total as f64),
|
||||||
|
100 * current_stat.pixels / pixel_total,
|
||||||
|
human.format(pixel_diff as f64 / time_diff.as_secs_f64()),
|
||||||
|
human.format(ray_diff as f64 / time_diff.as_secs_f64())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RenderStats {
|
||||||
|
fn default() -> Self {
|
||||||
|
RenderStats { rays: 0, pixels: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum Request {
|
enum Request {
|
||||||
Pixel { x: usize, y: usize },
|
Pixel { x: usize, y: usize },
|
||||||
Line { width: usize, y: usize },
|
Line { width: usize, y: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Response {
|
enum Response {
|
||||||
Pixel { x: usize, y: usize, pixel: Vec3 },
|
Pixel {
|
||||||
Line { y: usize, pixels: Vec<Vec3> },
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
pixel: Vec3,
|
||||||
|
rs: RenderStats,
|
||||||
|
},
|
||||||
|
Line {
|
||||||
|
y: usize,
|
||||||
|
pixels: Vec<Vec3>,
|
||||||
|
rs: RenderStats,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
static PIXEL_COUNT: AtomicUsize = AtomicUsize::new(0);
|
fn render_pixel(scene: &Scene, x: usize, y: usize) -> (Vec3, usize) {
|
||||||
|
let (pixel, rays) = if let Some(threshold) = scene.adaptive_subsampling {
|
||||||
fn render_pixel(scene: &Scene, x: usize, y: usize) -> Vec3 {
|
|
||||||
let mut pixel: Vec3 = Default::default();
|
|
||||||
let pixel = if let Some(threshold) = scene.adaptive_subsampling {
|
|
||||||
trace_pixel_adaptive(
|
trace_pixel_adaptive(
|
||||||
MAX_ADAPTIVE_DEPTH,
|
MAX_ADAPTIVE_DEPTH,
|
||||||
threshold,
|
threshold,
|
||||||
@ -411,15 +464,20 @@ fn render_pixel(scene: &Scene, x: usize, y: usize) -> Vec3 {
|
|||||||
scene,
|
scene,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
for _ in 0..scene.subsamples {
|
let (pixel, rays) = (0..scene.subsamples)
|
||||||
pixel = pixel + trace_pixel_random(x, y, scene);
|
.map(|_| trace_pixel_random(x, y, scene))
|
||||||
}
|
.fold(
|
||||||
pixel / scene.subsamples as f32
|
([0., 0., 0.].into(), 0),
|
||||||
|
|(p1, r1): (Vec3, usize), (p2, r2): (Vec3, usize)| ((p1 + p2), (r1 + r2)),
|
||||||
|
);
|
||||||
|
(pixel / scene.subsamples as f32, rays)
|
||||||
};
|
};
|
||||||
PIXEL_COUNT.fetch_add(1, Ordering::SeqCst);
|
|
||||||
// Gamma correct, use gamma 2 correction, which is 1/gamma where gamma=2 which is 1/2 or
|
// Gamma correct, use gamma 2 correction, which is 1/gamma where gamma=2 which is 1/2 or
|
||||||
// sqrt.
|
// sqrt.
|
||||||
Vec3::new(pixel[0].sqrt(), pixel[1].sqrt(), pixel[2].sqrt())
|
(
|
||||||
|
Vec3::new(pixel[0].sqrt(), pixel[1].sqrt(), pixel[2].sqrt()),
|
||||||
|
rays,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_worker(
|
fn render_worker(
|
||||||
@ -438,16 +496,33 @@ fn render_worker(
|
|||||||
Ok(req) => match req {
|
Ok(req) => match req {
|
||||||
Request::Line { width, y } => {
|
Request::Line { width, y } => {
|
||||||
trace!("tid {} width {} y {}", tid, width, y);
|
trace!("tid {} width {} y {}", tid, width, y);
|
||||||
let pixels = (0..width).map(|x| render_pixel(scene, x, y)).collect();
|
let (pixels, rays): (Vec<Vec3>, Vec<usize>) = (0..width)
|
||||||
|
.map(|x| render_pixel(scene, x, y))
|
||||||
|
.collect::<Vec<(_, _)>>()
|
||||||
|
.into_iter()
|
||||||
|
.unzip();
|
||||||
|
let rays = rays.iter().sum();
|
||||||
output_chan
|
output_chan
|
||||||
.send(Response::Line { y, pixels })
|
.send(Response::Line {
|
||||||
|
y,
|
||||||
|
pixels,
|
||||||
|
rs: RenderStats {
|
||||||
|
rays,
|
||||||
|
pixels: width,
|
||||||
|
},
|
||||||
|
})
|
||||||
.expect("failed to send pixel response");
|
.expect("failed to send pixel response");
|
||||||
}
|
}
|
||||||
Request::Pixel { x, y } => {
|
Request::Pixel { x, y } => {
|
||||||
trace!("tid {} x {} y {}", tid, x, y);
|
trace!("tid {} x {} y {}", tid, x, y);
|
||||||
let pixel = render_pixel(scene, x, y);
|
let (pixel, rays) = render_pixel(scene, x, y);
|
||||||
output_chan
|
output_chan
|
||||||
.send(Response::Pixel { x, y, pixel })
|
.send(Response::Pixel {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
pixel,
|
||||||
|
rs: RenderStats { rays, pixels: 1 },
|
||||||
|
})
|
||||||
.expect("failed to send line response");
|
.expect("failed to send line response");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -524,61 +599,41 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
|
|||||||
}
|
}
|
||||||
|
|
||||||
let pixel_total = scene.width * scene.height;
|
let pixel_total = scene.width * scene.height;
|
||||||
handles.push(thread::spawn(move || {
|
let mut last_time = time::Instant::now();
|
||||||
let mut last_time = time::Instant::now();
|
let mut last_stat: RenderStats = Default::default();
|
||||||
let mut last_pixel_count = PIXEL_COUNT.load(Ordering::SeqCst);
|
let mut current_stat: RenderStats = Default::default();
|
||||||
let mut last_ray_count = RAY_COUNT.load(Ordering::SeqCst);
|
|
||||||
let human = human::Formatter::new();
|
|
||||||
loop {
|
|
||||||
let sleep_time = time::Duration::from_secs(1);
|
|
||||||
thread::sleep(sleep_time);
|
|
||||||
let now = time::Instant::now();
|
|
||||||
let pixel_count = PIXEL_COUNT.load(Ordering::SeqCst);
|
|
||||||
let ray_count = RAY_COUNT.load(Ordering::SeqCst);
|
|
||||||
let time_diff = now - last_time;
|
|
||||||
let pixel_diff = pixel_count - last_pixel_count;
|
|
||||||
let ray_diff = ray_count - last_ray_count;
|
|
||||||
info!(
|
|
||||||
"{} / {}pixels ({}%) {}pixels/s {}rays/s",
|
|
||||||
human.format(pixel_count as f64),
|
|
||||||
human.format(pixel_total as f64),
|
|
||||||
100 * pixel_count / pixel_total,
|
|
||||||
human.format(pixel_diff as f64 / time_diff.as_secs_f64()),
|
|
||||||
human.format(ray_diff as f64 / time_diff.as_secs_f64())
|
|
||||||
);
|
|
||||||
last_time = now;
|
|
||||||
last_pixel_count = pixel_count;
|
|
||||||
last_ray_count = ray_count;
|
|
||||||
if pixel_count == pixel_total {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
for resp in pixel_resp_rx {
|
for resp in pixel_resp_rx {
|
||||||
match resp {
|
match resp {
|
||||||
Response::Pixel { x, y, pixel } => {
|
Response::Pixel { x, y, pixel, rs } => {
|
||||||
|
current_stat += rs;
|
||||||
output::set_pixel(output::MAIN_IMAGE, x, y, pixel);
|
output::set_pixel(output::MAIN_IMAGE, x, y, pixel);
|
||||||
}
|
}
|
||||||
Response::Line { y, pixels } => {
|
Response::Line { y, pixels, rs } => {
|
||||||
|
current_stat += rs;
|
||||||
for (x, pixel) in pixels.iter().enumerate() {
|
for (x, pixel) in pixels.iter().enumerate() {
|
||||||
output::set_pixel(output::MAIN_IMAGE, x, y, *pixel);
|
output::set_pixel(output::MAIN_IMAGE, x, y, *pixel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let now = time::Instant::now();
|
||||||
|
let time_diff = now - last_time;
|
||||||
|
if time_diff > time::Duration::from_secs(1) {
|
||||||
|
info!(
|
||||||
|
"{}",
|
||||||
|
progress(&last_stat, ¤t_stat, time_diff, pixel_total)
|
||||||
|
);
|
||||||
|
last_stat = current_stat;
|
||||||
|
last_time = now;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for thr in handles {
|
for thr in handles {
|
||||||
thr.join().expect("thread join");
|
thr.join().expect("thread join");
|
||||||
}
|
}
|
||||||
let human = human::Formatter::new();
|
|
||||||
let time_diff = time::Instant::now() - start_time;
|
let time_diff = time::Instant::now() - start_time;
|
||||||
let ray_count = RAY_COUNT.load(Ordering::SeqCst);
|
|
||||||
info!(
|
info!(
|
||||||
"{}pixels {:.2}s {}pixels/s {}rays/s",
|
"Summary: {}",
|
||||||
human.format(pixel_total as f64),
|
progress(&Default::default(), ¤t_stat, time_diff, pixel_total)
|
||||||
time_diff.as_secs_f64(),
|
|
||||||
human.format(pixel_total as f64 / time_diff.as_secs_f64()),
|
|
||||||
human.format(ray_count as f64 / time_diff.as_secs_f64())
|
|
||||||
);
|
);
|
||||||
|
|
||||||
output::write_images(output_dir)
|
output::write_images(output_dir)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user