Build clip paths before draw paths

This commit is contained in:
Patrick Walton 2020-02-15 14:00:36 -08:00
parent 496b55ee4e
commit f26eecae7a
2 changed files with 216 additions and 135 deletions

View File

@ -11,12 +11,14 @@
//! Packs data onto the GPU.
use crate::concurrent::executor::Executor;
use crate::gpu_data::{AlphaTile, FillBatchPrimitive, MaskTile, RenderCommand, TileObjectPrimitive};
use crate::gpu::renderer::MASK_TILES_ACROSS;
use crate::gpu_data::{AlphaTile, AlphaTileVertex, FillBatchPrimitive, MaskTile, MaskTileVertex};
use crate::gpu_data::{RenderCommand, TileObjectPrimitive};
use crate::options::{PreparedBuildOptions, RenderCommandListener};
use crate::paint::{PaintInfo, PaintMetadata};
use crate::scene::Scene;
use crate::tile_map::DenseTileMap;
use crate::tiles::{self, TILE_HEIGHT, TILE_WIDTH, Tiler};
use crate::tiles::{self, TILE_HEIGHT, TILE_WIDTH, Tiler, TilingPathInfo};
use crate::z_buffer::ZBuffer;
use pathfinder_geometry::line_segment::{LineSegment2F, LineSegmentU4, LineSegmentU8};
use pathfinder_geometry::vector::{Vector2F, Vector2I};
@ -36,6 +38,20 @@ pub(crate) struct SceneBuilder<'a> {
pub(crate) listener: Box<dyn RenderCommandListener>,
}
#[derive(Debug)]
pub(crate) struct ObjectBuilder {
pub built_path: BuiltPath,
pub fills: Vec<FillBatchPrimitive>,
pub tiles: DenseTileMap<TileObjectPrimitive>,
pub bounds: RectF,
}
#[derive(Debug)]
pub(crate) struct BuiltPath {
pub mask_tiles: Vec<MaskTile>,
pub alpha_tiles: Vec<AlphaTile>,
}
impl<'a> SceneBuilder<'a> {
pub(crate) fn new(
scene: &'a Scene,
@ -57,8 +73,11 @@ impl<'a> SceneBuilder<'a> {
let start_time = Instant::now();
let bounding_quad = self.built_options.bounding_quad();
let path_count = self.scene.paths.len();
self.listener.send(RenderCommand::Start { bounding_quad, path_count });
let clip_path_count = self.scene.clip_paths.len();
let draw_path_count = self.scene.paths.len();
let total_path_count = clip_path_count + draw_path_count;
self.listener.send(RenderCommand::Start { bounding_quad, path_count: total_path_count });
let PaintInfo {
data: paint_data,
@ -67,51 +86,91 @@ impl<'a> SceneBuilder<'a> {
self.listener.send(RenderCommand::AddPaintData(paint_data));
let effective_view_box = self.scene.effective_view_box(self.built_options);
let built_objects = executor.build_vector(path_count, |path_index| {
self.build_path(path_index,
effective_view_box,
&self.built_options,
&self.scene,
&paint_metadata)
let built_clip_paths = executor.build_vector(clip_path_count, |path_index| {
self.build_clip_path(path_index, effective_view_box, &self.built_options, &self.scene)
});
self.finish_building(&paint_metadata, built_objects);
let built_draw_paths = executor.build_vector(draw_path_count, |path_index| {
self.build_draw_path(path_index,
effective_view_box,
&self.built_options,
&self.scene,
&paint_metadata,
&built_clip_paths)
});
self.finish_building(&paint_metadata, built_clip_paths, built_draw_paths);
let build_time = Instant::now() - start_time;
self.listener.send(RenderCommand::Finish { build_time });
}
fn build_path(
fn build_clip_path(
&self,
path_index: usize,
view_box: RectF,
built_options: &PreparedBuildOptions,
scene: &Scene,
) -> BuiltPath {
let path_object = &scene.clip_paths[path_index];
let outline = scene.apply_render_options(path_object.outline(), built_options);
let mut tiler = Tiler::new(self,
&outline,
view_box,
path_index as u16,
TilingPathInfo::Clip);
tiler.generate_tiles();
self.listener.send(RenderCommand::AddFills(tiler.object_builder.fills));
tiler.object_builder.built_path
}
fn build_draw_path(
&self,
path_index: usize,
view_box: RectF,
built_options: &PreparedBuildOptions,
scene: &Scene,
paint_metadata: &[PaintMetadata],
) -> BuiltObject {
built_clip_paths: &[BuiltPath],
) -> BuiltPath {
let path_object = &scene.paths[path_index];
let outline = scene.apply_render_options(path_object.outline(), built_options);
let paint_id = path_object.paint();
let built_clip_path =
path_object.clip_path().map(|clip_path_id| &built_clip_paths[clip_path_id.0 as usize]);
let mut tiler = Tiler::new(self,
&outline,
view_box,
path_index as u16,
&paint_metadata[paint_id.0 as usize]);
TilingPathInfo::Draw {
paint_metadata: &paint_metadata[paint_id.0 as usize],
built_clip_path,
});
tiler.generate_tiles();
self.listener.send(RenderCommand::AddFills(tiler.object_builder.fills));
tiler.object_builder.built_object
tiler.object_builder.built_path
}
fn cull_tiles(&self, built_objects: Vec<BuiltObject>) -> CulledTiles {
fn cull_tiles(&self, built_clip_paths: Vec<BuiltPath>, built_draw_paths: Vec<BuiltPath>)
-> CulledTiles {
let mut culled_tiles = CulledTiles { mask_tiles: vec![], alpha_tiles: vec![] };
for built_object in built_objects {
culled_tiles.mask_tiles.extend_from_slice(&built_object.mask_tiles);
for built_clip_path in built_clip_paths {
culled_tiles.mask_tiles.extend_from_slice(&built_clip_path.mask_tiles);
}
for alpha_tile in built_object.alpha_tiles {
for built_draw_path in built_draw_paths {
culled_tiles.mask_tiles.extend_from_slice(&built_draw_path.mask_tiles);
for alpha_tile in built_draw_path.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) {
@ -141,9 +200,10 @@ impl<'a> SceneBuilder<'a> {
fn finish_building(&mut self,
paint_metadata: &[PaintMetadata],
built_objects: Vec<BuiltObject>) {
built_clip_paths: Vec<BuiltPath>,
built_draw_paths: Vec<BuiltPath>) {
self.listener.send(RenderCommand::FlushFills);
let culled_tiles = self.cull_tiles(built_objects);
let culled_tiles = self.cull_tiles(built_clip_paths, built_draw_paths);
self.pack_tiles(paint_metadata, culled_tiles);
}
}
@ -166,7 +226,7 @@ impl ObjectBuilder {
let tile_rect = tiles::round_rect_out_to_tile_bounds(bounds);
let tiles = DenseTileMap::new(tile_rect);
ObjectBuilder {
built_object: BuiltObject { mask_tiles: vec![], alpha_tiles: vec![] },
built_path: BuiltPath { mask_tiles: vec![], alpha_tiles: vec![] },
bounds,
fills: vec![],
tiles,
@ -349,16 +409,108 @@ impl ObjectBuilder {
}
}
#[derive(Debug)]
pub(crate) struct BuiltObject {
pub mask_tiles: Vec<MaskTile>,
pub alpha_tiles: Vec<AlphaTile>,
impl BuiltPath {
pub(crate) fn push_mask_tile(&mut self, tile: &TileObjectPrimitive, object_index: u16) {
self.mask_tiles.push(MaskTile {
upper_left: MaskTileVertex::new(tile.alpha_tile_index as u16,
Vector2I::default(),
object_index,
tile.backdrop as i16),
upper_right: MaskTileVertex::new(tile.alpha_tile_index as u16,
Vector2I::new(1, 0),
object_index,
tile.backdrop as i16),
lower_left: MaskTileVertex::new(tile.alpha_tile_index as u16,
Vector2I::new(0, 1),
object_index,
tile.backdrop as i16),
lower_right: MaskTileVertex::new(tile.alpha_tile_index as u16,
Vector2I::splat(1),
object_index,
tile.backdrop as i16),
});
}
pub(crate) fn push_alpha_tile(&mut self,
tile: &TileObjectPrimitive,
tile_coords: Vector2I,
object_index: u16,
paint_metadata: &PaintMetadata) {
self.alpha_tiles.push(AlphaTile {
upper_left: AlphaTileVertex::new(tile_coords,
tile.alpha_tile_index as u16,
Vector2I::default(),
object_index,
paint_metadata),
upper_right: AlphaTileVertex::new(tile_coords,
tile.alpha_tile_index as u16,
Vector2I::new(1, 0),
object_index,
paint_metadata),
lower_left: AlphaTileVertex::new(tile_coords,
tile.alpha_tile_index as u16,
Vector2I::new(0, 1),
object_index,
paint_metadata),
lower_right: AlphaTileVertex::new(tile_coords,
tile.alpha_tile_index as u16,
Vector2I::splat(1),
object_index,
paint_metadata),
});
}
}
#[derive(Debug)]
pub(crate) struct ObjectBuilder {
pub built_object: BuiltObject,
pub fills: Vec<FillBatchPrimitive>,
pub tiles: DenseTileMap<TileObjectPrimitive>,
pub bounds: RectF,
impl MaskTileVertex {
#[inline]
fn new(tile_index: u16, tile_offset: Vector2I, object_index: u16, backdrop: i16)
-> MaskTileVertex {
let mask_uv = calculate_mask_uv(tile_index, tile_offset);
MaskTileVertex {
tile_x: mask_uv.x() as u16,
tile_y: mask_uv.y() as u16,
mask_u: mask_uv.x() as u16,
mask_v: mask_uv.y() as u16,
backdrop,
object_index,
}
}
}
impl AlphaTileVertex {
#[inline]
fn new(tile_origin: Vector2I,
tile_index: u16,
tile_offset: Vector2I,
object_index: u16,
paint_metadata: &PaintMetadata)
-> AlphaTileVertex {
let tile_position = tile_origin + tile_offset;
let color_uv = paint_metadata.calculate_tex_coords(tile_position).scale(65535.0).to_i32();
let mask_uv = calculate_mask_uv(tile_index, tile_offset);
AlphaTileVertex {
tile_x: tile_position.x() as i16,
tile_y: tile_position.y() as i16,
color_u: color_uv.x() as u16,
color_v: color_uv.y() as u16,
mask_u: mask_uv.x() as u16,
mask_v: mask_uv.y() as u16,
object_index,
pad: 0,
}
}
#[inline]
pub fn tile_position(&self) -> Vector2I {
Vector2I::new(self.tile_x as i32, self.tile_y as i32)
}
}
fn calculate_mask_uv(tile_index: u16, tile_offset: Vector2I) -> Vector2I {
let mask_u = tile_index as i32 % MASK_TILES_ACROSS as i32;
let mask_v = tile_index as i32 / MASK_TILES_ACROSS as i32;
let mask_scale = 65535.0 / MASK_TILES_ACROSS as f32;
let mask_uv = Vector2I::new(mask_u, mask_v) + tile_offset;
mask_uv.to_f32().scale(mask_scale).to_i32()
}

View File

@ -8,9 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::builder::{ObjectBuilder, SceneBuilder};
use crate::gpu::renderer::MASK_TILES_ACROSS;
use crate::gpu_data::{AlphaTile, AlphaTileVertex, MaskTile, MaskTileVertex, TileObjectPrimitive};
use crate::builder::{BuiltPath, ObjectBuilder, SceneBuilder};
use crate::gpu_data::TileObjectPrimitive;
use crate::paint::PaintMetadata;
use pathfinder_content::outline::{Contour, Outline, PointIndex};
use pathfinder_content::segment::Segment;
@ -31,7 +30,7 @@ pub(crate) struct Tiler<'a> {
scene_builder: &'a SceneBuilder<'a>,
pub(crate) object_builder: ObjectBuilder,
outline: &'a Outline,
paint_metadata: &'a PaintMetadata,
path_info: TilingPathInfo<'a>,
object_index: u16,
point_queue: SortedVector<QueuedEndpoint>,
@ -39,6 +38,12 @@ pub(crate) struct Tiler<'a> {
old_active_edges: Vec<ActiveEdge>,
}
#[derive(Clone, Copy)]
pub(crate) enum TilingPathInfo<'a> {
Clip,
Draw { paint_metadata: &'a PaintMetadata, built_clip_path: Option<&'a BuiltPath> },
}
impl<'a> Tiler<'a> {
#[allow(clippy::or_fun_call)]
pub(crate) fn new(
@ -46,7 +51,7 @@ impl<'a> Tiler<'a> {
outline: &'a Outline,
view_box: RectF,
object_index: u16,
paint_metadata: &'a PaintMetadata,
path_info: TilingPathInfo<'a>,
) -> Tiler<'a> {
let bounds = outline
.bounds()
@ -59,7 +64,7 @@ impl<'a> Tiler<'a> {
object_builder,
outline,
object_index,
paint_metadata,
path_info,
point_queue: SortedVector::new(),
active_edges: SortedVector::new(),
@ -85,7 +90,7 @@ impl<'a> Tiler<'a> {
self.pack_and_cull();
// Done!
debug!("{:#?}", self.object_builder.built_object);
debug!("{:#?}", self.object_builder.built_path);
}
fn generate_strip(&mut self, strip_origin_y: i32) {
@ -108,6 +113,20 @@ impl<'a> Tiler<'a> {
}
fn pack_and_cull(&mut self) {
match self.path_info {
TilingPathInfo::Clip => unimplemented!(),
TilingPathInfo::Draw { .. } => self.pack_and_cull_draw_path(),
}
}
fn pack_and_cull_draw_path(&mut self) {
let (paint_metadata, built_clip_path) = match self.path_info {
TilingPathInfo::Clip => unreachable!(),
TilingPathInfo::Draw { paint_metadata, built_clip_path } => {
(paint_metadata, built_clip_path)
}
};
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);
@ -118,53 +137,17 @@ 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 {
if paint_metadata.is_opaque {
self.scene_builder.z_buffer.update(tile_coords, self.object_index);
continue;
}
}
self.object_builder.built_object.mask_tiles.push(MaskTile {
upper_left: MaskTileVertex::new(tile.alpha_tile_index as u16,
Vector2I::default(),
self.object_index,
tile.backdrop as i16),
upper_right: MaskTileVertex::new(tile.alpha_tile_index as u16,
Vector2I::new(1, 0),
self.object_index,
tile.backdrop as i16),
lower_left: MaskTileVertex::new(tile.alpha_tile_index as u16,
Vector2I::new(0, 1),
self.object_index,
tile.backdrop as i16),
lower_right: MaskTileVertex::new(tile.alpha_tile_index as u16,
Vector2I::splat(1),
self.object_index,
tile.backdrop as i16),
});
self.object_builder.built_object.alpha_tiles.push(AlphaTile {
upper_left: AlphaTileVertex::new(tile_coords,
tile.alpha_tile_index as u16,
Vector2I::default(),
self.object_index,
&self.paint_metadata),
upper_right: AlphaTileVertex::new(tile_coords,
tile.alpha_tile_index as u16,
Vector2I::new(1, 0),
self.object_index,
&self.paint_metadata),
lower_left: AlphaTileVertex::new(tile_coords,
tile.alpha_tile_index as u16,
Vector2I::new(0, 1),
self.object_index,
&self.paint_metadata),
lower_right: AlphaTileVertex::new(tile_coords,
tile.alpha_tile_index as u16,
Vector2I::splat(1),
self.object_index,
&self.paint_metadata),
});
self.object_builder.built_path.push_mask_tile(tile, self.object_index);
self.object_builder.built_path.push_alpha_tile(tile,
tile_coords,
self.object_index,
paint_metadata);
}
}
@ -555,60 +538,6 @@ impl PartialOrd<ActiveEdge> for ActiveEdge {
}
}
impl MaskTileVertex {
#[inline]
fn new(tile_index: u16, tile_offset: Vector2I, object_index: u16, backdrop: i16)
-> MaskTileVertex {
let mask_uv = calculate_mask_uv(tile_index, tile_offset);
MaskTileVertex {
tile_x: mask_uv.x() as u16,
tile_y: mask_uv.y() as u16,
mask_u: mask_uv.x() as u16,
mask_v: mask_uv.y() as u16,
backdrop,
object_index,
}
}
}
impl AlphaTileVertex {
#[inline]
fn new(tile_origin: Vector2I,
tile_index: u16,
tile_offset: Vector2I,
object_index: u16,
paint_metadata: &PaintMetadata)
-> AlphaTileVertex {
let tile_position = tile_origin + tile_offset;
let color_uv = paint_metadata.calculate_tex_coords(tile_position).scale(65535.0).to_i32();
let mask_uv = calculate_mask_uv(tile_index, tile_offset);
AlphaTileVertex {
tile_x: tile_position.x() as i16,
tile_y: tile_position.y() as i16,
color_u: color_uv.x() as u16,
color_v: color_uv.y() as u16,
mask_u: mask_uv.x() as u16,
mask_v: mask_uv.y() as u16,
object_index,
pad: 0,
}
}
#[inline]
pub fn tile_position(&self) -> Vector2I {
Vector2I::new(self.tile_x as i32, self.tile_y as i32)
}
}
fn calculate_mask_uv(tile_index: u16, tile_offset: Vector2I) -> Vector2I {
let mask_u = tile_index as i32 % MASK_TILES_ACROSS as i32;
let mask_v = tile_index as i32 / MASK_TILES_ACROSS as i32;
let mask_scale = 65535.0 / MASK_TILES_ACROSS as f32;
let mask_uv = Vector2I::new(mask_u, mask_v) + tile_offset;
mask_uv.to_f32().scale(mask_scale).to_i32()
}
impl Default for TileObjectPrimitive {
#[inline]
fn default() -> TileObjectPrimitive {