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 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 src/render/coordinator.rs (limited to 'src/render/coordinator.rs') 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); + } + } +} -- cgit v1.2.3