Refactor main rendering loop to use TileIterator

This removes the old multithreading code, but will be using rayon
soon.
This commit is contained in:
Matthew Gordon 2020-02-20 16:47:15 -05:00
parent d21d288013
commit bdd05f3527
5 changed files with 99 additions and 125 deletions

View File

@ -12,8 +12,8 @@ use crate::Real;
use std::sync::{Arc, Mutex};
struct ImageSampler<T: Real> {
image_height_pixels: u32,
image_width_pixels: u32,
image_height_pixels: usize,
image_width_pixels: usize,
film_width: T,
film_height: T,
@ -23,7 +23,7 @@ struct ImageSampler<T: Real> {
}
impl<T: Real> ImageSampler<T> {
pub fn new(width: u32, height: u32, camera_location: Point3<T>) -> ImageSampler<T> {
pub fn new(width: usize, height: usize, camera_location: Point3<T>) -> ImageSampler<T> {
let (film_width, film_height) = {
let width: T = convert(width as f64);
let height: T = convert(height as f64);
@ -44,7 +44,7 @@ impl<T: Real> ImageSampler<T> {
}
}
fn scale(i: u32, n: u32, l: T) -> T {
fn scale(i: usize, n: usize, l: T) -> T {
let one: T = convert(1.0);
let n: T = convert(n as f64);
let i: T = convert(i as f64);
@ -52,7 +52,7 @@ impl<T: Real> ImageSampler<T> {
(i + convert(0.5)) * pixel_size
}
fn ray_for_pixel(&self, row: u32, column: u32) -> Ray<T> {
fn ray_for_pixel(&self, row: usize, column: usize) -> Ray<T> {
Ray::new(
self.camera_location,
Vector3::new(
@ -75,12 +75,12 @@ pub fn render_scene<T: Real>(output_image: Arc<Mutex<ImageRgbF<T>>>, scene: Arc<
pub fn partial_render_scene<T: Real>(
output_image: Arc<Mutex<ImageRgbF<T>>>,
scene: Arc<Scene<T>>,
row_start: u32,
row_end: u32,
column_start: u32,
column_end: u32,
height: u32,
width: u32,
row_start: usize,
row_end: usize,
column_start: usize,
column_end: usize,
height: usize,
width: usize,
) {
let image_sampler = ImageSampler::new(width, height, scene.camera_location);
let ambient_intensity: T = convert(0.0);

View File

@ -8,12 +8,12 @@ use crate::Real;
pub struct ImageRgbU8 {
pixel_data: Vec<u8>,
width: u32,
height: u32,
width: usize,
height: usize,
}
impl ImageRgbU8 {
pub fn new(width: u32, height: u32) -> ImageRgbU8 {
pub fn new(width: usize, height: usize) -> ImageRgbU8 {
ImageRgbU8 {
width,
height,
@ -28,7 +28,7 @@ impl ImageRgbU8 {
self
}
pub fn get_colour(&self, row: u32, column: u32) -> ColourRgbU8 {
pub fn get_colour(&self, row: usize, column: usize) -> ColourRgbU8 {
assert!(row < self.height && column < self.width);
let index = self.calculate_index(row, column);
ColourRgbU8 {
@ -38,7 +38,7 @@ impl ImageRgbU8 {
}
}
pub fn set_colour(&mut self, row: u32, column: u32, colour: ColourRgbU8) {
pub fn set_colour(&mut self, row: usize, column: usize, colour: ColourRgbU8) {
assert!(row < self.height && column < self.width);
let index = self.calculate_index(row, column);
self.pixel_data[index..index + 3].copy_from_slice(&colour.values[..]);
@ -48,19 +48,19 @@ impl ImageRgbU8 {
&self.pixel_data
}
pub fn get_width(&self) -> u32 {
pub fn get_width(&self) -> usize {
self.width
}
pub fn get_height(&self) -> u32 {
pub fn get_height(&self) -> usize {
self.height
}
pub fn num_channels() -> u32 {
pub fn num_channels() -> usize {
3
}
fn calculate_index(&self, row: u32, column: u32) -> usize {
fn calculate_index(&self, row: usize, column: usize) -> usize {
assert!(row < self.height && column < self.width);
(((self.height - (row + 1)) * self.width + column) * Self::num_channels()) as usize
}
@ -68,12 +68,12 @@ impl ImageRgbU8 {
pub struct ImageRgbF<T: Real> {
pixel_data: Vec<T>,
width: u32,
height: u32,
width: usize,
height: usize,
}
impl<T: Real> ImageRgbF<T> {
pub fn new(width: u32, height: u32) -> ImageRgbF<T> {
pub fn new(width: usize, height: usize) -> ImageRgbF<T> {
ImageRgbF {
width,
height,
@ -88,13 +88,13 @@ impl<T: Real> ImageRgbF<T> {
self
}
pub fn get_colour(&self, row: u32, column: u32) -> ColourRgbF<T> {
pub fn get_colour(&self, row: usize, column: usize) -> ColourRgbF<T> {
assert!(row < self.height && column < self.width);
let index = self.calculate_index(row, column);
ColourRgbF::from_vector3(&Vector3::from_row_slice(&self.pixel_data[index..index + 3]))
}
pub fn set_colour(&mut self, row: u32, column: u32, colour: ColourRgbF<T>) {
pub fn set_colour(&mut self, row: usize, column: usize, colour: ColourRgbF<T>) {
assert!(row < self.height && column < self.width);
let index = self.calculate_index(row, column);
self.pixel_data[index..index + 3].copy_from_slice(&colour.as_vector3().as_slice());
@ -104,19 +104,19 @@ impl<T: Real> ImageRgbF<T> {
&self.pixel_data
}
pub fn get_width(&self) -> u32 {
pub fn get_width(&self) -> usize {
self.width
}
pub fn get_height(&self) -> u32 {
pub fn get_height(&self) -> usize {
self.height
}
pub fn num_channels() -> u32 {
pub fn num_channels() -> usize {
3
}
fn calculate_index(&self, row: u32, column: u32) -> usize {
fn calculate_index(&self, row: usize, column: usize) -> usize {
assert!(row < self.height && column < self.width);
(((self.height - (row + 1)) * self.width + column) * Self::num_channels()) as usize
}

View File

@ -7,20 +7,17 @@ use std::time::Duration;
use nalgebra::{Point3, Vector3};
use itertools::iproduct;
use std::cmp::min;
use std::path::Path;
use std::sync::{Arc, Mutex};
use std::thread;
use vanrijn::camera::partial_render_scene;
use vanrijn::colour::{ColourRgbF, NamedColour};
use vanrijn::image::{ClampingToneMapper, ImageRgbF, ImageRgbU8, ToneMapper};
use vanrijn::materials::{LambertianMaterial, PhongMaterial, ReflectiveMaterial};
use vanrijn::mesh::load_obj;
use vanrijn::raycasting::{Primitive, Plane, Sphere};
use vanrijn::raycasting::{Plane, Primitive, Sphere};
use vanrijn::scene::Scene;
use vanrijn::util::TileIterator;
fn update_texture(image: &ImageRgbU8, texture: &mut Texture) {
texture
@ -33,8 +30,8 @@ fn update_texture(image: &ImageRgbU8, texture: &mut Texture) {
}
fn init_canvas(
image_width: u32,
image_height: u32,
image_width: usize,
image_height: usize,
) -> Result<(Sdl, Canvas<sdl2::video::Window>), Box<dyn std::error::Error>> {
let sdl_context = sdl2::init()?;
let video_subsystem = sdl_context.video()?;
@ -50,8 +47,8 @@ fn init_canvas(
}
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
let image_width = 1200;
let image_height = 900;
let image_width = 320usize;
let image_height = 240usize;
let (sdl_context, mut canvas) = init_canvas(image_width, image_height)?;
@ -117,48 +114,21 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
});
let mut event_pump = sdl_context.event_pump()?;
let mut i = 0;
'running: loop {
let subtile_size = 16;
let tile_divisions = 4;
let tile_size = subtile_size * tile_divisions;
for tile_row in 0..=(image_height + 1) / tile_size {
for tile_column in 0..=(image_width + 1) / tile_size {
//let row_start = tile_row * tile_size;
//let row_end = min(tile_row * tile_size + tile_size, image_height);
//let column_start = tile_column * tile_size;
//let column_end = min(tile_column * tile_size + tile_size, image_width);
let join_handles: Vec<_> = iproduct!(0..tile_divisions, 0..tile_divisions)
.map(|(tile_i, tile_j)| {
let start_i = tile_row * tile_size + tile_i * subtile_size;
let start_j = tile_column * tile_size + tile_j * subtile_size;
(
start_i,
min(start_i + subtile_size, image_height),
start_j,
min(start_j + subtile_size, image_width),
)
})
.map(|(i_min, i_max, j_min, j_max)| {
'running: for tile in TileIterator::new(image_width as usize, image_height as usize, 16) {
let image_ptr = output_image.clone();
let scene_ptr = scene.clone();
thread::spawn(move || {
partial_render_scene(
image_ptr,
scene_ptr,
i_min,
i_max,
j_min,
j_max,
tile.start_row,
tile.end_row,
tile.start_column,
tile.end_column,
image_height,
image_width,
);
})
})
.collect();
for h in join_handles {
h.join();
}
let locked_image = output_image.lock().unwrap();
let mut output_image_rgbu8 = ImageRgbU8::new(image_width, image_height);
ClampingToneMapper {}.apply_tone_mapping(&locked_image, &mut output_image_rgbu8);
@ -177,7 +147,9 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
}
}
}
}
let mut i = 0;
'running: loop {
i = (i + 1) % 255;
for event in event_pump.poll_iter() {
match event {

View File

@ -1,4 +1,4 @@
use nalgebra::Point3;
use nalgebra::{Point2, Point3};
use crate::Real;

View File

@ -1,16 +1,17 @@
#[derive(Debug)]
pub struct Tile {
pub start_x: usize,
pub end_x: usize,
pub start_y: usize,
pub end_y: usize,
pub start_column: usize,
pub end_column: usize,
pub start_row: usize,
pub end_row: usize,
}
pub struct TileIterator {
tile_size: usize,
total_height: usize,
total_width: usize,
current_x: usize,
current_y: usize,
current_column: usize,
current_row: usize,
}
impl TileIterator {
@ -21,8 +22,8 @@ impl TileIterator {
tile_size,
total_width,
total_height,
current_x: 0,
current_y: 0,
current_column: 0,
current_row: 0,
}
}
}
@ -31,25 +32,25 @@ impl Iterator for TileIterator {
type Item = Tile;
fn next(&mut self) -> Option<Tile> {
if self.current_y >= self.total_height {
if self.current_row >= self.total_height {
None
} else {
let start_x = self.current_x;
let end_x = self.total_width.min(start_x + self.tile_size);
let start_y = self.current_y;
let end_y = self.total_height.min(start_y + self.tile_size);
let start_column = self.current_column;
let end_column = self.total_width.min(start_column + self.tile_size);
let start_row = self.current_row;
let end_row = self.total_height.min(start_row + self.tile_size);
self.current_x += self.tile_size;
if self.current_x >= self.total_width {
self.current_y += self.tile_size;
self.current_x = 0;
self.current_column += self.tile_size;
if self.current_column >= self.total_width {
self.current_row += self.tile_size;
self.current_column = 0;
}
Some(Tile {
start_x,
end_x,
start_y,
end_y,
start_column,
end_column,
start_row,
end_row,
})
}
}
@ -95,7 +96,7 @@ mod tests {
#[quickcheck]
fn tiles_are_expected_size(width: usize, height: usize, tile_size: usize) -> TestResult {
let max_size = 10000;
// Check size of width and height first, since width*height my overflow.
// Check size of width and height first, since width*height might overflow.
if width > max_size || height > max_size || width * height > max_size {
return TestResult::discard();
}
@ -105,7 +106,8 @@ mod tests {
let mut target = TileIterator::new(width, height, tile_size);
TestResult::from_bool(target.all(|tile| {
tile.end_x - tile.start_x <= tile_size && tile.end_y - tile.start_y <= tile_size
tile.end_column - tile.start_column <= tile_size
&& tile.end_row - tile.start_row <= tile_size
}))
}
@ -116,7 +118,7 @@ mod tests {
tile_size: usize,
) -> TestResult {
let max_size = 10000;
// Check size of width and height first, since width*height my overflow.
// Check size of width and height first, since width*height might overflow.
if width > max_size || height > max_size || width * height > max_size {
return TestResult::discard();
}
@ -128,9 +130,9 @@ mod tests {
let mut index_counts = vec![0; width * height];
let mut total_count = 0;
for tile in target {
for x in tile.start_x..tile.end_x {
for y in tile.start_y..tile.end_y {
index_counts[y * width + x] += 1;
for column in tile.start_column..tile.end_column {
for row in tile.start_row..tile.end_row {
index_counts[row * width + column] += 1;
total_count += 1;
if total_count > width * height {
return TestResult::failed();