2019-01-14 17:20:36 -05:00
|
|
|
// pathfinder/renderer/src/builder.rs
|
|
|
|
//
|
|
|
|
// Copyright © 2019 The Pathfinder Project Developers.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
//! Packs data onto the GPU.
|
|
|
|
|
2019-04-29 21:16:55 -04:00
|
|
|
use crate::concurrent::executor::Executor;
|
2020-03-19 19:44:45 -04:00
|
|
|
use crate::gpu::renderer::{BlendModeExt, MASK_TILES_ACROSS, MASK_TILES_DOWN};
|
2020-04-22 14:04:16 -04:00
|
|
|
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};
|
2020-04-02 14:17:03 -04:00
|
|
|
use crate::options::{PreparedBuildOptions, PreparedRenderTransform, RenderCommandListener};
|
2020-04-03 01:03:42 -04:00
|
|
|
use crate::paint::{PaintInfo, PaintMetadata};
|
2020-02-19 20:44:41 -05:00
|
|
|
use crate::scene::{DisplayItem, Scene};
|
2019-05-03 21:51:36 -04:00
|
|
|
use crate::tile_map::DenseTileMap;
|
2020-03-19 19:44:45 -04:00
|
|
|
use crate::tiles::{self, DrawTilingPathInfo, PackedTile, TILE_HEIGHT, TILE_WIDTH};
|
|
|
|
use crate::tiles::{Tiler, TilingPathInfo};
|
2020-03-03 17:26:38 -05:00
|
|
|
use crate::z_buffer::{DepthMetadata, ZBuffer};
|
2020-04-03 01:03:42 -04:00
|
|
|
use pathfinder_content::effects::{BlendMode, Filter};
|
2020-02-17 17:44:48 -05:00
|
|
|
use pathfinder_content::fill::FillRule;
|
2020-03-03 18:50:48 -05:00
|
|
|
use pathfinder_content::render_target::RenderTargetId;
|
2019-06-21 13:06:19 -04:00
|
|
|
use pathfinder_geometry::line_segment::{LineSegment2F, LineSegmentU4, LineSegmentU8};
|
|
|
|
use pathfinder_geometry::rect::{RectF, RectI};
|
2020-03-19 19:44:45 -04:00
|
|
|
use pathfinder_geometry::transform2d::Transform2F;
|
2019-05-03 21:51:36 -04:00
|
|
|
use pathfinder_geometry::util;
|
2020-03-31 14:28:34 -04:00
|
|
|
use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i};
|
2020-02-26 17:56:05 -05:00
|
|
|
use pathfinder_gpu::TextureSamplingFlags;
|
2019-05-03 21:51:36 -04:00
|
|
|
use pathfinder_simd::default::{F32x4, I32x4};
|
2020-04-21 16:09:23 -04:00
|
|
|
use std::sync::atomic::AtomicUsize;
|
2020-03-04 12:54:36 -05:00
|
|
|
use instant::Instant;
|
2020-04-15 23:13:37 -04:00
|
|
|
use std::u32;
|
2019-01-14 17:20:36 -05:00
|
|
|
|
2020-04-21 16:09:23 -04:00
|
|
|
pub(crate) const ALPHA_TILE_LEVEL_COUNT: usize = 2;
|
|
|
|
pub(crate) const ALPHA_TILES_PER_LEVEL: usize = 1 << (32 - ALPHA_TILE_LEVEL_COUNT + 1);
|
|
|
|
|
2020-04-17 14:50:46 -04:00
|
|
|
pub(crate) struct SceneBuilder<'a, 'b> {
|
|
|
|
scene: &'a mut Scene,
|
|
|
|
built_options: &'b PreparedBuildOptions,
|
2020-04-21 16:09:23 -04:00
|
|
|
next_alpha_tile_indices: [AtomicUsize; ALPHA_TILE_LEVEL_COUNT],
|
2019-04-15 16:21:24 -04:00
|
|
|
pub(crate) listener: Box<dyn RenderCommandListener>,
|
2019-03-29 22:11:38 -04:00
|
|
|
}
|
2019-01-14 17:20:36 -05:00
|
|
|
|
2020-02-15 17:00:36 -05:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub(crate) struct ObjectBuilder {
|
|
|
|
pub built_path: BuiltPath,
|
2020-04-15 23:13:37 -04:00
|
|
|
pub fills: Vec<FillBatchEntry>,
|
2020-02-15 17:00:36 -05:00
|
|
|
pub bounds: RectF,
|
|
|
|
}
|
|
|
|
|
2020-02-20 18:52:40 -05:00
|
|
|
#[derive(Debug)]
|
|
|
|
struct BuiltDrawPath {
|
|
|
|
path: BuiltPath,
|
|
|
|
blend_mode: BlendMode,
|
2020-04-03 01:03:42 -04:00
|
|
|
filter: Filter,
|
2020-04-13 14:52:10 -04:00
|
|
|
color_texture: Option<TileBatchTexture>,
|
2020-03-19 19:44:45 -04:00
|
|
|
sampling_flags_1: TextureSamplingFlags,
|
|
|
|
mask_0_fill_rule: FillRule,
|
2020-02-20 18:52:40 -05:00
|
|
|
}
|
|
|
|
|
2020-02-15 17:00:36 -05:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub(crate) struct BuiltPath {
|
2020-03-19 19:44:45 -04:00
|
|
|
pub solid_tiles: SolidTiles,
|
2020-04-15 23:13:37 -04:00
|
|
|
pub empty_tiles: Vec<BuiltTile>,
|
|
|
|
pub single_mask_tiles: Vec<BuiltTile>,
|
2020-04-21 16:09:23 -04:00
|
|
|
pub clip_tiles: Vec<BuiltClip>,
|
2020-02-15 21:01:23 -05:00
|
|
|
pub tiles: DenseTileMap<TileObjectPrimitive>,
|
2020-02-17 17:44:48 -05:00
|
|
|
pub fill_rule: FillRule,
|
2020-02-15 17:00:36 -05:00
|
|
|
}
|
|
|
|
|
2020-04-15 23:13:37 -04:00
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct BuiltTile {
|
|
|
|
pub page: u16,
|
|
|
|
pub tile: Tile,
|
|
|
|
}
|
|
|
|
|
2020-04-21 16:09:23 -04:00
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
|
|
pub struct BuiltClip {
|
|
|
|
pub clip: Clip,
|
|
|
|
pub key: ClipBatchKey,
|
|
|
|
}
|
|
|
|
|
2020-03-19 19:44:45 -04:00
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub(crate) enum SolidTiles {
|
|
|
|
Occluders(Vec<Occluder>),
|
2020-04-15 23:13:37 -04:00
|
|
|
Regular(Vec<BuiltTile>),
|
2020-03-19 19:44:45 -04:00
|
|
|
}
|
|
|
|
|
2020-02-19 20:44:41 -05:00
|
|
|
#[derive(Clone, Copy, Debug)]
|
2020-03-19 19:44:45 -04:00
|
|
|
pub(crate) struct Occluder {
|
2020-02-19 20:44:41 -05:00
|
|
|
pub(crate) coords: Vector2I,
|
|
|
|
}
|
|
|
|
|
2020-04-17 14:50:46 -04:00
|
|
|
impl<'a, 'b> SceneBuilder<'a, 'b> {
|
2019-05-03 15:35:19 -04:00
|
|
|
pub(crate) fn new(
|
2020-04-17 14:50:46 -04:00
|
|
|
scene: &'a mut Scene,
|
|
|
|
built_options: &'b PreparedBuildOptions,
|
2019-04-29 19:45:29 -04:00
|
|
|
listener: Box<dyn RenderCommandListener>,
|
2020-04-17 14:50:46 -04:00
|
|
|
) -> SceneBuilder<'a, 'b> {
|
2019-04-15 16:21:24 -04:00
|
|
|
SceneBuilder {
|
|
|
|
scene,
|
|
|
|
built_options,
|
2020-04-21 16:09:23 -04:00
|
|
|
next_alpha_tile_indices: [AtomicUsize::new(0), AtomicUsize::new(0)],
|
2019-04-15 16:21:24 -04:00
|
|
|
listener,
|
2019-03-29 22:11:38 -04:00
|
|
|
}
|
|
|
|
}
|
2019-01-14 17:20:36 -05:00
|
|
|
|
2019-04-29 21:16:55 -04:00
|
|
|
pub fn build<E>(&mut self, executor: &E) where E: Executor {
|
2019-04-30 22:13:28 -04:00
|
|
|
let start_time = Instant::now();
|
|
|
|
|
2020-02-22 17:39:03 -05:00
|
|
|
// Send the start rendering command.
|
2019-04-30 22:13:28 -04:00
|
|
|
let bounding_quad = self.built_options.bounding_quad();
|
2020-02-15 17:00:36 -05:00
|
|
|
|
|
|
|
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;
|
2019-04-30 22:13:28 -04:00
|
|
|
|
2020-02-22 17:39:03 -05:00
|
|
|
let needs_readable_framebuffer = self.needs_readable_framebuffer();
|
|
|
|
|
|
|
|
self.listener.send(RenderCommand::Start {
|
|
|
|
bounding_quad,
|
|
|
|
path_count: total_path_count,
|
|
|
|
needs_readable_framebuffer,
|
|
|
|
});
|
|
|
|
|
2020-04-02 14:17:03 -04:00
|
|
|
let render_transform = match self.built_options.transform {
|
2020-04-17 14:50:46 -04:00
|
|
|
PreparedRenderTransform::Transform2D(transform) => transform.inverse(),
|
2020-04-02 14:17:03 -04:00
|
|
|
_ => Transform2F::default()
|
|
|
|
};
|
|
|
|
|
2020-02-22 17:39:03 -05:00
|
|
|
// Build paint data.
|
2020-02-04 01:24:34 -05:00
|
|
|
let PaintInfo {
|
2020-03-04 18:49:38 -05:00
|
|
|
render_commands,
|
2020-03-05 15:22:01 -05:00
|
|
|
paint_metadata,
|
2020-04-06 14:08:57 -04:00
|
|
|
render_target_metadata: _,
|
2020-04-02 14:17:03 -04:00
|
|
|
} = self.scene.build_paint_info(render_transform);
|
2020-03-04 18:49:38 -05:00
|
|
|
for render_command in render_commands {
|
|
|
|
self.listener.send(render_command);
|
|
|
|
}
|
2019-04-30 22:13:28 -04:00
|
|
|
|
|
|
|
let effective_view_box = self.scene.effective_view_box(self.built_options);
|
2020-02-15 17:00:36 -05:00
|
|
|
|
|
|
|
let built_clip_paths = executor.build_vector(clip_path_count, |path_index| {
|
2020-03-19 19:44:45 -04:00
|
|
|
self.build_clip_path(PathBuildParams {
|
|
|
|
path_index,
|
|
|
|
view_box: effective_view_box,
|
|
|
|
built_options: &self.built_options,
|
|
|
|
scene: &self.scene,
|
|
|
|
})
|
2020-02-15 17:00:36 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
let built_draw_paths = executor.build_vector(draw_path_count, |path_index| {
|
2020-03-19 19:44:45 -04:00
|
|
|
self.build_draw_path(DrawPathBuildParams {
|
|
|
|
path_build_params: PathBuildParams {
|
|
|
|
path_index,
|
|
|
|
view_box: effective_view_box,
|
|
|
|
built_options: &self.built_options,
|
|
|
|
scene: &self.scene,
|
|
|
|
},
|
|
|
|
paint_metadata: &paint_metadata,
|
|
|
|
built_clip_paths: &built_clip_paths,
|
|
|
|
})
|
2019-04-29 21:16:55 -04:00
|
|
|
});
|
2019-04-30 22:13:28 -04:00
|
|
|
|
2020-04-03 01:03:42 -04:00
|
|
|
self.finish_building(&paint_metadata, built_draw_paths);
|
2019-04-30 22:13:28 -04:00
|
|
|
|
2020-04-09 20:49:28 -04:00
|
|
|
let cpu_build_time = Instant::now() - start_time;
|
|
|
|
self.listener.send(RenderCommand::Finish { cpu_build_time });
|
2019-04-15 16:21:24 -04:00
|
|
|
}
|
2019-03-29 22:11:38 -04:00
|
|
|
|
2020-03-19 19:44:45 -04:00
|
|
|
fn build_clip_path(&self, params: PathBuildParams) -> BuiltPath {
|
|
|
|
let PathBuildParams { path_index, view_box, built_options, scene } = params;
|
2020-02-15 17:00:36 -05:00
|
|
|
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,
|
2020-02-17 17:44:48 -05:00
|
|
|
path_object.fill_rule(),
|
2020-02-15 17:00:36 -05:00
|
|
|
view_box,
|
|
|
|
TilingPathInfo::Clip);
|
|
|
|
|
|
|
|
tiler.generate_tiles();
|
2020-04-15 23:13:37 -04:00
|
|
|
self.send_fills(tiler.object_builder.fills);
|
2020-02-15 17:00:36 -05:00
|
|
|
tiler.object_builder.built_path
|
|
|
|
}
|
|
|
|
|
2020-03-19 19:44:45 -04:00
|
|
|
fn build_draw_path(&self, params: DrawPathBuildParams) -> BuiltDrawPath {
|
|
|
|
let DrawPathBuildParams {
|
|
|
|
path_build_params: PathBuildParams { path_index, view_box, built_options, scene },
|
|
|
|
paint_metadata,
|
|
|
|
built_clip_paths,
|
|
|
|
} = params;
|
|
|
|
|
2019-05-10 15:03:38 -04:00
|
|
|
let path_object = &scene.paths[path_index];
|
2020-03-03 17:26:38 -05:00
|
|
|
let outline = scene.apply_render_options(path_object.outline(), built_options);
|
2020-03-03 14:17:09 -05:00
|
|
|
|
2019-05-14 18:21:15 -04:00
|
|
|
let paint_id = path_object.paint();
|
2020-02-21 17:46:10 -05:00
|
|
|
let paint_metadata = &paint_metadata[paint_id.0 as usize];
|
2020-03-26 23:01:17 -04:00
|
|
|
let built_clip_path = path_object.clip_path().map(|clip_path_id| {
|
|
|
|
&built_clip_paths[clip_path_id.0 as usize]
|
|
|
|
});
|
2020-02-15 17:00:36 -05:00
|
|
|
|
2019-05-14 21:09:01 -04:00
|
|
|
let mut tiler = Tiler::new(self,
|
|
|
|
&outline,
|
2020-02-17 17:44:48 -05:00
|
|
|
path_object.fill_rule(),
|
2019-05-14 21:09:01 -04:00
|
|
|
view_box,
|
2020-03-03 17:26:38 -05:00
|
|
|
TilingPathInfo::Draw(DrawTilingPathInfo {
|
2020-04-06 14:08:57 -04:00
|
|
|
paint_id,
|
2020-02-21 17:46:10 -05:00
|
|
|
paint_metadata,
|
2020-02-20 21:15:20 -05:00
|
|
|
blend_mode: path_object.blend_mode(),
|
2020-02-15 17:00:36 -05:00
|
|
|
built_clip_path,
|
2020-04-22 14:04:16 -04:00
|
|
|
fill_rule: path_object.fill_rule(),
|
2020-03-03 17:26:38 -05:00
|
|
|
}));
|
2019-03-29 22:11:38 -04:00
|
|
|
|
2019-04-15 16:21:24 -04:00
|
|
|
tiler.generate_tiles();
|
2020-04-15 23:13:37 -04:00
|
|
|
self.send_fills(tiler.object_builder.fills);
|
2020-02-20 18:52:40 -05:00
|
|
|
BuiltDrawPath {
|
|
|
|
path: tiler.object_builder.built_path,
|
|
|
|
blend_mode: path_object.blend_mode(),
|
2020-04-03 01:03:42 -04:00
|
|
|
filter: paint_metadata.filter(),
|
2020-04-13 14:52:10 -04:00
|
|
|
color_texture: paint_metadata.tile_batch_texture(),
|
2020-03-19 19:44:45 -04:00
|
|
|
sampling_flags_1: TextureSamplingFlags::empty(),
|
|
|
|
mask_0_fill_rule: path_object.fill_rule(),
|
2020-02-20 18:52:40 -05:00
|
|
|
}
|
2019-03-29 22:11:38 -04:00
|
|
|
}
|
|
|
|
|
2020-04-15 23:13:37 -04:00
|
|
|
fn send_fills(&self, fills: Vec<FillBatchEntry>) {
|
|
|
|
if !fills.is_empty() {
|
|
|
|
self.listener.send(RenderCommand::AddFills(fills));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-21 16:09:23 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
built_clip_tiles.sort_by_key(|built_clip_tile| built_clip_tile.key);
|
|
|
|
|
|
|
|
let mut batches: Vec<ClipBatch> = 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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-03 01:03:42 -04:00
|
|
|
fn cull_tiles(&self, paint_metadata: &[PaintMetadata], built_draw_paths: Vec<BuiltDrawPath>)
|
2020-02-15 17:00:36 -05:00
|
|
|
-> CulledTiles {
|
2020-03-19 19:44:45 -04:00
|
|
|
let mut culled_tiles = CulledTiles { display_list: vec![] };
|
2020-02-15 17:00:36 -05:00
|
|
|
|
2020-02-19 20:44:41 -05:00
|
|
|
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();
|
2020-03-02 23:04:09 -05:00
|
|
|
let first_solid_tiles = first_z_buffer.build_solid_tiles(paint_metadata);
|
2020-02-21 17:46:10 -05:00
|
|
|
for batch in first_solid_tiles.batches {
|
2020-03-19 19:44:45 -04:00
|
|
|
culled_tiles.display_list.push(CulledDisplayItem::DrawTiles(batch));
|
2020-02-19 20:44:41 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut layer_z_buffers_stack = vec![first_z_buffer];
|
2020-03-02 23:04:09 -05:00
|
|
|
let mut current_depth = 1;
|
2020-02-19 20:44:41 -05:00
|
|
|
|
|
|
|
for display_item in &self.scene.display_list {
|
2020-03-02 23:04:09 -05:00
|
|
|
match *display_item {
|
2020-02-22 00:42:15 -05:00
|
|
|
DisplayItem::PushRenderTarget(render_target_id) => {
|
|
|
|
culled_tiles.display_list
|
|
|
|
.push(CulledDisplayItem::PushRenderTarget(render_target_id));
|
2020-02-19 20:44:41 -05:00
|
|
|
|
|
|
|
let z_buffer = remaining_layer_z_buffers.pop().unwrap();
|
2020-03-02 23:04:09 -05:00
|
|
|
let solid_tiles = z_buffer.build_solid_tiles(paint_metadata);
|
2020-02-21 17:46:10 -05:00
|
|
|
for batch in solid_tiles.batches {
|
2020-03-19 19:44:45 -04:00
|
|
|
culled_tiles.display_list.push(CulledDisplayItem::DrawTiles(batch));
|
2020-02-19 20:44:41 -05:00
|
|
|
}
|
|
|
|
layer_z_buffers_stack.push(z_buffer);
|
|
|
|
}
|
2020-03-02 23:04:09 -05:00
|
|
|
|
2020-02-22 00:42:15 -05:00
|
|
|
DisplayItem::PopRenderTarget => {
|
|
|
|
culled_tiles.display_list.push(CulledDisplayItem::PopRenderTarget);
|
2020-02-19 20:44:41 -05:00
|
|
|
layer_z_buffers_stack.pop();
|
|
|
|
}
|
2020-03-02 23:04:09 -05:00
|
|
|
|
|
|
|
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];
|
2020-03-19 19:44:45 -04:00
|
|
|
let layer_z_buffer = layer_z_buffers_stack.last().unwrap();
|
2020-04-13 14:52:10 -04:00
|
|
|
let color_texture = built_draw_path.color_texture;
|
2020-03-19 19:44:45 -04:00
|
|
|
|
|
|
|
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,
|
2020-04-22 14:04:16 -04:00
|
|
|
built_draw_path.filter);
|
2020-03-19 19:44:45 -04:00
|
|
|
|
|
|
|
self.add_alpha_tiles(&mut culled_tiles,
|
|
|
|
layer_z_buffer,
|
|
|
|
&built_draw_path.path.single_mask_tiles,
|
|
|
|
current_depth,
|
2020-04-13 14:52:10 -04:00
|
|
|
color_texture,
|
2020-03-19 19:44:45 -04:00
|
|
|
built_draw_path.blend_mode,
|
2020-04-22 14:04:16 -04:00
|
|
|
built_draw_path.filter);
|
2020-03-02 23:04:09 -05:00
|
|
|
|
2020-03-19 19:44:45 -04:00
|
|
|
match built_draw_path.path.solid_tiles {
|
|
|
|
SolidTiles::Regular(ref tiles) => {
|
|
|
|
self.add_alpha_tiles(&mut culled_tiles,
|
|
|
|
layer_z_buffer,
|
|
|
|
tiles,
|
|
|
|
current_depth,
|
2020-04-13 14:52:10 -04:00
|
|
|
color_texture,
|
2020-03-19 19:44:45 -04:00
|
|
|
built_draw_path.blend_mode,
|
2020-04-22 14:04:16 -04:00
|
|
|
built_draw_path.filter);
|
2020-03-02 23:04:09 -05:00
|
|
|
}
|
2020-03-19 19:44:45 -04:00
|
|
|
SolidTiles::Occluders(_) => {}
|
2020-03-02 23:04:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
current_depth += 1;
|
2020-02-19 20:44:41 -05:00
|
|
|
}
|
2020-02-14 23:53:27 -05:00
|
|
|
}
|
|
|
|
}
|
2019-01-14 17:20:36 -05:00
|
|
|
}
|
2020-02-14 23:53:27 -05:00
|
|
|
|
|
|
|
culled_tiles
|
2019-01-14 17:20:36 -05:00
|
|
|
}
|
2019-03-29 22:11:38 -04:00
|
|
|
|
2020-02-20 18:52:40 -05:00
|
|
|
fn build_solid_tiles(&self, built_draw_paths: &[BuiltDrawPath]) -> Vec<ZBuffer> {
|
2020-02-19 20:44:41 -05:00
|
|
|
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];
|
2020-03-19 19:44:45 -04:00
|
|
|
let mut current_depth = 1;
|
2020-02-19 20:44:41 -05:00
|
|
|
|
|
|
|
// Create Z-buffers.
|
|
|
|
for display_item in &self.scene.display_list {
|
|
|
|
match *display_item {
|
2020-02-22 00:42:15 -05:00
|
|
|
DisplayItem::PushRenderTarget { .. } => {
|
2020-02-19 20:44:41 -05:00
|
|
|
z_buffer_index_stack.push(z_buffers.len());
|
|
|
|
z_buffers.push(ZBuffer::new(effective_view_box));
|
|
|
|
}
|
2020-02-22 00:42:15 -05:00
|
|
|
DisplayItem::PopRenderTarget => {
|
2020-02-19 20:44:41 -05:00
|
|
|
z_buffer_index_stack.pop();
|
|
|
|
}
|
|
|
|
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()];
|
2020-02-25 21:36:46 -05:00
|
|
|
for (path_subindex, built_draw_path) in
|
2020-02-19 20:44:41 -05:00
|
|
|
built_draw_paths[start_index..end_index].iter().enumerate() {
|
2020-03-02 23:04:09 -05:00
|
|
|
let path_index = (path_subindex + start_index) as u32;
|
2020-03-03 17:26:38 -05:00
|
|
|
let path = &self.scene.paths[path_index as usize];
|
2020-03-04 19:18:32 -05:00
|
|
|
let metadata = DepthMetadata { paint_id: path.paint() };
|
2020-03-19 19:44:45 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2020-03-02 23:04:09 -05:00
|
|
|
current_depth += 1;
|
2020-02-19 20:44:41 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
debug_assert_eq!(z_buffer_index_stack.len(), 1);
|
|
|
|
|
|
|
|
z_buffers
|
|
|
|
}
|
2020-02-17 17:44:48 -05:00
|
|
|
|
2020-03-19 19:44:45 -04:00
|
|
|
fn add_alpha_tiles(&self,
|
|
|
|
culled_tiles: &mut CulledTiles,
|
|
|
|
layer_z_buffer: &ZBuffer,
|
2020-04-15 23:13:37 -04:00
|
|
|
built_alpha_tiles: &[BuiltTile],
|
2020-03-19 19:44:45 -04:00
|
|
|
current_depth: u32,
|
2020-04-13 14:52:10 -04:00
|
|
|
color_texture: Option<TileBatchTexture>,
|
2020-03-19 19:44:45 -04:00
|
|
|
blend_mode: BlendMode,
|
2020-04-22 14:04:16 -04:00
|
|
|
filter: Filter) {
|
2020-04-15 23:13:37 -04:00
|
|
|
let mut batch_indices: Vec<BatchIndex> = 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;
|
|
|
|
}
|
2020-03-19 19:44:45 -04:00
|
|
|
|
2020-04-15 23:13:37 -04:00
|
|
|
// 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 {
|
2020-03-19 19:44:45 -04:00
|
|
|
tiles: vec![],
|
2020-04-13 14:52:10 -04:00
|
|
|
color_texture,
|
2020-03-19 19:44:45 -04:00
|
|
|
blend_mode,
|
2020-04-03 01:03:42 -04:00
|
|
|
filter,
|
2020-04-15 23:13:37 -04:00
|
|
|
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!(),
|
2020-03-19 19:44:45 -04:00
|
|
|
}
|
2020-02-17 17:44:48 -05:00
|
|
|
}
|
|
|
|
|
2020-04-15 23:13:37 -04:00
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
struct BatchIndex {
|
|
|
|
display_item_index: usize,
|
|
|
|
tile_page: u16,
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
// Create a new `DrawTiles` display item if we don't have one or if we have to break a
|
|
|
|
// batch due to blend mode or paint page. Note that every path with a blend mode that
|
|
|
|
// requires a readable framebuffer needs its own batch.
|
|
|
|
//
|
|
|
|
// TODO(pcwalton): If we really wanted to, we could use tile maps to avoid
|
|
|
|
// batch breaks in some cases…
|
|
|
|
|
2020-03-19 19:44:45 -04:00
|
|
|
// Fetch the destination alpha tiles buffer.
|
|
|
|
let culled_alpha_tiles = match *culled_tiles.display_list.last_mut().unwrap() {
|
|
|
|
CulledDisplayItem::DrawTiles(TileBatch { tiles: ref mut culled_alpha_tiles, .. }) => {
|
|
|
|
culled_alpha_tiles
|
|
|
|
}
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
|
|
|
|
for alpha_tile in alpha_tiles {
|
2020-04-06 14:08:57 -04:00
|
|
|
let alpha_tile_coords = alpha_tile.tile_position();
|
2020-03-19 19:44:45 -04:00
|
|
|
if layer_z_buffer.test(alpha_tile_coords, current_depth) {
|
|
|
|
culled_alpha_tiles.push(*alpha_tile);
|
|
|
|
}
|
|
|
|
}
|
2020-04-15 23:13:37 -04:00
|
|
|
*/
|
2020-03-19 19:44:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn pack_tiles(&mut self, culled_tiles: CulledTiles) {
|
2020-04-06 14:08:57 -04:00
|
|
|
self.listener.send(RenderCommand::BeginTileDrawing);
|
2020-02-19 20:44:41 -05:00
|
|
|
for display_item in culled_tiles.display_list {
|
|
|
|
match display_item {
|
2020-03-19 19:44:45 -04:00
|
|
|
CulledDisplayItem::DrawTiles(batch) => {
|
|
|
|
self.listener.send(RenderCommand::DrawTiles(batch))
|
2020-02-19 20:44:41 -05:00
|
|
|
}
|
2020-02-22 00:42:15 -05:00
|
|
|
CulledDisplayItem::PushRenderTarget(render_target_id) => {
|
|
|
|
self.listener.send(RenderCommand::PushRenderTarget(render_target_id))
|
|
|
|
}
|
|
|
|
CulledDisplayItem::PopRenderTarget => {
|
|
|
|
self.listener.send(RenderCommand::PopRenderTarget)
|
2020-02-19 20:44:41 -05:00
|
|
|
}
|
|
|
|
}
|
2019-03-29 22:11:38 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-04 01:24:34 -05:00
|
|
|
fn finish_building(&mut self,
|
|
|
|
paint_metadata: &[PaintMetadata],
|
2020-02-20 18:52:40 -05:00
|
|
|
built_draw_paths: Vec<BuiltDrawPath>) {
|
2019-04-15 16:21:24 -04:00
|
|
|
self.listener.send(RenderCommand::FlushFills);
|
2020-04-21 16:09:23 -04:00
|
|
|
self.build_clips(&built_draw_paths);
|
2020-04-03 01:03:42 -04:00
|
|
|
let culled_tiles = self.cull_tiles(paint_metadata, built_draw_paths);
|
2020-02-19 20:44:41 -05:00
|
|
|
self.pack_tiles(culled_tiles);
|
2019-03-29 22:11:38 -04:00
|
|
|
}
|
2020-02-16 16:45:15 -05:00
|
|
|
|
2020-02-22 17:39:03 -05:00
|
|
|
fn needs_readable_framebuffer(&self) -> bool {
|
|
|
|
let mut framebuffer_nesting = 0;
|
|
|
|
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 } => {
|
|
|
|
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();
|
2020-03-19 19:44:45 -04:00
|
|
|
if blend_mode.needs_readable_framebuffer() {
|
2020-02-22 17:39:03 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
false
|
|
|
|
}
|
2019-03-29 22:11:38 -04:00
|
|
|
}
|
|
|
|
|
2020-03-19 19:44:45 -04:00
|
|
|
struct PathBuildParams<'a> {
|
|
|
|
path_index: usize,
|
|
|
|
view_box: RectF,
|
|
|
|
built_options: &'a PreparedBuildOptions,
|
|
|
|
scene: &'a Scene,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct DrawPathBuildParams<'a> {
|
|
|
|
path_build_params: PathBuildParams<'a>,
|
|
|
|
paint_metadata: &'a [PaintMetadata],
|
|
|
|
built_clip_paths: &'a [BuiltPath],
|
|
|
|
}
|
|
|
|
|
2020-02-19 20:44:41 -05:00
|
|
|
impl BuiltPath {
|
2020-03-19 19:44:45 -04:00
|
|
|
fn new(path_bounds: RectF,
|
|
|
|
view_box_bounds: RectF,
|
|
|
|
fill_rule: FillRule,
|
|
|
|
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 &&
|
2020-04-06 14:08:57 -04:00
|
|
|
draw_tiling_path_info.blend_mode.occludes_backdrop()
|
2020-03-19 19:44:45 -04:00
|
|
|
}
|
|
|
|
TilingPathInfo::Clip => true,
|
|
|
|
};
|
|
|
|
|
|
|
|
let tile_map_bounds = if tiling_path_info.has_destructive_blend_mode() {
|
|
|
|
view_box_bounds
|
|
|
|
} else {
|
|
|
|
path_bounds
|
|
|
|
};
|
|
|
|
|
2020-02-19 20:44:41 -05:00
|
|
|
BuiltPath {
|
2020-04-15 23:13:37 -04:00
|
|
|
empty_tiles: vec![],
|
2020-03-19 19:44:45 -04:00
|
|
|
single_mask_tiles: vec![],
|
2020-04-21 16:09:23 -04:00
|
|
|
clip_tiles: vec![],
|
2020-03-19 19:44:45 -04:00
|
|
|
solid_tiles: if occludes {
|
|
|
|
SolidTiles::Occluders(vec![])
|
|
|
|
} else {
|
|
|
|
SolidTiles::Regular(vec![])
|
|
|
|
},
|
|
|
|
tiles: DenseTileMap::new(tiles::round_rect_out_to_tile_bounds(tile_map_bounds)),
|
2020-02-19 20:44:41 -05:00
|
|
|
fill_rule,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-19 19:44:45 -04:00
|
|
|
impl Occluder {
|
2020-02-19 20:44:41 -05:00
|
|
|
#[inline]
|
2020-03-19 19:44:45 -04:00
|
|
|
pub(crate) fn new(coords: Vector2I) -> Occluder {
|
|
|
|
Occluder { coords }
|
2020-02-19 20:44:41 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-15 00:55:37 -05:00
|
|
|
struct CulledTiles {
|
2020-02-19 20:44:41 -05:00
|
|
|
display_list: Vec<CulledDisplayItem>,
|
|
|
|
}
|
|
|
|
|
|
|
|
enum CulledDisplayItem {
|
2020-03-19 19:44:45 -04:00
|
|
|
DrawTiles(TileBatch),
|
2020-02-22 00:42:15 -05:00
|
|
|
PushRenderTarget(RenderTargetId),
|
|
|
|
PopRenderTarget,
|
2020-02-15 00:55:37 -05:00
|
|
|
}
|
|
|
|
|
2019-04-18 18:09:37 -04:00
|
|
|
#[derive(Clone, Copy, Debug, Default)]
|
|
|
|
pub struct TileStats {
|
|
|
|
pub solid_tile_count: u32,
|
|
|
|
pub alpha_tile_count: u32,
|
|
|
|
}
|
2019-05-03 21:51:36 -04:00
|
|
|
|
|
|
|
// Utilities for built objects
|
|
|
|
|
2020-02-14 23:53:27 -05:00
|
|
|
impl ObjectBuilder {
|
2020-03-19 19:44:45 -04:00
|
|
|
pub(crate) fn new(path_bounds: RectF,
|
|
|
|
view_box_bounds: RectF,
|
|
|
|
fill_rule: FillRule,
|
|
|
|
tiling_path_info: &TilingPathInfo)
|
|
|
|
-> ObjectBuilder {
|
|
|
|
ObjectBuilder {
|
|
|
|
built_path: BuiltPath::new(path_bounds, view_box_bounds, fill_rule, tiling_path_info),
|
|
|
|
bounds: path_bounds,
|
|
|
|
fills: vec![],
|
|
|
|
}
|
2019-05-03 21:51:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2019-05-29 22:13:42 -04:00
|
|
|
pub(crate) fn tile_rect(&self) -> RectI {
|
2020-02-15 21:01:23 -05:00
|
|
|
self.built_path.tiles.rect
|
2019-05-03 21:51:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn add_fill(
|
|
|
|
&mut self,
|
2020-02-14 23:53:27 -05:00
|
|
|
scene_builder: &SceneBuilder,
|
2019-06-25 17:43:13 -04:00
|
|
|
segment: LineSegment2F,
|
2019-06-03 15:39:29 -04:00
|
|
|
tile_coords: Vector2I,
|
2019-05-03 21:51:36 -04:00
|
|
|
) {
|
|
|
|
debug!("add_fill({:?} ({:?}))", segment, tile_coords);
|
|
|
|
|
|
|
|
// Ensure this fill is in bounds. If not, cull it.
|
|
|
|
if self.tile_coords_to_local_index(tile_coords).is_none() {
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
|
|
|
debug_assert_eq!(TILE_WIDTH, TILE_HEIGHT);
|
|
|
|
|
2019-06-25 17:43:13 -04:00
|
|
|
// Compute the upper left corner of the tile.
|
|
|
|
let tile_size = F32x4::splat(TILE_WIDTH as f32);
|
|
|
|
let tile_upper_left = tile_coords.to_f32().0.to_f32x4().xyxy() * tile_size;
|
2019-05-03 21:51:36 -04:00
|
|
|
|
2019-06-25 17:43:13 -04:00
|
|
|
// Convert to 4.8 fixed point.
|
2019-05-03 21:51:36 -04:00
|
|
|
let segment = (segment.0 - tile_upper_left) * F32x4::splat(256.0);
|
2019-06-25 17:43:13 -04:00
|
|
|
let (min, max) = (F32x4::default(), F32x4::splat((TILE_WIDTH * 256 - 1) as f32));
|
|
|
|
let segment = segment.clamp(min, max).to_i32x4();
|
|
|
|
let (from_x, from_y, to_x, to_y) = (segment[0], segment[1], segment[2], segment[3]);
|
2019-05-03 21:51:36 -04:00
|
|
|
|
|
|
|
// Cull degenerate fills.
|
2019-06-25 17:43:13 -04:00
|
|
|
if from_x == to_x {
|
2019-05-03 21:51:36 -04:00
|
|
|
debug!("... culling!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-04-15 23:13:37 -04:00
|
|
|
// Allocate a global tile if necessary.
|
|
|
|
let alpha_tile_id = self.get_or_allocate_alpha_tile_index(scene_builder, tile_coords);
|
2019-05-03 21:51:36 -04:00
|
|
|
|
2019-06-25 17:43:13 -04:00
|
|
|
// Pack whole pixels.
|
2019-12-29 15:38:36 -05:00
|
|
|
let px = (segment & I32x4::splat(0xf00)).to_u32x4();
|
|
|
|
let px = (px >> 8).to_i32x4() | (px >> 4).to_i32x4().yxwz();
|
2019-06-25 17:43:13 -04:00
|
|
|
|
|
|
|
// Pack instance data.
|
2019-05-03 21:51:36 -04:00
|
|
|
debug!("... OK, pushing");
|
2020-04-15 23:13:37 -04:00
|
|
|
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(),
|
2019-06-25 17:43:13 -04:00
|
|
|
},
|
2019-05-03 21:51:36 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_or_allocate_alpha_tile_index(
|
|
|
|
&mut self,
|
2020-02-14 23:53:27 -05:00
|
|
|
scene_builder: &SceneBuilder,
|
2019-06-03 15:39:29 -04:00
|
|
|
tile_coords: Vector2I,
|
2020-04-15 23:13:37 -04:00
|
|
|
) -> AlphaTileId {
|
2020-02-15 21:01:23 -05:00
|
|
|
let local_tile_index = self.built_path.tiles.coords_to_index_unchecked(tile_coords);
|
2020-04-15 23:13:37 -04:00
|
|
|
let alpha_tile_id = self.built_path.tiles.data[local_tile_index].alpha_tile_id;
|
|
|
|
if alpha_tile_id.is_valid() {
|
|
|
|
return alpha_tile_id;
|
2019-05-03 21:51:36 -04:00
|
|
|
}
|
|
|
|
|
2020-04-21 16:09:23 -04:00
|
|
|
let alpha_tile_id = AlphaTileId::new(&scene_builder.next_alpha_tile_indices, 0);
|
2020-04-15 23:13:37 -04:00
|
|
|
self.built_path.tiles.data[local_tile_index].alpha_tile_id = alpha_tile_id;
|
|
|
|
alpha_tile_id
|
2019-05-03 21:51:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn add_active_fill(
|
|
|
|
&mut self,
|
2020-02-14 23:53:27 -05:00
|
|
|
scene_builder: &SceneBuilder,
|
2019-05-03 21:51:36 -04:00
|
|
|
left: f32,
|
|
|
|
right: f32,
|
|
|
|
mut winding: i32,
|
2019-06-03 15:39:29 -04:00
|
|
|
tile_coords: Vector2I,
|
2019-05-03 21:51:36 -04:00
|
|
|
) {
|
|
|
|
let tile_origin_y = (tile_coords.y() * TILE_HEIGHT as i32) as f32;
|
2020-03-31 14:28:34 -04:00
|
|
|
let left = vec2f(left, tile_origin_y);
|
|
|
|
let right = vec2f(right, tile_origin_y);
|
2019-05-03 21:51:36 -04:00
|
|
|
|
|
|
|
let segment = if winding < 0 {
|
2019-06-03 15:39:29 -04:00
|
|
|
LineSegment2F::new(left, right)
|
2019-05-03 21:51:36 -04:00
|
|
|
} else {
|
2019-06-03 15:39:29 -04:00
|
|
|
LineSegment2F::new(right, left)
|
2019-05-03 21:51:36 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
debug!(
|
|
|
|
"... emitting active fill {} -> {} winding {} @ tile {:?}",
|
|
|
|
left.x(),
|
|
|
|
right.x(),
|
|
|
|
winding,
|
|
|
|
tile_coords
|
|
|
|
);
|
|
|
|
|
|
|
|
while winding != 0 {
|
2020-02-14 23:53:27 -05:00
|
|
|
self.add_fill(scene_builder, segment, tile_coords);
|
2019-05-03 21:51:36 -04:00
|
|
|
if winding < 0 {
|
|
|
|
winding += 1
|
|
|
|
} else {
|
|
|
|
winding -= 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn generate_fill_primitives_for_line(
|
|
|
|
&mut self,
|
2020-02-14 23:53:27 -05:00
|
|
|
scene_builder: &SceneBuilder,
|
2019-06-03 15:39:29 -04:00
|
|
|
mut segment: LineSegment2F,
|
2019-05-03 21:51:36 -04:00
|
|
|
tile_y: i32,
|
|
|
|
) {
|
|
|
|
debug!(
|
|
|
|
"... generate_fill_primitives_for_line(): segment={:?} tile_y={} ({}-{})",
|
|
|
|
segment,
|
|
|
|
tile_y,
|
|
|
|
tile_y as f32 * TILE_HEIGHT as f32,
|
|
|
|
(tile_y + 1) as f32 * TILE_HEIGHT as f32
|
|
|
|
);
|
|
|
|
|
|
|
|
let winding = segment.from_x() > segment.to_x();
|
|
|
|
let (segment_left, segment_right) = if !winding {
|
|
|
|
(segment.from_x(), segment.to_x())
|
|
|
|
} else {
|
|
|
|
(segment.to_x(), segment.from_x())
|
|
|
|
};
|
|
|
|
|
|
|
|
// FIXME(pcwalton): Optimize this.
|
|
|
|
let segment_tile_left = f32::floor(segment_left) as i32 / TILE_WIDTH as i32;
|
|
|
|
let segment_tile_right =
|
|
|
|
util::alignup_i32(f32::ceil(segment_right) as i32, TILE_WIDTH as i32);
|
|
|
|
debug!(
|
|
|
|
"segment_tile_left={} segment_tile_right={} tile_rect={:?}",
|
|
|
|
segment_tile_left,
|
|
|
|
segment_tile_right,
|
|
|
|
self.tile_rect()
|
|
|
|
);
|
|
|
|
|
|
|
|
for subsegment_tile_x in segment_tile_left..segment_tile_right {
|
|
|
|
let (mut fill_from, mut fill_to) = (segment.from(), segment.to());
|
|
|
|
let subsegment_tile_right =
|
|
|
|
((i32::from(subsegment_tile_x) + 1) * TILE_HEIGHT as i32) as f32;
|
|
|
|
if subsegment_tile_right < segment_right {
|
|
|
|
let x = subsegment_tile_right;
|
2019-06-03 15:39:29 -04:00
|
|
|
let point = Vector2F::new(x, segment.solve_y_for_x(x));
|
2019-05-03 21:51:36 -04:00
|
|
|
if !winding {
|
|
|
|
fill_to = point;
|
2019-06-03 15:39:29 -04:00
|
|
|
segment = LineSegment2F::new(point, segment.to());
|
2019-05-03 21:51:36 -04:00
|
|
|
} else {
|
|
|
|
fill_from = point;
|
2019-06-03 15:39:29 -04:00
|
|
|
segment = LineSegment2F::new(segment.from(), point);
|
2019-05-03 21:51:36 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-03 15:39:29 -04:00
|
|
|
let fill_segment = LineSegment2F::new(fill_from, fill_to);
|
2020-03-31 14:28:34 -04:00
|
|
|
let fill_tile_coords = vec2i(subsegment_tile_x, tile_y);
|
2020-02-14 23:53:27 -05:00
|
|
|
self.add_fill(scene_builder, fill_segment, fill_tile_coords);
|
2019-05-03 21:51:36 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2019-06-03 15:39:29 -04:00
|
|
|
pub(crate) fn tile_coords_to_local_index(&self, coords: Vector2I) -> Option<u32> {
|
2020-02-15 21:01:23 -05:00
|
|
|
self.built_path.tiles.coords_to_index(coords).map(|index| index as u32)
|
2019-05-03 21:51:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2019-06-03 15:39:29 -04:00
|
|
|
pub(crate) fn local_tile_index_to_coords(&self, tile_index: u32) -> Vector2I {
|
2020-02-15 21:01:23 -05:00
|
|
|
self.built_path.tiles.index_to_coords(tile_index as usize)
|
2019-05-03 21:51:36 -04:00
|
|
|
}
|
2020-03-19 19:44:45 -04:00
|
|
|
}
|
2020-02-14 23:53:27 -05:00
|
|
|
|
2020-03-19 19:44:45 -04:00
|
|
|
impl<'a> PackedTile<'a> {
|
|
|
|
pub(crate) fn add_to(&self,
|
2020-04-15 23:13:37 -04:00
|
|
|
tiles: &mut Vec<BuiltTile>,
|
2020-04-21 16:09:23 -04:00
|
|
|
clips: &mut Vec<BuiltClip>,
|
|
|
|
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),
|
|
|
|
});
|
|
|
|
}
|
2020-04-15 23:13:37 -04:00
|
|
|
Some(clip_tile) => {
|
2020-04-21 16:09:23 -04:00
|
|
|
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),
|
|
|
|
});
|
2020-04-15 23:13:37 -04:00
|
|
|
}
|
2020-04-21 16:09:23 -04:00
|
|
|
}
|
2020-02-15 17:00:36 -05:00
|
|
|
}
|
2020-02-14 23:53:27 -05:00
|
|
|
}
|
|
|
|
|
2020-04-06 14:08:57 -04:00
|
|
|
impl Tile {
|
2020-02-15 17:00:36 -05:00
|
|
|
#[inline]
|
2020-03-19 19:44:45 -04:00
|
|
|
fn new_alpha(tile_origin: Vector2I,
|
|
|
|
draw_tile_index: u16,
|
2020-04-06 14:08:57 -04:00
|
|
|
draw_tile_backdrop: i8,
|
2020-03-19 19:44:45 -04:00
|
|
|
draw_tiling_path_info: &DrawTilingPathInfo)
|
2020-04-06 14:08:57 -04:00
|
|
|
-> Tile {
|
|
|
|
let mask_0_uv = calculate_mask_uv(draw_tile_index);
|
2020-04-22 14:04:16 -04:00
|
|
|
|
|
|
|
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,
|
|
|
|
}
|
|
|
|
|
2020-04-06 14:08:57 -04:00
|
|
|
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,
|
2020-03-19 19:44:45 -04:00
|
|
|
mask_0_backdrop: draw_tile_backdrop,
|
2020-04-22 14:04:16 -04:00
|
|
|
ctrl: ctrl as u16,
|
2020-04-21 16:09:23 -04:00
|
|
|
pad: 0,
|
2020-04-06 14:08:57 -04:00
|
|
|
color: draw_tiling_path_info.paint_id.0,
|
2020-02-15 17:00:36 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn tile_position(&self) -> Vector2I {
|
2020-03-31 14:28:34 -04:00
|
|
|
vec2i(self.tile_x as i32, self.tile_y as i32)
|
2020-02-15 17:00:36 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-21 16:09:23 -04:00
|
|
|
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,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-06 14:08:57 -04:00
|
|
|
fn calculate_mask_uv(tile_index: u16) -> Vector2I {
|
|
|
|
debug_assert_eq!(MASK_TILES_ACROSS, MASK_TILES_DOWN);
|
2020-02-15 17:00:36 -05:00
|
|
|
let mask_u = tile_index as i32 % MASK_TILES_ACROSS as i32;
|
|
|
|
let mask_v = tile_index as i32 / MASK_TILES_ACROSS as i32;
|
2020-04-06 14:08:57 -04:00
|
|
|
vec2i(mask_u, mask_v)
|
2020-02-17 17:44:48 -05:00
|
|
|
}
|