2019-01-14 17:20:36 -05:00
|
|
|
// pathfinder/renderer/src/builder.rs
|
|
|
|
//
|
|
|
|
// Copyright © 2019 The Pathfinder Project Developers.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
//! Packs data onto the GPU.
|
|
|
|
|
2019-04-29 21:16:55 -04:00
|
|
|
use crate::concurrent::executor::Executor;
|
2020-02-14 23:53:27 -05:00
|
|
|
use crate::gpu_data::{AlphaTile, FillBatchPrimitive, RenderCommand, TileObjectPrimitive};
|
2019-06-05 17:35:46 -04:00
|
|
|
use crate::options::{PreparedBuildOptions, RenderCommandListener};
|
2020-02-04 01:24:34 -05:00
|
|
|
use crate::paint::{PaintInfo, PaintMetadata};
|
2019-04-11 22:38:31 -04:00
|
|
|
use crate::scene::Scene;
|
2019-05-03 21:51:36 -04:00
|
|
|
use crate::tile_map::DenseTileMap;
|
|
|
|
use crate::tiles::{self, TILE_HEIGHT, TILE_WIDTH, Tiler};
|
2019-01-14 17:20:36 -05:00
|
|
|
use crate::z_buffer::ZBuffer;
|
2019-06-21 13:06:19 -04:00
|
|
|
use pathfinder_geometry::line_segment::{LineSegment2F, LineSegmentU4, LineSegmentU8};
|
|
|
|
use pathfinder_geometry::vector::{Vector2F, Vector2I};
|
|
|
|
use pathfinder_geometry::rect::{RectF, RectI};
|
2019-05-03 21:51:36 -04:00
|
|
|
use pathfinder_geometry::util;
|
|
|
|
use pathfinder_simd::default::{F32x4, I32x4};
|
|
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
2019-04-30 22:13:28 -04:00
|
|
|
use std::time::Instant;
|
2019-01-14 17:20:36 -05:00
|
|
|
use std::u16;
|
|
|
|
|
2019-05-03 15:35:19 -04:00
|
|
|
pub(crate) struct SceneBuilder<'a> {
|
2019-03-29 22:11:38 -04:00
|
|
|
scene: &'a Scene,
|
2019-06-05 17:35:46 -04:00
|
|
|
built_options: &'a PreparedBuildOptions,
|
2019-01-14 17:20:36 -05:00
|
|
|
|
2019-04-15 16:21:24 -04:00
|
|
|
pub(crate) next_alpha_tile_index: AtomicUsize,
|
|
|
|
pub(crate) z_buffer: ZBuffer,
|
|
|
|
pub(crate) listener: Box<dyn RenderCommandListener>,
|
2019-03-29 22:11:38 -04:00
|
|
|
}
|
2019-01-14 17:20:36 -05:00
|
|
|
|
2019-04-15 16:21:24 -04:00
|
|
|
impl<'a> SceneBuilder<'a> {
|
2019-05-03 15:35:19 -04:00
|
|
|
pub(crate) fn new(
|
2019-04-29 19:45:29 -04:00
|
|
|
scene: &'a Scene,
|
2019-06-05 17:35:46 -04:00
|
|
|
built_options: &'a PreparedBuildOptions,
|
2019-04-29 19:45:29 -04:00
|
|
|
listener: Box<dyn RenderCommandListener>,
|
|
|
|
) -> SceneBuilder<'a> {
|
2019-04-15 16:21:24 -04:00
|
|
|
let effective_view_box = scene.effective_view_box(built_options);
|
|
|
|
SceneBuilder {
|
|
|
|
scene,
|
|
|
|
built_options,
|
2019-01-14 17:20:36 -05:00
|
|
|
|
2019-04-15 16:21:24 -04:00
|
|
|
next_alpha_tile_index: AtomicUsize::new(0),
|
|
|
|
z_buffer: ZBuffer::new(effective_view_box),
|
|
|
|
listener,
|
2019-03-29 22:11:38 -04:00
|
|
|
}
|
|
|
|
}
|
2019-01-14 17:20:36 -05:00
|
|
|
|
2019-04-29 21:16:55 -04:00
|
|
|
pub fn build<E>(&mut self, executor: &E) where E: Executor {
|
2019-04-30 22:13:28 -04:00
|
|
|
let start_time = Instant::now();
|
|
|
|
|
|
|
|
let bounding_quad = self.built_options.bounding_quad();
|
2019-05-10 15:03:38 -04:00
|
|
|
let path_count = self.scene.paths.len();
|
|
|
|
self.listener.send(RenderCommand::Start { bounding_quad, path_count });
|
2019-04-30 22:13:28 -04:00
|
|
|
|
2020-02-04 01:24:34 -05:00
|
|
|
let PaintInfo {
|
|
|
|
data: paint_data,
|
|
|
|
metadata: paint_metadata,
|
|
|
|
} = self.scene.build_paint_info();
|
|
|
|
self.listener.send(RenderCommand::AddPaintData(paint_data));
|
2019-04-30 22:13:28 -04:00
|
|
|
|
|
|
|
let effective_view_box = self.scene.effective_view_box(self.built_options);
|
2020-02-14 23:53:27 -05:00
|
|
|
let built_objects = executor.build_vector(path_count, |path_index| {
|
2020-02-04 01:24:34 -05:00
|
|
|
self.build_path(path_index,
|
|
|
|
effective_view_box,
|
|
|
|
&self.built_options,
|
|
|
|
&self.scene,
|
|
|
|
&paint_metadata)
|
2019-04-29 21:16:55 -04:00
|
|
|
});
|
2019-04-30 22:13:28 -04:00
|
|
|
|
2020-02-14 23:53:27 -05:00
|
|
|
self.finish_building(&paint_metadata, built_objects);
|
2019-04-30 22:13:28 -04:00
|
|
|
|
|
|
|
let build_time = Instant::now() - start_time;
|
|
|
|
self.listener.send(RenderCommand::Finish { build_time });
|
2019-04-15 16:21:24 -04:00
|
|
|
}
|
2019-03-29 22:11:38 -04:00
|
|
|
|
2019-05-10 15:03:38 -04:00
|
|
|
fn build_path(
|
2019-04-29 19:45:29 -04:00
|
|
|
&self,
|
2019-05-10 15:03:38 -04:00
|
|
|
path_index: usize,
|
2019-05-29 22:13:42 -04:00
|
|
|
view_box: RectF,
|
2019-06-05 17:35:46 -04:00
|
|
|
built_options: &PreparedBuildOptions,
|
2019-04-29 19:45:29 -04:00
|
|
|
scene: &Scene,
|
2020-02-04 01:24:34 -05:00
|
|
|
paint_metadata: &[PaintMetadata],
|
2020-02-14 23:53:27 -05:00
|
|
|
) -> BuiltObject {
|
2019-05-10 15:03:38 -04:00
|
|
|
let path_object = &scene.paths[path_index];
|
|
|
|
let outline = scene.apply_render_options(path_object.outline(), built_options);
|
2019-05-14 18:21:15 -04:00
|
|
|
let paint_id = path_object.paint();
|
2019-05-14 21:09:01 -04:00
|
|
|
|
|
|
|
let mut tiler = Tiler::new(self,
|
|
|
|
&outline,
|
|
|
|
view_box,
|
|
|
|
path_index as u16,
|
2020-02-04 01:24:34 -05:00
|
|
|
&paint_metadata[paint_id.0 as usize]);
|
2019-03-29 22:11:38 -04:00
|
|
|
|
2019-04-15 16:21:24 -04:00
|
|
|
tiler.generate_tiles();
|
2019-01-14 17:20:36 -05:00
|
|
|
|
2020-02-14 23:53:27 -05:00
|
|
|
self.listener.send(RenderCommand::AddFills(tiler.object_builder.fills));
|
|
|
|
tiler.object_builder.built_object
|
2019-03-29 22:11:38 -04:00
|
|
|
}
|
|
|
|
|
2020-02-14 23:53:27 -05:00
|
|
|
fn cull_tiles(&self, built_objects: Vec<BuiltObject>) -> CulledTiles {
|
|
|
|
let mut culled_tiles = CulledTiles { alpha_tiles: vec![] };
|
2019-04-11 21:54:03 -04:00
|
|
|
|
2020-02-14 23:53:27 -05:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2019-01-14 17:20:36 -05:00
|
|
|
}
|
2020-02-14 23:53:27 -05:00
|
|
|
|
|
|
|
culled_tiles
|
2019-01-14 17:20:36 -05:00
|
|
|
}
|
2019-03-29 22:11:38 -04:00
|
|
|
|
2020-02-14 23:53:27 -05:00
|
|
|
fn pack_tiles(&mut self, paint_metadata: &[PaintMetadata], culled_tiles: CulledTiles) {
|
2019-05-10 15:03:38 -04:00
|
|
|
let path_count = self.scene.paths.len() as u32;
|
2020-02-04 01:24:34 -05:00
|
|
|
let solid_tiles = self.z_buffer.build_solid_tiles(&self.scene.paths,
|
|
|
|
paint_metadata,
|
|
|
|
0..path_count);
|
2019-04-15 16:21:24 -04:00
|
|
|
if !solid_tiles.is_empty() {
|
2019-05-13 21:20:21 -04:00
|
|
|
self.listener.send(RenderCommand::SolidTile(solid_tiles));
|
2019-04-15 16:21:24 -04:00
|
|
|
}
|
2020-02-14 23:53:27 -05:00
|
|
|
if !culled_tiles.alpha_tiles.is_empty() {
|
|
|
|
self.listener.send(RenderCommand::AlphaTile(culled_tiles.alpha_tiles));
|
2019-03-29 22:11:38 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-04 01:24:34 -05:00
|
|
|
fn finish_building(&mut self,
|
|
|
|
paint_metadata: &[PaintMetadata],
|
2020-02-14 23:53:27 -05:00
|
|
|
built_objects: Vec<BuiltObject>) {
|
2019-04-15 16:21:24 -04:00
|
|
|
self.listener.send(RenderCommand::FlushFills);
|
2020-02-14 23:53:27 -05:00
|
|
|
let culled_tiles = self.cull_tiles(built_objects);
|
|
|
|
self.pack_tiles(paint_metadata, culled_tiles);
|
2019-03-29 22:11:38 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-18 18:09:37 -04:00
|
|
|
#[derive(Clone, Copy, Debug, Default)]
|
|
|
|
pub struct TileStats {
|
|
|
|
pub solid_tile_count: u32,
|
|
|
|
pub alpha_tile_count: u32,
|
|
|
|
}
|
2019-05-03 21:51:36 -04:00
|
|
|
|
|
|
|
// Utilities for built objects
|
|
|
|
|
2020-02-14 23:53:27 -05:00
|
|
|
impl ObjectBuilder {
|
|
|
|
pub(crate) fn new(bounds: RectF) -> ObjectBuilder {
|
2019-05-03 21:51:36 -04:00
|
|
|
let tile_rect = tiles::round_rect_out_to_tile_bounds(bounds);
|
|
|
|
let tiles = DenseTileMap::new(tile_rect);
|
2020-02-14 23:53:27 -05:00
|
|
|
ObjectBuilder {
|
|
|
|
built_object: BuiltObject { alpha_tiles: vec![] },
|
2019-05-03 21:51:36 -04:00
|
|
|
bounds,
|
|
|
|
fills: vec![],
|
|
|
|
tiles,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2019-05-29 22:13:42 -04:00
|
|
|
pub(crate) fn tile_rect(&self) -> RectI {
|
2019-05-03 21:51:36 -04:00
|
|
|
self.tiles.rect
|
|
|
|
}
|
|
|
|
|
|
|
|
fn add_fill(
|
|
|
|
&mut self,
|
2020-02-14 23:53:27 -05:00
|
|
|
scene_builder: &SceneBuilder,
|
2019-06-25 17:43:13 -04:00
|
|
|
segment: LineSegment2F,
|
2019-06-03 15:39:29 -04:00
|
|
|
tile_coords: Vector2I,
|
2019-05-03 21:51:36 -04:00
|
|
|
) {
|
|
|
|
debug!("add_fill({:?} ({:?}))", segment, tile_coords);
|
|
|
|
|
|
|
|
// Ensure this fill is in bounds. If not, cull it.
|
|
|
|
if self.tile_coords_to_local_index(tile_coords).is_none() {
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
|
|
|
debug_assert_eq!(TILE_WIDTH, TILE_HEIGHT);
|
|
|
|
|
2019-06-25 17:43:13 -04:00
|
|
|
// Compute the upper left corner of the tile.
|
|
|
|
let tile_size = F32x4::splat(TILE_WIDTH as f32);
|
|
|
|
let tile_upper_left = tile_coords.to_f32().0.to_f32x4().xyxy() * tile_size;
|
2019-05-03 21:51:36 -04:00
|
|
|
|
2019-06-25 17:43:13 -04:00
|
|
|
// Convert to 4.8 fixed point.
|
2019-05-03 21:51:36 -04:00
|
|
|
let segment = (segment.0 - tile_upper_left) * F32x4::splat(256.0);
|
2019-06-25 17:43:13 -04:00
|
|
|
let (min, max) = (F32x4::default(), F32x4::splat((TILE_WIDTH * 256 - 1) as f32));
|
|
|
|
let segment = segment.clamp(min, max).to_i32x4();
|
|
|
|
let (from_x, from_y, to_x, to_y) = (segment[0], segment[1], segment[2], segment[3]);
|
2019-05-03 21:51:36 -04:00
|
|
|
|
|
|
|
// Cull degenerate fills.
|
2019-06-25 17:43:13 -04:00
|
|
|
if from_x == to_x {
|
2019-05-03 21:51:36 -04:00
|
|
|
debug!("... culling!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allocate global tile if necessary.
|
2020-02-14 23:53:27 -05:00
|
|
|
let alpha_tile_index = self.get_or_allocate_alpha_tile_index(scene_builder, tile_coords);
|
2019-05-03 21:51:36 -04:00
|
|
|
|
2019-06-25 17:43:13 -04:00
|
|
|
// Pack whole pixels.
|
2019-12-29 15:38:36 -05:00
|
|
|
let px = (segment & I32x4::splat(0xf00)).to_u32x4();
|
|
|
|
let px = (px >> 8).to_i32x4() | (px >> 4).to_i32x4().yxwz();
|
2019-06-25 17:43:13 -04:00
|
|
|
|
|
|
|
// Pack instance data.
|
2019-05-03 21:51:36 -04:00
|
|
|
debug!("... OK, pushing");
|
|
|
|
self.fills.push(FillBatchPrimitive {
|
2019-06-25 17:43:13 -04:00
|
|
|
px: LineSegmentU4 { from: px[0] as u8, to: px[2] as u8 },
|
|
|
|
subpx: LineSegmentU8 {
|
|
|
|
from_x: from_x as u8,
|
|
|
|
from_y: from_y as u8,
|
|
|
|
to_x: to_x as u8,
|
|
|
|
to_y: to_y as u8,
|
|
|
|
},
|
2019-05-03 21:51:36 -04:00
|
|
|
alpha_tile_index,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_or_allocate_alpha_tile_index(
|
|
|
|
&mut self,
|
2020-02-14 23:53:27 -05:00
|
|
|
scene_builder: &SceneBuilder,
|
2019-06-03 15:39:29 -04:00
|
|
|
tile_coords: Vector2I,
|
2019-05-03 21:51:36 -04:00
|
|
|
) -> u16 {
|
|
|
|
let local_tile_index = self.tiles.coords_to_index_unchecked(tile_coords);
|
|
|
|
let alpha_tile_index = self.tiles.data[local_tile_index].alpha_tile_index;
|
|
|
|
if alpha_tile_index != !0 {
|
|
|
|
return alpha_tile_index;
|
|
|
|
}
|
|
|
|
|
2020-02-14 23:53:27 -05:00
|
|
|
let alpha_tile_index = scene_builder
|
2019-05-03 21:51:36 -04:00
|
|
|
.next_alpha_tile_index
|
|
|
|
.fetch_add(1, Ordering::Relaxed) as u16;
|
|
|
|
self.tiles.data[local_tile_index].alpha_tile_index = alpha_tile_index;
|
|
|
|
alpha_tile_index
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn add_active_fill(
|
|
|
|
&mut self,
|
2020-02-14 23:53:27 -05:00
|
|
|
scene_builder: &SceneBuilder,
|
2019-05-03 21:51:36 -04:00
|
|
|
left: f32,
|
|
|
|
right: f32,
|
|
|
|
mut winding: i32,
|
2019-06-03 15:39:29 -04:00
|
|
|
tile_coords: Vector2I,
|
2019-05-03 21:51:36 -04:00
|
|
|
) {
|
|
|
|
let tile_origin_y = (tile_coords.y() * TILE_HEIGHT as i32) as f32;
|
2019-06-03 15:39:29 -04:00
|
|
|
let left = Vector2F::new(left, tile_origin_y);
|
|
|
|
let right = Vector2F::new(right, tile_origin_y);
|
2019-05-03 21:51:36 -04:00
|
|
|
|
|
|
|
let segment = if winding < 0 {
|
2019-06-03 15:39:29 -04:00
|
|
|
LineSegment2F::new(left, right)
|
2019-05-03 21:51:36 -04:00
|
|
|
} else {
|
2019-06-03 15:39:29 -04:00
|
|
|
LineSegment2F::new(right, left)
|
2019-05-03 21:51:36 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
debug!(
|
|
|
|
"... emitting active fill {} -> {} winding {} @ tile {:?}",
|
|
|
|
left.x(),
|
|
|
|
right.x(),
|
|
|
|
winding,
|
|
|
|
tile_coords
|
|
|
|
);
|
|
|
|
|
|
|
|
while winding != 0 {
|
2020-02-14 23:53:27 -05:00
|
|
|
self.add_fill(scene_builder, segment, tile_coords);
|
2019-05-03 21:51:36 -04:00
|
|
|
if winding < 0 {
|
|
|
|
winding += 1
|
|
|
|
} else {
|
|
|
|
winding -= 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn generate_fill_primitives_for_line(
|
|
|
|
&mut self,
|
2020-02-14 23:53:27 -05:00
|
|
|
scene_builder: &SceneBuilder,
|
2019-06-03 15:39:29 -04:00
|
|
|
mut segment: LineSegment2F,
|
2019-05-03 21:51:36 -04:00
|
|
|
tile_y: i32,
|
|
|
|
) {
|
|
|
|
debug!(
|
|
|
|
"... generate_fill_primitives_for_line(): segment={:?} tile_y={} ({}-{})",
|
|
|
|
segment,
|
|
|
|
tile_y,
|
|
|
|
tile_y as f32 * TILE_HEIGHT as f32,
|
|
|
|
(tile_y + 1) as f32 * TILE_HEIGHT as f32
|
|
|
|
);
|
|
|
|
|
|
|
|
let winding = segment.from_x() > segment.to_x();
|
|
|
|
let (segment_left, segment_right) = if !winding {
|
|
|
|
(segment.from_x(), segment.to_x())
|
|
|
|
} else {
|
|
|
|
(segment.to_x(), segment.from_x())
|
|
|
|
};
|
|
|
|
|
|
|
|
// FIXME(pcwalton): Optimize this.
|
|
|
|
let segment_tile_left = f32::floor(segment_left) as i32 / TILE_WIDTH as i32;
|
|
|
|
let segment_tile_right =
|
|
|
|
util::alignup_i32(f32::ceil(segment_right) as i32, TILE_WIDTH as i32);
|
|
|
|
debug!(
|
|
|
|
"segment_tile_left={} segment_tile_right={} tile_rect={:?}",
|
|
|
|
segment_tile_left,
|
|
|
|
segment_tile_right,
|
|
|
|
self.tile_rect()
|
|
|
|
);
|
|
|
|
|
|
|
|
for subsegment_tile_x in segment_tile_left..segment_tile_right {
|
|
|
|
let (mut fill_from, mut fill_to) = (segment.from(), segment.to());
|
|
|
|
let subsegment_tile_right =
|
|
|
|
((i32::from(subsegment_tile_x) + 1) * TILE_HEIGHT as i32) as f32;
|
|
|
|
if subsegment_tile_right < segment_right {
|
|
|
|
let x = subsegment_tile_right;
|
2019-06-03 15:39:29 -04:00
|
|
|
let point = Vector2F::new(x, segment.solve_y_for_x(x));
|
2019-05-03 21:51:36 -04:00
|
|
|
if !winding {
|
|
|
|
fill_to = point;
|
2019-06-03 15:39:29 -04:00
|
|
|
segment = LineSegment2F::new(point, segment.to());
|
2019-05-03 21:51:36 -04:00
|
|
|
} else {
|
|
|
|
fill_from = point;
|
2019-06-03 15:39:29 -04:00
|
|
|
segment = LineSegment2F::new(segment.from(), point);
|
2019-05-03 21:51:36 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-03 15:39:29 -04:00
|
|
|
let fill_segment = LineSegment2F::new(fill_from, fill_to);
|
|
|
|
let fill_tile_coords = Vector2I::new(subsegment_tile_x, tile_y);
|
2020-02-14 23:53:27 -05:00
|
|
|
self.add_fill(scene_builder, fill_segment, fill_tile_coords);
|
2019-05-03 21:51:36 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2019-06-03 15:39:29 -04:00
|
|
|
pub(crate) fn tile_coords_to_local_index(&self, coords: Vector2I) -> Option<u32> {
|
2019-05-03 21:51:36 -04:00
|
|
|
self.tiles.coords_to_index(coords).map(|index| index as u32)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2019-06-03 15:39:29 -04:00
|
|
|
pub(crate) fn local_tile_index_to_coords(&self, tile_index: u32) -> Vector2I {
|
2019-05-03 21:51:36 -04:00
|
|
|
self.tiles.index_to_coords(tile_index as usize)
|
|
|
|
}
|
|
|
|
}
|
2020-02-14 23:53:27 -05:00
|
|
|
|
|
|
|
#[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>,
|
|
|
|
}
|