rtiow: lockless non-global stats keeping.
This commit is contained in:
parent
27ca936264
commit
848e9879cb
@ -1,11 +1,10 @@
|
||||
use std::fmt;
|
||||
use std::ops::AddAssign;
|
||||
use std::ops::Range;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::str;
|
||||
use std::sync;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::mpsc::sync_channel;
|
||||
use std::sync::mpsc::Receiver;
|
||||
use std::sync::mpsc::SyncSender;
|
||||
@ -49,7 +48,6 @@ impl MockPrometheus {
|
||||
fn inc(&self) {}
|
||||
}
|
||||
|
||||
static RAY_COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||
#[cfg(not(feature = "prom"))]
|
||||
static RAY_COUNTER: MockPrometheus = MockPrometheus {};
|
||||
|
||||
@ -240,40 +238,41 @@ fn color(
|
||||
depth: usize,
|
||||
global_illumination: bool,
|
||||
env_map: &Option<EnvMap>,
|
||||
) -> Vec3 {
|
||||
RAY_COUNT.fetch_add(1, Ordering::SeqCst);
|
||||
) -> (Vec3, usize) {
|
||||
RAY_COUNTER.with_label_values(&[&depth.to_string()]).inc();
|
||||
if let Some(rec) = world.hit(r, 0.001, std::f32::MAX) {
|
||||
let (u, v) = rec.uv;
|
||||
let emitted = rec.material.emitted(u, v, rec.p);
|
||||
let scatter_response = rec.material.scatter(&r, &rec);
|
||||
if depth < 50 && scatter_response.reflected {
|
||||
return emitted
|
||||
+ scatter_response.attenutation
|
||||
* color(
|
||||
scatter_response.scattered,
|
||||
world,
|
||||
depth + 1,
|
||||
global_illumination,
|
||||
env_map,
|
||||
);
|
||||
let (c, rays) = color(
|
||||
scatter_response.scattered,
|
||||
world,
|
||||
depth + 1,
|
||||
global_illumination,
|
||||
env_map,
|
||||
);
|
||||
return (emitted + scatter_response.attenutation * c, rays + 1);
|
||||
} else {
|
||||
return emitted;
|
||||
return (emitted, 1);
|
||||
}
|
||||
}
|
||||
if global_illumination {
|
||||
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 => {
|
||||
let unit_direction = r.direction.unit_vector();
|
||||
// No hit, choose color from background.
|
||||
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.
|
||||
Vec3::new(0., 0., 0.)
|
||||
(Vec3::new(0., 0., 0.), 1)
|
||||
}
|
||||
|
||||
const MAX_ADAPTIVE_DEPTH: usize = 10;
|
||||
@ -285,13 +284,13 @@ fn trace_pixel_adaptive(
|
||||
x_range: Range<f32>,
|
||||
y_range: Range<f32>,
|
||||
scene: &Scene,
|
||||
) -> Vec3 {
|
||||
) -> (Vec3, usize) {
|
||||
let w = scene.width as f32;
|
||||
let h = scene.height as f32;
|
||||
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 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.world.as_ref(),
|
||||
0,
|
||||
@ -300,7 +299,7 @@ fn trace_pixel_adaptive(
|
||||
);
|
||||
if depth == 0 {
|
||||
output::set_pixel(output::ADAPTIVE_DEPTH, x, y, [1., 0., 0.].into());
|
||||
return center;
|
||||
return (center, rays);
|
||||
}
|
||||
// t = top
|
||||
// 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 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()
|
||||
.map(|(u, v)| {
|
||||
color(
|
||||
@ -327,10 +326,13 @@ fn trace_pixel_adaptive(
|
||||
&scene.env_map,
|
||||
)
|
||||
})
|
||||
.fold([0., 0., 0.].into(), |a: Vec3, b: Vec3| a + b)
|
||||
/ 5.;
|
||||
.fold(
|
||||
([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 {
|
||||
let pixel = (trace_pixel_adaptive(
|
||||
let tl = trace_pixel_adaptive(
|
||||
depth - 1,
|
||||
threshold,
|
||||
x,
|
||||
@ -338,7 +340,8 @@ fn trace_pixel_adaptive(
|
||||
x_range.start..x_mid,
|
||||
y_range.start..y_mid,
|
||||
scene,
|
||||
) + trace_pixel_adaptive(
|
||||
);
|
||||
let tr = trace_pixel_adaptive(
|
||||
depth - 1,
|
||||
threshold,
|
||||
x,
|
||||
@ -346,7 +349,8 @@ fn trace_pixel_adaptive(
|
||||
x_mid..x_range.end,
|
||||
y_range.start..y_mid,
|
||||
scene,
|
||||
) + trace_pixel_adaptive(
|
||||
);
|
||||
let bl = trace_pixel_adaptive(
|
||||
depth - 1,
|
||||
threshold,
|
||||
x,
|
||||
@ -354,7 +358,8 @@ fn trace_pixel_adaptive(
|
||||
x_range.start..x_mid,
|
||||
y_mid..y_range.end,
|
||||
scene,
|
||||
) + trace_pixel_adaptive(
|
||||
);
|
||||
let br = trace_pixel_adaptive(
|
||||
depth - 1,
|
||||
threshold,
|
||||
x,
|
||||
@ -362,17 +367,19 @@ fn trace_pixel_adaptive(
|
||||
x_mid..x_range.end,
|
||||
y_mid..y_range.end,
|
||||
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 {
|
||||
if depth == MAX_ADAPTIVE_DEPTH {
|
||||
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 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;
|
||||
@ -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 {
|
||||
Pixel { x: usize, y: usize },
|
||||
Line { width: usize, y: usize },
|
||||
}
|
||||
|
||||
enum Response {
|
||||
Pixel { x: usize, y: usize, pixel: Vec3 },
|
||||
Line { y: usize, pixels: Vec<Vec3> },
|
||||
Pixel {
|
||||
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 {
|
||||
let mut pixel: Vec3 = Default::default();
|
||||
let pixel = if let Some(threshold) = scene.adaptive_subsampling {
|
||||
fn render_pixel(scene: &Scene, x: usize, y: usize) -> (Vec3, usize) {
|
||||
let (pixel, rays) = if let Some(threshold) = scene.adaptive_subsampling {
|
||||
trace_pixel_adaptive(
|
||||
MAX_ADAPTIVE_DEPTH,
|
||||
threshold,
|
||||
@ -411,15 +464,20 @@ fn render_pixel(scene: &Scene, x: usize, y: usize) -> Vec3 {
|
||||
scene,
|
||||
)
|
||||
} else {
|
||||
for _ in 0..scene.subsamples {
|
||||
pixel = pixel + trace_pixel_random(x, y, scene);
|
||||
}
|
||||
pixel / scene.subsamples as f32
|
||||
let (pixel, rays) = (0..scene.subsamples)
|
||||
.map(|_| trace_pixel_random(x, y, scene))
|
||||
.fold(
|
||||
([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
|
||||
// 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(
|
||||
@ -438,16 +496,33 @@ fn render_worker(
|
||||
Ok(req) => match req {
|
||||
Request::Line { 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
|
||||
.send(Response::Line { y, pixels })
|
||||
.send(Response::Line {
|
||||
y,
|
||||
pixels,
|
||||
rs: RenderStats {
|
||||
rays,
|
||||
pixels: width,
|
||||
},
|
||||
})
|
||||
.expect("failed to send pixel response");
|
||||
}
|
||||
Request::Pixel { 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
|
||||
.send(Response::Pixel { x, y, pixel })
|
||||
.send(Response::Pixel {
|
||||
x,
|
||||
y,
|
||||
pixel,
|
||||
rs: RenderStats { rays, pixels: 1 },
|
||||
})
|
||||
.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;
|
||||
handles.push(thread::spawn(move || {
|
||||
let mut last_time = time::Instant::now();
|
||||
let mut last_pixel_count = PIXEL_COUNT.load(Ordering::SeqCst);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
let mut last_time = time::Instant::now();
|
||||
let mut last_stat: RenderStats = Default::default();
|
||||
let mut current_stat: RenderStats = Default::default();
|
||||
for resp in pixel_resp_rx {
|
||||
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);
|
||||
}
|
||||
Response::Line { y, pixels } => {
|
||||
Response::Line { y, pixels, rs } => {
|
||||
current_stat += rs;
|
||||
for (x, pixel) in pixels.iter().enumerate() {
|
||||
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 {
|
||||
thr.join().expect("thread join");
|
||||
}
|
||||
let human = human::Formatter::new();
|
||||
let time_diff = time::Instant::now() - start_time;
|
||||
let ray_count = RAY_COUNT.load(Ordering::SeqCst);
|
||||
info!(
|
||||
"{}pixels {:.2}s {}pixels/s {}rays/s",
|
||||
human.format(pixel_total as f64),
|
||||
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())
|
||||
"Summary: {}",
|
||||
progress(&Default::default(), ¤t_stat, time_diff, pixel_total)
|
||||
);
|
||||
|
||||
output::write_images(output_dir)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user