From ff07b4f0340ac8fe65d1245be5622f3ea14602d1 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Sun, 6 Jan 2019 12:02:47 -0800 Subject: [PATCH] Cull on-the-fly --- utils/tile-svg/src/main.rs | 123 ++++++++++++++++++++++++------------- 1 file changed, 80 insertions(+), 43 deletions(-) diff --git a/utils/tile-svg/src/main.rs b/utils/tile-svg/src/main.rs index dc208af4..e8a1e91c 100644 --- a/utils/tile-svg/src/main.rs +++ b/utils/tile-svg/src/main.rs @@ -18,7 +18,7 @@ extern crate rand; use byteorder::{LittleEndian, WriteBytesExt}; use clap::{App, Arg}; -use euclid::{Point2D, Rect, Size2D, Transform2D, Vector2D}; +use euclid::{Point2D, Rect, Size2D, Transform2D}; use fixedbitset::FixedBitSet; use hashbrown::HashMap; use jemallocator; @@ -29,7 +29,7 @@ use lyon_path::PathEvent; use lyon_path::iterator::PathIter; use pathfinder_path_utils::stroke::{StrokeStyle, StrokeToFillIter}; use rayon::ThreadPoolBuilder; -use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; +use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use simdeez::Simd; use simdeez::overloads::I32x4_41; use simdeez::sse41::Sse41; @@ -41,6 +41,7 @@ use std::io::{self, BufWriter, Write}; use std::mem; use std::ops::{Mul, Sub}; use std::path::PathBuf; +use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; use std::time::Instant; use std::u16; use svgtypes::Color as SvgColor; @@ -102,14 +103,18 @@ fn main() { let start_time = Instant::now(); let mut built_scene = BuiltScene::new(&scene.view_box, vec![]); for _ in 0..runs { + let z_buffer = ZBuffer::new(&scene.view_box); + let built_objects = match jobs { - Some(1) => scene.build_objects_sequentially(), - _ => scene.build_objects(), + Some(1) => scene.build_objects_sequentially(&z_buffer), + _ => scene.build_objects(&z_buffer), }; + let built_shaders = scene.build_shaders(); built_scene = BuiltScene::from_objects_and_shaders(&scene.view_box, &built_objects, - built_shaders); + built_shaders, + &z_buffer); } let elapsed_time = Instant::now() - start_time; @@ -261,17 +266,25 @@ impl Scene { self.paints.iter().map(|paint| ObjectShader { fill_color: paint.color }).collect() } - fn build_objects_sequentially(&self) -> Vec { - self.objects.iter().map(|object| { - let mut tiler = Tiler::new(&object.outline, &self.view_box, ShaderId(object.paint.0)); + fn build_objects_sequentially(&self, z_buffer: &ZBuffer) -> Vec { + self.objects.iter().enumerate().map(|(object_index, object)| { + let mut tiler = Tiler::new(&object.outline, + &self.view_box, + object_index as u16, + ShaderId(object.paint.0), + z_buffer); tiler.generate_tiles(); tiler.built_object }).collect() } - fn build_objects(&self) -> Vec { - self.objects.par_iter().map(|object| { - let mut tiler = Tiler::new(&object.outline, &self.view_box, ShaderId(object.paint.0)); + fn build_objects(&self, z_buffer: &ZBuffer) -> Vec { + self.objects.par_iter().enumerate().map(|(object_index, object)| { + let mut tiler = Tiler::new(&object.outline, + &self.view_box, + object_index as u16, + ShaderId(object.paint.0), + z_buffer); tiler.generate_tiles(); tiler.built_object }).collect() @@ -813,9 +826,11 @@ bitflags! { const TILE_WIDTH: u32 = 16; const TILE_HEIGHT: u32 = 16; -struct Tiler<'o> { +struct Tiler<'o, 'z> { outline: &'o Outline, built_object: BuiltObject, + object_index: u16, + z_buffer: &'z ZBuffer, view_box: Rect, bounds: Rect, @@ -825,14 +840,21 @@ struct Tiler<'o> { old_active_edges: Vec, } -impl<'o> Tiler<'o> { - fn new(outline: &'o Outline, view_box: &Rect, shader: ShaderId) -> Tiler<'o> { +impl<'o, 'z> Tiler<'o, 'z> { + fn new(outline: &'o Outline, + view_box: &Rect, + object_index: u16, + shader: ShaderId, + z_buffer: &'z ZBuffer) + -> Tiler<'o, 'z> { let bounds = outline.bounds.intersection(&view_box).unwrap_or(Rect::zero()); let built_object = BuiltObject::new(&bounds, shader); Tiler { outline, built_object, + object_index, + z_buffer, view_box: *view_box, bounds, @@ -857,6 +879,8 @@ impl<'o> Tiler<'o> { self.generate_strip(strip_origin_y); } + // Cull. + self.cull(); //println!("{:#?}", self.built_object); } @@ -874,6 +898,15 @@ impl<'o> Tiler<'o> { } } + fn cull(&self) { + for solid_tile_index in self.built_object.solid_tiles.ones() { + let tile = &self.built_object.tiles[solid_tile_index]; + if tile.backdrop != 0 { + self.z_buffer.update(tile.tile_x, tile.tile_y, self.object_index); + } + } + } + fn process_old_active_edges(&mut self, tile_y: i16) { let mut current_tile_x = self.built_object.tile_rect.origin.x; let mut current_subtile_x = 0.0; @@ -1087,14 +1120,13 @@ impl BuiltScene { #[inline(never)] fn from_objects_and_shaders(view_box: &Rect, objects: &[BuiltObject], - shaders: Vec) + shaders: Vec, + z_buffer: &ZBuffer) -> BuiltScene { let mut scene = BuiltScene::new(view_box, shaders); scene.add_batch(); // Initialize z-buffer, and fill solid tiles. - let mut z_buffer = ZBuffer::new(&scene.tile_rect.size); - z_buffer.cull(&scene, objects); z_buffer.push_solid_tiles(&mut scene, objects); // Build batches. @@ -1112,7 +1144,9 @@ impl BuiltScene { } // Cull occluded tiles. - let scene_tile_index = scene.scene_tile_index(tile.tile_x, tile.tile_y); + let scene_tile_index = scene_tile_index(tile.tile_x, + tile.tile_y, + &scene.tile_rect); if !z_buffer.test(scene_tile_index, object_index as u16) { object_tile_index_to_scene_mask_tile_index.push(BLANK); continue; @@ -1168,59 +1202,62 @@ impl BuiltScene { }; } - fn scene_tile_index(&self, tile_x: i16, tile_y: i16) -> u32 { - (tile_y - self.tile_rect.origin.y) as u32 * self.tile_rect.size.width as u32 + - (tile_x - self.tile_rect.origin.x) as u32 - } - fn add_batch(&mut self) { self.batches.push(Batch::new()); } } +fn scene_tile_index(tile_x: i16, tile_y: i16, tile_rect: &Rect) -> u32 { + (tile_y - tile_rect.origin.y) as u32 * tile_rect.size.width as u32 + + (tile_x - tile_rect.origin.x) as u32 +} + // Culling struct ZBuffer { - buffer: Vec, + buffer: Vec, + tile_rect: Rect, } impl ZBuffer { - fn new(tile_size: &Size2D) -> ZBuffer { + fn new(view_box: &Rect) -> ZBuffer { + let tile_rect = round_rect_out_to_tile_bounds(view_box); + let tile_area = tile_rect.size.width as usize * tile_rect.size.height as usize; ZBuffer { - buffer: vec![0; tile_size.width as usize * tile_size.height as usize], + buffer: (0..tile_area).map(|_| AtomicUsize::new(0)).collect(), + tile_rect, } } fn test(&self, scene_tile_index: u32, object_index: u16) -> bool { - self.buffer[scene_tile_index as usize] < object_index + 1 + let existing_depth = self.buffer[scene_tile_index as usize].load(AtomicOrdering::SeqCst); + existing_depth < object_index as usize + 1 } - #[inline(never)] - fn cull(&mut self, scene: &BuiltScene, objects: &[BuiltObject]) { - for (object_index, object) in objects.iter().enumerate().rev() { - for solid_tile_index in object.solid_tiles.ones() { - let tile = &object.tiles[solid_tile_index]; - if tile.backdrop == 0 { - // Tile is transparent and can't be solid. - continue - } - - let scene_tile_index = scene.scene_tile_index(tile.tile_x, tile.tile_y); - if self.test(scene_tile_index, object_index as u16) { - self.buffer[scene_tile_index as usize] = (object_index + 1) as u16; - } + fn update(&self, tile_x: i16, tile_y: i16, object_index: u16) { + let scene_tile_index = scene_tile_index(tile_x, tile_y, &self.tile_rect) as usize; + let mut old_depth = self.buffer[scene_tile_index].load(AtomicOrdering::SeqCst); + let new_depth = (object_index + 1) as usize; + while old_depth < new_depth { + let prev_depth = self.buffer[scene_tile_index] + .compare_and_swap(old_depth, + new_depth, + AtomicOrdering::SeqCst); + if prev_depth == old_depth { + // Successfully written. + return } + old_depth = prev_depth; } } - #[inline(never)] fn push_solid_tiles(&self, scene: &mut BuiltScene, objects: &[BuiltObject]) { let tile_rect = scene.tile_rect; for scene_tile_y in 0..tile_rect.size.height { for scene_tile_x in 0..tile_rect.size.width { let scene_tile_index = scene_tile_y as usize * tile_rect.size.width as usize + scene_tile_x as usize; - let depth = self.buffer[scene_tile_index]; + let depth = self.buffer[scene_tile_index].load(AtomicOrdering::Relaxed); if depth == 0 { continue }