From 31becb1570bd738f290591eee0178a2dad696c89 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 14 Feb 2020 20:53:27 -0800 Subject: [PATCH] Flatten objects' tiles ourselves instead of having Rayon do it. A prerequisite for clips. --- demo/common/src/concurrent.rs | 8 +-- renderer/src/builder.rs | 96 ++++++++++++++++------------- renderer/src/concurrent/executor.rs | 12 ++-- renderer/src/concurrent/rayon.rs | 6 +- renderer/src/gpu_data.rs | 10 --- renderer/src/tiles.rs | 83 ++++++++++++------------- 6 files changed, 106 insertions(+), 109 deletions(-) diff --git a/demo/common/src/concurrent.rs b/demo/common/src/concurrent.rs index 77144624..de8caf73 100644 --- a/demo/common/src/concurrent.rs +++ b/demo/common/src/concurrent.rs @@ -34,12 +34,12 @@ impl DemoExecutor { } impl Executor for DemoExecutor { - fn flatten_into_vector(&self, length: usize, builder: F) -> Vec - where T: Send, F: Fn(usize) -> Vec + Send + Sync { + fn build_vector(&self, length: usize, builder: F) -> Vec + where T: Send, F: Fn(usize) -> T + Send + Sync { if self.sequential_mode { - SequentialExecutor.flatten_into_vector(length, builder) + SequentialExecutor.build_vector(length, builder) } else { - RayonExecutor.flatten_into_vector(length, builder) + RayonExecutor.build_vector(length, builder) } } } diff --git a/renderer/src/builder.rs b/renderer/src/builder.rs index c37a4dc1..b98d52da 100644 --- a/renderer/src/builder.rs +++ b/renderer/src/builder.rs @@ -11,7 +11,7 @@ //! Packs data onto the GPU. use crate::concurrent::executor::Executor; -use crate::gpu_data::{AlphaTile, BuiltObject, FillBatchPrimitive, RenderCommand}; +use crate::gpu_data::{AlphaTile, FillBatchPrimitive, RenderCommand, TileObjectPrimitive}; use crate::options::{PreparedBuildOptions, RenderCommandListener}; use crate::paint::{PaintInfo, PaintMetadata}; use crate::scene::Scene; @@ -23,7 +23,6 @@ use pathfinder_geometry::vector::{Vector2F, Vector2I}; use pathfinder_geometry::rect::{RectF, RectI}; use pathfinder_geometry::util; use pathfinder_simd::default::{F32x4, I32x4}; -use std::i16; use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::Instant; use std::u16; @@ -68,7 +67,7 @@ impl<'a> SceneBuilder<'a> { self.listener.send(RenderCommand::AddPaintData(paint_data)); let effective_view_box = self.scene.effective_view_box(self.built_options); - let alpha_tiles = executor.flatten_into_vector(path_count, |path_index| { + let built_objects = executor.build_vector(path_count, |path_index| { self.build_path(path_index, effective_view_box, &self.built_options, @@ -76,7 +75,7 @@ impl<'a> SceneBuilder<'a> { &paint_metadata) }); - self.finish_building(&paint_metadata, alpha_tiles); + self.finish_building(&paint_metadata, built_objects); let build_time = Instant::now() - start_time; self.listener.send(RenderCommand::Finish { build_time }); @@ -89,7 +88,7 @@ impl<'a> SceneBuilder<'a> { built_options: &PreparedBuildOptions, scene: &Scene, paint_metadata: &[PaintMetadata], - ) -> Vec { + ) -> BuiltObject { let path_object = &scene.paths[path_index]; let outline = scene.apply_render_options(path_object.outline(), built_options); let paint_id = path_object.paint(); @@ -102,33 +101,27 @@ impl<'a> SceneBuilder<'a> { tiler.generate_tiles(); - self.listener.send(RenderCommand::AddFills(tiler.built_object.fills)); - tiler.built_object.alpha_tiles + self.listener.send(RenderCommand::AddFills(tiler.object_builder.fills)); + tiler.object_builder.built_object } - fn cull_alpha_tiles(&self, alpha_tiles: &mut Vec) { - for alpha_tile in alpha_tiles { - let alpha_tile_coords = alpha_tile.upper_left.tile_position(); - if self - .z_buffer - .test(alpha_tile_coords, alpha_tile.upper_left.object_index as u32) - { - continue; + fn cull_tiles(&self, built_objects: Vec) -> CulledTiles { + let mut culled_tiles = CulledTiles { alpha_tiles: vec![] }; + + for built_object in built_objects { + for alpha_tile in built_object.alpha_tiles { + let alpha_tile_coords = alpha_tile.upper_left.tile_position(); + if self.z_buffer.test(alpha_tile_coords, + alpha_tile.upper_left.object_index as u32) { + culled_tiles.alpha_tiles.push(alpha_tile); + } } - - // FIXME(pcwalton): Clean this up. - alpha_tile.upper_left.tile_x = i16::MIN; - alpha_tile.upper_left.tile_y = i16::MIN; - alpha_tile.upper_right.tile_x = i16::MIN; - alpha_tile.upper_right.tile_y = i16::MIN; - alpha_tile.lower_left.tile_x = i16::MIN; - alpha_tile.lower_left.tile_y = i16::MIN; - alpha_tile.lower_right.tile_x = i16::MIN; - alpha_tile.lower_right.tile_y = i16::MIN; } + + culled_tiles } - fn pack_alpha_tiles(&mut self, paint_metadata: &[PaintMetadata], alpha_tiles: Vec) { + fn pack_tiles(&mut self, paint_metadata: &[PaintMetadata], culled_tiles: CulledTiles) { let path_count = self.scene.paths.len() as u32; let solid_tiles = self.z_buffer.build_solid_tiles(&self.scene.paths, paint_metadata, @@ -136,17 +129,17 @@ impl<'a> SceneBuilder<'a> { if !solid_tiles.is_empty() { self.listener.send(RenderCommand::SolidTile(solid_tiles)); } - if !alpha_tiles.is_empty() { - self.listener.send(RenderCommand::AlphaTile(alpha_tiles)); + if !culled_tiles.alpha_tiles.is_empty() { + self.listener.send(RenderCommand::AlphaTile(culled_tiles.alpha_tiles)); } } fn finish_building(&mut self, paint_metadata: &[PaintMetadata], - mut alpha_tiles: Vec) { + built_objects: Vec) { self.listener.send(RenderCommand::FlushFills); - self.cull_alpha_tiles(&mut alpha_tiles); - self.pack_alpha_tiles(paint_metadata, alpha_tiles); + let culled_tiles = self.cull_tiles(built_objects); + self.pack_tiles(paint_metadata, culled_tiles); } } @@ -158,14 +151,14 @@ pub struct TileStats { // Utilities for built objects -impl BuiltObject { - pub(crate) fn new(bounds: RectF) -> BuiltObject { +impl ObjectBuilder { + pub(crate) fn new(bounds: RectF) -> ObjectBuilder { let tile_rect = tiles::round_rect_out_to_tile_bounds(bounds); let tiles = DenseTileMap::new(tile_rect); - BuiltObject { + ObjectBuilder { + built_object: BuiltObject { alpha_tiles: vec![] }, bounds, fills: vec![], - alpha_tiles: vec![], tiles, } } @@ -177,7 +170,7 @@ impl BuiltObject { fn add_fill( &mut self, - builder: &SceneBuilder, + scene_builder: &SceneBuilder, segment: LineSegment2F, tile_coords: Vector2I, ) { @@ -207,7 +200,7 @@ impl BuiltObject { } // Allocate global tile if necessary. - let alpha_tile_index = self.get_or_allocate_alpha_tile_index(builder, tile_coords); + let alpha_tile_index = self.get_or_allocate_alpha_tile_index(scene_builder, tile_coords); // Pack whole pixels. let px = (segment & I32x4::splat(0xf00)).to_u32x4(); @@ -229,7 +222,7 @@ impl BuiltObject { fn get_or_allocate_alpha_tile_index( &mut self, - builder: &SceneBuilder, + scene_builder: &SceneBuilder, tile_coords: Vector2I, ) -> u16 { let local_tile_index = self.tiles.coords_to_index_unchecked(tile_coords); @@ -238,7 +231,7 @@ impl BuiltObject { return alpha_tile_index; } - let alpha_tile_index = builder + let alpha_tile_index = scene_builder .next_alpha_tile_index .fetch_add(1, Ordering::Relaxed) as u16; self.tiles.data[local_tile_index].alpha_tile_index = alpha_tile_index; @@ -247,7 +240,7 @@ impl BuiltObject { pub(crate) fn add_active_fill( &mut self, - builder: &SceneBuilder, + scene_builder: &SceneBuilder, left: f32, right: f32, mut winding: i32, @@ -272,7 +265,7 @@ impl BuiltObject { ); while winding != 0 { - self.add_fill(builder, segment, tile_coords); + self.add_fill(scene_builder, segment, tile_coords); if winding < 0 { winding += 1 } else { @@ -283,7 +276,7 @@ impl BuiltObject { pub(crate) fn generate_fill_primitives_for_line( &mut self, - builder: &SceneBuilder, + scene_builder: &SceneBuilder, mut segment: LineSegment2F, tile_y: i32, ) { @@ -331,7 +324,7 @@ impl BuiltObject { let fill_segment = LineSegment2F::new(fill_from, fill_to); let fill_tile_coords = Vector2I::new(subsegment_tile_x, tile_y); - self.add_fill(builder, fill_segment, fill_tile_coords); + self.add_fill(scene_builder, fill_segment, fill_tile_coords); } } @@ -345,3 +338,20 @@ impl BuiltObject { self.tiles.index_to_coords(tile_index as usize) } } + +#[derive(Debug)] +pub(crate) struct BuiltObject { + pub alpha_tiles: Vec, +} + +#[derive(Debug)] +pub(crate) struct ObjectBuilder { + pub built_object: BuiltObject, + pub fills: Vec, + pub tiles: DenseTileMap, + pub bounds: RectF, +} + +struct CulledTiles { + alpha_tiles: Vec, +} diff --git a/renderer/src/concurrent/executor.rs b/renderer/src/concurrent/executor.rs index 0ed50bae..150f4d32 100644 --- a/renderer/src/concurrent/executor.rs +++ b/renderer/src/concurrent/executor.rs @@ -15,17 +15,17 @@ pub trait Executor { /// Like the Rayon snippet: /// /// ```norun - /// (0..length).into_par_iter().flat_map(builder).collect() + /// (0..length).into_par_iter().map(builder).collect() /// ``` - fn flatten_into_vector(&self, length: usize, builder: F) -> Vec - where T: Send, F: Fn(usize) -> Vec + Send + Sync; + fn build_vector(&self, length: usize, builder: F) -> Vec + where T: Send, F: Fn(usize) -> T + Send + Sync; } pub struct SequentialExecutor; impl Executor for SequentialExecutor { - fn flatten_into_vector(&self, length: usize, builder: F) -> Vec - where T: Send, F: Fn(usize) -> Vec + Send + Sync { - (0..length).into_iter().flat_map(builder).collect() + fn build_vector(&self, length: usize, builder: F) -> Vec + where T: Send, F: Fn(usize) -> T + Send + Sync { + (0..length).into_iter().map(builder).collect() } } diff --git a/renderer/src/concurrent/rayon.rs b/renderer/src/concurrent/rayon.rs index 45191bcf..e11d28b1 100644 --- a/renderer/src/concurrent/rayon.rs +++ b/renderer/src/concurrent/rayon.rs @@ -16,8 +16,8 @@ use rayon::iter::{IntoParallelIterator, ParallelIterator}; pub struct RayonExecutor; impl Executor for RayonExecutor { - fn flatten_into_vector(&self, length: usize, builder: F) -> Vec - where T: Send, F: Fn(usize) -> Vec + Send + Sync { - (0..length).into_par_iter().flat_map(builder).collect() + fn build_vector(&self, length: usize, builder: F) -> Vec + where T: Send, F: Fn(usize) -> T + Send + Sync { + (0..length).into_par_iter().map(builder).collect() } } diff --git a/renderer/src/gpu_data.rs b/renderer/src/gpu_data.rs index fa09d83a..aa3b8b2f 100644 --- a/renderer/src/gpu_data.rs +++ b/renderer/src/gpu_data.rs @@ -11,22 +11,12 @@ //! Packed data ready to be sent to the GPU. use crate::options::BoundingQuad; -use crate::tile_map::DenseTileMap; use pathfinder_color::ColorU; use pathfinder_geometry::line_segment::{LineSegmentU4, LineSegmentU8}; -use pathfinder_geometry::rect::RectF; use pathfinder_geometry::vector::Vector2I; use std::fmt::{Debug, Formatter, Result as DebugResult}; use std::time::Duration; -#[derive(Debug)] -pub(crate) struct BuiltObject { - pub bounds: RectF, - pub fills: Vec, - pub alpha_tiles: Vec, - pub tiles: DenseTileMap, -} - pub enum RenderCommand { Start { path_count: usize, bounding_quad: BoundingQuad }, AddPaintData(PaintData), diff --git a/renderer/src/tiles.rs b/renderer/src/tiles.rs index bbe3c1ea..5b5ce353 100644 --- a/renderer/src/tiles.rs +++ b/renderer/src/tiles.rs @@ -8,9 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::builder::SceneBuilder; +use crate::builder::{ObjectBuilder, SceneBuilder}; use crate::gpu::renderer::MASK_TILES_ACROSS; -use crate::gpu_data::{AlphaTile, AlphaTileVertex, BuiltObject, TileObjectPrimitive}; +use crate::gpu_data::{AlphaTile, AlphaTileVertex, TileObjectPrimitive}; use crate::paint::PaintMetadata; use pathfinder_content::outline::{Contour, Outline, PointIndex}; use pathfinder_content::segment::Segment; @@ -28,9 +28,9 @@ pub const TILE_WIDTH: u32 = 16; pub const TILE_HEIGHT: u32 = 16; pub(crate) struct Tiler<'a> { - builder: &'a SceneBuilder<'a>, + scene_builder: &'a SceneBuilder<'a>, + pub(crate) object_builder: ObjectBuilder, outline: &'a Outline, - pub built_object: BuiltObject, paint_metadata: &'a PaintMetadata, object_index: u16, @@ -42,7 +42,7 @@ pub(crate) struct Tiler<'a> { impl<'a> Tiler<'a> { #[allow(clippy::or_fun_call)] pub(crate) fn new( - builder: &'a SceneBuilder<'a>, + scene_builder: &'a SceneBuilder<'a>, outline: &'a Outline, view_box: RectF, object_index: u16, @@ -52,12 +52,12 @@ impl<'a> Tiler<'a> { .bounds() .intersection(view_box) .unwrap_or(RectF::default()); - let built_object = BuiltObject::new(bounds); + let object_builder = ObjectBuilder::new(bounds); Tiler { - builder, + scene_builder, + object_builder, outline, - built_object, object_index, paint_metadata, @@ -76,7 +76,7 @@ impl<'a> Tiler<'a> { self.old_active_edges.clear(); // Generate strips. - let tile_rect = self.built_object.tile_rect(); + let tile_rect = self.object_builder.tile_rect(); for strip_origin_y in tile_rect.min_y()..tile_rect.max_y() { self.generate_strip(strip_origin_y); } @@ -85,7 +85,7 @@ impl<'a> Tiler<'a> { self.pack_and_cull(); // Done! - debug!("{:#?}", self.built_object); + debug!("{:#?}", self.object_builder.built_object); } fn generate_strip(&mut self, strip_origin_y: i32) { @@ -108,10 +108,8 @@ impl<'a> Tiler<'a> { } fn pack_and_cull(&mut self) { - for (tile_index, tile) in self.built_object.tiles.data.iter().enumerate() { - let tile_coords = self - .built_object - .local_tile_index_to_coords(tile_index as u32); + for (tile_index, tile) in self.object_builder.tiles.data.iter().enumerate() { + let tile_coords = self.object_builder.local_tile_index_to_coords(tile_index as u32); if tile.is_solid() { // Blank tiles are always skipped. @@ -121,12 +119,12 @@ impl<'a> Tiler<'a> { // If this is a solid tile, poke it into the Z-buffer and stop here. if self.paint_metadata.is_opaque { - self.builder.z_buffer.update(tile_coords, self.object_index); + self.scene_builder.z_buffer.update(tile_coords, self.object_index); continue; } } - self.built_object.alpha_tiles.push(AlphaTile { + self.object_builder.built_object.alpha_tiles.push(AlphaTile { upper_left: AlphaTileVertex::new(tile_coords, tile.alpha_tile_index as u16, Vector2I::default(), @@ -156,7 +154,7 @@ impl<'a> Tiler<'a> { } fn process_old_active_edges(&mut self, tile_y: i32) { - let mut current_tile_x = self.built_object.tile_rect().min_x(); + let mut current_tile_x = self.object_builder.tile_rect().min_x(); let mut current_subtile_x = 0.0; let mut current_winding = 0; @@ -208,8 +206,8 @@ impl<'a> Tiler<'a> { (i32::from(current_tile_x) * TILE_WIDTH as i32) as f32 + current_subtile_x; let tile_right_x = ((i32::from(current_tile_x) + 1) * TILE_WIDTH as i32) as f32; let current_tile_coords = Vector2I::new(current_tile_x, tile_y); - self.built_object.add_active_fill( - self.builder, + self.object_builder.add_active_fill( + self.scene_builder, current_x, tile_right_x, current_winding, @@ -226,12 +224,10 @@ impl<'a> Tiler<'a> { current_winding, current_tile_x ); let current_tile_coords = Vector2I::new(current_tile_x, tile_y); - if let Some(tile_index) = self - .built_object - .tile_coords_to_local_index(current_tile_coords) - { + if let Some(tile_index) = self.object_builder + .tile_coords_to_local_index(current_tile_coords) { // FIXME(pcwalton): Handle winding overflow. - self.built_object.tiles.data[tile_index as usize].backdrop = + self.object_builder.tiles.data[tile_index as usize].backdrop = current_winding as i8; } @@ -247,8 +243,8 @@ impl<'a> Tiler<'a> { let current_x = (i32::from(current_tile_x) * TILE_WIDTH as i32) as f32 + current_subtile_x; let current_tile_coords = Vector2I::new(current_tile_x, tile_y); - self.built_object.add_active_fill( - self.builder, + self.object_builder.add_active_fill( + self.scene_builder, current_x, segment_x, current_winding, @@ -263,7 +259,7 @@ impl<'a> Tiler<'a> { // Process the edge. debug!("about to process existing active edge {:#?}", active_edge); debug_assert!(f32::abs(active_edge.crossing.y() - tile_top) < 0.1); - active_edge.process(self.builder, &mut self.built_object, tile_y); + active_edge.process(self.scene_builder, &mut self.object_builder, tile_y); if !active_edge.segment.is_none() { self.active_edges.push(active_edge); } @@ -299,8 +295,8 @@ impl<'a> Tiler<'a> { contour, prev_endpoint_index, &mut self.active_edges, - self.builder, - &mut self.built_object, + self.scene_builder, + &mut self.object_builder, tile_y, ); @@ -323,8 +319,8 @@ impl<'a> Tiler<'a> { contour, point_index.point(), &mut self.active_edges, - self.builder, - &mut self.built_object, + self.scene_builder, + &mut self.object_builder, tile_y, ); @@ -381,12 +377,12 @@ fn process_active_segment( from_endpoint_index: u32, active_edges: &mut SortedVector, builder: &SceneBuilder, - built_object: &mut BuiltObject, + object_builder: &mut ObjectBuilder, tile_y: i32, ) { let mut active_edge = ActiveEdge::from_segment(&contour.segment_after(from_endpoint_index)); debug!("... process_active_segment({:#?})", active_edge); - active_edge.process(builder, built_object, tile_y); + active_edge.process(builder, object_builder, tile_y); if !active_edge.segment.is_none() { debug!("... ... pushing resulting active edge: {:#?}", active_edge); active_edges.push(active_edge); @@ -433,7 +429,10 @@ impl ActiveEdge { ActiveEdge { segment: *segment, crossing } } - fn process(&mut self, builder: &SceneBuilder, built_object: &mut BuiltObject, tile_y: i32) { + fn process(&mut self, + builder: &SceneBuilder, + object_builder: &mut ObjectBuilder, + tile_y: i32) { let tile_bottom = ((i32::from(tile_y) + 1) * TILE_HEIGHT as i32) as f32; debug!( "process_active_edge({:#?}, tile_y={}({}))", @@ -446,7 +445,7 @@ impl ActiveEdge { if segment.is_line() { let line_segment = segment.as_line_segment(); self.segment = - match self.process_line_segment(line_segment, builder, built_object, tile_y) { + match self.process_line_segment(line_segment, builder, object_builder, tile_y) { Some(lower_part) => Segment::line(lower_part), None => Segment::none(), }; @@ -462,10 +461,8 @@ impl ActiveEdge { if self.crossing.y() < segment.baseline.min_y() { let first_line_segment = LineSegment2F::new(self.crossing, segment.baseline.upper_point()).orient(winding); - if self - .process_line_segment(first_line_segment, builder, built_object, tile_y) - .is_some() - { + if self.process_line_segment(first_line_segment, builder, object_builder, tile_y) + .is_some() { return; } } @@ -494,7 +491,7 @@ impl ActiveEdge { ); let line = before_segment.baseline.orient(winding); - match self.process_line_segment(line, builder, built_object, tile_y) { + match self.process_line_segment(line, builder, object_builder, tile_y) { Some(lower_part) if split_t == 1.0 => { self.segment = Segment::line(lower_part); return; @@ -516,7 +513,7 @@ impl ActiveEdge { &mut self, line_segment: LineSegment2F, builder: &SceneBuilder, - built_object: &mut BuiltObject, + object_builder: &mut ObjectBuilder, tile_y: i32, ) -> Option { let tile_bottom = ((i32::from(tile_y) + 1) * TILE_HEIGHT as i32) as f32; @@ -526,12 +523,12 @@ impl ActiveEdge { ); if line_segment.max_y() <= tile_bottom { - built_object.generate_fill_primitives_for_line(builder, line_segment, tile_y); + object_builder.generate_fill_primitives_for_line(builder, line_segment, tile_y); return None; } let (upper_part, lower_part) = line_segment.split_at_y(tile_bottom); - built_object.generate_fill_primitives_for_line(builder, upper_part, tile_y); + object_builder.generate_fill_primitives_for_line(builder, upper_part, tile_y); self.crossing = lower_part.upper_point(); Some(lower_part) }