Transition the existing postprocessing system to a layers system.

This is preparatory work for composite ops and blurs.

Closes #261.
This commit is contained in:
Patrick Walton 2020-02-19 17:44:41 -08:00
parent 25f9346160
commit d9e994e46d
14 changed files with 524 additions and 361 deletions

View File

@ -142,6 +142,11 @@ impl ColorF {
ColorF::default()
}
#[inline]
pub fn black() -> ColorF {
ColorF(F32x4::new(0.0, 0.0, 0.0, 1.0))
}
#[inline]
pub fn white() -> ColorF {
ColorF(F32x4::splat(1.0))

View File

@ -22,7 +22,6 @@ use crate::device::{GroundProgram, GroundVertexArray};
use crate::ui::{DemoUIModel, DemoUIPresenter, ScreenshotInfo, ScreenshotType, UIAction};
use crate::window::{Event, Keycode, SVGPath, Window, WindowSize};
use clap::{App, Arg};
use pathfinder_color::ColorU;
use pathfinder_export::{Export, FileFormat};
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2F;
@ -32,9 +31,9 @@ use pathfinder_gpu::resources::ResourceLoader;
use pathfinder_gpu::Device;
use pathfinder_renderer::concurrent::scene_proxy::{RenderCommandStream, SceneProxy};
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions};
use pathfinder_renderer::gpu::renderer::{RenderStats, RenderTime, Renderer};
use pathfinder_renderer::gpu::renderer::{PostprocessOptions, RenderStats, RenderTime, Renderer};
use pathfinder_renderer::options::{BuildOptions, RenderTransform};
use pathfinder_renderer::post::STEM_DARKENING_FACTORS;
use pathfinder_renderer::post::{DEFRINGING_KERNEL_CORE_GRAPHICS, STEM_DARKENING_FACTORS};
use pathfinder_renderer::scene::Scene;
use pathfinder_svg::BuiltSVG;
use pathfinder_ui::{MousePosition, UIEvent};
@ -63,25 +62,6 @@ const CAMERA_ZOOM_AMOUNT_2D: f32 = 0.1;
// Half of the eye separation distance.
const DEFAULT_EYE_OFFSET: f32 = 0.025;
const LIGHT_BG_COLOR: ColorU = ColorU {
r: 248,
g: 248,
b: 248,
a: 255,
};
const DARK_BG_COLOR: ColorU = ColorU {
r: 32,
g: 32,
b: 32,
a: 255,
};
const TRANSPARENT_BG_COLOR: ColorU = ColorU {
r: 0,
g: 0,
b: 0,
a: 0,
};
const APPROX_FONT_SIZE: f32 = 16.0;
const MESSAGE_TIMEOUT_SECS: u64 = 5;
@ -101,6 +81,7 @@ pub struct DemoApp<W> where W: Window {
window_size: WindowSize,
svg_tree: Tree,
scene_metadata: SceneMetadata,
render_transform: Option<RenderTransform>,
render_command_stream: Option<RenderCommandStream>,
@ -151,7 +132,12 @@ impl<W> DemoApp<W> where W: Window {
// Set up the executor.
let executor = DemoExecutor::new(options.jobs);
let mut built_svg = load_scene(resources, &options.input_path);
let mut ui_model = DemoUIModel::new(&options);
let render_options = RendererOptions { background_color: None };
let effects = build_effects(&ui_model);
let (mut built_svg, svg_tree) = load_scene(resources, &options.input_path, effects);
let message = get_svg_building_message(&built_svg);
let viewport = window.viewport(options.mode.view(0));
@ -159,12 +145,9 @@ impl<W> DemoApp<W> where W: Window {
viewport,
window_size: window_size.device_size(),
};
// FIXME(pcwalton)
let render_options = RendererOptions {
background_color: None,
};
let renderer = Renderer::new(device, resources, dest_framebuffer, render_options);
let scene_metadata = SceneMetadata::new_clipping_view_box(&mut built_svg.scene,
viewport.size());
let camera = Camera::new(options.mode, scene_metadata.view_box, viewport.size());
@ -177,8 +160,6 @@ impl<W> DemoApp<W> where W: Window {
&renderer.quad_vertex_positions_buffer(),
&renderer.quad_vertex_indices_buffer());
let mut ui_model = DemoUIModel::new(&options);
let mut message_epoch = 0;
emit_message::<W>(
&mut ui_model,
@ -196,6 +177,7 @@ impl<W> DemoApp<W> where W: Window {
window_size,
svg_tree,
scene_metadata,
render_transform: None,
render_command_stream: None,
@ -445,7 +427,11 @@ impl<W> DemoApp<W> where W: Window {
}
Event::OpenSVG(ref svg_path) => {
let mut built_svg = load_scene(self.window.resource_loader(), svg_path);
let effects = build_effects(&self.ui_model);
let (mut built_svg, svg_tree) = load_scene(self.window.resource_loader(),
svg_path,
effects);
self.ui_model.message = get_svg_building_message(&built_svg);
let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size();
@ -456,6 +442,7 @@ impl<W> DemoApp<W> where W: Window {
viewport_size);
self.scene_proxy.replace_scene(built_svg.scene);
self.svg_tree = svg_tree;
self.dirty = true;
}
@ -499,8 +486,6 @@ impl<W> DemoApp<W> where W: Window {
.to_f32()
.scale(self.window_size.backing_scale_factor);
self.ui_presenter.set_show_text_effects(self.scene_metadata.monochrome_color.is_some());
let mut ui_action = UIAction::None;
if self.options.ui == UIVisibility::All {
self.ui_presenter.update(
@ -599,6 +584,15 @@ impl<W> DemoApp<W> where W: Window {
match ui_action {
UIAction::None => {}
UIAction::ModelChanged => self.dirty = true,
UIAction::EffectsChanged => {
let effects = build_effects(&self.ui_model);
let mut built_svg = build_svg_tree(&self.svg_tree, effects);
let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size();
self.scene_metadata =
SceneMetadata::new_clipping_view_box(&mut built_svg.scene, viewport_size);
self.scene_proxy.replace_scene(built_svg.scene);
self.dirty = true;
}
UIAction::TakeScreenshot(ref info) => {
self.pending_screenshot_info = Some((*info).clone());
self.dirty = true;
@ -636,14 +630,6 @@ impl<W> DemoApp<W> where W: Window {
}
}
}
fn background_color(&self) -> ColorU {
match self.ui_model.background_color {
BackgroundColor::Light => LIGHT_BG_COLOR,
BackgroundColor::Dark => DARK_BG_COLOR,
BackgroundColor::Transparent => TRANSPARENT_BG_COLOR,
}
}
}
#[derive(Clone)]
@ -756,7 +742,10 @@ pub enum UIVisibility {
All,
}
fn load_scene(resource_loader: &dyn ResourceLoader, input_path: &SVGPath) -> BuiltSVG {
fn load_scene(resource_loader: &dyn ResourceLoader,
input_path: &SVGPath,
effects: Option<PostprocessOptions>)
-> (BuiltSVG, Tree) {
let mut data;
match *input_path {
SVGPath::Default => data = resource_loader.slurp(DEFAULT_SVG_VIRTUAL_PATH).unwrap(),
@ -767,7 +756,24 @@ fn load_scene(resource_loader: &dyn ResourceLoader, input_path: &SVGPath) -> Bui
}
};
BuiltSVG::from_tree(Tree::from_data(&data, &UsvgOptions::default()).unwrap())
let tree = Tree::from_data(&data, &UsvgOptions::default()).expect("Failed to parse the SVG!");
let built_svg = build_svg_tree(&tree, effects);
(built_svg, tree)
}
fn build_svg_tree(tree: &Tree, effects: Option<PostprocessOptions>) -> BuiltSVG {
let mut scene = Scene::new();
if let Some(effects) = effects {
scene.push_layer(effects);
}
let mut built_svg = BuiltSVG::from_tree_and_scene(&tree, scene);
if effects.is_some() {
built_svg.scene.pop_layer();
}
built_svg
}
fn center_of_window(window_size: &WindowSize) -> Vector2F {
@ -842,7 +848,6 @@ impl BackgroundColor {
struct SceneMetadata {
view_box: RectF,
monochrome_color: Option<ColorU>,
}
impl SceneMetadata {
@ -850,8 +855,25 @@ impl SceneMetadata {
// Can we simplify this?
fn new_clipping_view_box(scene: &mut Scene, viewport_size: Vector2I) -> SceneMetadata {
let view_box = scene.view_box();
let monochrome_color = scene.monochrome_color();
scene.set_view_box(RectF::new(Vector2F::default(), viewport_size.to_f32()));
SceneMetadata { view_box, monochrome_color }
SceneMetadata { view_box }
}
}
fn build_effects(ui_model: &DemoUIModel) -> Option<PostprocessOptions> {
if !ui_model.gamma_correction_effect_enabled && !ui_model.subpixel_aa_effect_enabled {
return None;
}
Some(PostprocessOptions {
fg_color: ui_model.foreground_color().to_f32(),
bg_color: ui_model.background_color().to_f32(),
gamma_correction: ui_model.gamma_correction_effect_enabled,
defringing_kernel: if ui_model.subpixel_aa_effect_enabled {
// TODO(pcwalton): Select FreeType defringing kernel as necessary.
Some(DEFRINGING_KERNEL_CORE_GRAPHICS)
} else {
None
},
})
}

View File

@ -21,10 +21,8 @@ use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::transform3d::Transform4F;
use pathfinder_geometry::vector::{Vector2I, Vector4F};
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions};
use pathfinder_renderer::gpu::renderer::PostprocessOptions;
use pathfinder_renderer::gpu_data::RenderCommand;
use pathfinder_renderer::options::RenderTransform;
use pathfinder_renderer::post::DEFRINGING_KERNEL_CORE_GRAPHICS;
use std::path::PathBuf;
const GROUND_SOLID_COLOR: ColorU = ColorU {
@ -88,7 +86,7 @@ impl<W> DemoApp<W> where W: Window {
// Clear to the appropriate color.
let clear_color = match mode {
Mode::TwoD => Some(self.background_color().to_f32()),
Mode::TwoD => Some(self.ui_model.background_color().to_f32()),
Mode::ThreeD => None,
Mode::VR => Some(ColorF::transparent_black()),
};
@ -221,7 +219,7 @@ impl<W> DemoApp<W> where W: Window {
// Don't clear the first scene after drawing it.
let clear_color = if render_scene_index == 0 {
Some(self.background_color().to_f32())
Some(self.ui_model.background_color().to_f32())
} else {
None
};
@ -251,23 +249,6 @@ impl<W> DemoApp<W> where W: Window {
}
fn render_vector_scene(&mut self) {
match self.scene_metadata.monochrome_color {
None => self.renderer.set_postprocess_options(None),
Some(fg_color) => {
self.renderer.set_postprocess_options(Some(PostprocessOptions {
fg_color: fg_color.to_f32(),
bg_color: self.background_color().to_f32(),
gamma_correction: self.ui_model.gamma_correction_effect_enabled,
defringing_kernel: if self.ui_model.subpixel_aa_effect_enabled {
// TODO(pcwalton): Select FreeType defringing kernel as necessary.
Some(DEFRINGING_KERNEL_CORE_GRAPHICS)
} else {
None
},
}))
}
}
if self.ui_model.mode == Mode::TwoD {
self.renderer.disable_depth();
} else {

View File

@ -11,8 +11,9 @@
use crate::camera::Mode;
use crate::window::Window;
use crate::{BackgroundColor, Options};
use pathfinder_geometry::vector::Vector2I;
use pathfinder_color::ColorU;
use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::vector::Vector2I;
use pathfinder_gpu::resources::ResourceLoader;
use pathfinder_gpu::Device;
use pathfinder_renderer::gpu::debug::DebugUIPresenter;
@ -39,6 +40,10 @@ const SCREENSHOT_PANEL_HEIGHT: i32 = BUTTON_HEIGHT * 2;
const ROTATE_PANEL_WIDTH: i32 = SLIDER_WIDTH + PADDING * 2;
const ROTATE_PANEL_HEIGHT: i32 = PADDING * 2 + SLIDER_HEIGHT;
const LIGHT_BG_COLOR: ColorU = ColorU { r: 248, g: 248, b: 248, a: 255, };
const DARK_BG_COLOR: ColorU = ColorU { r: 32, g: 32, b: 32, a: 255, };
const TRANSPARENT_BG_COLOR: ColorU = ColorU { r: 0, g: 0, b: 0, a: 0, };
static EFFECTS_PNG_NAME: &'static str = "demo-effects";
static OPEN_PNG_NAME: &'static str = "demo-open";
static ROTATE_PNG_NAME: &'static str = "demo-rotate";
@ -74,6 +79,22 @@ impl DemoUIModel {
fn rotation(&self) -> f32 {
(self.rotation as f32 / SLIDER_WIDTH as f32 * 2.0 - 1.0) * PI
}
// Only relevant if in monochrome mode.
pub fn foreground_color(&self) -> ColorU {
match self.background_color {
BackgroundColor::Light | BackgroundColor::Transparent => ColorU::black(),
BackgroundColor::Dark => ColorU::white(),
}
}
pub fn background_color(&self) -> ColorU {
match self.background_color {
BackgroundColor::Light => LIGHT_BG_COLOR,
BackgroundColor::Dark => DARK_BG_COLOR,
BackgroundColor::Transparent => TRANSPARENT_BG_COLOR,
}
}
}
pub struct DemoUIPresenter<D>
@ -93,8 +114,6 @@ where
background_panel_visible: bool,
screenshot_panel_visible: bool,
rotate_panel_visible: bool,
show_text_effects: bool,
}
impl<D> DemoUIPresenter<D>
@ -126,15 +145,9 @@ where
background_panel_visible: false,
screenshot_panel_visible: false,
rotate_panel_visible: false,
show_text_effects: true,
}
}
pub fn set_show_text_effects(&mut self, show_text_effects: bool) {
self.show_text_effects = show_text_effects;
}
pub fn update<W>(
&mut self,
device: &D,
@ -156,22 +169,18 @@ where
let button_size = Vector2I::new(BUTTON_WIDTH, BUTTON_HEIGHT);
// Draw text effects button.
if self.show_text_effects {
if debug_ui_presenter.ui_presenter.draw_button(device,
position,
&self.effects_texture) {
self.effects_panel_visible = !self.effects_panel_visible;
}
if !self.effects_panel_visible {
debug_ui_presenter.ui_presenter.draw_tooltip(
device,
"Text Effects",
RectI::new(position, button_size),
);
}
position += Vector2I::new(button_size.x() + PADDING, 0);
// Draw effects button.
if debug_ui_presenter.ui_presenter.draw_button(device, position, &self.effects_texture) {
self.effects_panel_visible = !self.effects_panel_visible;
}
if !self.effects_panel_visible {
debug_ui_presenter.ui_presenter.draw_tooltip(
device,
"Effects",
RectI::new(position, button_size),
);
}
position += Vector2I::new(button_size.x() + PADDING, 0);
// Draw open button.
if debug_ui_presenter.ui_presenter.draw_button(device, position, &self.open_texture) {
@ -245,7 +254,7 @@ where
position += Vector2I::new(button_size.x() + PADDING, 0);
// Draw effects panel, if necessary.
self.draw_effects_panel(device, debug_ui_presenter, model);
self.draw_effects_panel(device, debug_ui_presenter, model, action);
// Draw rotate and zoom buttons, if applicable.
if model.mode != Mode::TwoD {
@ -324,7 +333,8 @@ where
fn draw_effects_panel(&mut self,
device: &D,
debug_ui_presenter: &mut DebugUIPresenter<D>,
model: &mut DemoUIModel) {
model: &mut DemoUIModel,
action: &mut UIAction) {
if !self.effects_panel_visible {
return;
}
@ -340,30 +350,30 @@ where
WINDOW_COLOR,
);
model.gamma_correction_effect_enabled = self.draw_effects_switch(
self.draw_effects_switch(
device,
action,
debug_ui_presenter,
"Gamma Correction",
0,
effects_panel_y,
model.gamma_correction_effect_enabled,
);
model.stem_darkening_effect_enabled = self.draw_effects_switch(
&mut model.gamma_correction_effect_enabled);
self.draw_effects_switch(
device,
action,
debug_ui_presenter,
"Stem Darkening",
1,
effects_panel_y,
model.stem_darkening_effect_enabled,
);
model.subpixel_aa_effect_enabled = self.draw_effects_switch(
&mut model.stem_darkening_effect_enabled);
self.draw_effects_switch(
device,
action,
debug_ui_presenter,
"Subpixel AA",
2,
effects_panel_y,
model.subpixel_aa_effect_enabled,
);
&mut model.subpixel_aa_effect_enabled);
}
fn draw_screenshot_panel<W>(
@ -592,12 +602,12 @@ where
fn draw_effects_switch(
&self,
device: &D,
action: &mut UIAction,
debug_ui_presenter: &mut DebugUIPresenter<D>,
text: &str,
index: i32,
window_y: i32,
value: bool,
) -> bool {
value: &mut bool) {
let text_x = PADDING * 2;
let text_y = window_y + PADDING + BUTTON_TEXT_OFFSET + (BUTTON_HEIGHT + PADDING) * index;
debug_ui_presenter
@ -608,10 +618,16 @@ where
let switch_x = PADDING + EFFECTS_PANEL_WIDTH - (switch_width + PADDING);
let switch_y = window_y + PADDING + (BUTTON_HEIGHT + PADDING) * index;
let switch_position = Vector2I::new(switch_x, switch_y);
debug_ui_presenter
.ui_presenter
.draw_text_switch(device, switch_position, &["Off", "On"], value as u8)
!= 0
let new_value =
debug_ui_presenter
.ui_presenter
.draw_text_switch(device, switch_position, &["Off", "On"], *value as u8) != 0;
if new_value != *value {
*action = UIAction::EffectsChanged;
*value = new_value;
}
}
}
@ -619,6 +635,7 @@ where
pub enum UIAction {
None,
ModelChanged,
EffectsChanged,
TakeScreenshot(ScreenshotInfo),
ZoomIn,
ZoomActualSize,

View File

@ -478,6 +478,11 @@ impl Device for GLDevice {
&framebuffer.texture
}
#[inline]
fn texture_format(&self, texture: &Self::Texture) -> TextureFormat {
texture.format
}
#[inline]
fn texture_size(&self, texture: &Self::Texture) -> Vector2I {
texture.size

View File

@ -70,6 +70,7 @@ pub trait Device: Sized {
mode: BufferUploadMode,
);
fn framebuffer_texture<'f>(&self, framebuffer: &'f Self::Framebuffer) -> &'f Self::Texture;
fn texture_format(&self, texture: &Self::Texture) -> TextureFormat;
fn texture_size(&self, texture: &Self::Texture) -> Vector2I;
fn upload_to_texture(&self, texture: &Self::Texture, rect: RectI, data: TextureDataRef);
fn read_pixels(&self, target: &RenderTarget<Self>, viewport: RectI)

View File

@ -437,6 +437,17 @@ impl Device for MetalDevice {
&framebuffer.0
}
fn texture_format(&self, texture: &MetalTexture) -> TextureFormat {
match texture.texture.pixel_format() {
MTLPixelFormat::R8Unorm => TextureFormat::R8,
MTLPixelFormat::R16Float => TextureFormat::R16F,
MTLPixelFormat::RGBA8Unorm => TextureFormat::RGBA8,
MTLPixelFormat::RGBA16Float => TextureFormat::RGBA16F,
MTLPixelFormat::RGBA32Float => TextureFormat::RGBA32F,
_ => panic!("Unexpected Metal texture format!"),
}
}
fn texture_size(&self, texture: &MetalTexture) -> Vector2I {
Vector2I::new(texture.texture.width() as i32, texture.texture.height() as i32)
}

View File

@ -11,12 +11,12 @@
//! Packs data onto the GPU.
use crate::concurrent::executor::Executor;
use crate::gpu::renderer::MASK_TILES_ACROSS;
use crate::gpu::renderer::{MASK_TILES_ACROSS, PostprocessOptions};
use crate::gpu_data::{AlphaTile, AlphaTileVertex, FillBatchPrimitive, MaskTile, MaskTileVertex};
use crate::gpu_data::{RenderCommand, TileObjectPrimitive};
use crate::gpu_data::{RenderCommand, SolidTileVertex, TileObjectPrimitive};
use crate::options::{PreparedBuildOptions, RenderCommandListener};
use crate::paint::{PaintInfo, PaintMetadata};
use crate::scene::Scene;
use crate::scene::{DisplayItem, Scene};
use crate::tile_map::DenseTileMap;
use crate::tiles::{self, TILE_HEIGHT, TILE_WIDTH, Tiler, TilingPathInfo};
use crate::z_buffer::ZBuffer;
@ -37,7 +37,6 @@ pub(crate) struct SceneBuilder<'a> {
next_alpha_tile_index: AtomicUsize,
next_mask_tile_index: AtomicUsize,
pub(crate) z_buffer: ZBuffer,
pub(crate) listener: Box<dyn RenderCommandListener>,
}
@ -52,17 +51,22 @@ pub(crate) struct ObjectBuilder {
pub(crate) struct BuiltPath {
pub mask_tiles: Vec<MaskTile>,
pub alpha_tiles: Vec<AlphaTile>,
pub solid_tiles: Vec<SolidTile>,
pub tiles: DenseTileMap<TileObjectPrimitive>,
pub fill_rule: FillRule,
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct SolidTile {
pub(crate) coords: Vector2I,
}
impl<'a> SceneBuilder<'a> {
pub(crate) fn new(
scene: &'a Scene,
built_options: &'a PreparedBuildOptions,
listener: Box<dyn RenderCommandListener>,
) -> SceneBuilder<'a> {
let effective_view_box = scene.effective_view_box(built_options);
SceneBuilder {
scene,
built_options,
@ -70,7 +74,6 @@ impl<'a> SceneBuilder<'a> {
next_alpha_tile_index: AtomicUsize::new(0),
next_mask_tile_index: AtomicUsize::new(0),
z_buffer: ZBuffer::new(effective_view_box),
listener,
}
}
@ -167,26 +170,84 @@ impl<'a> SceneBuilder<'a> {
tiler.object_builder.built_path
}
fn cull_tiles(&self, built_clip_paths: Vec<BuiltPath>, built_draw_paths: Vec<BuiltPath>)
fn cull_tiles(&self,
paint_metadata: &[PaintMetadata],
built_clip_paths: Vec<BuiltPath>,
built_draw_paths: Vec<BuiltPath>)
-> CulledTiles {
let mut culled_tiles = CulledTiles {
mask_winding_tiles: vec![],
mask_evenodd_tiles: vec![],
alpha_tiles: vec![],
display_list: vec![],
};
for built_clip_path in built_clip_paths {
culled_tiles.push_mask_tiles(&built_clip_path);
}
for built_draw_path in built_draw_paths {
culled_tiles.push_mask_tiles(&built_draw_path);
let mut remaining_layer_z_buffers = self.build_solid_tiles(&built_draw_paths);
remaining_layer_z_buffers.reverse();
for alpha_tile in built_draw_path.alpha_tiles {
let alpha_tile_coords = alpha_tile.upper_left.tile_position();
if self.z_buffer.test(alpha_tile_coords,
alpha_tile.upper_left.object_index as u32) {
culled_tiles.alpha_tiles.push(alpha_tile);
// Process first Z-buffer.
let first_z_buffer = remaining_layer_z_buffers.pop().unwrap();
let first_solid_tiles = first_z_buffer.build_solid_tiles(&self.scene.paths,
paint_metadata);
if !first_solid_tiles.is_empty() {
culled_tiles.display_list
.push(CulledDisplayItem::DrawSolidTiles(first_solid_tiles));
}
let mut layer_z_buffers_stack = vec![first_z_buffer];
for display_item in &self.scene.display_list {
// Just pass through `PushLayer` and `PopLayer` commands.
let (start_draw_path_index, end_draw_path_index) = match *display_item {
DisplayItem::PushLayer { effects } => {
culled_tiles.display_list.push(CulledDisplayItem::PushLayer { effects });
let z_buffer = remaining_layer_z_buffers.pop().unwrap();
let solid_tiles = z_buffer.build_solid_tiles(&self.scene.paths,
paint_metadata);
if !solid_tiles.is_empty() {
culled_tiles.display_list
.push(CulledDisplayItem::DrawSolidTiles(solid_tiles));
}
layer_z_buffers_stack.push(z_buffer);
continue;
}
DisplayItem::PopLayer => {
culled_tiles.display_list.push(CulledDisplayItem::PopLayer);
layer_z_buffers_stack.pop();
continue;
}
DisplayItem::DrawPaths { start_index, end_index } => (start_index, end_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];
culled_tiles.push_mask_tiles(built_draw_path);
// Create a `DrawAlphaTiles` display item if necessary.
match culled_tiles.display_list.last() {
Some(&CulledDisplayItem::DrawAlphaTiles(_)) => {}
_ => culled_tiles.display_list.push(CulledDisplayItem::DrawAlphaTiles(vec![])),
}
// Fetch the destination alpha tiles buffer.
let culled_alpha_tiles = match *culled_tiles.display_list.last_mut().unwrap() {
CulledDisplayItem::DrawAlphaTiles(ref mut culled_alpha_tiles) => {
culled_alpha_tiles
}
_ => unreachable!(),
};
let layer_z_buffer = layer_z_buffers_stack.last().unwrap();
for alpha_tile in &built_draw_path.alpha_tiles {
let alpha_tile_coords = alpha_tile.upper_left.tile_position();
if layer_z_buffer.test(alpha_tile_coords,
alpha_tile.upper_left.object_index as u32) {
culled_alpha_tiles.push(*alpha_tile);
}
}
}
}
@ -194,12 +255,37 @@ impl<'a> SceneBuilder<'a> {
culled_tiles
}
fn pack_tiles(&mut self, paint_metadata: &[PaintMetadata], culled_tiles: CulledTiles) {
let path_count = self.scene.paths.len() as u32;
let solid_tiles = self.z_buffer.build_solid_tiles(&self.scene.paths,
paint_metadata,
0..path_count);
fn build_solid_tiles(&self, built_draw_paths: &[BuiltPath]) -> Vec<ZBuffer> {
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];
// Create Z-buffers.
for display_item in &self.scene.display_list {
match *display_item {
DisplayItem::PushLayer { .. } => {
z_buffer_index_stack.push(z_buffers.len());
z_buffers.push(ZBuffer::new(effective_view_box));
}
DisplayItem::PopLayer => {
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()];
for (path_index, built_draw_path) in
built_draw_paths[start_index..end_index].iter().enumerate() {
z_buffer.update(&built_draw_path.solid_tiles, path_index as u32);
}
}
}
}
debug_assert_eq!(z_buffer_index_stack.len(), 1);
z_buffers
}
fn pack_tiles(&mut self, culled_tiles: CulledTiles) {
if !culled_tiles.mask_winding_tiles.is_empty() {
self.listener.send(RenderCommand::RenderMaskTiles {
tiles: culled_tiles.mask_winding_tiles,
@ -213,11 +299,19 @@ impl<'a> SceneBuilder<'a> {
});
}
if !solid_tiles.is_empty() {
self.listener.send(RenderCommand::DrawSolidTiles(solid_tiles));
}
if !culled_tiles.alpha_tiles.is_empty() {
self.listener.send(RenderCommand::DrawAlphaTiles(culled_tiles.alpha_tiles));
for display_item in culled_tiles.display_list {
match display_item {
CulledDisplayItem::DrawSolidTiles(tiles) => {
self.listener.send(RenderCommand::DrawSolidTiles(tiles))
}
CulledDisplayItem::DrawAlphaTiles(tiles) => {
self.listener.send(RenderCommand::DrawAlphaTiles(tiles))
}
CulledDisplayItem::PushLayer { effects } => {
self.listener.send(RenderCommand::PushLayer { effects })
}
CulledDisplayItem::PopLayer => self.listener.send(RenderCommand::PopLayer),
}
}
}
@ -226,8 +320,8 @@ impl<'a> SceneBuilder<'a> {
built_clip_paths: Vec<BuiltPath>,
built_draw_paths: Vec<BuiltPath>) {
self.listener.send(RenderCommand::FlushFills);
let culled_tiles = self.cull_tiles(built_clip_paths, built_draw_paths);
self.pack_tiles(paint_metadata, culled_tiles);
let culled_tiles = self.cull_tiles(paint_metadata, built_clip_paths, built_draw_paths);
self.pack_tiles(culled_tiles);
}
pub(crate) fn allocate_mask_tile_index(&self) -> u16 {
@ -236,10 +330,36 @@ impl<'a> SceneBuilder<'a> {
}
}
impl BuiltPath {
fn new(bounds: RectF, fill_rule: FillRule) -> BuiltPath {
BuiltPath {
mask_tiles: vec![],
alpha_tiles: vec![],
solid_tiles: vec![],
tiles: DenseTileMap::new(tiles::round_rect_out_to_tile_bounds(bounds)),
fill_rule,
}
}
}
impl SolidTile {
#[inline]
pub(crate) fn new(coords: Vector2I) -> SolidTile {
SolidTile { coords }
}
}
struct CulledTiles {
mask_winding_tiles: Vec<MaskTile>,
mask_evenodd_tiles: Vec<MaskTile>,
alpha_tiles: Vec<AlphaTile>,
display_list: Vec<CulledDisplayItem>,
}
enum CulledDisplayItem {
DrawSolidTiles(Vec<SolidTileVertex>),
DrawAlphaTiles(Vec<AlphaTile>),
PushLayer { effects: PostprocessOptions },
PopLayer,
}
#[derive(Clone, Copy, Debug, Default)]
@ -252,13 +372,7 @@ pub struct TileStats {
impl ObjectBuilder {
pub(crate) fn new(bounds: RectF, fill_rule: FillRule) -> ObjectBuilder {
let tile_rect = tiles::round_rect_out_to_tile_bounds(bounds);
let tiles = DenseTileMap::new(tile_rect);
ObjectBuilder {
built_path: BuiltPath { mask_tiles: vec![], alpha_tiles: vec![], tiles, fill_rule },
bounds,
fills: vec![],
}
ObjectBuilder { built_path: BuiltPath::new(bounds, fill_rule), bounds, fills: vec![] }
}
#[inline]

View File

@ -19,7 +19,7 @@ use crate::gpu_data::{AlphaTile, FillBatchPrimitive, MaskTile, PaintData};
use crate::gpu_data::{RenderCommand, SolidTileVertex};
use crate::post::DefringingKernel;
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
use pathfinder_color::{self as color, ColorF, ColorU};
use pathfinder_color::{self as color, ColorF};
use pathfinder_content::fill::FillRule;
use pathfinder_geometry::vector::{Vector2I, Vector4F};
use pathfinder_geometry::rect::RectI;
@ -43,6 +43,8 @@ static QUAD_VERTEX_INDICES: [u32; 6] = [0, 1, 3, 1, 2, 3];
pub(crate) const MASK_TILES_ACROSS: u32 = 256;
pub(crate) const MASK_TILES_DOWN: u32 = 256;
const FRAMEBUFFER_CACHE_SIZE: usize = 8;
// FIXME(pcwalton): Shrink this again!
const MASK_FRAMEBUFFER_WIDTH: i32 = TILE_WIDTH as i32 * MASK_TILES_ACROSS as i32;
const MASK_FRAMEBUFFER_HEIGHT: i32 = TILE_HEIGHT as i32 * MASK_TILES_DOWN as i32;
@ -75,9 +77,9 @@ where
fill_framebuffer: D::Framebuffer,
mask_framebuffer: D::Framebuffer,
paint_texture: Option<D::Texture>,
layer_framebuffer_stack: Vec<LayerFramebufferInfo<D>>,
// Postprocessing shader
postprocess_source_framebuffer: Option<D::Framebuffer>,
postprocess_program: PostprocessProgram<D>,
postprocess_vertex_array: PostprocessVertexArray<D>,
gamma_lut_texture: D::Texture,
@ -93,6 +95,7 @@ where
// Rendering state
framebuffer_flags: FramebufferFlags,
buffered_fills: Vec<FillBatchPrimitive>,
framebuffer_cache: FramebufferCache<D>,
// Debug
pub stats: RenderStats,
@ -102,7 +105,6 @@ where
pub debug_ui_presenter: DebugUIPresenter<D>,
// Extra info
postprocess_options: Option<PostprocessOptions>,
use_depth: bool,
}
@ -225,8 +227,8 @@ where
fill_framebuffer,
mask_framebuffer,
paint_texture: None,
layer_framebuffer_stack: vec![],
postprocess_source_framebuffer: None,
postprocess_program,
postprocess_vertex_array,
gamma_lut_texture,
@ -245,8 +247,8 @@ where
framebuffer_flags: FramebufferFlags::empty(),
buffered_fills: vec![],
framebuffer_cache: FramebufferCache::new(),
postprocess_options: None,
use_depth: false,
}
}
@ -254,7 +256,6 @@ where
pub fn begin_scene(&mut self) {
self.framebuffer_flags = FramebufferFlags::empty();
self.device.begin_commands();
self.init_postprocessing_framebuffer();
self.stats = RenderStats::default();
}
@ -277,6 +278,8 @@ where
self.upload_mask_tiles(mask_tiles, fill_rule);
self.draw_mask_tiles(count as u32, fill_rule);
}
RenderCommand::PushLayer { effects } => self.push_layer(effects),
RenderCommand::PopLayer => self.pop_layer(),
RenderCommand::DrawSolidTiles(ref solid_tile_vertices) => {
let count = solid_tile_vertices.len() / 4;
self.stats.solid_tile_count += count;
@ -294,10 +297,6 @@ where
}
pub fn end_scene(&mut self) {
if self.postprocess_options.is_some() {
self.postprocess();
}
self.end_composite_timer_query();
self.pending_timers.push_back(mem::replace(&mut self.current_timers, RenderTimers::new()));
@ -360,11 +359,6 @@ where
self.debug_ui_presenter.ui_presenter.set_framebuffer_size(new_framebuffer_size);
}
#[inline]
pub fn set_postprocess_options(&mut self, new_options: Option<PostprocessOptions>) {
self.postprocess_options = new_options;
}
#[inline]
pub fn disable_depth(&mut self) {
self.use_depth = false;
@ -386,17 +380,8 @@ where
}
fn upload_paint_data(&mut self, paint_data: &PaintData) {
// FIXME(pcwalton): This is a hack. We shouldn't be generating paint data at all on the
// renderer side.
let (paint_size, paint_texels): (Vector2I, &[ColorU]);
let dummy_paint = [ColorU::white(); 1];
if self.postprocess_options.is_some() {
paint_size = Vector2I::splat(1);
paint_texels = &dummy_paint;
} else {
paint_size = paint_data.size;
paint_texels = &paint_data.texels;
};
let paint_size = paint_data.size;
let paint_texels = &paint_data.texels;
match self.paint_texture {
Some(ref paint_texture) if self.device.texture_size(paint_texture) == paint_size => {}
@ -675,68 +660,6 @@ where
self.preserve_draw_framebuffer();
}
fn postprocess(&mut self) {
let mut clear_color = None;
if !self.framebuffer_flags
.contains(FramebufferFlags::MUST_PRESERVE_DEST_FRAMEBUFFER_CONTENTS) {
clear_color = self.options.background_color;
}
let postprocess_options = match self.postprocess_options {
None => return,
Some(ref options) => options,
};
let postprocess_source_framebuffer = self.postprocess_source_framebuffer.as_ref().unwrap();
let source_texture = self
.device
.framebuffer_texture(postprocess_source_framebuffer);
let source_texture_size = self.device.texture_size(source_texture);
let main_viewport = self.main_viewport();
let mut uniforms = vec![
(&self.postprocess_program.framebuffer_size_uniform,
UniformData::Vec2(main_viewport.size().to_f32().0)),
(&self.postprocess_program.source_uniform, UniformData::TextureUnit(0)),
(&self.postprocess_program.source_size_uniform,
UniformData::Vec2(source_texture_size.0.to_f32x2())),
(&self.postprocess_program.gamma_lut_uniform, UniformData::TextureUnit(1)),
(&self.postprocess_program.fg_color_uniform,
UniformData::Vec4(postprocess_options.fg_color.0)),
(&self.postprocess_program.bg_color_uniform,
UniformData::Vec4(postprocess_options.bg_color.0)),
(&self.postprocess_program.gamma_correction_enabled_uniform,
UniformData::Int(postprocess_options.gamma_correction as i32)),
];
match postprocess_options.defringing_kernel {
Some(ref kernel) => {
uniforms.push((&self.postprocess_program.kernel_uniform,
UniformData::Vec4(F32x4::from_slice(&kernel.0))));
}
None => {
uniforms.push((&self.postprocess_program.kernel_uniform,
UniformData::Vec4(F32x4::default())));
}
}
self.device.draw_elements(6, &RenderState {
target: &self.dest_render_target(),
program: &self.postprocess_program.program,
vertex_array: &self.postprocess_vertex_array.vertex_array,
primitive: Primitive::Triangles,
textures: &[&source_texture, &self.gamma_lut_texture],
uniforms: &uniforms,
viewport: main_viewport,
options: RenderOptions {
clear_ops: ClearOps { color: clear_color, ..ClearOps::default() },
..RenderOptions::default()
},
});
self.framebuffer_flags.insert(FramebufferFlags::MUST_PRESERVE_DEST_FRAMEBUFFER_CONTENTS);
}
fn draw_stencil(&mut self, quad_positions: &[Vector4F]) {
self.device.allocate_buffer(
&self.stencil_vertex_array.vertex_buffer,
@ -819,52 +742,96 @@ where
}
pub fn draw_render_target(&self) -> RenderTarget<D> {
if self.postprocess_options.is_some() {
RenderTarget::Framebuffer(self.postprocess_source_framebuffer.as_ref().unwrap())
} else {
self.dest_render_target()
}
}
pub fn dest_render_target(&self) -> RenderTarget<D> {
match self.dest_framebuffer {
DestFramebuffer::Default { .. } => RenderTarget::Default,
DestFramebuffer::Other(ref framebuffer) => RenderTarget::Framebuffer(framebuffer),
}
}
fn init_postprocessing_framebuffer(&mut self) {
if !self.postprocess_options.is_some() {
self.postprocess_source_framebuffer = None;
return;
}
let source_framebuffer_size = self.draw_viewport().size();
match self.postprocess_source_framebuffer {
Some(ref framebuffer)
if self
.device
.texture_size(self.device.framebuffer_texture(framebuffer))
== source_framebuffer_size => {}
_ => {
let texture = self
.device
.create_texture(TextureFormat::R8, source_framebuffer_size);
self.postprocess_source_framebuffer =
Some(self.device.create_framebuffer(texture));
match self.layer_framebuffer_stack.last() {
Some(ref layer_framebuffer_info) => {
RenderTarget::Framebuffer(&layer_framebuffer_info.framebuffer)
}
None => {
match self.dest_framebuffer {
DestFramebuffer::Default { .. } => RenderTarget::Default,
DestFramebuffer::Other(ref framebuffer) => {
RenderTarget::Framebuffer(framebuffer)
}
}
}
}
}
fn push_layer(&mut self, effects: PostprocessOptions) {
let main_framebuffer_size = self.main_viewport().size();
let framebuffer_size = if effects.defringing_kernel.is_some() {
main_framebuffer_size.scale_xy(Vector2I::new(3, 1))
} else {
main_framebuffer_size
};
/*
self.device.clear(&RenderTarget::Framebuffer(self.postprocess_source_framebuffer
.as_ref()
.unwrap()),
RectI::new(Vector2I::default(), source_framebuffer_size),
&ClearParams {
color: Some(ColorF::transparent_black()),
..ClearParams::default()
});
*/
let framebuffer = self.framebuffer_cache.create_framebuffer(&mut self.device,
TextureFormat::RGBA8,
framebuffer_size);
self.layer_framebuffer_stack.push(LayerFramebufferInfo {
framebuffer,
effects,
must_preserve_contents: false,
});
}
fn pop_layer(&mut self) {
let layer_framebuffer_info = self.layer_framebuffer_stack
.pop()
.expect("Where's the layer?");
let clear_color = self.clear_color_for_draw_operation();
let postprocess_source_framebuffer = &layer_framebuffer_info.framebuffer;
let source_texture = self
.device
.framebuffer_texture(postprocess_source_framebuffer);
let source_texture_size = self.device.texture_size(source_texture);
let main_viewport = self.main_viewport();
let mut uniforms = vec![
(&self.postprocess_program.framebuffer_size_uniform,
UniformData::Vec2(main_viewport.size().to_f32().0)),
(&self.postprocess_program.source_uniform, UniformData::TextureUnit(0)),
(&self.postprocess_program.source_size_uniform,
UniformData::Vec2(source_texture_size.0.to_f32x2())),
(&self.postprocess_program.gamma_lut_uniform, UniformData::TextureUnit(1)),
(&self.postprocess_program.fg_color_uniform,
UniformData::Vec4(layer_framebuffer_info.effects.fg_color.0)),
(&self.postprocess_program.bg_color_uniform,
UniformData::Vec4(layer_framebuffer_info.effects.bg_color.0)),
(&self.postprocess_program.gamma_correction_enabled_uniform,
UniformData::Int(layer_framebuffer_info.effects.gamma_correction as i32)),
];
match layer_framebuffer_info.effects.defringing_kernel {
Some(ref kernel) => {
uniforms.push((&self.postprocess_program.kernel_uniform,
UniformData::Vec4(F32x4::from_slice(&kernel.0))));
}
None => {
uniforms.push((&self.postprocess_program.kernel_uniform,
UniformData::Vec4(F32x4::default())));
}
}
self.device.draw_elements(6, &RenderState {
target: &self.draw_render_target(),
program: &self.postprocess_program.program,
vertex_array: &self.postprocess_vertex_array.vertex_array,
primitive: Primitive::Triangles,
textures: &[&source_texture, &self.gamma_lut_texture],
uniforms: &uniforms,
viewport: main_viewport,
options: RenderOptions {
clear_ops: ClearOps { color: clear_color, ..ClearOps::default() },
..RenderOptions::default()
},
});
self.preserve_draw_framebuffer();
self.framebuffer_cache.release_framebuffer(layer_framebuffer_info.framebuffer);
}
fn stencil_state(&self) -> Option<StencilState> {
@ -881,16 +848,17 @@ where
}
fn clear_color_for_draw_operation(&mut self) -> Option<ColorF> {
let postprocessing_needed = self.postprocess_options.is_some();
let flag = if postprocessing_needed {
FramebufferFlags::MUST_PRESERVE_POSTPROCESS_FRAMEBUFFER_CONTENTS
} else {
FramebufferFlags::MUST_PRESERVE_DEST_FRAMEBUFFER_CONTENTS
let must_preserve_contents = match self.layer_framebuffer_stack.last() {
Some(ref layer_framebuffer_info) => layer_framebuffer_info.must_preserve_contents,
None => {
self.framebuffer_flags
.contains(FramebufferFlags::MUST_PRESERVE_DEST_FRAMEBUFFER_CONTENTS)
}
};
if self.framebuffer_flags.contains(flag) {
if must_preserve_contents {
None
} else if !postprocessing_needed {
} else if self.layer_framebuffer_stack.is_empty() {
self.options.background_color
} else {
Some(ColorF::default())
@ -898,21 +866,24 @@ where
}
fn preserve_draw_framebuffer(&mut self) {
let flag = if self.postprocess_options.is_some() {
FramebufferFlags::MUST_PRESERVE_POSTPROCESS_FRAMEBUFFER_CONTENTS
} else {
FramebufferFlags::MUST_PRESERVE_DEST_FRAMEBUFFER_CONTENTS
};
self.framebuffer_flags.insert(flag);
match self.layer_framebuffer_stack.last_mut() {
Some(ref mut layer_framebuffer_info) => {
layer_framebuffer_info.must_preserve_contents = true;
}
None => {
self.framebuffer_flags
.insert(FramebufferFlags::MUST_PRESERVE_DEST_FRAMEBUFFER_CONTENTS);
}
}
}
pub fn draw_viewport(&self) -> RectI {
let main_viewport = self.main_viewport();
match self.postprocess_options {
Some(PostprocessOptions { defringing_kernel: Some(_), .. }) => {
RectI::new(Vector2I::default(), main_viewport.size().scale_xy(Vector2I::new(3, 1)))
match self.layer_framebuffer_stack.last() {
Some(ref layer_framebuffer_info) => {
let texture = self.device.framebuffer_texture(&layer_framebuffer_info.framebuffer);
RectI::new(Vector2I::default(), self.device.texture_size(texture))
}
_ => main_viewport,
None => self.main_viewport(),
}
}
@ -953,7 +924,8 @@ where
}
}
#[derive(Clone, Copy, Default)]
// FIXME(pcwalton): Rename to `Effects` and move to `pathfinder_content`, perhaps?
#[derive(Clone, Copy, Default, Debug)]
pub struct PostprocessOptions {
pub fg_color: ColorF,
pub bg_color: ColorF,
@ -1035,7 +1007,46 @@ bitflags! {
struct FramebufferFlags: u8 {
const MUST_PRESERVE_FILL_FRAMEBUFFER_CONTENTS = 0x01;
const MUST_PRESERVE_MASK_FRAMEBUFFER_CONTENTS = 0x02;
const MUST_PRESERVE_POSTPROCESS_FRAMEBUFFER_CONTENTS = 0x04;
const MUST_PRESERVE_DEST_FRAMEBUFFER_CONTENTS = 0x08;
const MUST_PRESERVE_DEST_FRAMEBUFFER_CONTENTS = 0x04;
}
}
struct FramebufferCache<D> where D: Device {
framebuffers: Vec<D::Framebuffer>,
}
impl<D> FramebufferCache<D> where D: Device {
fn new() -> FramebufferCache<D> {
FramebufferCache { framebuffers: vec![] }
}
fn create_framebuffer(&mut self, device: &mut D, format: TextureFormat, size: Vector2I)
-> D::Framebuffer {
for index in 0..self.framebuffers.len() {
{
let texture = device.framebuffer_texture(&self.framebuffers[index]);
if device.texture_size(texture) != size ||
device.texture_format(texture) != format {
continue;
}
}
return self.framebuffers.remove(index);
}
let texture = device.create_texture(format, size);
device.create_framebuffer(texture)
}
fn release_framebuffer(&mut self, framebuffer: D::Framebuffer) {
if self.framebuffers.len() == FRAMEBUFFER_CACHE_SIZE {
self.framebuffers.pop();
}
self.framebuffers.insert(0, framebuffer);
}
}
struct LayerFramebufferInfo<D> where D: Device {
framebuffer: D::Framebuffer,
effects: PostprocessOptions,
must_preserve_contents: bool,
}

View File

@ -10,6 +10,7 @@
//! Packed data ready to be sent to the GPU.
use crate::gpu::renderer::PostprocessOptions;
use crate::options::BoundingQuad;
use pathfinder_color::ColorU;
use pathfinder_content::fill::FillRule;
@ -24,6 +25,8 @@ pub enum RenderCommand {
AddFills(Vec<FillBatchPrimitive>),
FlushFills,
RenderMaskTiles { tiles: Vec<MaskTile>, fill_rule: FillRule },
PushLayer { effects: PostprocessOptions },
PopLayer,
DrawAlphaTiles(Vec<AlphaTile>),
DrawSolidTiles(Vec<SolidTileVertex>),
Finish { build_time: Duration },
@ -125,6 +128,8 @@ impl Debug for RenderCommand {
RenderCommand::RenderMaskTiles { ref tiles, fill_rule } => {
write!(formatter, "RenderMaskTiles(x{}, {:?})", tiles.len(), fill_rule)
}
RenderCommand::PushLayer { .. } => write!(formatter, "PushLayer"),
RenderCommand::PopLayer => write!(formatter, "PopLayer"),
RenderCommand::DrawAlphaTiles(ref tiles) => {
write!(formatter, "DrawAlphaTiles(x{})", tiles.len())
}

View File

@ -12,10 +12,10 @@
use crate::builder::SceneBuilder;
use crate::concurrent::executor::Executor;
use crate::gpu::renderer::PostprocessOptions;
use crate::options::{BuildOptions, PreparedBuildOptions};
use crate::options::{PreparedRenderTransform, RenderCommandListener};
use crate::paint::{Paint, PaintId, PaintInfo, Palette};
use pathfinder_color::ColorU;
use pathfinder_content::fill::FillRule;
use pathfinder_geometry::vector::Vector2F;
use pathfinder_geometry::rect::RectF;
@ -24,6 +24,7 @@ use pathfinder_content::outline::Outline;
#[derive(Clone)]
pub struct Scene {
pub(crate) display_list: Vec<DisplayItem>,
pub(crate) paths: Vec<DrawPath>,
pub(crate) clip_paths: Vec<ClipPath>,
palette: Palette,
@ -35,6 +36,7 @@ impl Scene {
#[inline]
pub fn new() -> Scene {
Scene {
display_list: vec![],
paths: vec![],
clip_paths: vec![],
palette: Palette::new(),
@ -46,6 +48,19 @@ impl Scene {
pub fn push_path(&mut self, path: DrawPath) {
self.bounds = self.bounds.union_rect(path.outline.bounds());
self.paths.push(path);
let new_path_count = self.paths.len() as u32;
if let Some(DisplayItem::DrawPaths {
start_index: _,
ref mut end_index
}) = self.display_list.last_mut() {
*end_index = new_path_count;
} else {
self.display_list.push(DisplayItem::DrawPaths {
start_index: new_path_count - 1,
end_index: new_path_count,
});
}
}
pub fn push_clip_path(&mut self, clip_path: ClipPath) -> ClipPathId {
@ -55,6 +70,14 @@ impl Scene {
clip_path_id
}
pub fn push_layer(&mut self, effects: PostprocessOptions) {
self.display_list.push(DisplayItem::PushLayer { effects });
}
pub fn pop_layer(&mut self) {
self.display_list.push(DisplayItem::PopLayer);
}
#[inline]
pub fn build_paint_info(&self) -> PaintInfo {
self.palette.build_paint_info(self.view_box.size().to_i32())
@ -142,27 +165,6 @@ impl Scene {
outline
}
pub fn monochrome_color(&self) -> Option<ColorU> {
if self.paths.is_empty() {
return None;
}
let first_paint_id = self.paths[0].paint;
if self
.paths
.iter()
.skip(1)
.any(|path_object| path_object.paint != first_paint_id) {
return None;
}
match self.palette.paints[first_paint_id.0 as usize] {
Paint::Color(color) => Some(color),
Paint::Gradient(_) => None,
Paint::Pattern(_) => None,
}
}
#[inline]
pub(crate) fn effective_view_box(&self, render_options: &PreparedBuildOptions) -> RectF {
if render_options.subpixel_aa_enabled {
@ -229,6 +231,13 @@ pub struct ClipPath {
#[derive(Clone, Copy, Debug)]
pub struct ClipPathId(pub u32);
#[derive(Clone, Debug)]
pub enum DisplayItem {
DrawPaths { start_index: u32, end_index: u32 },
PushLayer { effects: PostprocessOptions },
PopLayer,
}
impl DrawPath {
#[inline]
pub fn new(outline: Outline,

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::builder::{BuiltPath, ObjectBuilder, SceneBuilder};
use crate::builder::{BuiltPath, ObjectBuilder, SceneBuilder, SolidTile};
use crate::gpu_data::TileObjectPrimitive;
use crate::paint::PaintMetadata;
use pathfinder_content::fill::FillRule;
@ -174,10 +174,9 @@ impl<'a> Tiler<'a> {
(FillRule::EvenOdd, _) => {}
}
// Next, if this is a solid tile, just poke it into the Z-buffer. We don't need
// to do anything else here.
// Next, if this is a solid tile, record that fact and stop here.
if paint_metadata.is_opaque {
self.scene_builder.z_buffer.update(tile_coords, self.object_index);
self.object_builder.built_path.solid_tiles.push(SolidTile::new(tile_coords));
continue;
}
}

View File

@ -10,6 +10,7 @@
//! Software occlusion culling.
use crate::builder::SolidTile;
use crate::gpu_data::SolidTileVertex;
use crate::paint::PaintMetadata;
use crate::scene::DrawPath;
@ -17,62 +18,43 @@ use crate::tile_map::DenseTileMap;
use crate::tiles;
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::vector::Vector2I;
use std::ops::Range;
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
pub struct ZBuffer {
buffer: DenseTileMap<AtomicUsize>,
pub(crate) struct ZBuffer {
buffer: DenseTileMap<u32>,
}
impl ZBuffer {
pub fn new(view_box: RectF) -> ZBuffer {
pub(crate) fn new(view_box: RectF) -> ZBuffer {
let tile_rect = tiles::round_rect_out_to_tile_bounds(view_box);
ZBuffer {
buffer: DenseTileMap::from_builder(|_| AtomicUsize::new(0), tile_rect),
buffer: DenseTileMap::from_builder(|_| 0, tile_rect),
}
}
pub fn test(&self, coords: Vector2I, object_index: u32) -> bool {
pub(crate) fn test(&self, coords: Vector2I, object_index: u32) -> bool {
let tile_index = self.buffer.coords_to_index_unchecked(coords);
let existing_depth = self.buffer.data[tile_index as usize].load(AtomicOrdering::SeqCst);
existing_depth < object_index as usize + 1
self.buffer.data[tile_index as usize] < object_index + 1
}
pub fn update(&self, coords: Vector2I, object_index: u16) {
let tile_index = self.buffer.coords_to_index_unchecked(coords);
let mut old_depth = self.buffer.data[tile_index].load(AtomicOrdering::SeqCst);
let new_depth = (object_index + 1) as usize;
while old_depth < new_depth {
let prev_depth = self.buffer.data[tile_index].compare_and_swap(
old_depth,
new_depth,
AtomicOrdering::SeqCst,
);
if prev_depth == old_depth {
// Successfully written.
return;
}
old_depth = prev_depth;
pub(crate) fn update(&mut self, solid_tiles: &[SolidTile], object_index: u32) {
for solid_tile in solid_tiles {
let tile_index = self.buffer.coords_to_index_unchecked(solid_tile.coords);
let z_dest = &mut self.buffer.data[tile_index as usize];
*z_dest = u32::max(*z_dest, object_index + 1);
}
}
pub fn build_solid_tiles(&self,
paths: &[DrawPath],
paint_metadata: &[PaintMetadata],
object_range: Range<u32>)
-> Vec<SolidTileVertex> {
pub(crate) fn build_solid_tiles(&self, paths: &[DrawPath], paint_metadata: &[PaintMetadata])
-> Vec<SolidTileVertex> {
let mut solid_tiles = vec![];
for tile_index in 0..self.buffer.data.len() {
let depth = self.buffer.data[tile_index].load(AtomicOrdering::Relaxed);
let depth = self.buffer.data[tile_index];
if depth == 0 {
continue;
}
let tile_coords = self.buffer.index_to_coords(tile_index);
let object_index = (depth - 1) as u32;
if object_index < object_range.start || object_index >= object_range.end {
continue;
}
let paint_id = paths[object_index as usize].paint();
let paint_metadata = &paint_metadata[paint_id.0 as usize];

View File

@ -27,7 +27,6 @@ use pathfinder_geometry::vector::Vector2F;
use pathfinder_renderer::paint::Paint;
use pathfinder_renderer::scene::{ClipPath, ClipPathId, DrawPath, Scene};
use std::fmt::{Display, Formatter, Result as FormatResult};
use std::mem;
use usvg::{Color as SvgColor, FillRule as UsvgFillRule, LineCap as UsvgLineCap};
use usvg::{LineJoin as UsvgLineJoin, Node, NodeExt, NodeKind, Opacity, Paint as UsvgPaint};
use usvg::{PathSegment as UsvgPathSegment, Rect as UsvgRect, Transform as UsvgTransform};
@ -65,11 +64,17 @@ bitflags! {
impl BuiltSVG {
// TODO(pcwalton): Allow a global transform to be set.
pub fn from_tree(tree: Tree) -> BuiltSVG {
#[inline]
pub fn from_tree(tree: &Tree) -> BuiltSVG {
BuiltSVG::from_tree_and_scene(tree, Scene::new())
}
// TODO(pcwalton): Allow a global transform to be set.
pub fn from_tree_and_scene(tree: &Tree, scene: Scene) -> BuiltSVG {
// TODO(pcwalton): Maybe have a `SVGBuilder` type to hold the clip path IDs and other
// transient data separate from `BuiltSVG`?
let mut built_svg = BuiltSVG {
scene: Scene::new(),
scene,
result_flags: BuildResultFlags::empty(),
clip_paths: HashMap::new(),
};
@ -83,11 +88,7 @@ impl BuiltSVG {
}
}
_ => unreachable!(),
};
// FIXME(pcwalton): This is needed to avoid stack exhaustion in debug builds when
// recursively dropping reference counts on very large SVGs. :(
mem::forget(tree);
}
built_svg
}