diff options
author | Julian T <julian@jtle.dk> | 2021-02-09 20:00:52 +0100 |
---|---|---|
committer | Julian T <julian@jtle.dk> | 2021-02-09 20:00:52 +0100 |
commit | 3a144c8c1fc83150fc06d792082db5cc4bce3cc5 (patch) | |
tree | e408c35a782c8595f2704c4d4dbff3424035ce82 /src | |
parent | bb5d37b3685ae8da38865f837cf697dac0055b9d (diff) |
Add tiling for rendering
Diffstat (limited to 'src')
-rw-r--r-- | src/camera/film.rs | 2 | ||||
-rw-r--r-- | src/core/bound.rs | 17 | ||||
-rw-r--r-- | src/core/vector2.rs | 55 | ||||
-rw-r--r-- | src/lib.rs | 3 | ||||
-rw-r--r-- | src/main.rs | 13 | ||||
-rw-r--r-- | src/render/coordinator.rs | 115 | ||||
-rw-r--r-- | src/render/mod.rs | 5 | ||||
-rw-r--r-- | src/render/task.rs (renamed from src/render.rs) | 5 |
8 files changed, 179 insertions, 36 deletions
diff --git a/src/camera/film.rs b/src/camera/film.rs index 7a7f2dc..852ae9e 100644 --- a/src/camera/film.rs +++ b/src/camera/film.rs @@ -14,7 +14,7 @@ pub struct Pixel { } pub struct Film { - size: Vector2i, + pub size: Vector2i, pub frame: Bound2i, pixels: Vec<Pixel>, diff --git a/src/core/bound.rs b/src/core/bound.rs index a1c1070..404424e 100644 --- a/src/core/bound.rs +++ b/src/core/bound.rs @@ -45,6 +45,15 @@ impl<T: Number> Bound2<T> { ) } + /// Finds the intersected area between two bounds + pub fn intersect(&self, b: &Bound2<T>) -> Bound2<T> { + Bound2::new( + &Vector2::new_xy(max(self.min.x, b.min.x), max(self.min.y, b.min.y)), + &Vector2::new_xy(min(self.max.x, b.max.x), min(self.max.y, b.max.y)), + ) + } + + /// Calculates the diagonal vector /// /// Can be used to calculate the size of the bound @@ -96,14 +105,6 @@ impl From<&Bound2f> for Bound2i { } } -/// Finds the intersected area between two bounds -pub fn intersect<T: Number>(a: &Bound2<T>, b: &Bound2<T>) -> Bound2<T> { - Bound2::new( - &Vector2::new_xy(max(a.min.x, b.min.x), max(a.min.y, b.min.y)), - &Vector2::new_xy(min(a.max.x, b.max.x), min(a.max.y, b.max.y)), - ) -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/core/vector2.rs b/src/core/vector2.rs index 7e40394..0e0165e 100644 --- a/src/core/vector2.rs +++ b/src/core/vector2.rs @@ -2,7 +2,7 @@ //! //! This is implemented generictly with types that fit in the Number trait use crate::{Float, Number}; -use std::ops::{Sub, Add, Mul}; +use std::ops::{Sub, Add, Mul, Div}; use std::fmt; use std::cmp::min; @@ -15,6 +15,7 @@ pub struct Vector2<T: Number> { pub type Vector2f = Vector2<Float>; pub type Vector2i = Vector2<i32>; + impl<T: Number> Vector2<T> { pub fn new(initial: T) -> Vector2<T> { Vector2 { x: initial, y: initial } @@ -45,6 +46,16 @@ impl<T: Number> Add for Vector2<T> { } } +impl<T: Number> Mul for Vector2<T> { + type Output = Self; + fn mul(self, op: Self) -> Self::Output { + Self::new_xy( + self.x * op.x, + self.y * op.y, + ) + } +} + impl<T: Number> Mul<T> for Vector2<T> { type Output = Self; fn mul(self, op: T) -> Self::Output { @@ -55,25 +66,19 @@ impl<T: Number> Mul<T> for Vector2<T> { } } -impl<T: Number> fmt::Display for Vector2<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_fmt(format_args!("[{}, {}]", self.x, self.y)) - } -} - -impl Vector2f { - pub fn ceil(&self) -> Self { +impl<T: Number> Div for Vector2<T> { + type Output = Self; + fn div(self, op: Self) -> Self::Output { Self::new_xy( - self.x.ceil(), - self.y.ceil() - ) + self.x / op.x, + self.y / op.y, + ) } +} - pub fn floor(&self) -> Self { - Self::new_xy( - self.x.floor(), - self.y.floor() - ) +impl<T: Number> fmt::Display for Vector2<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_fmt(format_args!("[{}, {}]", self.x, self.y)) } } @@ -96,6 +101,8 @@ impl From<Vector2f> for Vector2i { } impl Vector2i { + pub const ZERO: Self = Vector2i {x: 0, y: 0}; + pub fn cap(&self, x: i32, y: i32) -> Self { Self::new_xy( min(self.x, x), @@ -108,6 +115,20 @@ impl Vector2f { pub fn len(&self) -> Float { (self.x*self.x + self.y*self.y).sqrt() } + + pub fn ceil(&self) -> Self { + Self::new_xy( + self.x.ceil(), + self.y.ceil() + ) + } + + pub fn floor(&self) -> Self { + Self::new_xy( + self.x.floor(), + self.y.floor() + ) + } } @@ -6,7 +6,7 @@ pub mod trace; pub mod sample; pub mod material; -use std::ops::{Add, Sub, Mul, DivAssign, AddAssign, Neg}; +use std::ops::{Add, Sub, Mul, DivAssign, AddAssign, Neg, Div}; use std::cmp; use std::fmt; use std::f64::consts::PI; @@ -21,6 +21,7 @@ pub trait Number: Add<Output = Self> + Mul<Output = Self> + Neg<Output = Self> + + Div<Output = Self> + DivAssign + AddAssign + fmt::Display diff --git a/src/main.rs b/src/main.rs index e606cba..2247834 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ use rendering::scene::{Scene, Object}; use rendering::trace::DefaultTracer; use rendering::scene::shapes::Sphere; use rendering::core::{Vector2i, Vector3f, Spectrum}; -use rendering::render::{RenderContext, RenderTask}; +use rendering::render::{RenderContext, RenderCoord}; use rendering::sample::UniformSampler; use rendering::material::{Reflectant, Lambertian}; @@ -33,19 +33,18 @@ fn main() { Object::new(brown.clone(), Box::new(Sphere::new(100.0, Vector3f::new_xyz(0.0, -100.5, -1.0)))), ]); - let tracer = DefaultTracer::new(&scn, None); + let tracer = DefaultTracer::new(&scn, Some(50)); let mut sampler = UniformSampler::new(); let ctx = RenderContext { cam: &cam, trc: &tracer }; let mut film = Film::new(res); - let tile = film.get_tile(&film.frame); + { + let coord = RenderCoord::new(&mut film, Vector2i::new_xy(32, 32), 100); - let mut task = RenderTask::new(Box::new(tile), 100); - task.render(&ctx, &mut sampler); - - film.commit_tile(&task.tile); + coord.work(&ctx, &mut sampler); + } let image = film.finalize_image(); if let Err(e) = image.save("test.png") { diff --git a/src/render/coordinator.rs b/src/render/coordinator.rs new file mode 100644 index 0000000..a9a7d06 --- /dev/null +++ b/src/render/coordinator.rs @@ -0,0 +1,115 @@ +//! Split a image into small tiles +//! +//! This enables multithreaded rendering. + +use super::{RenderTask, RenderContext}; +use crate::camera::Film; +use crate::core::{Bound2i, Vector2i}; +use crate::sample::Sampler; + +use std::sync::Mutex; + +struct Tiler { + tilesize: Vector2i, + + fullbound: Bound2i, + tilemap_size: Vector2i, + tilemap_area: i32, + + next_tile: i32, + tiles_done: i32, +} + +pub struct RenderCoord<'a> { + film: Mutex<&'a mut Film>, + samples: u32, + tiler: Mutex<Tiler>, +} + +impl Tiler { + pub fn new(fullsize: Vector2i, tilesize: Vector2i) -> Tiler { + assert!(tilesize.x != 0 && tilesize.y != 0); + + let tilemap_size = fullsize / tilesize; + + Self { + tilesize, + + fullbound: Bound2i::new(&Vector2i::ZERO, &fullsize), + tilemap_size, + tilemap_area: (tilemap_size.x * tilemap_size.y), + + next_tile: 0, + tiles_done: 0, + } + + } + + pub fn next_tile(&mut self) -> Option<(Bound2i, i32)> { + // Check if we are outside + if self.next_tile >= self.tilemap_area { + return None; + } + + // Convert the tile to xy in tilemap + let tile = Vector2i::new_xy(self.next_tile / self.tilemap_size.x, self.next_tile % self.tilemap_size.x); + let tile_index = self.next_tile; + + self.next_tile += 1; + + // Create a bound from the tilecoordinate + let tile_start = tile * self.tilesize; + // We need to make sure the resulting tile is inside the image bound + Some(( + Bound2i::new(&tile_start, &(tile_start + self.tilesize)).intersect(&self.fullbound), + tile_index, + )) + } + + /// Mark a index as done and return the overall process + pub fn mark_done(&mut self, _index: i32) -> f32 { + self.tiles_done += 1; + + println!("Progress: {}/{}", self.tiles_done, self.tilemap_area); + self.tiles_done as f32 / self.tilemap_area as f32 + } +} + +impl<'a> RenderCoord<'a> { + pub fn new(film: &'a mut Film, tilesize: Vector2i, samples: u32) -> Self { + let size = film.size; + + Self { + film: Mutex::new(film), + samples, + tiler: Mutex::new(Tiler::new(size, tilesize)), + } + } + + pub fn next_task(&self) -> Option<RenderTask> { + let (tile_bound, index) = self.tiler.lock().unwrap().next_tile()?; + + let film_tile = { + let film = self.film.lock().unwrap(); + Box::new(film.get_tile(&tile_bound)) + }; + + Some(RenderTask::new(film_tile, self.samples, index)) + } + + pub fn finish_task(&self, task: &RenderTask) { + { + let mut film = self.film.lock().unwrap(); + film.commit_tile(task.tile.as_ref()); + } + + self.tiler.lock().unwrap().mark_done(task.tile_index); + } + + pub fn work(&self, ctx: &RenderContext, sampler: &mut dyn Sampler) { + while let Some(mut task) = self.next_task() { + task.render(ctx, sampler); + self.finish_task(&task); + } + } +} diff --git a/src/render/mod.rs b/src/render/mod.rs new file mode 100644 index 0000000..b81aaf5 --- /dev/null +++ b/src/render/mod.rs @@ -0,0 +1,5 @@ +mod task; +mod coordinator; + +pub use task::{RenderContext, RenderTask}; +pub use coordinator::RenderCoord; diff --git a/src/render.rs b/src/render/task.rs index 9a6b905..017fe24 100644 --- a/src/render.rs +++ b/src/render/task.rs @@ -12,6 +12,7 @@ use crate::Float; pub struct RenderTask { pub tile: Box<FilmTile>, samples: u32, + pub tile_index: i32, } pub struct RenderContext<'a> { @@ -20,8 +21,8 @@ pub struct RenderContext<'a> { } impl RenderTask { - pub fn new(tile: Box<FilmTile>, samples: u32) -> Self { - Self { tile, samples } + pub fn new(tile: Box<FilmTile>, samples: u32, tile_index: i32) -> Self { + Self { tile, samples, tile_index } } fn render_at(&mut self, ctx: &RenderContext, x: i32, y: i32, sampler: &mut dyn Sampler) { |