Flatten objects' tiles ourselves instead of having Rayon do it.

A prerequisite for clips.
This commit is contained in:
Patrick Walton 2020-02-14 20:53:27 -08:00
parent 84ffc3151e
commit 31becb1570
6 changed files with 106 additions and 109 deletions

View File

@ -34,12 +34,12 @@ impl DemoExecutor {
}
impl Executor for DemoExecutor {
fn flatten_into_vector<T, F>(&self, length: usize, builder: F) -> Vec<T>
where T: Send, F: Fn(usize) -> Vec<T> + Send + Sync {
fn build_vector<T, F>(&self, length: usize, builder: F) -> Vec<T>
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)
}
}
}

View File

@ -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<AlphaTile> {
) -> 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<AlphaTile>) {
for alpha_tile in alpha_tiles {
fn cull_tiles(&self, built_objects: Vec<BuiltObject>) -> 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)
{
continue;
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;
}
}
fn pack_alpha_tiles(&mut self, paint_metadata: &[PaintMetadata], alpha_tiles: Vec<AlphaTile>) {
culled_tiles
}
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<AlphaTile>) {
built_objects: Vec<BuiltObject>) {
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<AlphaTile>,
}
#[derive(Debug)]
pub(crate) struct ObjectBuilder {
pub built_object: BuiltObject,
pub fills: Vec<FillBatchPrimitive>,
pub tiles: DenseTileMap<TileObjectPrimitive>,
pub bounds: RectF,
}
struct CulledTiles {
alpha_tiles: Vec<AlphaTile>,
}

View File

@ -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<T, F>(&self, length: usize, builder: F) -> Vec<T>
where T: Send, F: Fn(usize) -> Vec<T> + Send + Sync;
fn build_vector<T, F>(&self, length: usize, builder: F) -> Vec<T>
where T: Send, F: Fn(usize) -> T + Send + Sync;
}
pub struct SequentialExecutor;
impl Executor for SequentialExecutor {
fn flatten_into_vector<T, F>(&self, length: usize, builder: F) -> Vec<T>
where T: Send, F: Fn(usize) -> Vec<T> + Send + Sync {
(0..length).into_iter().flat_map(builder).collect()
fn build_vector<T, F>(&self, length: usize, builder: F) -> Vec<T>
where T: Send, F: Fn(usize) -> T + Send + Sync {
(0..length).into_iter().map(builder).collect()
}
}

View File

@ -16,8 +16,8 @@ use rayon::iter::{IntoParallelIterator, ParallelIterator};
pub struct RayonExecutor;
impl Executor for RayonExecutor {
fn flatten_into_vector<T, F>(&self, length: usize, builder: F) -> Vec<T>
where T: Send, F: Fn(usize) -> Vec<T> + Send + Sync {
(0..length).into_par_iter().flat_map(builder).collect()
fn build_vector<T, F>(&self, length: usize, builder: F) -> Vec<T>
where T: Send, F: Fn(usize) -> T + Send + Sync {
(0..length).into_par_iter().map(builder).collect()
}
}

View File

@ -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<FillBatchPrimitive>,
pub alpha_tiles: Vec<AlphaTile>,
pub tiles: DenseTileMap<TileObjectPrimitive>,
}
pub enum RenderCommand {
Start { path_count: usize, bounding_quad: BoundingQuad },
AddPaintData(PaintData),

View File

@ -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<ActiveEdge>,
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<LineSegment2F> {
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)
}