aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian T <julian@jtle.dk>2021-02-09 20:00:52 +0100
committerJulian T <julian@jtle.dk>2021-02-09 20:00:52 +0100
commit3a144c8c1fc83150fc06d792082db5cc4bce3cc5 (patch)
treee408c35a782c8595f2704c4d4dbff3424035ce82
parentbb5d37b3685ae8da38865f837cf697dac0055b9d (diff)
Add tiling for rendering
-rw-r--r--src/camera/film.rs2
-rw-r--r--src/core/bound.rs17
-rw-r--r--src/core/vector2.rs55
-rw-r--r--src/lib.rs3
-rw-r--r--src/main.rs13
-rw-r--r--src/render/coordinator.rs115
-rw-r--r--src/render/mod.rs5
-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()
+ )
+ }
}
diff --git a/src/lib.rs b/src/lib.rs
index 1593471..f3047d6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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) {