From f92c631f9728f20d3c50e92565fcf6f3f2fde9f3 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 23 Jun 2020 13:06:22 -0700 Subject: [PATCH] Rework the tile builder for D3D11 --- renderer/Cargo.toml | 5 +- renderer/src/builder.rs | 1323 ++++++++++++++++++++++++-------------- renderer/src/tile_map.rs | 42 +- renderer/src/tiler.rs | 220 ++++--- renderer/src/tiles.rs | 152 +---- 5 files changed, 1013 insertions(+), 729 deletions(-) diff --git a/renderer/Cargo.toml b/renderer/Cargo.toml index 0bac653e..8d84d49e 100644 --- a/renderer/Cargo.toml +++ b/renderer/Cargo.toml @@ -10,11 +10,13 @@ homepage = "https://github.com/servo/pathfinder" [dependencies] bitflags = "1.0" +byte-slice-cast = "0.3" byteorder = "1.2" crossbeam-channel = "0.4" fxhash = "0.2" half = "1.5" hashbrown = "0.7" +log = "0.4" rayon = "1.0" serde = "1.0" serde_json = "1.0" @@ -22,9 +24,6 @@ smallvec = "1.2" vec_map = "0.8" instant = { version = "0.1.2", features = ["wasm-bindgen"] } -[dependencies.log] -version = "0.4" - [dependencies.pathfinder_color] path = "../color" version = "0.5" diff --git a/renderer/src/builder.rs b/renderer/src/builder.rs index 4758cc72..07ae83fe 100644 --- a/renderer/src/builder.rs +++ b/renderer/src/builder.rs @@ -11,86 +11,119 @@ //! Packs data onto the GPU. use crate::concurrent::executor::Executor; -use crate::gpu::renderer::{BlendModeExt, MASK_TILES_ACROSS, MASK_TILES_DOWN}; -use crate::gpu_data::{AlphaTileId, Clip, ClipBatch, ClipBatchKey, ClipBatchKind, Fill}; -use crate::gpu_data::{FillBatchEntry, RenderCommand, TILE_CTRL_MASK_0_SHIFT}; -use crate::gpu_data::{TILE_CTRL_MASK_EVEN_ODD, TILE_CTRL_MASK_WINDING, Tile, TileBatch}; -use crate::gpu_data::{TileBatchTexture, TileObjectPrimitive}; -use crate::options::{PreparedBuildOptions, PreparedRenderTransform, RenderCommandListener}; -use crate::paint::{PaintInfo, PaintMetadata}; -use crate::scene::{DisplayItem, Scene}; +use crate::gpu::blend::BlendModeExt; +use crate::gpu::options::RendererLevel; +use crate::gpu_data::{AlphaTileId, BackdropInfoD3D11, Clip, ClippedPathInfo, DiceMetadataD3D11}; +use crate::gpu_data::{DrawTileBatch, DrawTileBatchD3D9, DrawTileBatchD3D11, Fill, PathBatchIndex}; +use crate::gpu_data::{PathSource, PrepareTilesInfoD3D11}; +use crate::gpu_data::{PropagateMetadataD3D11, RenderCommand, SegmentIndicesD3D11, SegmentsD3D11}; +use crate::gpu_data::{TileBatchDataD3D11, TileBatchId, TileBatchTexture}; +use crate::gpu_data::{TileObjectPrimitive, TilePathInfoD3D11}; +use crate::options::{PrepareMode, PreparedBuildOptions, PreparedRenderTransform}; +use crate::paint::{PaintId, PaintInfo, PaintMetadata}; +use crate::scene::{ClipPathId, DisplayItem, DrawPath, DrawPathId, LastSceneInfo, PathId}; +use crate::scene::{Scene, SceneSink}; use crate::tile_map::DenseTileMap; use crate::tiler::Tiler; -use crate::tiles::{self, DrawTilingPathInfo, PackedTile, TILE_HEIGHT, TILE_WIDTH, TilingPathInfo}; -use crate::z_buffer::{DepthMetadata, ZBuffer}; +use crate::tiles::{self, DrawTilingPathInfo, TILE_HEIGHT, TILE_WIDTH, TilingPathInfo}; +use fxhash::FxHashMap; +use instant::Instant; use pathfinder_content::effects::{BlendMode, Filter}; use pathfinder_content::fill::FillRule; -use pathfinder_content::render_target::RenderTargetId; -use pathfinder_geometry::line_segment::{LineSegment2F, LineSegmentU4, LineSegmentU8}; -use pathfinder_geometry::rect::RectF; +use pathfinder_content::outline::{Outline, PointFlags}; +use pathfinder_geometry::line_segment::{LineSegment2F, LineSegmentU16}; +use pathfinder_geometry::rect::{RectF, RectI}; use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::vector::{Vector2I, vec2i}; use pathfinder_gpu::TextureSamplingFlags; -use pathfinder_simd::default::{F32x4, I32x4}; +use pathfinder_simd::default::F32x4; +use std::borrow::Cow; +use std::ops::Range; use std::sync::atomic::AtomicUsize; -use instant::Instant; use std::u32; pub(crate) const ALPHA_TILE_LEVEL_COUNT: usize = 2; pub(crate) const ALPHA_TILES_PER_LEVEL: usize = 1 << (32 - ALPHA_TILE_LEVEL_COUNT + 1); -pub(crate) struct SceneBuilder<'a, 'b> { +const CURVE_IS_QUADRATIC: u32 = 0x80000000; +const CURVE_IS_CUBIC: u32 = 0x40000000; + +pub(crate) struct SceneBuilder<'a, 'b, 'c, 'd> { scene: &'a mut Scene, built_options: &'b PreparedBuildOptions, next_alpha_tile_indices: [AtomicUsize; ALPHA_TILE_LEVEL_COUNT], - pub(crate) listener: Box, + pub(crate) sink: &'c mut SceneSink<'d>, } #[derive(Debug)] pub(crate) struct ObjectBuilder { pub built_path: BuiltPath, - /// During tiling, this stores the sum of backdrops for tile columns above the viewport. - pub current_backdrops: Vec, - pub fills: Vec, + pub fills: Vec, pub bounds: RectF, } -#[derive(Debug)] +// Derives `Clone` just so we can use `Cow`, not because we actually want to clone it. +#[derive(Clone, Debug)] struct BuiltDrawPath { path: BuiltPath, + clip_path_id: Option, blend_mode: BlendMode, filter: Filter, color_texture: Option, sampling_flags_1: TextureSamplingFlags, mask_0_fill_rule: FillRule, + occludes: bool, } -#[derive(Debug)] +impl BuiltDrawPath { + fn new(built_path: BuiltPath, path_object: &DrawPath, paint_metadata: &PaintMetadata) + -> BuiltDrawPath { + let blend_mode = path_object.blend_mode(); + let occludes = paint_metadata.is_opaque && blend_mode.occludes_backdrop(); + BuiltDrawPath { + path: built_path, + clip_path_id: path_object.clip_path(), + filter: paint_metadata.filter(), + color_texture: paint_metadata.tile_batch_texture(), + sampling_flags_1: TextureSamplingFlags::empty(), + mask_0_fill_rule: path_object.fill_rule(), + blend_mode, + occludes, + } + } +} + +// Derives `Clone` just so we can use `Cow`, not because we actually want to clone it. +#[derive(Clone, Debug)] pub(crate) struct BuiltPath { - pub solid_tiles: SolidTiles, - pub empty_tiles: Vec, - pub single_mask_tiles: Vec, - pub clip_tiles: Vec, - pub tiles: DenseTileMap, + pub data: BuiltPathData, + pub tile_bounds: RectI, pub fill_rule: FillRule, + pub clip_path_id: Option, + pub ctrl_byte: u8, + pub paint_id: PaintId, } #[derive(Clone, Debug)] -pub struct BuiltTile { - pub page: u16, - pub tile: Tile, -} - -#[derive(Clone, Copy, Debug)] -pub struct BuiltClip { - pub clip: Clip, - pub key: ClipBatchKey, +pub(crate) enum BuiltPathData { + CPU(BuiltPathBinCPUData), + TransformCPUBinGPU(BuiltPathTransformCPUBinGPUData), + GPU, } #[derive(Clone, Debug)] -pub(crate) enum SolidTiles { - Occluders(Vec), - Regular(Vec), +pub(crate) struct BuiltPathBinCPUData { + /// During tiling, or if backdrop computation is done on GPU, this stores the sum of backdrops + /// for tile columns above the viewport. + pub backdrops: Vec, + pub tiles: DenseTileMap, + pub clip_tiles: Option>, +} + +#[derive(Clone, Debug)] +pub(crate) struct BuiltPathTransformCPUBinGPUData { + /// The transformed outline. + pub outline: Outline, } #[derive(Clone, Copy, Debug)] @@ -98,18 +131,16 @@ pub(crate) struct Occluder { pub(crate) coords: Vector2I, } -impl<'a, 'b> SceneBuilder<'a, 'b> { - pub(crate) fn new( - scene: &'a mut Scene, - built_options: &'b PreparedBuildOptions, - listener: Box, - ) -> SceneBuilder<'a, 'b> { - let effective_view_box = scene.effective_view_box(built_options); +impl<'a, 'b, 'c, 'd> SceneBuilder<'a, 'b, 'c, 'd> { + pub(crate) fn new(scene: &'a mut Scene, + built_options: &'b PreparedBuildOptions, + sink: &'c mut SceneSink<'d>) + -> SceneBuilder<'a, 'b, 'c, 'd> { SceneBuilder { scene, built_options, next_alpha_tile_indices: [AtomicUsize::new(0), AtomicUsize::new(0)], - listener, + sink, } } @@ -119,18 +150,20 @@ impl<'a, 'b> SceneBuilder<'a, 'b> { // Send the start rendering command. let bounding_quad = self.built_options.bounding_quad(); - let clip_path_count = self.scene.clip_paths.len(); - let draw_path_count = self.scene.paths.len(); + let clip_path_count = self.scene.clip_paths().len(); + let draw_path_count = self.scene.draw_paths().len(); let total_path_count = clip_path_count + draw_path_count; let needs_readable_framebuffer = self.needs_readable_framebuffer(); - self.listener.send(RenderCommand::Start { + self.sink.listener.send(RenderCommand::Start { bounding_quad, path_count: total_path_count, needs_readable_framebuffer, }); + let prepare_mode = self.built_options.to_prepare_mode(self.sink.renderer_level); + let render_transform = match self.built_options.transform { PreparedRenderTransform::Transform2D(transform) => transform.inverse(), _ => Transform2F::default() @@ -143,25 +176,73 @@ impl<'a, 'b> SceneBuilder<'a, 'b> { render_target_metadata: _, } = self.scene.build_paint_info(render_transform); for render_command in render_commands { - self.listener.send(render_command); + self.sink.listener.send(render_command); } + let built_paths = match prepare_mode { + PrepareMode::CPU | PrepareMode::TransformCPUBinGPU => { + Some(self.build_paths_on_cpu(executor, &paint_metadata, &prepare_mode)) + } + PrepareMode::GPU { .. } => None, + }; + + // TODO(pcwalton): Do this earlier? + let scene_is_dirty = match (&prepare_mode, &self.sink.last_scene) { + (&PrepareMode::GPU { .. }, &None) => true, + (&PrepareMode::GPU { .. }, &Some(LastSceneInfo { + scene_id: ref last_scene_id, + scene_epoch: ref last_scene_epoch, + .. + })) => *last_scene_id != self.scene.id() || *last_scene_epoch != self.scene.epoch(), + _ => false, + }; + + if scene_is_dirty { + let built_segments = BuiltSegments::from_scene(&self.scene); + self.sink.listener.send(RenderCommand::UploadSceneD3D11 { + draw_segments: built_segments.draw_segments, + clip_segments: built_segments.clip_segments, + }); + self.sink.last_scene = Some(LastSceneInfo { + scene_id: self.scene.id(), + scene_epoch: self.scene.epoch(), + draw_segment_ranges: built_segments.draw_segment_ranges, + clip_segment_ranges: built_segments.clip_segment_ranges, + }); + } + + self.finish_building(&paint_metadata, built_paths, &prepare_mode); + + let cpu_build_time = Instant::now() - start_time; + self.sink.listener.send(RenderCommand::Finish { cpu_build_time }); + } + + fn build_paths_on_cpu(&mut self, + executor: &E, + paint_metadata: &[PaintMetadata], + prepare_mode: &PrepareMode) + -> BuiltPaths + where E: Executor { + let clip_path_count = self.scene.clip_paths().len(); + let draw_path_count = self.scene.draw_paths().len(); let effective_view_box = self.scene.effective_view_box(self.built_options); let built_clip_paths = executor.build_vector(clip_path_count, |path_index| { - self.build_clip_path(PathBuildParams { - path_index, + self.build_clip_path_on_cpu(PathBuildParams { + path_id: PathId(path_index as u32), view_box: effective_view_box, + prepare_mode: *prepare_mode, built_options: &self.built_options, scene: &self.scene, }) }); let built_draw_paths = executor.build_vector(draw_path_count, |path_index| { - self.build_draw_path(DrawPathBuildParams { + self.build_draw_path_on_cpu(DrawPathBuildParams { path_build_params: PathBuildParams { - path_index, + path_id: PathId(path_index as u32), view_box: effective_view_box, + prepare_mode: *prepare_mode, built_options: &self.built_options, scene: &self.scene, }, @@ -170,21 +251,21 @@ impl<'a, 'b> SceneBuilder<'a, 'b> { }) }); - self.finish_building(&paint_metadata, built_draw_paths); - - let cpu_build_time = Instant::now() - start_time; - self.listener.send(RenderCommand::Finish { cpu_build_time }); + BuiltPaths { draw: built_draw_paths } } - fn build_clip_path(&self, params: PathBuildParams) -> BuiltPath { - let PathBuildParams { path_index, view_box, built_options, scene } = params; - let path_object = &scene.clip_paths[path_index]; + fn build_clip_path_on_cpu(&self, params: PathBuildParams) -> BuiltPath { + let PathBuildParams { path_id, view_box, built_options, scene, prepare_mode } = params; + let path_object = &scene.get_clip_path(path_id.to_clip_path_id()); let outline = scene.apply_render_options(path_object.outline(), built_options); let mut tiler = Tiler::new(self, + path_id, &outline, path_object.fill_rule(), view_box, + &prepare_mode, + &[], TilingPathInfo::Clip); tiler.generate_tiles(); @@ -192,320 +273,108 @@ impl<'a, 'b> SceneBuilder<'a, 'b> { tiler.object_builder.built_path } - fn build_draw_path(&self, params: DrawPathBuildParams) -> BuiltDrawPath { + fn build_draw_path_on_cpu(&self, params: DrawPathBuildParams) -> BuiltDrawPath { let DrawPathBuildParams { - path_build_params: PathBuildParams { path_index, view_box, built_options, scene }, + path_build_params: PathBuildParams { + path_id, + view_box, + built_options, + prepare_mode, + scene, + }, paint_metadata, built_clip_paths, } = params; - let path_object = &scene.paths[path_index]; + let path_object = scene.get_draw_path(path_id.to_draw_path_id()); let outline = scene.apply_render_options(path_object.outline(), built_options); let paint_id = path_object.paint(); let paint_metadata = &paint_metadata[paint_id.0 as usize]; - 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, + path_id, &outline, path_object.fill_rule(), view_box, + &prepare_mode, + &built_clip_paths, TilingPathInfo::Draw(DrawTilingPathInfo { paint_id, - paint_metadata, blend_mode: path_object.blend_mode(), - built_clip_path, + clip_path_id: path_object.clip_path(), fill_rule: path_object.fill_rule(), })); tiler.generate_tiles(); self.send_fills(tiler.object_builder.fills); - BuiltDrawPath { - path: tiler.object_builder.built_path, - blend_mode: path_object.blend_mode(), - filter: paint_metadata.filter(), - color_texture: paint_metadata.tile_batch_texture(), - sampling_flags_1: TextureSamplingFlags::empty(), - mask_0_fill_rule: path_object.fill_rule(), - } + + BuiltDrawPath::new(tiler.object_builder.built_path, path_object, paint_metadata) } - fn send_fills(&self, fills: Vec) { + fn send_fills(&self, fills: Vec) { if !fills.is_empty() { - self.listener.send(RenderCommand::AddFills(fills)); + self.sink.listener.send(RenderCommand::AddFillsD3D9(fills)); } } - fn build_clips(&self, built_draw_paths: &[BuiltDrawPath]) { - let mut built_clip_tiles = vec![]; - for built_draw_path in built_draw_paths { - for built_clip_tile in &built_draw_path.path.clip_tiles { - built_clip_tiles.push(*built_clip_tile); - } - } + fn build_tile_batches(&mut self, + paint_metadata: &[PaintMetadata], + prepare_mode: &PrepareMode, + built_paths: Option) { + let mut tile_batch_builder = TileBatchBuilder::new(&prepare_mode, built_paths); - built_clip_tiles.sort_by_key(|built_clip_tile| built_clip_tile.key); - - let mut batches: Vec = vec![]; - for built_clip_tile in built_clip_tiles { - if batches.is_empty() || batches.last_mut().unwrap().key != built_clip_tile.key { - batches.push(ClipBatch { key: built_clip_tile.key, clips: vec![] }); - } - batches.last_mut().unwrap().clips.push(built_clip_tile.clip); - } - - if !batches.is_empty() { - self.listener.send(RenderCommand::ClipTiles(batches)); - } - } - - fn cull_tiles(&self, paint_metadata: &[PaintMetadata], built_draw_paths: Vec) - -> CulledTiles { - let mut culled_tiles = CulledTiles { display_list: vec![] }; - - let mut remaining_layer_z_buffers = self.build_solid_tiles(&built_draw_paths); - remaining_layer_z_buffers.reverse(); - - // Process first Z-buffer. - let first_z_buffer = remaining_layer_z_buffers.pop().unwrap(); - let first_solid_tiles = first_z_buffer.build_solid_tiles(paint_metadata); - for batch in first_solid_tiles.batches { - culled_tiles.display_list.push(CulledDisplayItem::DrawTiles(batch)); - } - - let mut layer_z_buffers_stack = vec![first_z_buffer]; - let mut current_depth = 1; - - for display_item in &self.scene.display_list { + // Prepare display items. + for display_item in self.scene.display_list() { match *display_item { DisplayItem::PushRenderTarget(render_target_id) => { - culled_tiles.display_list - .push(CulledDisplayItem::PushRenderTarget(render_target_id)); - - let z_buffer = remaining_layer_z_buffers.pop().unwrap(); - let solid_tiles = z_buffer.build_solid_tiles(paint_metadata); - for batch in solid_tiles.batches { - culled_tiles.display_list.push(CulledDisplayItem::DrawTiles(batch)); - } - layer_z_buffers_stack.push(z_buffer); - } - - DisplayItem::PopRenderTarget => { - culled_tiles.display_list.push(CulledDisplayItem::PopRenderTarget); - layer_z_buffers_stack.pop(); - } - - DisplayItem::DrawPaths { - start_index: start_draw_path_index, - end_index: end_draw_path_index, - } => { - for draw_path_index in start_draw_path_index..end_draw_path_index { - let built_draw_path = &built_draw_paths[draw_path_index as usize]; - let layer_z_buffer = layer_z_buffers_stack.last().unwrap(); - let color_texture = built_draw_path.color_texture; - - debug_assert!(built_draw_path.path.empty_tiles.is_empty() || - built_draw_path.blend_mode.is_destructive()); - self.add_alpha_tiles(&mut culled_tiles, - layer_z_buffer, - &built_draw_path.path.empty_tiles, - current_depth, - None, - built_draw_path.blend_mode, - built_draw_path.filter); - - self.add_alpha_tiles(&mut culled_tiles, - layer_z_buffer, - &built_draw_path.path.single_mask_tiles, - current_depth, - color_texture, - built_draw_path.blend_mode, - built_draw_path.filter); - - match built_draw_path.path.solid_tiles { - SolidTiles::Regular(ref tiles) => { - self.add_alpha_tiles(&mut culled_tiles, - layer_z_buffer, - tiles, - current_depth, - color_texture, - built_draw_path.blend_mode, - built_draw_path.filter); - } - SolidTiles::Occluders(_) => {} - } - - current_depth += 1; - } - } - } - } - - culled_tiles - } - - fn build_solid_tiles(&self, built_draw_paths: &[BuiltDrawPath]) -> Vec { - let effective_view_box = self.scene.effective_view_box(self.built_options); - let mut z_buffers = vec![ZBuffer::new(effective_view_box)]; - let mut z_buffer_index_stack = vec![0]; - let mut current_depth = 1; - - // Create Z-buffers. - for display_item in &self.scene.display_list { - match *display_item { - DisplayItem::PushRenderTarget { .. } => { - z_buffer_index_stack.push(z_buffers.len()); - z_buffers.push(ZBuffer::new(effective_view_box)); + tile_batch_builder.draw_commands + .push(RenderCommand::PushRenderTarget(render_target_id)) } DisplayItem::PopRenderTarget => { - z_buffer_index_stack.pop(); + tile_batch_builder.draw_commands.push(RenderCommand::PopRenderTarget) } - DisplayItem::DrawPaths { start_index, end_index } => { - let (start_index, end_index) = (start_index as usize, end_index as usize); - let z_buffer = &mut z_buffers[*z_buffer_index_stack.last().unwrap()]; - for (path_subindex, built_draw_path) in - built_draw_paths[start_index..end_index].iter().enumerate() { - let path_index = (path_subindex + start_index) as u32; - let path = &self.scene.paths[path_index as usize]; - let metadata = DepthMetadata { paint_id: path.paint() }; - match built_draw_path.path.solid_tiles { - SolidTiles::Regular(_) => { - z_buffer.update(&[], current_depth, metadata); - } - SolidTiles::Occluders(ref occluders) => { - z_buffer.update(occluders, current_depth, metadata); - } - } - current_depth += 1; - } + DisplayItem::DrawPaths(ref path_id_range) => { + tile_batch_builder.build_tile_batches_for_draw_path_display_item( + &self.scene, + &self.sink, + self.built_options, + path_id_range.start..path_id_range.end, + paint_metadata, + prepare_mode); } } } - debug_assert_eq!(z_buffer_index_stack.len(), 1); - z_buffers - } - - fn add_alpha_tiles(&self, - culled_tiles: &mut CulledTiles, - layer_z_buffer: &ZBuffer, - built_alpha_tiles: &[BuiltTile], - current_depth: u32, - color_texture: Option, - blend_mode: BlendMode, - filter: Filter) { - let mut batch_indices: Vec = vec![]; - for built_alpha_tile in built_alpha_tiles { - // Early cull if possible. - let alpha_tile_coords = built_alpha_tile.tile.tile_position(); - if !layer_z_buffer.test(alpha_tile_coords, current_depth) { - continue; - } - - // Find an appropriate batch if we can. - let mut dest_batch_index = batch_indices.iter().filter(|&batch_index| { - batch_index.tile_page == built_alpha_tile.page - }).next().cloned(); - - // If no batch was found, try to reuse the last batch in the display list. - // - // TODO(pcwalton): We could try harder to find a batch by taking tile positions into - // account... - if dest_batch_index.is_none() { - match culled_tiles.display_list.last() { - Some(&CulledDisplayItem::DrawTiles(TileBatch { - tiles: _, - color_texture: ref batch_color_texture, - blend_mode: batch_blend_mode, - filter: batch_filter, - tile_page: batch_tile_page - })) if *batch_color_texture == color_texture && - batch_blend_mode == blend_mode && - batch_filter == filter && - !batch_blend_mode.needs_readable_framebuffer() && - batch_tile_page == built_alpha_tile.page => { - dest_batch_index = Some(BatchIndex { - display_item_index: culled_tiles.display_list.len() - 1, - tile_page: batch_tile_page, - }); - batch_indices.push(dest_batch_index.unwrap()); - } - _ => {} - } - } - - // If it's still the case that no suitable batch was found, then make a new one. - if dest_batch_index.is_none() { - dest_batch_index = Some(BatchIndex { - display_item_index: culled_tiles.display_list.len(), - tile_page: built_alpha_tile.page, - }); - batch_indices.push(dest_batch_index.unwrap()); - culled_tiles.display_list.push(CulledDisplayItem::DrawTiles(TileBatch { - tiles: vec![], - color_texture, - blend_mode, - filter, - tile_page: built_alpha_tile.page, - })); - } - - // Add to the appropriate batch. - match culled_tiles.display_list[dest_batch_index.unwrap().display_item_index] { - CulledDisplayItem::DrawTiles(ref mut tiles) => { - tiles.tiles.push(built_alpha_tile.tile); - } - _ => unreachable!(), - } - } - - #[derive(Clone, Copy)] - struct BatchIndex { - display_item_index: usize, - tile_page: u16, - } - } - - fn pack_tiles(&mut self, culled_tiles: CulledTiles) { - self.listener.send(RenderCommand::BeginTileDrawing); - for display_item in culled_tiles.display_list { - match display_item { - CulledDisplayItem::DrawTiles(batch) => { - self.listener.send(RenderCommand::DrawTiles(batch)) - } - CulledDisplayItem::PushRenderTarget(render_target_id) => { - self.listener.send(RenderCommand::PushRenderTarget(render_target_id)) - } - CulledDisplayItem::PopRenderTarget => { - self.listener.send(RenderCommand::PopRenderTarget) - } - } - } + // Send commands. + tile_batch_builder.send_to(&self.sink); } fn finish_building(&mut self, paint_metadata: &[PaintMetadata], - built_draw_paths: Vec) { - self.listener.send(RenderCommand::FlushFills); - self.build_clips(&built_draw_paths); - let culled_tiles = self.cull_tiles(paint_metadata, built_draw_paths); - self.pack_tiles(culled_tiles); + built_paths: Option, + prepare_mode: &PrepareMode) { + match self.sink.renderer_level { + RendererLevel::D3D9 => self.sink.listener.send(RenderCommand::FlushFillsD3D9), + RendererLevel::D3D11 => {} + } + + self.build_tile_batches(paint_metadata, prepare_mode, built_paths); } fn needs_readable_framebuffer(&self) -> bool { let mut framebuffer_nesting = 0; - for display_item in &self.scene.display_list { + for display_item in self.scene.display_list() { match *display_item { DisplayItem::PushRenderTarget(_) => framebuffer_nesting += 1, DisplayItem::PopRenderTarget => framebuffer_nesting -= 1, - DisplayItem::DrawPaths { start_index, end_index } => { + DisplayItem::DrawPaths(ref draw_path_id_range) => { if framebuffer_nesting > 0 { continue; } - for path_index in start_index..end_index { - let blend_mode = self.scene.paths[path_index as usize].blend_mode(); + for draw_path_id in draw_path_id_range.start.0..draw_path_id_range.end.0 { + let draw_path_id = DrawPathId(draw_path_id); + let blend_mode = self.scene.get_draw_path(draw_path_id).blend_mode(); if blend_mode.needs_readable_framebuffer() { return true; } @@ -517,10 +386,15 @@ impl<'a, 'b> SceneBuilder<'a, 'b> { } } +struct BuiltPaths { + draw: Vec, +} + struct PathBuildParams<'a> { - path_index: usize, + path_id: PathId, view_box: RectF, built_options: &'a PreparedBuildOptions, + prepare_mode: PrepareMode, scene: &'a Scene, } @@ -531,74 +405,102 @@ struct DrawPathBuildParams<'a> { } impl BuiltPath { - fn new(path_bounds: RectF, + fn new(path_id: PathId, + path_bounds: RectF, view_box_bounds: RectF, fill_rule: FillRule, + prepare_mode: &PrepareMode, tiling_path_info: &TilingPathInfo) -> BuiltPath { - let occludes = match *tiling_path_info { - TilingPathInfo::Draw(ref draw_tiling_path_info) => { - draw_tiling_path_info.paint_metadata.is_opaque && - draw_tiling_path_info.blend_mode.occludes_backdrop() - } - TilingPathInfo::Clip => true, + let paint_id = match *tiling_path_info { + TilingPathInfo::Draw(ref draw_tiling_path_info) => draw_tiling_path_info.paint_id, + TilingPathInfo::Clip => PaintId(0), }; + let ctrl_byte = tiling_path_info.to_ctrl(); + let tile_map_bounds = if tiling_path_info.has_destructive_blend_mode() { view_box_bounds } else { path_bounds }; + let tile_bounds = tiles::round_rect_out_to_tile_bounds(tile_map_bounds); + + let clip_path_id = match *tiling_path_info { + TilingPathInfo::Draw(ref draw_tiling_path_info) => { + draw_tiling_path_info.clip_path_id + } + _ => None, + }; + + let data = match *prepare_mode { + PrepareMode::CPU => { + BuiltPathData::CPU(BuiltPathBinCPUData { + backdrops: vec![0; tile_bounds.width() as usize], + tiles: DenseTileMap::from_builder(|tile_coord| { + TileObjectPrimitive { + tile_x: tile_coord.x() as i16, + tile_y: tile_coord.y() as i16, + alpha_tile_id: AlphaTileId(!0), + path_id, + color: paint_id.0, + backdrop: 0, + ctrl: ctrl_byte, + } + }, tile_bounds), + clip_tiles: match *tiling_path_info { + TilingPathInfo::Draw(_) if clip_path_id.is_some() => { + Some(DenseTileMap::from_builder(|_| { + Clip { + dest_tile_id: AlphaTileId(!0), + dest_backdrop: 0, + src_tile_id: AlphaTileId(!0), + src_backdrop: 0, + } + }, tile_bounds)) + } + _ => None, + }, + }) + } + PrepareMode::TransformCPUBinGPU => { + BuiltPathData::TransformCPUBinGPU(BuiltPathTransformCPUBinGPUData { + outline: Outline::new(), + }) + } + PrepareMode::GPU { .. } => BuiltPathData::GPU, + }; + BuiltPath { - empty_tiles: vec![], - single_mask_tiles: vec![], - clip_tiles: vec![], - solid_tiles: if occludes { - SolidTiles::Occluders(vec![]) - } else { - SolidTiles::Regular(vec![]) - }, - tiles: DenseTileMap::new(tiles::round_rect_out_to_tile_bounds(tile_map_bounds)), + data, + tile_bounds, + clip_path_id, fill_rule, + ctrl_byte, + paint_id, } } } -impl Occluder { - #[inline] - pub(crate) fn new(coords: Vector2I) -> Occluder { - Occluder { coords } - } -} - -struct CulledTiles { - display_list: Vec, -} - -enum CulledDisplayItem { - DrawTiles(TileBatch), - PushRenderTarget(RenderTargetId), - PopRenderTarget, -} - -#[derive(Clone, Copy, Debug, Default)] -pub struct TileStats { - pub solid_tile_count: u32, - pub alpha_tile_count: u32, -} - // Utilities for built objects impl ObjectBuilder { - pub(crate) fn new(path_bounds: RectF, + // If `outline` is `None`, then tiling is being done on CPU. Otherwise, it's done on GPU. + pub(crate) fn new(path_id: PathId, + path_bounds: RectF, view_box_bounds: RectF, fill_rule: FillRule, + prepare_mode: &PrepareMode, tiling_path_info: &TilingPathInfo) -> ObjectBuilder { - let built_path = BuiltPath::new(path_bounds, view_box_bounds, fill_rule, tiling_path_info); - let current_backdrops = vec![0; built_path.tiles.rect.width() as usize]; - ObjectBuilder { built_path, bounds: path_bounds, current_backdrops, fills: vec![] } + let built_path = BuiltPath::new(path_id, + path_bounds, + view_box_bounds, + fill_rule, + prepare_mode, + tiling_path_info); + ObjectBuilder { built_path, bounds: path_bounds, fills: vec![] } } pub(crate) fn add_fill(&mut self, @@ -618,7 +520,7 @@ impl ObjectBuilder { let tile_size = F32x4::splat(TILE_WIDTH as f32); let tile_upper_left = tile_coords.to_f32().0.to_f32x4().xyxy() * tile_size; - // Convert to 4.8 fixed point. + // Convert to 8.8 fixed point. let segment = (segment.0 - tile_upper_left) * F32x4::splat(256.0); let (min, max) = (F32x4::default(), F32x4::splat((TILE_WIDTH * 256 - 1) as f32)); let segment = segment.clamp(min, max).to_i32x4(); @@ -633,181 +535,604 @@ impl ObjectBuilder { // Allocate a global tile if necessary. let alpha_tile_id = self.get_or_allocate_alpha_tile_index(scene_builder, tile_coords); - // Pack whole pixels. - let px = (segment & I32x4::splat(0xf00)).to_u32x4(); - let px = (px >> 8).to_i32x4() | (px >> 4).to_i32x4().yxwz(); - // Pack instance data. debug!("... OK, pushing"); - self.fills.push(FillBatchEntry { - page: alpha_tile_id.page(), - fill: Fill { - 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, - }, - alpha_tile_index: alpha_tile_id.tile(), + self.fills.push(Fill { + line_segment: LineSegmentU16 { + from_x: from_x as u16, + from_y: from_y as u16, + to_x: to_x as u16, + to_y: to_y as u16, }, + // If fills are being done with compute, then this value will be overwritten later. + link: alpha_tile_id.0, }); } - fn get_or_allocate_alpha_tile_index( - &mut self, - scene_builder: &SceneBuilder, - tile_coords: Vector2I, - ) -> AlphaTileId { - let local_tile_index = self.built_path.tiles.coords_to_index_unchecked(tile_coords); - let alpha_tile_id = self.built_path.tiles.data[local_tile_index].alpha_tile_id; + fn get_or_allocate_alpha_tile_index(&mut self, + scene_builder: &SceneBuilder, + tile_coords: Vector2I) + -> AlphaTileId { + let local_tile_index = self.tile_coords_to_local_index_unchecked(tile_coords) as usize; + + let tiles = match self.built_path.data { + BuiltPathData::CPU(ref mut cpu_data) => &mut cpu_data.tiles, + BuiltPathData::GPU | BuiltPathData::TransformCPUBinGPU(_) => { + panic!("Can't allocate alpha tile index on CPU if not doing building on CPU!") + } + }; + + let alpha_tile_id = tiles.data[local_tile_index].alpha_tile_id; if alpha_tile_id.is_valid() { return alpha_tile_id; } let alpha_tile_id = AlphaTileId::new(&scene_builder.next_alpha_tile_indices, 0); - self.built_path.tiles.data[local_tile_index].alpha_tile_id = alpha_tile_id; + tiles.data[local_tile_index].alpha_tile_id = alpha_tile_id; alpha_tile_id } #[inline] - pub(crate) fn tile_coords_to_local_index(&self, coords: Vector2I) -> Option { - self.built_path.tiles.coords_to_index(coords).map(|index| index as u32) + pub(crate) fn tile_coords_to_local_index_unchecked(&self, coords: Vector2I) -> u32 { + let tile_rect = self.built_path.tile_bounds; + let offset = coords - tile_rect.origin(); + (offset.x() + tile_rect.width() * offset.y()) as u32 } #[inline] - pub(crate) fn local_tile_index_to_coords(&self, tile_index: u32) -> Vector2I { - self.built_path.tiles.index_to_coords(tile_index as usize) + pub(crate) fn tile_coords_to_local_index(&self, coords: Vector2I) -> Option { + if self.built_path.tile_bounds.contains_point(coords) { + Some(self.tile_coords_to_local_index_unchecked(coords)) + } else { + None + } } #[inline] pub(crate) fn adjust_alpha_tile_backdrop(&mut self, tile_coords: Vector2I, delta: i8) { - let tile_offset = tile_coords - self.built_path.tiles.rect.origin(); - if tile_offset.x() < 0 || tile_offset.x() >= self.built_path.tiles.rect.width() || - tile_offset.y() >= self.built_path.tiles.rect.height() { + let (tiles, backdrops) = match self.built_path.data { + BuiltPathData::CPU(ref mut tiled_data) => { + (&mut tiled_data.tiles, &mut tiled_data.backdrops) + } + BuiltPathData::TransformCPUBinGPU(_) | BuiltPathData::GPU => unreachable!(), + }; + + let tile_offset = tile_coords - tiles.rect.origin(); + if tile_offset.x() < 0 || tile_offset.x() >= tiles.rect.width() || + tile_offset.y() >= tiles.rect.height() { return; } if tile_offset.y() < 0 { - self.current_backdrops[tile_offset.x() as usize] += delta; + backdrops[tile_offset.x() as usize] += delta as i32; return; } - let local_tile_index = self.built_path.tiles.coords_to_index_unchecked(tile_coords); - self.built_path.tiles.data[local_tile_index].backdrop += delta; + let local_tile_index = tiles.coords_to_index_unchecked(tile_coords); + tiles.data[local_tile_index].backdrop += delta; } } -impl<'a> PackedTile<'a> { - pub(crate) fn add_to(&self, - tiles: &mut Vec, - clips: &mut Vec, - draw_tiling_path_info: &DrawTilingPathInfo, - scene_builder: &SceneBuilder) { - let draw_tile_page = self.draw_tile.alpha_tile_id.page() as u16; - let draw_tile_index = self.draw_tile.alpha_tile_id.tile() as u16; - let draw_tile_backdrop = self.draw_tile.backdrop as i8; - - match self.clip_tile { - None => { - tiles.push(BuiltTile { - page: draw_tile_page, - tile: Tile::new_alpha(self.tile_coords, - draw_tile_index, - draw_tile_backdrop, - draw_tiling_path_info), - }); - } - Some(clip_tile) => { - let clip_tile_page = clip_tile.alpha_tile_id.page() as u16; - let clip_tile_index = clip_tile.alpha_tile_id.tile() as u16; - let clip_tile_backdrop = clip_tile.backdrop; - - let dest_tile_id = AlphaTileId::new(&scene_builder.next_alpha_tile_indices, 1); - let dest_tile_page = dest_tile_id.page() as u16; - let dest_tile_index = dest_tile_id.tile() as u16; - - clips.push(BuiltClip { - clip: Clip::new(dest_tile_index, draw_tile_index, draw_tile_backdrop), - key: ClipBatchKey { - src_page: draw_tile_page, - dest_page: dest_tile_page, - kind: ClipBatchKind::Draw, - }, - }); - clips.push(BuiltClip { - clip: Clip::new(dest_tile_index, clip_tile_index, clip_tile_backdrop), - key: ClipBatchKey { - src_page: clip_tile_page, - dest_page: dest_tile_page, - kind: ClipBatchKind::Clip, - }, - }); - tiles.push(BuiltTile { - page: dest_tile_page, - tile: Tile::new_alpha(self.tile_coords, - dest_tile_index, - 0, - draw_tiling_path_info), - }); - } +impl TileBatchDataD3D11 { + fn new(batch_id: TileBatchId, mode: &PrepareMode, path_source: PathSource) + -> TileBatchDataD3D11 { + TileBatchDataD3D11 { + batch_id, + path_count: 0, + tile_count: 0, + segment_count: 0, + path_source, + prepare_info: match *mode { + PrepareMode::CPU => unimplemented!(), + PrepareMode::TransformCPUBinGPU => { + PrepareTilesInfoD3D11 { + backdrops: vec![], + propagate_metadata: vec![], + dice_metadata: vec![], + tile_path_info: vec![], + transform: Transform2F::default(), + } + } + PrepareMode::GPU { ref transform } => { + PrepareTilesInfoD3D11 { + backdrops: vec![], + propagate_metadata: vec![], + dice_metadata: vec![], + tile_path_info: vec![], + transform: *transform, + } + } + }, + clipped_path_info: None, } } -} -impl Tile { - #[inline] - fn new_alpha(tile_origin: Vector2I, - draw_tile_index: u16, - draw_tile_backdrop: i8, - draw_tiling_path_info: &DrawTilingPathInfo) - -> Tile { - let mask_0_uv = calculate_mask_uv(draw_tile_index); + fn push(&mut self, + path: &BuiltPath, + global_path_id: PathId, + batch_clip_path_index: Option, + z_write: bool, + sink: &SceneSink) + -> PathBatchIndex { + let batch_path_index = PathBatchIndex(self.path_count); + self.path_count += 1; - let mut ctrl = 0; - match draw_tiling_path_info.fill_rule { - FillRule::EvenOdd => ctrl |= TILE_CTRL_MASK_EVEN_ODD << TILE_CTRL_MASK_0_SHIFT, - FillRule::Winding => ctrl |= TILE_CTRL_MASK_WINDING << TILE_CTRL_MASK_0_SHIFT, + self.prepare_info.propagate_metadata.push(PropagateMetadataD3D11 { + tile_rect: path.tile_bounds, + tile_offset: self.tile_count, + path_index: batch_path_index, + z_write: z_write as u32, + clip_path_index: batch_clip_path_index.unwrap_or(PathBatchIndex::none()), + backdrop_offset: self.prepare_info.backdrops.len() as u32, + pad0: 0, + pad1: 0, + pad2: 0, + }); + + match path.data { + BuiltPathData::CPU(ref data) => { + self.prepare_info.backdrops.reserve(data.backdrops.len()); + for (tile_x_offset, backdrop) in data.backdrops.iter().enumerate() { + self.prepare_info.backdrops.push(BackdropInfoD3D11 { + initial_backdrop: *backdrop as i32, + tile_x_offset: tile_x_offset as i32, + path_index: batch_path_index, + }); + } + } + BuiltPathData::TransformCPUBinGPU(_) | BuiltPathData::GPU => { + init_backdrops(&mut self.prepare_info.backdrops, + batch_path_index, + path.tile_bounds); + } } - Tile { - tile_x: tile_origin.x() as i16, - tile_y: tile_origin.y() as i16, - mask_0_u: mask_0_uv.x() as u8, - mask_0_v: mask_0_uv.y() as u8, - mask_0_backdrop: draw_tile_backdrop, - ctrl: ctrl as u16, + // Add tiles. + let last_scene = sink.last_scene.as_ref().unwrap(); + let segment_ranges = match self.path_source { + PathSource::Draw => &last_scene.draw_segment_ranges, + PathSource::Clip => &last_scene.clip_segment_ranges, + }; + let segment_range = &segment_ranges[global_path_id.0 as usize]; + self.prepare_info.dice_metadata.push(DiceMetadataD3D11 { + first_batch_segment_index: self.segment_count, + first_global_segment_index: segment_range.start, + global_path_id, pad: 0, - color: draw_tiling_path_info.paint_id.0, - } - } + }); + self.prepare_info.tile_path_info.push(TilePathInfoD3D11 { + tile_min_x: path.tile_bounds.min_x() as i16, + tile_min_y: path.tile_bounds.min_y() as i16, + tile_max_x: path.tile_bounds.max_x() as i16, + tile_max_y: path.tile_bounds.max_y() as i16, + first_tile_index: self.tile_count, + color: path.paint_id.0, + ctrl: path.ctrl_byte, + backdrop: 0, + }); - #[inline] - pub fn tile_position(&self) -> Vector2I { - vec2i(self.tile_x as i32, self.tile_y as i32) + self.tile_count += path.tile_bounds.area() as u32; + self.segment_count += segment_range.end - segment_range.start; + + // Handle clip. + + if batch_clip_path_index.is_none() { + return batch_path_index; + } + + if self.clipped_path_info.is_none() { + self.clipped_path_info = Some(ClippedPathInfo { + clip_batch_id: TileBatchId(0), + clipped_path_count: 0, + max_clipped_tile_count: 0, + clips: match sink.renderer_level { + RendererLevel::D3D9 => Some(vec![]), + RendererLevel::D3D11 => None, + }, + }); + } + + let clipped_path_info = self.clipped_path_info.as_mut().unwrap(); + clipped_path_info.clipped_path_count += 1; + clipped_path_info.max_clipped_tile_count += path.tile_bounds.area() as u32; + + // If clips are computed on CPU, add them to this batch. + if let Some(ref mut dest_clips) = clipped_path_info.clips { + let src_tiles = match path.data { + BuiltPathData::CPU(BuiltPathBinCPUData { + clip_tiles: Some(ref src_tiles), + .. + }) => src_tiles, + _ => panic!("Clip tiles weren't computed on CPU!"), + }; + dest_clips.extend_from_slice(&src_tiles.data); + } + + batch_path_index } } -impl Clip { - #[inline] - fn new(dest_tile_index: u16, src_tile_index: u16, src_backdrop: i8) -> Clip { - let dest_uv = calculate_mask_uv(dest_tile_index); - let src_uv = calculate_mask_uv(src_tile_index); - Clip { - dest_u: dest_uv.x() as u8, - dest_v: dest_uv.y() as u8, - src_u: src_uv.x() as u8, - src_v: src_uv.y() as u8, - backdrop: src_backdrop, - pad_0: 0, - pad_1: 0, +fn init_backdrops(backdrops: &mut Vec, + path_index: PathBatchIndex, + tile_rect: RectI) { + for tile_x_offset in 0..tile_rect.width() { + backdrops.push(BackdropInfoD3D11 { initial_backdrop: 0, path_index, tile_x_offset }); + } +} + +struct BuiltSegments { + draw_segments: SegmentsD3D11, + clip_segments: SegmentsD3D11, + draw_segment_ranges: Vec>, + clip_segment_ranges: Vec>, +} + +impl BuiltSegments { + fn from_scene(scene: &Scene) -> BuiltSegments { + let mut built_segments = BuiltSegments { + draw_segments: SegmentsD3D11::new(), + clip_segments: SegmentsD3D11::new(), + draw_segment_ranges: Vec::with_capacity(scene.draw_paths().len()), + clip_segment_ranges: Vec::with_capacity(scene.clip_paths().len()), + }; + + for clip_path in scene.clip_paths() { + let range = built_segments.clip_segments.add_path(clip_path.outline()); + built_segments.clip_segment_ranges.push(range); + } + for draw_path in scene.draw_paths() { + let range = built_segments.draw_segments.add_path(draw_path.outline()); + built_segments.draw_segment_ranges.push(range); + } + + built_segments + } +} + +impl SegmentsD3D11 { + fn new() -> SegmentsD3D11 { + SegmentsD3D11 { points: vec![], indices: vec![] } + } + + fn add_path(&mut self, outline: &Outline) -> Range { + let first_segment_index = self.indices.len() as u32; + for contour in outline.contours() { + let point_count = contour.len() as u32; + self.points.reserve(point_count as usize); + + for point_index in 0..point_count { + if !contour.flags_of(point_index).intersects(PointFlags::CONTROL_POINT_0 | + PointFlags::CONTROL_POINT_1) { + let mut flags = 0; + if point_index + 1 < point_count && + contour.flags_of(point_index + 1) + .contains(PointFlags::CONTROL_POINT_0) { + if point_index + 2 < point_count && + contour.flags_of(point_index + 2) + .contains(PointFlags::CONTROL_POINT_1) { + flags = CURVE_IS_CUBIC + } else { + flags = CURVE_IS_QUADRATIC + } + } + + self.indices.push(SegmentIndicesD3D11 { + first_point_index: self.points.len() as u32, + flags, + }); + } + + self.points.push(contour.position_of(point_index)); + } + + self.points.push(contour.position_of(0)); + } + + let last_segment_index = self.indices.len() as u32; + first_segment_index..last_segment_index + } +} + +struct TileBatchBuilder { + prepare_commands: Vec, + draw_commands: Vec, + clip_id_to_path_batch_index: FxHashMap, + next_batch_id: TileBatchId, + level: TileBatchBuilderLevel, +} + +enum TileBatchBuilderLevel { + D3D9 { built_paths: BuiltPaths }, + D3D11 { clip_prepare_batch: TileBatchDataD3D11 }, +} + +impl TileBatchBuilder { + fn new(prepare_mode: &PrepareMode, built_paths: Option) -> TileBatchBuilder { + TileBatchBuilder { + prepare_commands: vec![], + draw_commands: vec![], + next_batch_id: TileBatchId(1), + clip_id_to_path_batch_index: FxHashMap::default(), + level: match built_paths { + None => { + TileBatchBuilderLevel::D3D11 { + clip_prepare_batch: TileBatchDataD3D11::new(TileBatchId(0), + &prepare_mode, + PathSource::Clip), + } + } + Some(built_paths) => TileBatchBuilderLevel::D3D9 { built_paths }, + }, + } + } + + fn build_tile_batches_for_draw_path_display_item(&mut self, + scene: &Scene, + sink: &SceneSink, + built_options: &PreparedBuildOptions, + draw_path_id_range: Range, + paint_metadata: &[PaintMetadata], + prepare_mode: &PrepareMode) { + let mut draw_tile_batch = None; + for draw_path_id in draw_path_id_range.start.0..draw_path_id_range.end.0 { + let draw_path_id = DrawPathId(draw_path_id); + let draw_path = match self.level { + TileBatchBuilderLevel::D3D11 { .. } => { + match self.prepare_draw_path_for_gpu_binning(scene, + built_options, + draw_path_id, + prepare_mode, + paint_metadata) { + None => continue, + Some(built_draw_path) => Cow::Owned(built_draw_path), + } + } + TileBatchBuilderLevel::D3D9 { ref built_paths } => { + Cow::Borrowed(&built_paths.draw[draw_path_id.0 as usize]) + } + }; + + // Try to reuse the current batch if we can. + let flush_needed = match draw_tile_batch { + Some(DrawTileBatch::D3D11(ref mut existing_batch)) => { + !fixup_batch_for_new_path_if_possible(&mut existing_batch.color_texture, + &draw_path) + } + Some(DrawTileBatch::D3D9(ref mut existing_batch)) => { + !fixup_batch_for_new_path_if_possible(&mut existing_batch.color_texture, + &draw_path) + } + None => false, + }; + + // If we couldn't reuse the batch, flush it. + if flush_needed { + match draw_tile_batch.take() { + Some(DrawTileBatch::D3D11(batch_to_flush)) => { + self.draw_commands.push(RenderCommand::DrawTilesD3D11(batch_to_flush)); + } + Some(DrawTileBatch::D3D9(batch_to_flush)) => { + self.draw_commands.push(RenderCommand::DrawTilesD3D9(batch_to_flush)); + } + _ => {} + } + } + + // Create a new batch if necessary. + if draw_tile_batch.is_none() { + draw_tile_batch = match self.level { + TileBatchBuilderLevel::D3D9 { .. } => { + let tile_bounds = tiles::round_rect_out_to_tile_bounds(scene.view_box()); + Some(DrawTileBatch::D3D9(DrawTileBatchD3D9 { + tiles: vec![], + clips: vec![], + z_buffer_data: DenseTileMap::from_builder(|_| 0, tile_bounds), + color_texture: draw_path.color_texture, + filter: draw_path.filter, + blend_mode: draw_path.blend_mode, + })) + } + TileBatchBuilderLevel::D3D11 { .. } => { + Some(DrawTileBatch::D3D11(DrawTileBatchD3D11 { + tile_batch_data: TileBatchDataD3D11::new(self.next_batch_id, + &prepare_mode, + PathSource::Draw), + color_texture: draw_path.color_texture, + })) + } + }; + self.next_batch_id.0 += 1; + } + + // Add clip path if necessary. + let clip_path = match draw_path.clip_path_id { + None => None, + Some(clip_path_id) => { + match self.clip_id_to_path_batch_index.get(&clip_path_id) { + Some(&clip_path_batch_index) => Some(clip_path_batch_index), + None => { + match self.level { + TileBatchBuilderLevel::D3D9 { .. } => None, + TileBatchBuilderLevel::D3D11 { ref mut clip_prepare_batch } => { + let clip_path = + prepare_clip_path_for_gpu_binning(scene, + built_options, + clip_path_id, + prepare_mode); + let clip_path_index = + clip_prepare_batch.push(&clip_path, + clip_path_id.to_path_id(), + None, + true, + sink); + self.clip_id_to_path_batch_index.insert(clip_path_id, + clip_path_index); + Some(clip_path_index) + } + } + + } + } + } + }; + + let draw_tile_batch = draw_tile_batch.as_mut().unwrap(); + match *draw_tile_batch { + DrawTileBatch::D3D11(ref mut draw_tile_batch) => { + draw_tile_batch.tile_batch_data.push(&draw_path.path, + draw_path_id.to_path_id(), + clip_path, + draw_path.occludes, + sink); + } + DrawTileBatch::D3D9(ref mut draw_tile_batch) => { + let built_paths = match self.level { + TileBatchBuilderLevel::D3D9 { ref built_paths } => built_paths, + TileBatchBuilderLevel::D3D11 { .. } => unreachable!(), + }; + + let cpu_data = match built_paths.draw[draw_path_id.0 as usize].path.data { + BuiltPathData::CPU(ref cpu_data) => cpu_data, + BuiltPathData::GPU | BuiltPathData::TransformCPUBinGPU(_) => { + unreachable!() + } + }; + + for tile in &cpu_data.tiles.data { + if tile.alpha_tile_id == AlphaTileId(!0) && tile.backdrop == 0 { + continue; + } + + draw_tile_batch.tiles.push(*tile); + + if !draw_path.occludes || tile.alpha_tile_id != AlphaTileId(!0) { + continue; + } + + let tile_coords = vec2i(tile.tile_x as i32, tile.tile_y as i32); + let z_value = draw_tile_batch.z_buffer_data + .get_mut(tile_coords) + .expect("Z value out of bounds!"); + *z_value = (*z_value).max(draw_path_id.0 as i32); + } + + let clip_tiles = match cpu_data.clip_tiles { + None => continue, + Some(ref clip_tiles) => clip_tiles, + }; + for clip_tile in &clip_tiles.data { + if clip_tile.dest_tile_id != AlphaTileId(!0) && + clip_tile.src_tile_id != AlphaTileId(!0) { + draw_tile_batch.clips.push(*clip_tile); + } + } + } + } + } + + match draw_tile_batch { + Some(DrawTileBatch::D3D11(draw_tile_batch)) => { + self.draw_commands.push(RenderCommand::DrawTilesD3D11(draw_tile_batch)); + } + Some(DrawTileBatch::D3D9(draw_tile_batch)) => { + self.draw_commands.push(RenderCommand::DrawTilesD3D9(draw_tile_batch)); + } + None => {} + } + } + + fn prepare_draw_path_for_gpu_binning(&self, + scene: &Scene, + built_options: &PreparedBuildOptions, + draw_path_id: DrawPathId, + prepare_mode: &PrepareMode, + paint_metadata: &[PaintMetadata]) + -> Option { + let transform = match *prepare_mode { + PrepareMode::GPU { transform } => transform, + PrepareMode::CPU | PrepareMode::TransformCPUBinGPU => { + panic!("`prepare_draw_path_for_gpu_binning()` requires a GPU prepare mode!") + } + }; + + let effective_view_box = scene.effective_view_box(built_options); + let draw_path = scene.get_draw_path(draw_path_id); + + let mut path_bounds = transform * draw_path.outline().bounds(); + match path_bounds.intersection(effective_view_box) { + Some(intersection) => path_bounds = intersection, + None => return None, + } + + let paint_id = draw_path.paint(); + let paint_metadata = &paint_metadata[paint_id.0 as usize]; + let built_path = BuiltPath::new(draw_path_id.to_path_id(), + path_bounds, + effective_view_box, + draw_path.fill_rule(), + &prepare_mode, + &TilingPathInfo::Draw(DrawTilingPathInfo { + paint_id, + blend_mode: draw_path.blend_mode(), + clip_path_id: draw_path.clip_path(), + fill_rule: draw_path.fill_rule(), + })); + Some(BuiltDrawPath::new(built_path, draw_path, paint_metadata)) + } + + fn send_to(self, sink: &SceneSink) { + if let TileBatchBuilderLevel::D3D11 { clip_prepare_batch } = self.level { + if clip_prepare_batch.path_count > 0 { + sink.listener.send(RenderCommand::PrepareClipTilesD3D11(clip_prepare_batch)); + } + } + + for command in self.prepare_commands { + sink.listener.send(command); + } + for command in self.draw_commands { + sink.listener.send(command); } } } -fn calculate_mask_uv(tile_index: u16) -> Vector2I { - debug_assert_eq!(MASK_TILES_ACROSS, MASK_TILES_DOWN); - let mask_u = tile_index as i32 % MASK_TILES_ACROSS as i32; - let mask_v = tile_index as i32 / MASK_TILES_ACROSS as i32; - vec2i(mask_u, mask_v) +fn prepare_clip_path_for_gpu_binning(scene: &Scene, + built_options: &PreparedBuildOptions, + clip_path_id: ClipPathId, + prepare_mode: &PrepareMode) + -> BuiltPath { + let transform = match *prepare_mode { + PrepareMode::GPU { transform } => transform, + PrepareMode::CPU | PrepareMode::TransformCPUBinGPU => { + panic!("`prepare_clip_path_for_gpu_binning()` requires a GPU prepare mode!") + } + }; + let effective_view_box = scene.effective_view_box(built_options); + let clip_path = scene.get_clip_path(clip_path_id); + let path_bounds = transform * clip_path.outline().bounds(); + // TODO(pcwalton): Clip to view box! + BuiltPath::new(clip_path_id.to_path_id(), + path_bounds, + effective_view_box, + clip_path.fill_rule(), + &prepare_mode, + &TilingPathInfo::Clip) +} + +fn fixup_batch_for_new_path_if_possible(batch_color_texture: &mut Option, + draw_path: &BuiltDrawPath) + -> bool { + if draw_path.color_texture.is_some() { + if batch_color_texture.is_none() { + *batch_color_texture = draw_path.color_texture; + return true; + } + if draw_path.color_texture != *batch_color_texture { + debug!("batch break: path color texture {:?} batch color texture {:?}", + draw_path.color_texture, + batch_color_texture); + return false; + } + } + true } diff --git a/renderer/src/tile_map.rs b/renderer/src/tile_map.rs index 8b86a858..3922b6b3 100644 --- a/renderer/src/tile_map.rs +++ b/renderer/src/tile_map.rs @@ -11,35 +11,23 @@ use pathfinder_geometry::rect::RectI; use pathfinder_geometry::vector::{Vector2I, vec2i}; -#[derive(Debug)] -pub struct DenseTileMap { +#[derive(Clone, Debug)] +pub struct DenseTileMap where T: Clone + Copy { pub data: Vec, pub rect: RectI, } -impl DenseTileMap { +impl DenseTileMap where T: Clone + Copy { #[inline] - pub fn new(rect: RectI) -> DenseTileMap - where - T: Copy + Clone + Default, - { - let length = rect.size().x() as usize * rect.size().y() as usize; - DenseTileMap { - data: vec![T::default(); length], - rect, - } - } - - #[inline] - pub fn from_builder(build: F, rect: RectI) -> DenseTileMap - 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, + pub fn from_builder(mut build: F, rect: RectI) -> DenseTileMap + where F: FnMut(Vector2I) -> T { + 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() { + for x in rect.min_x()..rect.max_x() { + data.push(build(vec2i(x, y))); + } } + DenseTileMap { data, rect } } #[inline] @@ -47,6 +35,14 @@ impl DenseTileMap { 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] pub fn coords_to_index(&self, coords: Vector2I) -> Option { if self.rect.contains_point(coords) { diff --git a/renderer/src/tiler.rs b/renderer/src/tiler.rs index 7f93ec36..cb0dcf6a 100644 --- a/renderer/src/tiler.rs +++ b/renderer/src/tiler.rs @@ -11,8 +11,12 @@ //! Implements the fast lattice-clipping algorithm from Nehab and Hoppe, "Random-Access Rendering //! of General Vector Graphics" 2006. -use crate::builder::{ObjectBuilder, Occluder, SceneBuilder, SolidTiles}; -use crate::tiles::{PackedTile, TILE_HEIGHT, TILE_WIDTH, TileType, TilingPathInfo}; +use crate::builder::{BuiltPath, BuiltPathBinCPUData, BuiltPathData, ObjectBuilder, SceneBuilder}; +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::outline::{ContourIterFlags, Outline}; use pathfinder_content::segment::Segment; @@ -23,102 +27,172 @@ use pathfinder_simd::default::{F32x2, U32x2}; const FLATTENING_TOLERANCE: f32 = 0.25; -pub(crate) struct Tiler<'a, 'b> { - scene_builder: &'a SceneBuilder<'b, 'a>, +pub(crate) struct Tiler<'a, 'b, 'c, 'd> { + scene_builder: &'a SceneBuilder<'b, 'a, 'c, 'd>, pub(crate) object_builder: ObjectBuilder, outline: &'a Outline, - path_info: TilingPathInfo<'a>, + clip_path: Option<&'a BuiltPath>, } -impl<'a, 'b> Tiler<'a, 'b> { - pub(crate) fn new(scene_builder: &'a SceneBuilder<'b, 'a>, +impl<'a, 'b, 'c, 'd> Tiler<'a, 'b, 'c, 'd> { + pub(crate) fn new(scene_builder: &'a SceneBuilder<'b, 'a, 'c, 'd>, + path_id: PathId, outline: &'a Outline, fill_rule: FillRule, view_box: RectF, - path_info: TilingPathInfo<'a>) - -> Tiler<'a, 'b> { + prepare_mode: &PrepareMode, + 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 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) { + 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 segment in contour.iter(ContourIterFlags::empty()) { process_segment(&segment, self.scene_builder, &mut self.object_builder); } } - - self.propagate_backdrops(); - self.pack_and_cull(); } - fn propagate_backdrops(&mut self) { - let tiles_across = self.object_builder.built_path.tiles.rect.width() as usize; - for (draw_tile_index, draw_tile) in self.object_builder - .built_path - .tiles - .data - .iter_mut() - .enumerate() { - 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, + fn prepare_tiles(&mut self) { + // Don't do this here if the GPU will do it. + let (backdrops, tiles, clips) = match self.object_builder.built_path.data { + BuiltPathData::CPU(ref mut tiled_data) => { + (&mut tiled_data.backdrops, &mut tiled_data.tiles, &mut tiled_data.clip_tiles) + } + BuiltPathData::TransformCPUBinGPU(_) | BuiltPathData::GPU => { + panic!("We shouldn't be preparing tiles on CPU!") + } }; - 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 - .built_path - .tiles - .data - .iter() - .enumerate() { - let packed_tile = PackedTile::new(draw_tile_index as u32, - draw_tile, - &draw_tiling_path_info, - &self.object_builder); + let mut draw_alpha_tile_id = draw_tile.alpha_tile_id; + let mut draw_tile_backdrop = backdrops[column] as i8; - match packed_tile.tile_type { - TileType::Solid => { - match self.object_builder.built_path.solid_tiles { - SolidTiles::Occluders(ref mut occluders) => { - occluders.push(Occluder::new(packed_tile.tile_coords)); - } - SolidTiles::Regular(ref mut solid_tiles) => { - packed_tile.add_to(solid_tiles, - &mut self.object_builder.built_path.clip_tiles, - &draw_tiling_path_info, - &self.scene_builder); + if let Some(built_clip_path) = self.clip_path { + let clip_tiles = match built_clip_path.data { + BuiltPathData::CPU(BuiltPathBinCPUData { ref tiles, .. }) => tiles, + _ => unreachable!(), + }; + match clip_tiles.get(tile_coords) { + Some(clip_tile) => { + if clip_tile.alpha_tile_id != AlphaTileId(!0) && + draw_alpha_tile_id != AlphaTileId(!0) { + // Hard case: We have an alpha tile and a clip tile with masks. Add a + // 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; } } - } - TileType::SingleMask => { - debug_assert_ne!(packed_tile.draw_tile.alpha_tile_id.page(), !0); - packed_tile.add_to(&mut self.object_builder.built_path.single_mask_tiles, - &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. + None => { + // This draw tile is outside the clip path rect. Cull the tile. + draw_alpha_tile_id = AlphaTileId(!0); + draw_tile_backdrop = 0; + } } } + + 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)`. let step = Vector2I((vector_is_negative | U32x2::splat(1)).to_i32x2()); - // Compute `first_tile_crossing = (from_tile_coords + vec2i(vector.x > 0 ? 1 : 0, - // vector.y > 0 ? 1 : 0)) * tile_size`. + // Compute `first_tile_crossing = (from_tile_coords + vec2i(vector.x >= 0 ? 1 : 0, + // vector.y >= 0 ? 1 : 0)) * tile_size`. let first_tile_crossing = (from_tile_coords + Vector2I((!vector_is_negative & U32x2::splat(1)).to_i32x2())).to_f32() * tile_size; diff --git a/renderer/src/tiles.rs b/renderer/src/tiles.rs index 4b2777b6..a56406c2 100644 --- a/renderer/src/tiles.rs +++ b/renderer/src/tiles.rs @@ -8,33 +8,33 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::builder::{BuiltPath, ObjectBuilder}; -use crate::gpu_data::{AlphaTileId, TileObjectPrimitive}; -use crate::paint::{PaintId, PaintMetadata}; +use crate::gpu_data::{TILE_CTRL_MASK_0_SHIFT, TILE_CTRL_MASK_EVEN_ODD}; +use crate::gpu_data::{TILE_CTRL_MASK_WINDING, TileObjectPrimitive}; +use crate::paint::PaintId; +use crate::scene::ClipPathId; use pathfinder_content::effects::BlendMode; use pathfinder_content::fill::FillRule; 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_HEIGHT: u32 = 16; #[derive(Clone, Copy)] -pub(crate) enum TilingPathInfo<'a> { +pub(crate) enum TilingPathInfo { Clip, - Draw(DrawTilingPathInfo<'a>), + Draw(DrawTilingPathInfo), } #[derive(Clone, Copy)] -pub(crate) struct DrawTilingPathInfo<'a> { +pub(crate) struct DrawTilingPathInfo { pub(crate) paint_id: PaintId, - pub(crate) paint_metadata: &'a PaintMetadata, pub(crate) blend_mode: BlendMode, - pub(crate) built_clip_path: Option<&'a BuiltPath>, pub(crate) fill_rule: FillRule, + pub(crate) clip_path_id: Option, } -impl<'a> TilingPathInfo<'a> { +impl TilingPathInfo { pub(crate) fn has_destructive_blend_mode(&self) -> bool { match *self { TilingPathInfo::Draw(ref draw_tiling_path_info) => { @@ -43,126 +43,23 @@ impl<'a> TilingPathInfo<'a> { TilingPathInfo::Clip => false, } } -} -pub(crate) struct PackedTile<'a> { - pub(crate) tile_type: TileType, - pub(crate) tile_coords: Vector2I, - pub(crate) draw_tile: &'a TileObjectPrimitive, - pub(crate) clip_tile: Option<&'a TileObjectPrimitive>, -} - -#[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, - }; + pub(crate) fn to_ctrl(&self) -> u8 { + let mut ctrl = 0; + match *self { + TilingPathInfo::Draw(ref draw_tiling_path_info) => { + match draw_tiling_path_info.fill_rule { + FillRule::EvenOdd => { + ctrl |= (TILE_CTRL_MASK_EVEN_ODD << TILE_CTRL_MASK_0_SHIFT) as u8 } - Some(clip_tile) if clip_tile.is_solid() => { - if clip_tile.backdrop != 0 { - // 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, - }; - } + FillRule::Winding => { + ctrl |= (TILE_CTRL_MASK_WINDING << TILE_CTRL_MASK_0_SHIFT) as u8 } - 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() } -impl Default for TileObjectPrimitive { - #[inline] - fn default() -> TileObjectPrimitive { - TileObjectPrimitive { backdrop: 0, alpha_tile_id: AlphaTileId::invalid() } - } -} - impl TileObjectPrimitive { #[inline] pub fn is_solid(&self) -> bool { !self.alpha_tile_id.is_valid() }