From b18db476d1c8a75354aab72f103d112f00ddbb88 Mon Sep 17 00:00:00 2001 From: Jonas Paus Date: Mon, 12 Jul 2021 09:58:54 +0200 Subject: [PATCH 1/5] Add communication with tev and hard coded updating behaviour --- .gitignore | 1 + Cargo.lock | 34 +++- Cargo.toml | 2 + src/core/display.rs | 437 +++++++++++++++++++++++++++++++++++++++++ src/core/film.rs | 2 +- src/core/integrator.rs | 44 ++++- src/core/mod.rs | 1 + 7 files changed, 508 insertions(+), 13 deletions(-) create mode 100644 src/core/display.rs diff --git a/.gitignore b/.gitignore index eb5a316c..5bd05d62 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ target +.idea \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index c03f9d6f..4dbe3779 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "adler" version = "0.2.3" @@ -253,11 +255,10 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.1" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" dependencies = [ - "autocfg", "cfg-if 1.0.0", "lazy_static", ] @@ -416,9 +417,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.59" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3262021842bf00fe07dbd6cf34ff25c99d7a7ebef8deea84db72be3ea3bb0aff" +checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" [[package]] name = "linked-hash-map" @@ -460,6 +461,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "murmurhash64" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa29fe48ea8985625453d7753b2007f6b6d7f35ef1b06c9298eeb33330bfe404" +dependencies = [ + "rand 0.3.23", +] + [[package]] name = "num" version = "0.4.0" @@ -740,6 +750,16 @@ dependencies = [ "proc-macro2 1.0.24", ] +[[package]] +name = "rand" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" +dependencies = [ + "libc", + "rand 0.4.6", +] + [[package]] name = "rand" version = "0.4.6" @@ -826,10 +846,12 @@ dependencies = [ "byteorder", "crossbeam", "crossbeam-channel", + "crossbeam-utils", "hexf", "image", "impl_ops", "lazy_static", + "murmurhash64", "num", "num_cpus", "pbr", @@ -1042,7 +1064,7 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" dependencies = [ - "rand", + "rand 0.4.6", "remove_dir_all", ] diff --git a/Cargo.toml b/Cargo.toml index c0a460fe..a74e1fb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,12 +15,14 @@ atomic = "0.5.0" byteorder = "1.4.3" crossbeam = "0.8.0" crossbeam-channel = "0.5.1" +crossbeam-utils = "0.8.5" hexf = "0.2.1" image = "0.23.14" impl_ops = "0.1.1" lazy_static = "1.4.0" num = "0.4.0" num_cpus = "1.13.0" +murmurhash64 = "0.3.1" pbr = "1.0.4" pest = "2.1.3" pest_derive = "2.1.0" diff --git a/src/core/display.rs b/src/core/display.rs new file mode 100644 index 00000000..80d8a4fd --- /dev/null +++ b/src/core/display.rs @@ -0,0 +1,437 @@ +use core::str; +use std::cmp::min; +use std::io::Write; +use std::mem::size_of; +use std::net::TcpStream; +use std::sync::{Arc, Mutex, RwLock}; +use std::sync::atomic::AtomicBool; +use std::thread::{self, ThreadId}; +use std::time; + +use atomic::Ordering; +use murmurhash64::murmur_hash64a; + +use crate::core::geometry::{Bounds2i, Point2i}; +use crate::core::pbrt::Float; +use crossbeam_utils::thread::Scope; +use crate::core::film::Pixel; + +const TILE_SIZE: i32 = 128; + +type Pixels<'a> = Arc<&'a RwLock>>; +type Function = fn(Bounds2i, Pixels, usize, &mut Vec>); + +struct DisplayDirective; + +impl DisplayDirective { + pub const OPEN_IMAGE: u8 = 0; + pub const RELOAD_IMAGE: u8 = 1; + pub const CLOSE_IMAGE: u8 = 2; + pub const UPDATE_IMAGE: u8 = 3; + pub const CREATE_IMAGE: u8 = 4; +} + +struct DisplayItem<'a> { + title: String, + resolution: Point2i, + get_tile_values: Function, + channel_buffers: Vec, + vec: Pixels<'a>, + opened_image: bool, +} + +impl<'a> DisplayItem<'a> { + pub fn new( + base_title: &str, + resolution: Point2i, + channel_names: Vec, + vec: Pixels<'a>, + get_tile_values: Function, + ) -> DisplayItem<'a> { + // let title = format!("{} {:?}", base_title, get_thread_id()); + let title = format!("{}", base_title); + + let mut channel_buffers = Vec::new(); + + + for channel_name in channel_names { + channel_buffers.push(ImageChannelBuffer::new( + channel_name, + title.clone(), + )); + } + + DisplayItem { + title, + resolution, + get_tile_values, + channel_buffers, + vec, + opened_image: false, + } + } + + pub fn display(&mut self, ipc_channel: &mut TcpStream) -> bool { + // Open image in tev if not done already + if !self.opened_image { + let opened = self.send_open_image(ipc_channel); + if let Err(err) = opened { + println!("Could not send open image packet: {}", err); + return false; + } + self.opened_image = true; + } + + // Create image buffer + let size = self.channel_buffers.len(); + let inner_size = (TILE_SIZE * TILE_SIZE) as usize; + let mut display_values: Vec> = Vec::with_capacity(size); + for _ in 0..size { display_values.push(Vec::with_capacity(inner_size)); } + + let mut tile_index = 0; + let mut y = 0; + while y < self.resolution.y { + let mut x = 0; + while x < self.resolution.x { + let height = min(y + TILE_SIZE, self.resolution.y) - y; + let width = min(x + TILE_SIZE, self.resolution.x) - x; + + for channel_buffer in &mut self.channel_buffers { + channel_buffer.buffer.resize(channel_buffer.channel_values_offset, 0); + channel_buffer.set_tile_bounds(x, y, width, height); + } + + let b = Bounds2i { + p_min: Point2i { x, y }, + p_max: Point2i { + x: x + width, + y: y + height, + }, + }; + + // empty out display old values + + for values in display_values.iter_mut() { values.clear(); } + (self.get_tile_values)(b, self.vec.clone(), self.resolution.x as usize, &mut display_values); + // Send the RGB buffers if they differ from the last sent version + for (channel_buffer, values) in self.channel_buffers.iter_mut().zip(&display_values) { + // Insert image values into channel buffer + let values: Vec = values.iter().flat_map(|x| x.to_le_bytes()).collect(); + channel_buffer.buffer.extend_from_slice(&values); + } + + for channel_buffer in self.channel_buffers.iter_mut() { + let sent = channel_buffer.send_if_changed(ipc_channel, tile_index); + if !sent { + // self.opened_image = false; + return false; + } + } + + x += TILE_SIZE; + tile_index += 1; + } + + y += TILE_SIZE; + } + + true + } + + fn send_open_image(&self, ipc_channel: &mut TcpStream) -> std::io::Result { + // Create "open the image" message buffer + let mut buffer = Vec::with_capacity(1024); + create_open_packet(&self.title, &self.resolution, &mut buffer); + + // Send off the buffer + ipc_channel.write(&buffer) + } +} + +struct ImageChannelBuffer { + buffer: Vec, + tile_bounds_offset: usize, + channel_values_offset: usize, + tile_hashes: Vec, + // Should probably be a map? + set_count: i32, + tile_index: i32, +} + +impl ImageChannelBuffer { + fn new(channel_name: String, title: String) -> Self { + let buffer_size = + TILE_SIZE as usize * TILE_SIZE as usize * size_of::() + title.capacity() + 32; + let mut buffer = Vec::with_capacity(buffer_size); + + buffer.extend_from_slice(&0_i32.to_le_bytes()); // reserve space for message length? + buffer.extend_from_slice(&DisplayDirective::UPDATE_IMAGE.to_le_bytes()); + buffer.push(0); // grab focus + buffer.extend_from_slice(title.as_bytes()); + buffer.push(0); // Null terminate string + buffer.extend_from_slice(channel_name.as_bytes()); + buffer.push(0); // Null terminate string + + let tile_bounds_offset = buffer.len(); + + // The original implementation says we have a problem with the offset now not being float-aligned + // As far as I can tell this should be u8 aligned + // Also this is apparently not a problem on x86... + let channel_values_offset = tile_bounds_offset; + + // buffer should already be zeroed where we didn't overwrite yet + // let zero_hash = murmur_hash64a(&buffer[channel_values_offset..], 0); + let tile_hashes: Vec = Vec::new(); + + // for _ in 0..n_tiles { + // tile_hashes.push(zero_hash); + // } + + ImageChannelBuffer { + buffer, + tile_bounds_offset, + channel_values_offset, + tile_hashes, + set_count: 0, + tile_index: 0, + } + } + + fn set_tile_bounds(&mut self, x: i32, y: i32, width: i32, height: i32) { + self.buffer.extend_from_slice(&x.to_le_bytes()); + self.buffer.extend_from_slice(&y.to_le_bytes()); + self.buffer.extend_from_slice(&width.to_le_bytes()); + self.buffer.extend_from_slice(&height.to_le_bytes()); + + self.set_count = width * height; + } + + fn send_if_changed(&mut self, ipc_channel: &mut TcpStream, tile_index: usize) -> bool { + let hash = murmur_hash64a(&self.buffer[self.channel_values_offset..], 0); + if let Some(&tile_hash) = self.tile_hashes.get(tile_index) { + if tile_hash == hash { return true; } + } + + let message_length = self.buffer.len(); + let message_length_bytes = (message_length as i32).to_ne_bytes(); + self.buffer.splice(..4, message_length_bytes); + + // println!("Sending Channel Buffer with index {}", tile_index); + // println!("{:?}", &self.buffer[..(min(100, self.buffer.len()))]); + // println!("buffer size: {}", self.buffer.len()); + let sent = ipc_channel.write(&self.buffer); + if let Err(err) = sent { + dbg!(err); + return false; + } + + self.tile_hashes.insert(tile_index, hash); + true + } +} + +fn get_thread_id() -> ThreadId { + thread::current().id() +} + +fn create_open_packet(title: &str, resolution: &Point2i, buf: &mut Vec) { + buf.extend_from_slice(&0_i32.to_le_bytes()); // Make space for message length + buf.extend_from_slice(&4_u8.to_le_bytes()); // Create Image + buf.push(1); // Grab focus + buf.extend_from_slice(title.as_bytes()); + buf.push(0); // Null terminate string + buf.extend_from_slice(&resolution.x.to_le_bytes()); + buf.extend_from_slice(&resolution.y.to_le_bytes()); + + let channels = ["R", "G", "B"]; + let size = channels.len() as i32; + buf.extend_from_slice(&size.to_le_bytes()); + for channel in channels { + buf.extend_from_slice(channel.as_bytes()); + buf.push(0); // Null terminate string + } + + let message_length = buf.len(); + let message_length_bytes = (message_length as i32).to_le_bytes(); + buf.splice(..4, message_length_bytes); +} + +pub struct Preview<'a> { + exit_thread: Arc, + dynamic_items: Arc>>>, + // update_thread: Option>, + dynamic_channel: TcpStream, +} + +impl<'a> Preview<'a> { + pub fn connect_to_display_server(host: &str) -> Preview { + let exit_thread = Arc::new(AtomicBool::new(false)); + let dynamic_channel = TcpStream::connect(host).unwrap(); + let dynamic_items: Arc>> = Arc::new(Mutex::new(Vec::new())); + + // let update_thread = None; + + Preview { + exit_thread, + // update_thread, + dynamic_items, + dynamic_channel, + } + } + + pub fn disconnect_from_display_server(&mut self) { + // if self.update_thread.is_none() { return; } + // + // if self.update_thread.as_ref().unwrap().thread().id() != thread::current().id() { + self.exit_thread.store(true, Ordering::Relaxed); + // self.update_thread.take().unwrap().join(); + // } + } + + pub fn display_dynamic(&mut self, title: &str, resolution: Point2i, channel_names: Vec, + scope: &Scope<'a>, arc: Arc<&'a RwLock>>, get_tile_values: Function) { + dbg!("display_dynamic"); + + let cloned_exit_thread = self.exit_thread.clone(); + let cloned_dynamic_items = self.dynamic_items.clone(); + let mut cloned_channel = self.dynamic_channel.try_clone().unwrap(); + // self.update_thread = Some( + scope.spawn(move |_| { + update_dynamic_items(cloned_exit_thread, &mut cloned_channel, cloned_dynamic_items); + }); + // ); + let mut display_items = self.dynamic_items.lock().unwrap(); + display_items.push(DisplayItem::new(title, resolution, channel_names, arc, get_tile_values)) + } + + fn display_static(&mut self, title: &str, resolution: Point2i, vec: Pixels, channel_names: Vec, + get_tile_values: Function) { + let mut item = DisplayItem::new(title, resolution, channel_names, vec, get_tile_values); + if !item.display( &mut self.dynamic_channel) { + println!("Unable to display static content {}", title); + } + } +} + +fn update_dynamic_items(exit_thread: Arc, channel: &mut TcpStream, items: Arc>>) { + while !exit_thread.load(Ordering::Relaxed) { + thread::sleep(time::Duration::from_millis(250)); + + let mut items = items.lock().unwrap(); + for item in items.iter_mut() { + item.display( channel); + } + } + + let mut items = items.lock().unwrap(); + for item in items.iter_mut() { + item.display(channel); + } + + items.clear(); +} + + +#[cfg(test)] +mod test { + use std::thread; + use std::time; + + use crate::core::display::{Preview, Pixels}; + use crate::core::geometry::{Bounds2i, Point2i}; + use crate::core::pbrt::Float; + use std::sync::{Arc, RwLock, Mutex}; + use std::time::Duration; + use crossbeam_utils::thread::Scope; + use crate::core::film::Pixel; + + + #[test] + /// Manual test for tev remote + fn display_remote() { + let address = "127.0.0.1:14158"; + + let mut display = Preview::connect_to_display_server(address); + let resolution = Point2i { x: 200, y: 200 }; + + let mut image: Vec = Vec::with_capacity(resolution.x as usize); + for x in 0..resolution.x { + for y in 0..resolution.y { + let color = (x * y) as Float / (resolution.x * resolution.y - 1) as Float; + let mut pixel = Pixel::default(); + pixel.xyz = [color; 3]; + image.push(pixel); + } + } + + let data = &RwLock::new(image); + let arc = Arc::new(data); + + let get_values = move |b: Bounds2i, arc: Arc<&RwLock>>, width: usize, values: &mut Vec>| { + for col in b.p_min.y as usize.. b.p_max.y as usize { + for row in b.p_min.x as usize .. b.p_max.x as usize { + let v = { + let clone = arc.read().unwrap(); + clone[col * width + row].xyz + }; + + for i in 0..3 { + values[i].push(v[i]); + } + } + } + }; + + crossbeam::scope(|scope| { + display.display_dynamic("Test", resolution, + vec!["R".to_string(), "G".to_string(), "B".to_string()], scope, arc.clone(), get_values); + + thread::sleep(time::Duration::from_millis(1000)); + for cols in 0..resolution.x as usize { + for rows in 0..resolution.y as usize { + let mut arc = arc.write().unwrap(); + arc[cols * resolution.x as usize + rows] = Pixel::default(); + } + } + thread::sleep(time::Duration::from_millis(1000)); + display.disconnect_from_display_server(); + }); + } + + #[test] + fn mutate_data_while_sharing() { + let num = (0, 0); + + let arc = Arc::new(Mutex::new(num)); + let clone = arc.clone(); + let clone2 = arc.clone(); + + crossbeam::scope(|scope| { + mutate(scope, clone); + for _ in 0..100 { + { + let mut arc = arc.lock().unwrap(); + arc.0 += 1; + } + thread::sleep(Duration::from_millis(3)); + } + }).unwrap(); + + let num = clone2.lock().unwrap(); + println!("{:?}", num); + println!("Done."); + } + + fn mutate(scope: &Scope, clone: Arc>) { + scope.spawn(move |_| { + for _ in 0..10 { + { + let mut clone = clone.lock().unwrap(); + println!("{:?}", clone.0); + clone.1 += 1; + } + thread::sleep(Duration::from_millis(2)); + } + }); + } +} diff --git a/src/core/film.rs b/src/core/film.rs index 99944ec3..0a744cc3 100644 --- a/src/core/film.rs +++ b/src/core/film.rs @@ -36,7 +36,7 @@ const FILTER_TABLE_WIDTH: usize = 16; #[derive(Debug, Clone)] pub struct Pixel { - xyz: [Float; 3], + pub(crate) xyz: [Float; 3], filter_weight_sum: Float, splat_xyz: [Float; 3], pad: Float, diff --git a/src/core/integrator.rs b/src/core/integrator.rs index 6d0e0417..6995fa82 100644 --- a/src/core/integrator.rs +++ b/src/core/integrator.rs @@ -2,7 +2,7 @@ //! class that implements the **Integrator** interface. // std -use std::sync::Arc; +use std::sync::{Arc, RwLock}; // pbrt use crate::blockqueue::BlockQueue; use crate::core::camera::{Camera, CameraSample}; @@ -25,6 +25,8 @@ use crate::integrators::path::PathIntegrator; use crate::integrators::sppm::SPPMIntegrator; use crate::integrators::volpath::VolPathIntegrator; use crate::integrators::whitted::WhittedIntegrator; +use crate::core::film::Pixel; +use crate::core::display::Preview; // see integrator.h @@ -206,11 +208,41 @@ impl SamplerIntegrator { } // spawn thread to collect pixels and render image to file scope.spawn(move |_| { - for _ in pbr::PbIter::new(0..bq.len()) { - let film_tile = pixel_rx.recv().unwrap(); - // merge image tile into _Film_ - film.merge_film_tile(&film_tile); - } + crossbeam::scope (|sub_scope| { + // This should not + let address = "127.0.0.1:14158"; + let mut display = Preview::connect_to_display_server(address); + + let arc = Arc::new(&film.pixels); + + // If we always need this function and never need another one we can move this inside the display + let get_values = move |b: Bounds2i, arc: Arc<&RwLock>>, width: usize, values: &mut Vec>| { + for col in b.p_min.y..b.p_max.y { + for row in b.p_min.x..b.p_max.x { + let v = { + let vec = arc.read().unwrap(); + vec[col as usize * width + row as usize].xyz + }; + + for (channel, value) in values.iter_mut().zip(v) { + // Todo: We probably need to scale the values here? + channel.push(value); + } + } + } + }; + + display.display_dynamic("Test", film.full_resolution, + vec!["R".to_string(), "G".to_string(), "B".to_string()], + sub_scope, arc, get_values); + + for _ in pbr::PbIter::new(0..bq.len()) { + let film_tile = pixel_rx.recv().unwrap(); + // merge image tile into _Film_ + film.merge_film_tile(&film_tile); + } + display.disconnect_from_display_server(); + }); }); }) .unwrap(); diff --git a/src/core/mod.rs b/src/core/mod.rs index bd218a77..77ae4414 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -3,6 +3,7 @@ pub mod api; pub mod bssrdf; pub mod camera; +pub mod display; pub mod efloat; pub mod film; pub mod filter; From 1cfe2c422c651131d5f45107002d053a80f14d33 Mon Sep 17 00:00:00 2001 From: Jonas Paus Date: Sat, 17 Jul 2021 00:22:43 +0200 Subject: [PATCH 2/5] Clean up --- src/core/display.rs | 47 ++++++++---------------------------------- src/core/integrator.rs | 2 +- 2 files changed, 10 insertions(+), 39 deletions(-) diff --git a/src/core/display.rs b/src/core/display.rs index 80d8a4fd..a1822691 100644 --- a/src/core/display.rs +++ b/src/core/display.rs @@ -23,6 +23,7 @@ type Function = fn(Bounds2i, Pixels, usize, &mut Vec>); struct DisplayDirective; +#[allow(unused)] impl DisplayDirective { pub const OPEN_IMAGE: u8 = 0; pub const RELOAD_IMAGE: u8 = 1; @@ -48,12 +49,9 @@ impl<'a> DisplayItem<'a> { vec: Pixels<'a>, get_tile_values: Function, ) -> DisplayItem<'a> { - // let title = format!("{} {:?}", base_title, get_thread_id()); - let title = format!("{}", base_title); + let title = format!("{} {:?}", base_title, get_thread_id()); let mut channel_buffers = Vec::new(); - - for channel_name in channel_names { channel_buffers.push(ImageChannelBuffer::new( channel_name, @@ -110,7 +108,6 @@ impl<'a> DisplayItem<'a> { }; // empty out display old values - for values in display_values.iter_mut() { values.clear(); } (self.get_tile_values)(b, self.vec.clone(), self.resolution.x as usize, &mut display_values); // Send the RGB buffers if they differ from the last sent version @@ -150,12 +147,9 @@ impl<'a> DisplayItem<'a> { struct ImageChannelBuffer { buffer: Vec, - tile_bounds_offset: usize, channel_values_offset: usize, tile_hashes: Vec, // Should probably be a map? - set_count: i32, - tile_index: i32, } impl ImageChannelBuffer { @@ -164,7 +158,7 @@ impl ImageChannelBuffer { TILE_SIZE as usize * TILE_SIZE as usize * size_of::() + title.capacity() + 32; let mut buffer = Vec::with_capacity(buffer_size); - buffer.extend_from_slice(&0_i32.to_le_bytes()); // reserve space for message length? + buffer.extend_from_slice(&0_i32.to_le_bytes()); // reserve space for message length. Maybe we should check somewhere, that the message length will fit in 32 bits buffer.extend_from_slice(&DisplayDirective::UPDATE_IMAGE.to_le_bytes()); buffer.push(0); // grab focus buffer.extend_from_slice(title.as_bytes()); @@ -172,28 +166,17 @@ impl ImageChannelBuffer { buffer.extend_from_slice(channel_name.as_bytes()); buffer.push(0); // Null terminate string - let tile_bounds_offset = buffer.len(); - // The original implementation says we have a problem with the offset now not being float-aligned // As far as I can tell this should be u8 aligned // Also this is apparently not a problem on x86... - let channel_values_offset = tile_bounds_offset; + let channel_values_offset = buffer.len(); - // buffer should already be zeroed where we didn't overwrite yet - // let zero_hash = murmur_hash64a(&buffer[channel_values_offset..], 0); let tile_hashes: Vec = Vec::new(); - // for _ in 0..n_tiles { - // tile_hashes.push(zero_hash); - // } - ImageChannelBuffer { buffer, - tile_bounds_offset, channel_values_offset, tile_hashes, - set_count: 0, - tile_index: 0, } } @@ -202,8 +185,6 @@ impl ImageChannelBuffer { self.buffer.extend_from_slice(&y.to_le_bytes()); self.buffer.extend_from_slice(&width.to_le_bytes()); self.buffer.extend_from_slice(&height.to_le_bytes()); - - self.set_count = width * height; } fn send_if_changed(&mut self, ipc_channel: &mut TcpStream, tile_index: usize) -> bool { @@ -216,9 +197,6 @@ impl ImageChannelBuffer { let message_length_bytes = (message_length as i32).to_ne_bytes(); self.buffer.splice(..4, message_length_bytes); - // println!("Sending Channel Buffer with index {}", tile_index); - // println!("{:?}", &self.buffer[..(min(100, self.buffer.len()))]); - // println!("buffer size: {}", self.buffer.len()); let sent = ipc_channel.write(&self.buffer); if let Err(err) = sent { dbg!(err); @@ -236,7 +214,7 @@ fn get_thread_id() -> ThreadId { fn create_open_packet(title: &str, resolution: &Point2i, buf: &mut Vec) { buf.extend_from_slice(&0_i32.to_le_bytes()); // Make space for message length - buf.extend_from_slice(&4_u8.to_le_bytes()); // Create Image + buf.push(DisplayDirective::CREATE_IMAGE); // Create Image buf.push(1); // Grab focus buf.extend_from_slice(title.as_bytes()); buf.push(0); // Null terminate string @@ -269,23 +247,15 @@ impl<'a> Preview<'a> { let dynamic_channel = TcpStream::connect(host).unwrap(); let dynamic_items: Arc>> = Arc::new(Mutex::new(Vec::new())); - // let update_thread = None; - Preview { exit_thread, - // update_thread, dynamic_items, dynamic_channel, } } pub fn disconnect_from_display_server(&mut self) { - // if self.update_thread.is_none() { return; } - // - // if self.update_thread.as_ref().unwrap().thread().id() != thread::current().id() { - self.exit_thread.store(true, Ordering::Relaxed); - // self.update_thread.take().unwrap().join(); - // } + self.exit_thread.store(true, Ordering::Relaxed); } pub fn display_dynamic(&mut self, title: &str, resolution: Point2i, channel_names: Vec, @@ -304,6 +274,7 @@ impl<'a> Preview<'a> { display_items.push(DisplayItem::new(title, resolution, channel_names, arc, get_tile_values)) } + #[allow(unused)] fn display_static(&mut self, title: &str, resolution: Point2i, vec: Pixels, channel_names: Vec, get_tile_values: Function) { let mut item = DisplayItem::new(title, resolution, channel_names, vec, get_tile_values); @@ -337,7 +308,7 @@ mod test { use std::thread; use std::time; - use crate::core::display::{Preview, Pixels}; + use crate::core::display::Preview; use crate::core::geometry::{Bounds2i, Point2i}; use crate::core::pbrt::Float; use std::sync::{Arc, RwLock, Mutex}; @@ -395,7 +366,7 @@ mod test { } thread::sleep(time::Duration::from_millis(1000)); display.disconnect_from_display_server(); - }); + }).unwrap(); } #[test] diff --git a/src/core/integrator.rs b/src/core/integrator.rs index 6995fa82..59b185bd 100644 --- a/src/core/integrator.rs +++ b/src/core/integrator.rs @@ -242,7 +242,7 @@ impl SamplerIntegrator { film.merge_film_tile(&film_tile); } display.disconnect_from_display_server(); - }); + }).unwrap(); }); }) .unwrap(); From 8f7be6c7ce04627b6af75b3de5c3316bc82d6e8d Mon Sep 17 00:00:00 2001 From: Jonas Paus Date: Sun, 18 Jul 2021 23:37:39 +0200 Subject: [PATCH 3/5] Add tolerance to the display server not connecting --- src/core/display.rs | 14 ++++++++------ src/core/integrator.rs | 43 +++++++++++++++++++++++------------------- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/core/display.rs b/src/core/display.rs index a1822691..ac69076c 100644 --- a/src/core/display.rs +++ b/src/core/display.rs @@ -6,7 +6,7 @@ use std::net::TcpStream; use std::sync::{Arc, Mutex, RwLock}; use std::sync::atomic::AtomicBool; use std::thread::{self, ThreadId}; -use std::time; +use std::{time, io}; use atomic::Ordering; use murmurhash64::murmur_hash64a; @@ -242,16 +242,16 @@ pub struct Preview<'a> { } impl<'a> Preview<'a> { - pub fn connect_to_display_server(host: &str) -> Preview { + pub fn connect_to_display_server(host: &str) -> io::Result { let exit_thread = Arc::new(AtomicBool::new(false)); - let dynamic_channel = TcpStream::connect(host).unwrap(); + let dynamic_channel = TcpStream::connect(host)?; let dynamic_items: Arc>> = Arc::new(Mutex::new(Vec::new())); - Preview { + Ok(Preview { exit_thread, dynamic_items, dynamic_channel, - } + }) } pub fn disconnect_from_display_server(&mut self) { @@ -322,7 +322,9 @@ mod test { fn display_remote() { let address = "127.0.0.1:14158"; - let mut display = Preview::connect_to_display_server(address); + let display = Preview::connect_to_display_server(address); + assert!(display.is_ok()); + let mut display = display.unwrap(); let resolution = Point2i { x: 200, y: 200 }; let mut image: Vec = Vec::with_capacity(resolution.x as usize); diff --git a/src/core/integrator.rs b/src/core/integrator.rs index 59b185bd..6532b178 100644 --- a/src/core/integrator.rs +++ b/src/core/integrator.rs @@ -212,36 +212,41 @@ impl SamplerIntegrator { // This should not let address = "127.0.0.1:14158"; let mut display = Preview::connect_to_display_server(address); + let connected = display.is_ok(); - let arc = Arc::new(&film.pixels); + if connected { + let display = display.as_mut().unwrap(); + let arc = Arc::new(&film.pixels); - // If we always need this function and never need another one we can move this inside the display - let get_values = move |b: Bounds2i, arc: Arc<&RwLock>>, width: usize, values: &mut Vec>| { - for col in b.p_min.y..b.p_max.y { - for row in b.p_min.x..b.p_max.x { - let v = { - let vec = arc.read().unwrap(); - vec[col as usize * width + row as usize].xyz - }; + // If we always need this function and never need another one we can move this inside the display + let get_values = move |b: Bounds2i, arc: Arc<&RwLock>>, width: usize, values: &mut Vec>| { + for col in b.p_min.y..b.p_max.y { + for row in b.p_min.x..b.p_max.x { + let v = { + let vec = arc.read().unwrap(); + vec[col as usize * width + row as usize].xyz + }; - for (channel, value) in values.iter_mut().zip(v) { - // Todo: We probably need to scale the values here? - channel.push(value); + for (channel, value) in values.iter_mut().zip(v) { + // Todo: We probably need to scale the values here? + channel.push(value); + } } } - } - }; - - display.display_dynamic("Test", film.full_resolution, - vec!["R".to_string(), "G".to_string(), "B".to_string()], - sub_scope, arc, get_values); + }; + display.display_dynamic("Test", film.full_resolution, + vec!["R".to_string(), "G".to_string(), "B".to_string()], + sub_scope, arc, get_values); + } for _ in pbr::PbIter::new(0..bq.len()) { let film_tile = pixel_rx.recv().unwrap(); // merge image tile into _Film_ film.merge_film_tile(&film_tile); } - display.disconnect_from_display_server(); + if connected { + display.unwrap().disconnect_from_display_server(); + } }).unwrap(); }); }) From f4248767c7ab8bf1bb9003b912689a89529131e3 Mon Sep 17 00:00:00 2001 From: Jonas Paus Date: Sat, 26 Nov 2022 21:08:31 +0100 Subject: [PATCH 4/5] Migrate from own packets to tev_client Also add display-server command line arg --- Cargo.lock | 7 + Cargo.toml | 1 + examples/parse_ass_file.rs | 2 +- examples/parse_blend_file.rs | 4 +- src/bin/rs_pbrt.rs | 6 +- src/core/api.rs | 7 +- src/core/display.rs | 355 ++++++++++++++++------------------- src/core/integrator.rs | 27 +-- 8 files changed, 195 insertions(+), 214 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4dbe3779..6a2d32be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -863,6 +863,7 @@ dependencies = [ "structopt", "strum", "strum_macros", + "tev_client", "typed-arena", ] @@ -1068,6 +1069,12 @@ dependencies = [ "remove_dir_all", ] +[[package]] +name = "tev_client" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c845c2d56d4f732d09a32c9ea2cd3f01923be7a5f98d9f7f0a347205c3141036" + [[package]] name = "textwrap" version = "0.11.0" diff --git a/Cargo.toml b/Cargo.toml index a74e1fb4..adfc16d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ structopt = "0.3.21" strum = "0.21.0" strum_macros = "0.21.1" typed-arena = "2.0.1" +tev_client = "0.5.2" [[bin]] name = "rs_pbrt" diff --git a/examples/parse_ass_file.rs b/examples/parse_ass_file.rs index c448edab..6f069a67 100644 --- a/examples/parse_ass_file.rs +++ b/examples/parse_ass_file.rs @@ -1200,7 +1200,7 @@ fn main() -> std::io::Result<()> { if let Some(mut integrator) = some_integrator { let scene = make_scene(&primitives, lights); let num_threads: u8 = num_cpus::get() as u8; - integrator.render(&scene, num_threads); + integrator.render(&scene, num_threads, None); } else { panic!("Unable to create integrator."); } diff --git a/examples/parse_blend_file.rs b/examples/parse_blend_file.rs index 0c4a3e6b..fd1f3ed9 100644 --- a/examples/parse_blend_file.rs +++ b/examples/parse_blend_file.rs @@ -4407,7 +4407,7 @@ fn main() -> std::io::Result<()> { if let Some(mut integrator) = some_integrator { let scene = make_scene(&render_options.primitives, render_options.lights); let num_threads: u8 = num_cpus::get() as u8; - integrator.render(&scene, num_threads); + integrator.render(&scene, num_threads, None); } else { panic!("Unable to create integrator."); } @@ -4434,7 +4434,7 @@ fn main() -> std::io::Result<()> { if let Some(mut integrator) = some_integrator { let scene = make_scene(&render_options.primitives, render_options.lights); let num_threads: u8 = num_cpus::get() as u8; - integrator.render(&scene, num_threads); + integrator.render(&scene, num_threads, None); } else { panic!("Unable to create integrator."); } diff --git a/src/bin/rs_pbrt.rs b/src/bin/rs_pbrt.rs index e6d23a01..b94920a9 100644 --- a/src/bin/rs_pbrt.rs +++ b/src/bin/rs_pbrt.rs @@ -57,6 +57,9 @@ struct Cli { /// The path to the file to read #[structopt(parse(from_os_str))] path: std::path::PathBuf, + /// The address and port of the display server + #[structopt(long = "--display-server")] + display_server: Option, } // Accelerator @@ -868,6 +871,7 @@ fn main() { let cropx1: f32 = args.cropx1; let cropy0: f32 = args.cropy0; let cropy1: f32 = args.cropy1; + let display_server: Option = args.display_server; let num_cores = num_cpus::get(); let git_describe = option_env!("GIT_DESCRIBE").unwrap_or("unknown"); println!( @@ -877,7 +881,7 @@ fn main() { println!("Copyright (c) 2016-2021 Jan Douglas Bert Walter."); println!("Rust code based on C++ code by Matt Pharr, Greg Humphreys, and Wenzel Jakob."); let (mut api_state, mut bsdf_state) = - pbrt_init(number_of_threads, cropx0, cropx1, cropy0, cropy1); + pbrt_init(number_of_threads, cropx0, cropx1, cropy0, cropy1 , display_server ); parse_file( args.path.into_os_string().into_string().unwrap(), &mut api_state, diff --git a/src/core/api.rs b/src/core/api.rs index aff6a0fa..65747e30 100644 --- a/src/core/api.rs +++ b/src/core/api.rs @@ -126,6 +126,7 @@ pub struct ApiState { pushed_transforms: Vec, pushed_active_transform_bits: Vec, param_set: ParamSet, + display_server: Option, } impl Default for ApiState { @@ -161,6 +162,7 @@ impl Default for ApiState { pushed_transforms: Vec::new(), pushed_active_transform_bits: Vec::new(), param_set: ParamSet::default(), + display_server: None, } } } @@ -2264,6 +2266,7 @@ pub fn pbrt_init( cropx1: f32, cropy0: f32, cropy1: f32, + display_server: Option ) -> (ApiState, BsdfState) { let mut api_state: ApiState = ApiState::default(); let bsdf_state: BsdfState = BsdfState::default(); @@ -2278,6 +2281,7 @@ pub fn pbrt_init( y: clamp_t(cropy1.max(cropy0), 0.0, 1.0), }, }; + api_state.display_server = display_server; (api_state, bsdf_state) } @@ -2296,7 +2300,8 @@ pub fn pbrt_cleanup(api_state: &ApiState) { if let Some(mut integrator) = some_integrator { let scene = api_state.render_options.make_scene(); let num_threads: u8 = api_state.number_of_threads; - integrator.render(&scene, num_threads); + let display_server = api_state.display_server.clone(); + integrator.render(&scene, num_threads, display_server); } else { panic!("Unable to create integrator."); } diff --git a/src/core/display.rs b/src/core/display.rs index ac69076c..25a066cb 100644 --- a/src/core/display.rs +++ b/src/core/display.rs @@ -3,194 +3,138 @@ use std::cmp::min; use std::io::Write; use std::mem::size_of; use std::net::TcpStream; -use std::sync::{Arc, Mutex, RwLock}; use std::sync::atomic::AtomicBool; +use std::sync::{Arc, Mutex, RwLock}; use std::thread::{self, ThreadId}; -use std::{time, io}; +use std::time; use atomic::Ordering; +use crossbeam_utils::thread::Scope; use murmurhash64::murmur_hash64a; +use tev_client::{PacketCreateImage, PacketUpdateImage, TevClient, TevError}; use crate::core::geometry::{Bounds2i, Point2i}; use crate::core::pbrt::Float; -use crossbeam_utils::thread::Scope; -use crate::core::film::Pixel; const TILE_SIZE: i32 = 128; -type Pixels<'a> = Arc<&'a RwLock>>; -type Function = fn(Bounds2i, Pixels, usize, &mut Vec>); - -struct DisplayDirective; - -#[allow(unused)] -impl DisplayDirective { - pub const OPEN_IMAGE: u8 = 0; - pub const RELOAD_IMAGE: u8 = 1; - pub const CLOSE_IMAGE: u8 = 2; - pub const UPDATE_IMAGE: u8 = 3; - pub const CREATE_IMAGE: u8 = 4; -} +type Pixels<'a, T> = Arc<&'a RwLock>>; +type Function = fn(Bounds2i, Pixels, usize, &mut Vec>); -struct DisplayItem<'a> { +struct DisplayItem<'a, T: Send + Sync> { title: String, resolution: Point2i, - get_tile_values: Function, - channel_buffers: Vec, - vec: Pixels<'a>, + get_tile_values: Function, + // channel_buffers: Vec, + channel_names: Vec, + vec: Pixels<'a, T>, opened_image: bool, } -impl<'a> DisplayItem<'a> { +impl<'a, T: Send + Sync> DisplayItem<'a, T> { pub fn new( base_title: &str, resolution: Point2i, channel_names: Vec, - vec: Pixels<'a>, - get_tile_values: Function, - ) -> DisplayItem<'a> { + vec: Pixels<'a, T>, + get_tile_values: Function, + ) -> DisplayItem<'a, T> { let title = format!("{} {:?}", base_title, get_thread_id()); - let mut channel_buffers = Vec::new(); - for channel_name in channel_names { - channel_buffers.push(ImageChannelBuffer::new( - channel_name, - title.clone(), - )); - } DisplayItem { title, resolution, get_tile_values, - channel_buffers, + channel_names, vec, opened_image: false, } } - pub fn display(&mut self, ipc_channel: &mut TcpStream) -> bool { - // Open image in tev if not done already + pub fn display_with_tev_client(&mut self, client: &mut TevClient) -> bool { + // Open image if not opened already if !self.opened_image { - let opened = self.send_open_image(ipc_channel); - if let Err(err) = opened { - println!("Could not send open image packet: {}", err); + if let Ok(_) = self.send_create_image_packet(client) { + self.opened_image = true; + } else { return false; } - self.opened_image = true; } // Create image buffer - let size = self.channel_buffers.len(); + let size = self.channel_names.len(); let inner_size = (TILE_SIZE * TILE_SIZE) as usize; let mut display_values: Vec> = Vec::with_capacity(size); - for _ in 0..size { display_values.push(Vec::with_capacity(inner_size)); } - - let mut tile_index = 0; - let mut y = 0; - while y < self.resolution.y { - let mut x = 0; - while x < self.resolution.x { - let height = min(y + TILE_SIZE, self.resolution.y) - y; - let width = min(x + TILE_SIZE, self.resolution.x) - x; - - for channel_buffer in &mut self.channel_buffers { - channel_buffer.buffer.resize(channel_buffer.channel_values_offset, 0); - channel_buffer.set_tile_bounds(x, y, width, height); - } + for _ in 0..size { + display_values.push(Vec::with_capacity(inner_size)); + } - let b = Bounds2i { - p_min: Point2i { x, y }, - p_max: Point2i { - x: x + width, - y: y + height, - }, - }; - - // empty out display old values - for values in display_values.iter_mut() { values.clear(); } - (self.get_tile_values)(b, self.vec.clone(), self.resolution.x as usize, &mut display_values); - // Send the RGB buffers if they differ from the last sent version - for (channel_buffer, values) in self.channel_buffers.iter_mut().zip(&display_values) { - // Insert image values into channel buffer - let values: Vec = values.iter().flat_map(|x| x.to_le_bytes()).collect(); - channel_buffer.buffer.extend_from_slice(&values); - } + // Bounds for Tile + let bounds = Bounds2i { + p_min: Point2i { x: 0, y: 0 }, + p_max: self.resolution, + }; - for channel_buffer in self.channel_buffers.iter_mut() { - let sent = channel_buffer.send_if_changed(ipc_channel, tile_index); - if !sent { - // self.opened_image = false; - return false; - } - } + // Retrieve image values for Tile with bounds + (self.get_tile_values)( + bounds, + self.vec.clone(), + self.resolution.x as usize, + &mut display_values, + ); + + debug_assert!(!display_values.iter().all(|x| x.iter().all(|y| *y == 0.0)), + "All display values are zero"); + + + let channel0 = display_values[0].iter(); + let channel1 = display_values[1].iter(); + let channel2 = display_values[2].iter(); + let mut data: Vec = vec![]; + data.push(1.0); + for ((x, y), z) in channel0.zip(channel1).zip(channel2) { + data.push(*x); + data.push(*y); + data.push(*z); + } - x += TILE_SIZE; - tile_index += 1; - } + let packet = PacketUpdateImage { + image_name: "TestImage", + grab_focus: false, + channel_names: &vec!["R", "G", "B"], + channel_offsets: &[1, 2, 3], + channel_strides: &[3, 3, 3], + x: 0, + y: 0, + width: self.resolution.x as u32, + height: self.resolution.y as u32, + data: &data, + }; - y += TILE_SIZE; - } + client.send(packet).expect("TODO: panic message"); true } - fn send_open_image(&self, ipc_channel: &mut TcpStream) -> std::io::Result { - // Create "open the image" message buffer - let mut buffer = Vec::with_capacity(1024); - create_open_packet(&self.title, &self.resolution, &mut buffer); - - // Send off the buffer - ipc_channel.write(&buffer) + fn send_create_image_packet(&self, client: &mut TevClient) -> std::io::Result<()> { + client.send(PacketCreateImage { + image_name: "TestImage", + grab_focus: false, + width: self.resolution.x as u32, + height: self.resolution.y as u32, + channel_names: &["R", "G", "B"], + }) } } -struct ImageChannelBuffer { - buffer: Vec, - channel_values_offset: usize, - tile_hashes: Vec, - // Should probably be a map? -} - -impl ImageChannelBuffer { - fn new(channel_name: String, title: String) -> Self { - let buffer_size = - TILE_SIZE as usize * TILE_SIZE as usize * size_of::() + title.capacity() + 32; - let mut buffer = Vec::with_capacity(buffer_size); - - buffer.extend_from_slice(&0_i32.to_le_bytes()); // reserve space for message length. Maybe we should check somewhere, that the message length will fit in 32 bits - buffer.extend_from_slice(&DisplayDirective::UPDATE_IMAGE.to_le_bytes()); - buffer.push(0); // grab focus - buffer.extend_from_slice(title.as_bytes()); - buffer.push(0); // Null terminate string - buffer.extend_from_slice(channel_name.as_bytes()); - buffer.push(0); // Null terminate string - - // The original implementation says we have a problem with the offset now not being float-aligned - // As far as I can tell this should be u8 aligned - // Also this is apparently not a problem on x86... - let channel_values_offset = buffer.len(); - - let tile_hashes: Vec = Vec::new(); - - ImageChannelBuffer { - buffer, - channel_values_offset, - tile_hashes, - } - } - - fn set_tile_bounds(&mut self, x: i32, y: i32, width: i32, height: i32) { - self.buffer.extend_from_slice(&x.to_le_bytes()); - self.buffer.extend_from_slice(&y.to_le_bytes()); - self.buffer.extend_from_slice(&width.to_le_bytes()); - self.buffer.extend_from_slice(&height.to_le_bytes()); - } - - fn send_if_changed(&mut self, ipc_channel: &mut TcpStream, tile_index: usize) -> bool { +/*impl ImageChannelBuffer { + fn send_if_changed(&mut self, ipc_channel: &mut TcpStream, tile_index: usize) -> bool { let hash = murmur_hash64a(&self.buffer[self.channel_values_offset..], 0); if let Some(&tile_hash) = self.tile_hashes.get(tile_index) { - if tile_hash == hash { return true; } + if tile_hash == hash { + return true; + } } let message_length = self.buffer.len(); @@ -206,46 +150,23 @@ impl ImageChannelBuffer { self.tile_hashes.insert(tile_index, hash); true } -} +}*/ fn get_thread_id() -> ThreadId { thread::current().id() } -fn create_open_packet(title: &str, resolution: &Point2i, buf: &mut Vec) { - buf.extend_from_slice(&0_i32.to_le_bytes()); // Make space for message length - buf.push(DisplayDirective::CREATE_IMAGE); // Create Image - buf.push(1); // Grab focus - buf.extend_from_slice(title.as_bytes()); - buf.push(0); // Null terminate string - buf.extend_from_slice(&resolution.x.to_le_bytes()); - buf.extend_from_slice(&resolution.y.to_le_bytes()); - - let channels = ["R", "G", "B"]; - let size = channels.len() as i32; - buf.extend_from_slice(&size.to_le_bytes()); - for channel in channels { - buf.extend_from_slice(channel.as_bytes()); - buf.push(0); // Null terminate string - } - - let message_length = buf.len(); - let message_length_bytes = (message_length as i32).to_le_bytes(); - buf.splice(..4, message_length_bytes); -} - -pub struct Preview<'a> { - exit_thread: Arc, - dynamic_items: Arc>>>, - // update_thread: Option>, +pub struct Preview<'a, T: Send + Sync> { + pub exit_thread: Arc, + dynamic_items: Arc>>>, dynamic_channel: TcpStream, } -impl<'a> Preview<'a> { - pub fn connect_to_display_server(host: &str) -> io::Result { +impl<'a, T: Send + Sync> Preview<'a, T> { + pub fn connect_to_display_server(host: &str) -> Result, TevError> { let exit_thread = Arc::new(AtomicBool::new(false)); let dynamic_channel = TcpStream::connect(host)?; - let dynamic_items: Arc>> = Arc::new(Mutex::new(Vec::new())); + let dynamic_items: Arc>>> = Arc::new(Mutex::new(Vec::new())); Ok(Preview { exit_thread, @@ -255,67 +176,94 @@ impl<'a> Preview<'a> { } pub fn disconnect_from_display_server(&mut self) { + dbg!("Disconnecting from Tev"); self.exit_thread.store(true, Ordering::Relaxed); } - pub fn display_dynamic(&mut self, title: &str, resolution: Point2i, channel_names: Vec, - scope: &Scope<'a>, arc: Arc<&'a RwLock>>, get_tile_values: Function) { - dbg!("display_dynamic"); - + pub fn display_dynamic( + mut self, + title: &str, + resolution: Point2i, + channel_names: Vec, + scope: &Scope<'a>, + arc: Arc<&'a RwLock>>, + get_tile_values: Function, + ) { let cloned_exit_thread = self.exit_thread.clone(); let cloned_dynamic_items = self.dynamic_items.clone(); let mut cloned_channel = self.dynamic_channel.try_clone().unwrap(); - // self.update_thread = Some( scope.spawn(move |_| { - update_dynamic_items(cloned_exit_thread, &mut cloned_channel, cloned_dynamic_items); + update_dynamic_items( + cloned_exit_thread, + &mut cloned_channel, + cloned_dynamic_items, + ); }); - // ); - let mut display_items = self.dynamic_items.lock().unwrap(); - display_items.push(DisplayItem::new(title, resolution, channel_names, arc, get_tile_values)) + + let cloned_items = self.dynamic_items.clone(); + let mut display_items = cloned_items.lock().unwrap(); + display_items.push(DisplayItem::new( + title, + resolution, + channel_names, + arc, + get_tile_values, + )); } #[allow(unused)] - fn display_static(&mut self, title: &str, resolution: Point2i, vec: Pixels, channel_names: Vec, - get_tile_values: Function) { + fn display_static( + &mut self, + title: &str, + resolution: Point2i, + vec: Pixels, + channel_names: Vec, + get_tile_values: Function, + ) { let mut item = DisplayItem::new(title, resolution, channel_names, vec, get_tile_values); - if !item.display( &mut self.dynamic_channel) { + let mut client = TevClient::wrap(self.dynamic_channel.try_clone().unwrap()); + if !item.display_with_tev_client(&mut client) { println!("Unable to display static content {}", title); } } } -fn update_dynamic_items(exit_thread: Arc, channel: &mut TcpStream, items: Arc>>) { +fn update_dynamic_items( + exit_thread: Arc, + channel: &mut TcpStream, + items: Arc>>>, +) { + let mut client = TevClient::wrap(channel.try_clone().expect("Could not clone TCP Channel")); while !exit_thread.load(Ordering::Relaxed) { thread::sleep(time::Duration::from_millis(250)); - let mut items = items.lock().unwrap(); + let mut items = items.lock().expect("Could not lock"); for item in items.iter_mut() { - item.display( channel); + item.display_with_tev_client(&mut client); } } - let mut items = items.lock().unwrap(); + let mut items = items.lock().expect("Could not lock"); for item in items.iter_mut() { - item.display(channel); + item.display_with_tev_client(&mut client); } items.clear(); } - #[cfg(test)] mod test { + use std::sync::{Arc, Mutex, RwLock}; use std::thread; use std::time; + use std::time::Duration; + + use crossbeam_utils::thread::Scope; use crate::core::display::Preview; + use crate::core::film::Pixel; use crate::core::geometry::{Bounds2i, Point2i}; use crate::core::pbrt::Float; - use std::sync::{Arc, RwLock, Mutex}; - use std::time::Duration; - use crossbeam_utils::thread::Scope; - use crate::core::film::Pixel; - #[test] /// Manual test for tev remote @@ -323,7 +271,10 @@ mod test { let address = "127.0.0.1:14158"; let display = Preview::connect_to_display_server(address); - assert!(display.is_ok()); + // Do not fail the test if tev is not running + if display.is_err() { + return; + } let mut display = display.unwrap(); let resolution = Point2i { x: 200, y: 200 }; @@ -340,9 +291,12 @@ mod test { let data = &RwLock::new(image); let arc = Arc::new(data); - let get_values = move |b: Bounds2i, arc: Arc<&RwLock>>, width: usize, values: &mut Vec>| { - for col in b.p_min.y as usize.. b.p_max.y as usize { - for row in b.p_min.x as usize .. b.p_max.x as usize { + let get_values = move |b: Bounds2i, + arc: Arc<&RwLock>>, + width: usize, + values: &mut Vec>| { + for col in b.p_min.y as usize..b.p_max.y as usize { + for row in b.p_min.x as usize..b.p_max.x as usize { let v = { let clone = arc.read().unwrap(); clone[col * width + row].xyz @@ -355,9 +309,16 @@ mod test { } }; + // let display = Arc::new(Mutex::new(display)); crossbeam::scope(|scope| { - display.display_dynamic("Test", resolution, - vec!["R".to_string(), "G".to_string(), "B".to_string()], scope, arc.clone(), get_values); + display.display_dynamic( + "Test", + resolution, + vec!["R".to_string(), "G".to_string(), "B".to_string()], + scope, + arc.clone(), + get_values, + ); thread::sleep(time::Duration::from_millis(1000)); for cols in 0..resolution.x as usize { @@ -367,8 +328,9 @@ mod test { } } thread::sleep(time::Duration::from_millis(1000)); - display.disconnect_from_display_server(); - }).unwrap(); + // display.disconnect_from_display_server(); + }) + .unwrap(); } #[test] @@ -388,7 +350,8 @@ mod test { } thread::sleep(Duration::from_millis(3)); } - }).unwrap(); + }) + .unwrap(); let num = clone2.lock().unwrap(); println!("{:?}", num); diff --git a/src/core/integrator.rs b/src/core/integrator.rs index 6532b178..561a3339 100644 --- a/src/core/integrator.rs +++ b/src/core/integrator.rs @@ -2,7 +2,8 @@ //! class that implements the **Integrator** interface. // std -use std::sync::{Arc, RwLock}; +use std::sync::{Arc, Mutex, RwLock}; +use std::sync::atomic::Ordering; // pbrt use crate::blockqueue::BlockQueue; use crate::core::camera::{Camera, CameraSample}; @@ -38,12 +39,12 @@ pub enum Integrator { } impl Integrator { - pub fn render(&mut self, scene: &Scene, num_threads: u8) { + pub fn render(&mut self, scene: &Scene, num_threads: u8, display_server: Option) { match self { Integrator::BDPT(integrator) => integrator.render(scene, num_threads), Integrator::MLT(integrator) => integrator.render(scene, num_threads), Integrator::SPPM(integrator) => integrator.render(scene, num_threads), - Integrator::Sampler(integrator) => integrator.render(scene, num_threads), + Integrator::Sampler(integrator) => integrator.render(scene, num_threads, display_server), } } } @@ -69,7 +70,7 @@ impl SamplerIntegrator { /// All [SamplerIntegrators](enum.SamplerIntegrator.html) use the /// same render loop, but call an individual /// [li()](enum.SamplerIntegrator.html#method.li) method. - pub fn render(&mut self, scene: &Scene, num_threads: u8) { + pub fn render(&mut self, scene: &Scene, num_threads: u8, display_server: Option) { let film = self.get_camera().get_film(); let sample_bounds: Bounds2i = film.get_sample_bounds(); self.preprocess(scene); @@ -100,6 +101,7 @@ impl SamplerIntegrator { let camera = &self.get_camera(); let film = &film; let pixel_bounds = &self.get_pixel_bounds(); + let address = display_server.unwrap_or("".to_string()); crossbeam::scope(|scope| { let (pixel_tx, pixel_rx) = crossbeam_channel::bounded(num_cores); // spawn worker threads @@ -210,12 +212,11 @@ impl SamplerIntegrator { scope.spawn(move |_| { crossbeam::scope (|sub_scope| { // This should not - let address = "127.0.0.1:14158"; - let mut display = Preview::connect_to_display_server(address); + let display = Preview::connect_to_display_server(&address); + let exit_thread_clone = display.as_ref().unwrap().exit_thread.clone(); let connected = display.is_ok(); - if connected { - let display = display.as_mut().unwrap(); + let display = display.unwrap(); let arc = Arc::new(&film.pixels); // If we always need this function and never need another one we can move this inside the display @@ -224,7 +225,8 @@ impl SamplerIntegrator { for row in b.p_min.x..b.p_max.x { let v = { let vec = arc.read().unwrap(); - vec[col as usize * width + row as usize].xyz + let px = &vec[col as usize * width + row as usize]; + px. }; for (channel, value) in values.iter_mut().zip(v) { @@ -245,12 +247,11 @@ impl SamplerIntegrator { film.merge_film_tile(&film_tile); } if connected { - display.unwrap().disconnect_from_display_server(); + exit_thread_clone.store(true, Ordering::Relaxed); } - }).unwrap(); + }).unwrap_or_default(); }); - }) - .unwrap(); + }).expect("What am I even?"); } film.write_image(1.0 as Float); } From 2cbb5227d2a8f5fe7e493d69c66cb7deed837564 Mon Sep 17 00:00:00 2001 From: Jonas Paus Date: Sat, 17 Dec 2022 16:14:14 +0100 Subject: [PATCH 5/5] Add (hopefully) correct scaling to tev preview Definitely very hacky up at this point --- src/core/display.rs | 13 ++++++++----- src/core/film.rs | 4 ++-- src/core/integrator.rs | 14 +++++++++++++- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/core/display.rs b/src/core/display.rs index 25a066cb..0aa57527 100644 --- a/src/core/display.rs +++ b/src/core/display.rs @@ -84,8 +84,8 @@ impl<'a, T: Send + Sync> DisplayItem<'a, T> { &mut display_values, ); - debug_assert!(!display_values.iter().all(|x| x.iter().all(|y| *y == 0.0)), - "All display values are zero"); + // debug_assert!(!display_values.iter().all(|x| x.iter().all(|y| *y == 0.0)), + // "All display values are zero"); let channel0 = display_values[0].iter(); @@ -254,6 +254,7 @@ fn update_dynamic_items( #[cfg(test)] mod test { use std::sync::{Arc, Mutex, RwLock}; + use std::sync::atomic::Ordering::Relaxed; use std::thread; use std::time; use std::time::Duration; @@ -265,6 +266,7 @@ mod test { use crate::core::geometry::{Bounds2i, Point2i}; use crate::core::pbrt::Float; + #[ignore] #[test] /// Manual test for tev remote fn display_remote() { @@ -276,14 +278,15 @@ mod test { return; } let mut display = display.unwrap(); + let exit_thread = display.exit_thread.clone(); let resolution = Point2i { x: 200, y: 200 }; let mut image: Vec = Vec::with_capacity(resolution.x as usize); for x in 0..resolution.x { for y in 0..resolution.y { - let color = (x * y) as Float / (resolution.x * resolution.y - 1) as Float; + let mut pixel = Pixel::default(); - pixel.xyz = [color; 3]; + pixel.xyz = [x as Float / resolution.x as Float, y as Float / resolution.y as Float, 0.0]; image.push(pixel); } } @@ -328,7 +331,7 @@ mod test { } } thread::sleep(time::Duration::from_millis(1000)); - // display.disconnect_from_display_server(); + exit_thread.store(true, Relaxed); }) .unwrap(); } diff --git a/src/core/film.rs b/src/core/film.rs index f0947b18..f4199966 100644 --- a/src/core/film.rs +++ b/src/core/film.rs @@ -37,7 +37,7 @@ const FILTER_TABLE_WIDTH: usize = 16; #[derive(Debug, Clone)] pub struct Pixel { pub(crate) xyz: [Float; 3], - filter_weight_sum: Float, + pub(crate) filter_weight_sum: Float, splat_xyz: [Float; 3], // pad: Float, } @@ -168,7 +168,7 @@ pub struct Film { // Film Private Data pub pixels: RwLock>, filter_table: [Float; FILTER_TABLE_WIDTH * FILTER_TABLE_WIDTH], - scale: Float, + pub scale: Float, max_sample_luminance: Float, } diff --git a/src/core/integrator.rs b/src/core/integrator.rs index 888c2482..a176361f 100644 --- a/src/core/integrator.rs +++ b/src/core/integrator.rs @@ -28,6 +28,7 @@ use crate::integrators::volpath::VolPathIntegrator; use crate::integrators::whitted::WhittedIntegrator; use crate::core::film::Pixel; use crate::core::display::Preview; +use crate::core::spectrum::xyz_to_rgb; // see integrator.h @@ -232,7 +233,18 @@ impl SamplerIntegrator { for row in b.p_min.x..b.p_max.x { let v = { let vec = arc.read().unwrap(); - vec[col as usize * width + row as usize].xyz + let mut rgb = [0.0; 3]; + let pixels = &vec[col as usize * width + row as usize]; + xyz_to_rgb(&pixels.xyz, &mut rgb); + + let filter_weight_sum = pixels.filter_weight_sum; + if filter_weight_sum != 0.0 as Float { + let inv_wt: Float = 1.0 as Float / filter_weight_sum; + rgb[0] = (rgb[0] * inv_wt).max(0.0 as Float); + rgb[1] = (rgb[1] * inv_wt).max(0.0 as Float); + rgb[2] = (rgb[2] * inv_wt).max(0.0 as Float); + } + rgb }; for (channel, value) in values.iter_mut().zip(v) {