Rework the tile builder for D3D11

This commit is contained in:
Patrick Walton 2020-06-23 13:06:22 -07:00
parent 17356311d6
commit f92c631f97
5 changed files with 1013 additions and 729 deletions

View File

@ -10,11 +10,13 @@ homepage = "https://github.com/servo/pathfinder"
[dependencies] [dependencies]
bitflags = "1.0" bitflags = "1.0"
byte-slice-cast = "0.3"
byteorder = "1.2" byteorder = "1.2"
crossbeam-channel = "0.4" crossbeam-channel = "0.4"
fxhash = "0.2" fxhash = "0.2"
half = "1.5" half = "1.5"
hashbrown = "0.7" hashbrown = "0.7"
log = "0.4"
rayon = "1.0" rayon = "1.0"
serde = "1.0" serde = "1.0"
serde_json = "1.0" serde_json = "1.0"
@ -22,9 +24,6 @@ smallvec = "1.2"
vec_map = "0.8" vec_map = "0.8"
instant = { version = "0.1.2", features = ["wasm-bindgen"] } instant = { version = "0.1.2", features = ["wasm-bindgen"] }
[dependencies.log]
version = "0.4"
[dependencies.pathfinder_color] [dependencies.pathfinder_color]
path = "../color" path = "../color"
version = "0.5" version = "0.5"

File diff suppressed because it is too large Load Diff

View File

@ -11,35 +11,23 @@
use pathfinder_geometry::rect::RectI; use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::vector::{Vector2I, vec2i}; use pathfinder_geometry::vector::{Vector2I, vec2i};
#[derive(Debug)] #[derive(Clone, Debug)]
pub struct DenseTileMap<T> { pub struct DenseTileMap<T> where T: Clone + Copy {
pub data: Vec<T>, pub data: Vec<T>,
pub rect: RectI, pub rect: RectI,
} }
impl<T> DenseTileMap<T> { impl<T> DenseTileMap<T> where T: Clone + Copy {
#[inline] #[inline]
pub fn new(rect: RectI) -> DenseTileMap<T> pub fn from_builder<F>(mut build: F, rect: RectI) -> DenseTileMap<T>
where where F: FnMut(Vector2I) -> T {
T: Copy + Clone + Default, let mut data = Vec::with_capacity(rect.size().x() as usize * rect.size().y() as usize);
{ for y in rect.min_y()..rect.max_y() {
let length = rect.size().x() as usize * rect.size().y() as usize; for x in rect.min_x()..rect.max_x() {
DenseTileMap { data.push(build(vec2i(x, y)));
data: vec![T::default(); length], }
rect,
}
}
#[inline]
pub fn from_builder<F>(build: F, rect: RectI) -> DenseTileMap<T>
where
F: FnMut(usize) -> T,
{
let length = rect.size().x() as usize * rect.size().y() as usize;
DenseTileMap {
data: (0..length).map(build).collect(),
rect,
} }
DenseTileMap { data, rect }
} }
#[inline] #[inline]
@ -47,6 +35,14 @@ impl<T> DenseTileMap<T> {
self.coords_to_index(coords).and_then(|index| self.data.get(index)) self.coords_to_index(coords).and_then(|index| self.data.get(index))
} }
#[inline]
pub fn get_mut(&mut self, coords: Vector2I) -> Option<&mut T> {
match self.coords_to_index(coords) {
None => None,
Some(index) => self.data.get_mut(index),
}
}
#[inline] #[inline]
pub fn coords_to_index(&self, coords: Vector2I) -> Option<usize> { pub fn coords_to_index(&self, coords: Vector2I) -> Option<usize> {
if self.rect.contains_point(coords) { if self.rect.contains_point(coords) {

View File

@ -11,8 +11,12 @@
//! Implements the fast lattice-clipping algorithm from Nehab and Hoppe, "Random-Access Rendering //! Implements the fast lattice-clipping algorithm from Nehab and Hoppe, "Random-Access Rendering
//! of General Vector Graphics" 2006. //! of General Vector Graphics" 2006.
use crate::builder::{ObjectBuilder, Occluder, SceneBuilder, SolidTiles}; use crate::builder::{BuiltPath, BuiltPathBinCPUData, BuiltPathData, ObjectBuilder, SceneBuilder};
use crate::tiles::{PackedTile, TILE_HEIGHT, TILE_WIDTH, TileType, TilingPathInfo}; use crate::gpu::options::RendererLevel;
use crate::gpu_data::AlphaTileId;
use crate::options::PrepareMode;
use crate::scene::PathId;
use crate::tiles::{DrawTilingPathInfo, TILE_HEIGHT, TILE_WIDTH, TilingPathInfo};
use pathfinder_content::fill::FillRule; use pathfinder_content::fill::FillRule;
use pathfinder_content::outline::{ContourIterFlags, Outline}; use pathfinder_content::outline::{ContourIterFlags, Outline};
use pathfinder_content::segment::Segment; use pathfinder_content::segment::Segment;
@ -23,102 +27,172 @@ use pathfinder_simd::default::{F32x2, U32x2};
const FLATTENING_TOLERANCE: f32 = 0.25; const FLATTENING_TOLERANCE: f32 = 0.25;
pub(crate) struct Tiler<'a, 'b> { pub(crate) struct Tiler<'a, 'b, 'c, 'd> {
scene_builder: &'a SceneBuilder<'b, 'a>, scene_builder: &'a SceneBuilder<'b, 'a, 'c, 'd>,
pub(crate) object_builder: ObjectBuilder, pub(crate) object_builder: ObjectBuilder,
outline: &'a Outline, outline: &'a Outline,
path_info: TilingPathInfo<'a>, clip_path: Option<&'a BuiltPath>,
} }
impl<'a, 'b> Tiler<'a, 'b> { impl<'a, 'b, 'c, 'd> Tiler<'a, 'b, 'c, 'd> {
pub(crate) fn new(scene_builder: &'a SceneBuilder<'b, 'a>, pub(crate) fn new(scene_builder: &'a SceneBuilder<'b, 'a, 'c, 'd>,
path_id: PathId,
outline: &'a Outline, outline: &'a Outline,
fill_rule: FillRule, fill_rule: FillRule,
view_box: RectF, view_box: RectF,
path_info: TilingPathInfo<'a>) prepare_mode: &PrepareMode,
-> Tiler<'a, 'b> { built_clip_paths: &'a [BuiltPath],
path_info: TilingPathInfo)
-> Tiler<'a, 'b, 'c, 'd> {
let bounds = outline.bounds().intersection(view_box).unwrap_or(RectF::default()); let bounds = outline.bounds().intersection(view_box).unwrap_or(RectF::default());
let object_builder = ObjectBuilder::new(bounds, view_box, fill_rule, &path_info);
Tiler { scene_builder, object_builder, outline, path_info } let clip_path = match path_info {
TilingPathInfo::Draw(DrawTilingPathInfo { clip_path_id: Some(clip_path_id), .. }) => {
Some(&built_clip_paths[clip_path_id.0 as usize])
}
_ => None,
};
let object_builder = ObjectBuilder::new(path_id,
bounds,
view_box,
fill_rule,
prepare_mode,
&path_info);
Tiler { scene_builder, object_builder, outline, clip_path }
} }
pub(crate) fn generate_tiles(&mut self) { pub(crate) fn generate_tiles(&mut self) {
match self.object_builder.built_path.data {
BuiltPathData::CPU(_) => {
self.generate_fills();
self.prepare_tiles();
}
BuiltPathData::TransformCPUBinGPU(ref mut data) => {
data.outline = (*self.outline).clone();
}
BuiltPathData::GPU => {
panic!("Shouldn't have generated a tiler at all if we're transforming on GPU!")
}
}
}
fn generate_fills(&mut self) {
debug_assert_eq!(self.scene_builder.sink.renderer_level, RendererLevel::D3D9);
for contour in self.outline.contours() { for contour in self.outline.contours() {
for segment in contour.iter(ContourIterFlags::empty()) { for segment in contour.iter(ContourIterFlags::empty()) {
process_segment(&segment, self.scene_builder, &mut self.object_builder); process_segment(&segment, self.scene_builder, &mut self.object_builder);
} }
} }
self.propagate_backdrops();
self.pack_and_cull();
} }
fn propagate_backdrops(&mut self) { fn prepare_tiles(&mut self) {
let tiles_across = self.object_builder.built_path.tiles.rect.width() as usize; // Don't do this here if the GPU will do it.
for (draw_tile_index, draw_tile) in self.object_builder let (backdrops, tiles, clips) = match self.object_builder.built_path.data {
.built_path BuiltPathData::CPU(ref mut tiled_data) => {
.tiles (&mut tiled_data.backdrops, &mut tiled_data.tiles, &mut tiled_data.clip_tiles)
.data }
.iter_mut() BuiltPathData::TransformCPUBinGPU(_) | BuiltPathData::GPU => {
.enumerate() { panic!("We shouldn't be preparing tiles on CPU!")
let column = draw_tile_index % tiles_across; }
let delta = draw_tile.backdrop;
draw_tile.backdrop = self.object_builder.current_backdrops[column];
self.object_builder.current_backdrops[column] += delta;
}
}
fn pack_and_cull(&mut self) {
let draw_tiling_path_info = match self.path_info {
TilingPathInfo::Clip => return,
TilingPathInfo::Draw(draw_tiling_path_info) => draw_tiling_path_info,
}; };
let blend_mode_is_destructive = draw_tiling_path_info.blend_mode.is_destructive(); // Propagate backdrops.
let tiles_across = tiles.rect.width() as usize;
for (draw_tile_index, draw_tile) in tiles.data.iter_mut().enumerate() {
let tile_coords = vec2i(draw_tile.tile_x as i32, draw_tile.tile_y as i32);
let column = draw_tile_index % tiles_across;
let delta = draw_tile.backdrop as i32;
for (draw_tile_index, draw_tile) in self.object_builder let mut draw_alpha_tile_id = draw_tile.alpha_tile_id;
.built_path let mut draw_tile_backdrop = backdrops[column] as i8;
.tiles
.data
.iter()
.enumerate() {
let packed_tile = PackedTile::new(draw_tile_index as u32,
draw_tile,
&draw_tiling_path_info,
&self.object_builder);
match packed_tile.tile_type { if let Some(built_clip_path) = self.clip_path {
TileType::Solid => { let clip_tiles = match built_clip_path.data {
match self.object_builder.built_path.solid_tiles { BuiltPathData::CPU(BuiltPathBinCPUData { ref tiles, .. }) => tiles,
SolidTiles::Occluders(ref mut occluders) => { _ => unreachable!(),
occluders.push(Occluder::new(packed_tile.tile_coords)); };
} match clip_tiles.get(tile_coords) {
SolidTiles::Regular(ref mut solid_tiles) => { Some(clip_tile) => {
packed_tile.add_to(solid_tiles, if clip_tile.alpha_tile_id != AlphaTileId(!0) &&
&mut self.object_builder.built_path.clip_tiles, draw_alpha_tile_id != AlphaTileId(!0) {
&draw_tiling_path_info, // Hard case: We have an alpha tile and a clip tile with masks. Add a
&self.scene_builder); // job to combine the two masks. Because the mask combining step
// applies the backdrops, zero out the backdrop in the draw tile itself
// so that we don't double-count it.
let clip = clips.as_mut()
.expect("Where are the clips?")
.get_mut(tile_coords)
.unwrap();
clip.dest_tile_id = draw_tile.alpha_tile_id;
clip.dest_backdrop = draw_tile_backdrop as i32;
clip.src_tile_id = clip_tile.alpha_tile_id;
clip.src_backdrop = clip_tile.backdrop as i32;
draw_tile_backdrop = 0;
} else if clip_tile.alpha_tile_id != AlphaTileId(!0) &&
draw_alpha_tile_id == AlphaTileId(!0) &&
draw_tile_backdrop != 0 {
// This is a solid draw tile, but there's a clip applied. Replace it
// with an alpha tile pointing directly to the clip mask.
draw_alpha_tile_id = clip_tile.alpha_tile_id;
draw_tile_backdrop = clip_tile.backdrop;
} else if clip_tile.alpha_tile_id == AlphaTileId(!0) &&
clip_tile.backdrop == 0 {
// This is a blank clip tile. Cull the draw tile entirely.
draw_alpha_tile_id = AlphaTileId(!0);
draw_tile_backdrop = 0;
} }
} }
} None => {
TileType::SingleMask => { // This draw tile is outside the clip path rect. Cull the tile.
debug_assert_ne!(packed_tile.draw_tile.alpha_tile_id.page(), !0); draw_alpha_tile_id = AlphaTileId(!0);
packed_tile.add_to(&mut self.object_builder.built_path.single_mask_tiles, draw_tile_backdrop = 0;
&mut self.object_builder.built_path.clip_tiles, }
&draw_tiling_path_info,
&self.scene_builder);
}
TileType::Empty if blend_mode_is_destructive => {
packed_tile.add_to(&mut self.object_builder.built_path.empty_tiles,
&mut self.object_builder.built_path.clip_tiles,
&draw_tiling_path_info,
&self.scene_builder);
}
TileType::Empty => {
// Just cull.
} }
} }
draw_tile.alpha_tile_id = draw_alpha_tile_id;
draw_tile.backdrop = draw_tile_backdrop;
backdrops[column] += delta;
} }
/*
// Calculate clips.
let built_clip_path = match self.path_info {
TilingPathInfo::Draw(DrawTilingPathInfo {
built_clip_path: Some(built_clip_path),
..
}) => built_clip_path,
_ => return,
};
let clip_tiles = self.object_builder
.built_path
.clip_tiles
.as_mut()
.expect("Where are the clip tiles?");
for draw_tile in &mut self.object_builder.built_path.tiles.data {
let tile_coords = vec2i(draw_tile.tile_x as i32, draw_tile.tile_y as i32);
let built_clip_tile = match built_clip_path.tiles.get(tile_coords) {
None => {
draw_tile.alpha_tile_id = AlphaTileId(!0);
continue;
}
Some(built_clip_tile) => built_clip_tile,
};
let clip_tile = clip_tiles.get_mut(tile_coords).unwrap();
clip_tile.dest_tile_id = draw_tile.alpha_tile_id;
clip_tile.dest_backdrop = draw_tile.backdrop as i32;
clip_tile.src_tile_id = built_clip_tile.alpha_tile_id;
clip_tile.src_backdrop = built_clip_tile.backdrop as i32;
}
*/
} }
} }
@ -165,8 +239,8 @@ fn process_line_segment(line_segment: LineSegment2F,
// Compute `step = vec2f(vector.x < 0 ? -1 : 1, vector.y < 0 ? -1 : 1)`. // Compute `step = vec2f(vector.x < 0 ? -1 : 1, vector.y < 0 ? -1 : 1)`.
let step = Vector2I((vector_is_negative | U32x2::splat(1)).to_i32x2()); let step = Vector2I((vector_is_negative | U32x2::splat(1)).to_i32x2());
// Compute `first_tile_crossing = (from_tile_coords + vec2i(vector.x > 0 ? 1 : 0, // Compute `first_tile_crossing = (from_tile_coords + vec2i(vector.x >= 0 ? 1 : 0,
// vector.y > 0 ? 1 : 0)) * tile_size`. // vector.y >= 0 ? 1 : 0)) * tile_size`.
let first_tile_crossing = (from_tile_coords + let first_tile_crossing = (from_tile_coords +
Vector2I((!vector_is_negative & U32x2::splat(1)).to_i32x2())).to_f32() * tile_size; Vector2I((!vector_is_negative & U32x2::splat(1)).to_i32x2())).to_f32() * tile_size;

View File

@ -8,33 +8,33 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use crate::builder::{BuiltPath, ObjectBuilder}; use crate::gpu_data::{TILE_CTRL_MASK_0_SHIFT, TILE_CTRL_MASK_EVEN_ODD};
use crate::gpu_data::{AlphaTileId, TileObjectPrimitive}; use crate::gpu_data::{TILE_CTRL_MASK_WINDING, TileObjectPrimitive};
use crate::paint::{PaintId, PaintMetadata}; use crate::paint::PaintId;
use crate::scene::ClipPathId;
use pathfinder_content::effects::BlendMode; use pathfinder_content::effects::BlendMode;
use pathfinder_content::fill::FillRule; use pathfinder_content::fill::FillRule;
use pathfinder_geometry::rect::{RectF, RectI}; use pathfinder_geometry::rect::{RectF, RectI};
use pathfinder_geometry::vector::{Vector2I, vec2f}; use pathfinder_geometry::vector::vec2f;
pub const TILE_WIDTH: u32 = 16; pub const TILE_WIDTH: u32 = 16;
pub const TILE_HEIGHT: u32 = 16; pub const TILE_HEIGHT: u32 = 16;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub(crate) enum TilingPathInfo<'a> { pub(crate) enum TilingPathInfo {
Clip, Clip,
Draw(DrawTilingPathInfo<'a>), Draw(DrawTilingPathInfo),
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub(crate) struct DrawTilingPathInfo<'a> { pub(crate) struct DrawTilingPathInfo {
pub(crate) paint_id: PaintId, pub(crate) paint_id: PaintId,
pub(crate) paint_metadata: &'a PaintMetadata,
pub(crate) blend_mode: BlendMode, pub(crate) blend_mode: BlendMode,
pub(crate) built_clip_path: Option<&'a BuiltPath>,
pub(crate) fill_rule: FillRule, pub(crate) fill_rule: FillRule,
pub(crate) clip_path_id: Option<ClipPathId>,
} }
impl<'a> TilingPathInfo<'a> { impl TilingPathInfo {
pub(crate) fn has_destructive_blend_mode(&self) -> bool { pub(crate) fn has_destructive_blend_mode(&self) -> bool {
match *self { match *self {
TilingPathInfo::Draw(ref draw_tiling_path_info) => { TilingPathInfo::Draw(ref draw_tiling_path_info) => {
@ -43,126 +43,23 @@ impl<'a> TilingPathInfo<'a> {
TilingPathInfo::Clip => false, TilingPathInfo::Clip => false,
} }
} }
}
pub(crate) struct PackedTile<'a> { pub(crate) fn to_ctrl(&self) -> u8 {
pub(crate) tile_type: TileType, let mut ctrl = 0;
pub(crate) tile_coords: Vector2I, match *self {
pub(crate) draw_tile: &'a TileObjectPrimitive, TilingPathInfo::Draw(ref draw_tiling_path_info) => {
pub(crate) clip_tile: Option<&'a TileObjectPrimitive>, match draw_tiling_path_info.fill_rule {
} FillRule::EvenOdd => {
ctrl |= (TILE_CTRL_MASK_EVEN_ODD << TILE_CTRL_MASK_0_SHIFT) as u8
#[derive(Clone, Copy, PartialEq)]
pub(crate) enum TileType {
Solid,
Empty,
SingleMask,
}
impl<'a> PackedTile<'a> {
pub(crate) fn new(draw_tile_index: u32,
draw_tile: &'a TileObjectPrimitive,
draw_tiling_path_info: &DrawTilingPathInfo<'a>,
object_builder: &ObjectBuilder)
-> PackedTile<'a> {
let tile_coords = object_builder.local_tile_index_to_coords(draw_tile_index as u32);
// First, if the draw tile is empty, cull it regardless of clip.
if draw_tile.is_solid() {
match (object_builder.built_path.fill_rule, draw_tile.backdrop) {
(FillRule::Winding, 0) => {
return PackedTile {
tile_type: TileType::Empty,
tile_coords,
draw_tile,
clip_tile: None,
};
}
(FillRule::Winding, _) => {}
(FillRule::EvenOdd, backdrop) if backdrop % 2 == 0 => {
return PackedTile {
tile_type: TileType::Empty,
tile_coords,
draw_tile,
clip_tile: None,
};
}
(FillRule::EvenOdd, _) => {}
}
}
// Figure out what clip tile we need, if any.
let clip_tile = match draw_tiling_path_info.built_clip_path {
None => None,
Some(built_clip_path) => {
match built_clip_path.tiles.get(tile_coords) {
None => {
// This tile is outside of the bounds of the clip path entirely. We can
// cull it.
return PackedTile {
tile_type: TileType::Empty,
tile_coords,
draw_tile,
clip_tile: None,
};
} }
Some(clip_tile) if clip_tile.is_solid() => { FillRule::Winding => {
if clip_tile.backdrop != 0 { ctrl |= (TILE_CTRL_MASK_WINDING << TILE_CTRL_MASK_0_SHIFT) as u8
// The clip tile is fully opaque, so this tile isn't clipped at
// all.
None
} else {
// This tile is completely clipped out. Cull it.
return PackedTile {
tile_type: TileType::Empty,
tile_coords,
draw_tile,
clip_tile: None,
};
}
} }
Some(clip_tile) => Some(clip_tile),
}
}
};
// Choose a tile type.
match clip_tile {
None if draw_tile.is_solid() => {
// This is a solid tile that completely occludes the background.
PackedTile { tile_type: TileType::Solid, tile_coords, draw_tile, clip_tile }
}
None => {
// We have a draw tile and no clip tile.
PackedTile {
tile_type: TileType::SingleMask,
tile_coords,
draw_tile,
clip_tile: None,
}
}
Some(clip_tile) if draw_tile.is_solid() => {
// We have a solid draw tile and a clip tile. This is effectively the same as
// having a draw tile and no clip tile.
//
// FIXME(pcwalton): This doesn't preserve the fill rule of the clip path!
PackedTile {
tile_type: TileType::SingleMask,
tile_coords,
draw_tile: clip_tile,
clip_tile: None,
}
}
Some(clip_tile) => {
// We have both a draw and clip mask. Composite them together.
PackedTile {
tile_type: TileType::SingleMask,
tile_coords,
draw_tile,
clip_tile: Some(clip_tile),
} }
} }
TilingPathInfo::Clip => {}
} }
ctrl
} }
} }
@ -170,13 +67,6 @@ pub fn round_rect_out_to_tile_bounds(rect: RectF) -> RectI {
(rect * vec2f(1.0 / TILE_WIDTH as f32, 1.0 / TILE_HEIGHT as f32)).round_out().to_i32() (rect * vec2f(1.0 / TILE_WIDTH as f32, 1.0 / TILE_HEIGHT as f32)).round_out().to_i32()
} }
impl Default for TileObjectPrimitive {
#[inline]
fn default() -> TileObjectPrimitive {
TileObjectPrimitive { backdrop: 0, alpha_tile_id: AlphaTileId::invalid() }
}
}
impl TileObjectPrimitive { impl TileObjectPrimitive {
#[inline] #[inline]
pub fn is_solid(&self) -> bool { !self.alpha_tile_id.is_valid() } pub fn is_solid(&self) -> bool { !self.alpha_tile_id.is_valid() }