From 3a144c8c1fc83150fc06d792082db5cc4bce3cc5 Mon Sep 17 00:00:00 2001 From: Julian T Date: Tue, 9 Feb 2021 20:00:52 +0100 Subject: Add tiling for rendering --- src/render/coordinator.rs | 115 ++++++++++++++++++++++++++++++++++++++++++++++ src/render/mod.rs | 5 ++ src/render/task.rs | 49 ++++++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 src/render/coordinator.rs create mode 100644 src/render/mod.rs create mode 100644 src/render/task.rs (limited to 'src/render') 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, +} + +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 { + 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, + 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, 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); + } + } + } +} -- cgit v1.2.3