rtiow: move image writer to an instance instead of wrappers around global.

This commit is contained in:
Bill Thiede 2023-10-29 09:17:32 -07:00
parent ed2ee749cd
commit 593632a9e3
2 changed files with 166 additions and 150 deletions

View File

@ -24,10 +24,6 @@ pub const ADAPTIVE_DEPTH: &str = "adaptive_depth";
// Grey scale showing rays cast per pixel.
pub const RAYS_PER_PIXEL: &str = "rays_per_pixel";
lazy_static! {
static ref DEBUGGER: Arc<Mutex<Debugger>> = Arc::new(Mutex::new(Debugger::new()));
}
#[derive(Serialize)]
struct ImageMetadata {
name: String,
@ -74,65 +70,54 @@ impl Image {
}
}
struct Debugger {
images: HashMap<String, (ImageType, Image)>,
pub struct OutputManager {
images: Arc<Mutex<HashMap<String, (ImageType, Image)>>>,
}
impl Debugger {
fn new() -> Debugger {
Debugger {
images: HashMap::new(),
impl OutputManager {
pub fn new() -> OutputManager {
OutputManager {
images: Arc::new(Mutex::new(HashMap::new())),
}
}
}
pub fn register_image(name: String, dimensions: (usize, usize), it: ImageType) {
let mut debugger = DEBUGGER.lock().unwrap();
debugger
.images
.insert(name, (it, Image::new(dimensions.0, dimensions.1)));
}
pub fn register_image(&self, name: String, dimensions: (usize, usize), it: ImageType) {
let mut images = self.images.lock().unwrap();
images.insert(name, (it, Image::new(dimensions.0, dimensions.1)));
}
pub fn set_pixel(name: &str, x: usize, y: usize, pixel: Vec3) {
let mut debugger = DEBUGGER.lock().unwrap();
let (_it, img) = debugger
.images
pub fn set_pixel(&self, name: &str, x: usize, y: usize, pixel: Vec3) {
let mut images = self.images.lock().unwrap();
let (_it, img) = images
.get_mut(name)
.unwrap_or_else(|| panic!("couldn't find image named '{}'", name));
let y_inv = img.h - y - 1;
img.put_pixel(x, y_inv, pixel);
}
}
pub fn set_pixel_grey(name: &str, x: usize, y: usize, grey: f32) {
let mut debugger = DEBUGGER.lock().unwrap();
let (_it, img) = debugger
.images
pub fn set_pixel_grey(&self, name: &str, x: usize, y: usize, grey: f32) {
let mut images = self.images.lock().unwrap();
let (_it, img) = images
.get_mut(name)
.unwrap_or_else(|| panic!("couldn't find image named '{}'", name));
let y_inv = img.h - y - 1;
img.put_pixel(x, y_inv, [grey, grey, grey].into());
}
trait ImageSaver {
fn save<Q>(&self, path: Q) -> std::io::Result<()>
where
Q: AsRef<Path> + Sized;
}
pub fn write_images<P: AsRef<Path>>(
}
pub fn write_images<P: AsRef<Path>>(
&self,
scene: &Scene,
render_time: time::Duration,
output_dir: P,
) -> std::io::Result<()> {
) -> std::io::Result<()> {
let output_dir: &Path = output_dir.as_ref();
let debugger = DEBUGGER.lock().unwrap();
let now = Local::now();
let images = self.images.lock().unwrap();
// Write out images in consistent order.
let mut names = debugger.images.keys().collect::<Vec<_>>();
let mut names = images.keys().collect::<Vec<_>>();
names.sort();
let mut image_metadata = Vec::new();
for name in &names {
let (it, img) = debugger.images.get(*name).unwrap();
let (it, img) = images.get(*name).unwrap();
let image = format!("{}.png", name);
let binary = format!("{}.json", name);
let ratio = img.w as f32 / img.h as f32;
@ -203,7 +188,10 @@ pub fn write_images<P: AsRef<Path>>(
)?;
}
ImageType::Grey01 | ImageType::GreyNormalized => {
serde_json::ser::to_writer(f, &img.pix.iter().map(|v| v.x).collect::<Vec<f32>>())?;
serde_json::ser::to_writer(
f,
&img.pix.iter().map(|v| v.x).collect::<Vec<f32>>(),
)?;
}
};
}
@ -219,4 +207,11 @@ pub fn write_images<P: AsRef<Path>>(
},
)?;
Ok(())
}
}
trait ImageSaver {
fn save<Q>(&self, path: Q) -> std::io::Result<()>
where
Q: AsRef<Path> + Sized;
}

View File

@ -25,6 +25,7 @@ use crate::{
human,
material::{Lambertian, Material},
output,
output::OutputManager,
ray::Ray,
scenes,
sphere::Sphere,
@ -109,6 +110,9 @@ pub struct Opt {
/// Use acceleration data structure, may be BVH or kd-tree depending on scene.
#[structopt(long = "use_accel")]
pub use_accel: bool,
/// Host:port of running tev instance.
#[structopt(long = "tev_addr")]
pub tev_addr: Option<String>,
/// Output directory
#[structopt(
@ -246,6 +250,7 @@ fn trace_pixel_adaptive(
x_range: Range<f32>,
y_range: Range<f32>,
scene: &Scene,
output: &OutputManager,
) -> (Vec3, usize) {
let w = scene.width as f32;
let h = scene.height as f32;
@ -260,7 +265,7 @@ fn trace_pixel_adaptive(
&scene.env_map,
);
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, rays);
}
// t = top
@ -302,6 +307,7 @@ fn trace_pixel_adaptive(
x_range.start..x_mid,
y_range.start..y_mid,
scene,
output,
);
let tr = trace_pixel_adaptive(
depth - 1,
@ -311,6 +317,7 @@ fn trace_pixel_adaptive(
x_mid..x_range.end,
y_range.start..y_mid,
scene,
output,
);
let bl = trace_pixel_adaptive(
depth - 1,
@ -320,6 +327,7 @@ fn trace_pixel_adaptive(
x_range.start..x_mid,
y_mid..y_range.end,
scene,
output,
);
let br = trace_pixel_adaptive(
depth - 1,
@ -329,13 +337,14 @@ fn trace_pixel_adaptive(
x_mid..x_range.end,
y_mid..y_range.end,
scene,
output,
);
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());
output.set_pixel(output::ADAPTIVE_DEPTH, x, y, [0., 1., 0.].into());
}
(corners, rays)
}
@ -418,7 +427,7 @@ enum Response {
},
}
fn render_pixel(scene: &Scene, x: usize, y: usize) -> (Vec3, usize) {
fn render_pixel(scene: &Scene, x: usize, y: usize, output: &OutputManager) -> (Vec3, usize) {
let (pixel, rays) = if let Some(threshold) = scene.adaptive_subsampling {
trace_pixel_adaptive(
MAX_ADAPTIVE_DEPTH,
@ -428,6 +437,7 @@ fn render_pixel(scene: &Scene, x: usize, y: usize) -> (Vec3, usize) {
0.0..1.0,
0.0..1.0,
scene,
output,
)
} else {
let (pixel, rays) = (0..scene.subsamples)
@ -436,7 +446,7 @@ fn render_pixel(scene: &Scene, x: usize, y: usize) -> (Vec3, usize) {
([0., 0., 0.].into(), 0),
|(p1, r1): (Vec3, usize), (p2, r2): (Vec3, usize)| ((p1 + p2), (r1 + r2)),
);
output::set_pixel_grey(output::RAYS_PER_PIXEL, x, y, rays as f32);
output.set_pixel_grey(output::RAYS_PER_PIXEL, x, y, rays as f32);
(pixel / scene.subsamples as f32, rays)
};
// Gamma correct, use gamma 2 correction, which is 1/gamma where gamma=2 which is 1/2 or
@ -452,6 +462,7 @@ fn render_worker(
scene: &Scene,
input_chan: Arc<Mutex<Receiver<Request>>>,
output_chan: &SyncSender<Response>,
output: &OutputManager,
) {
loop {
let job = { input_chan.lock().unwrap().recv() };
@ -466,7 +477,7 @@ fn render_worker(
let batch = false;
if batch {
let (pixels, rays): (Vec<Vec3>, Vec<usize>) = (0..width)
.map(|x| render_pixel(scene, x, y))
.map(|x| render_pixel(scene, x, y, output))
.collect::<Vec<(_, _)>>()
.into_iter()
.unzip();
@ -483,7 +494,7 @@ fn render_worker(
.expect("failed to send pixel response");
} else {
(0..width).for_each(|x| {
let (pixel, rays) = render_pixel(scene, x, y);
let (pixel, rays) = render_pixel(scene, x, y, output);
output_chan
.send(Response::Pixel {
x,
@ -497,7 +508,7 @@ fn render_worker(
}
Request::Pixel { x, y } => {
trace!("tid {} x {} y {}", tid, x, y);
let (pixel, rays) = render_pixel(scene, x, y);
let (pixel, rays) = render_pixel(scene, x, y, output);
output_chan
.send(Response::Pixel {
x,
@ -512,6 +523,12 @@ fn render_worker(
}
}
/*
lazy_static! {
static ref DEBUGGER: Arc<Mutex<OutputManager>> = Arc::new(Mutex::new(OutputManager::new()));
}
*/
pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::io::Error> {
// Default to half the cores to disable hyperthreading.
let num_threads = scene.num_threads.unwrap_or_else(|| num_cpus::get() / 2);
@ -528,20 +545,23 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
} else {
core_ids
};
let output = output::OutputManager::new();
let output = Arc::new(output);
info!("Creating {} render threads", core_ids.len());
output::register_image(
output.register_image(
output::MAIN_IMAGE.to_string(),
(scene.width, scene.height),
output::ImageType::RGB01,
);
if scene.adaptive_subsampling.is_some() {
output::register_image(
output.register_image(
output::ADAPTIVE_DEPTH.to_string(),
(scene.width, scene.height),
output::ImageType::RGB01,
);
}
output::register_image(
output.register_image(
output::RAYS_PER_PIXEL.to_string(),
(scene.width, scene.height),
output::ImageType::GreyNormalized,
@ -555,9 +575,10 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
let s = sync::Arc::clone(&scene);
let pixel_req_rx = pixel_req_rx.clone();
let pixel_resp_tx = pixel_resp_tx.clone();
let output = sync::Arc::clone(&output);
thread::spawn(move || {
core_affinity::set_for_current(id);
render_worker(i, &s, pixel_req_rx, &pixel_resp_tx);
render_worker(i, &s, pixel_req_rx, &pixel_resp_tx, &output);
})
})
.collect::<Vec<_>>();
@ -596,12 +617,12 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
match resp {
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, rs } => {
current_stat += rs;
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);
}
}
}
@ -639,5 +660,5 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i
)
);
output::write_images(&scene, time_diff, output_dir)
output.write_images(&scene, time_diff, output_dir)
}