aboutsummaryrefslogtreecommitdiff
path: root/src/render
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 /src/render
parentbb5d37b3685ae8da38865f837cf697dac0055b9d (diff)
Add tiling for rendering
Diffstat (limited to 'src/render')
-rw-r--r--src/render/coordinator.rs115
-rw-r--r--src/render/mod.rs5
-rw-r--r--src/render/task.rs49
3 files changed, 169 insertions, 0 deletions
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/task.rs b/src/render/task.rs
new file mode 100644
index 0000000..017fe24
--- /dev/null
+++ b/src/render/task.rs
@@ -0,0 +1,49 @@
+//! Implements the main render loop
+//!
+//! This is not a final design
+use crate::camera::film::FilmTile;
+use crate::camera::Camera;
+use crate::trace::{DefaultTracer, Tracer};
+use crate::sample::Sampler;
+
+use crate::core::{Vector2f};
+use crate::Float;
+
+pub struct RenderTask {
+ pub tile: Box<FilmTile>,
+ samples: u32,
+ pub tile_index: i32,
+}
+
+pub struct RenderContext<'a> {
+ pub cam: &'a Camera,
+ pub trc: &'a DefaultTracer<'a>,
+}
+
+impl RenderTask {
+ 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) {
+ let corner = Vector2f::new_xy(x as Float, y as Float);
+
+ for _ in 0..self.samples {
+ let p = corner + sampler.get_sample_2d();
+
+ // Create a ray
+ let (r, _) = ctx.cam.generate_ray(&p, sampler);
+
+ self.tile.add_sample(&p, ctx.trc.trace(sampler, &r));
+ }
+ }
+
+ pub fn render(&mut self, ctx: &RenderContext, sampler: &mut dyn Sampler) {
+ let b = self.tile.bounds.clone();
+ for y in b.min.y .. b.max.y {
+ for x in b.min.x .. b.max.x {
+ self.render_at(ctx, x, y, sampler);
+ }
+ }
+ }
+}