From eb2b62261553a1154d9c97ce9cb0f681a1de1659 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 2 Apr 2020 22:03:42 -0700 Subject: [PATCH] Remove `draw_render_target()` in favor of the standard tile-based rendering path --- canvas/src/lib.rs | 57 +++++++++------ content/src/effects.rs | 20 ++---- content/src/outline.rs | 19 +++++ content/src/pattern.rs | 86 +++++++++++++++++++++-- demo/common/src/lib.rs | 101 ++++++++++++++++----------- examples/canvas_nanovg/src/main.rs | 44 +++++------- renderer/src/builder.rs | 79 +++++---------------- renderer/src/gpu/renderer.rs | 18 +++-- renderer/src/gpu_data.rs | 4 +- renderer/src/paint.rs | 108 ++++++++++++++++------------- renderer/src/scene.rs | 47 ++++--------- renderer/src/z_buffer.rs | 17 +---- 12 files changed, 322 insertions(+), 278 deletions(-) diff --git a/canvas/src/lib.rs b/canvas/src/lib.rs index 09bb4d67..e4ee181e 100644 --- a/canvas/src/lib.rs +++ b/canvas/src/lib.rs @@ -12,18 +12,18 @@ use pathfinder_color::ColorU; use pathfinder_content::dash::OutlineDash; -use pathfinder_content::effects::{BlendMode, BlurDirection, Effects, Filter}; +use pathfinder_content::effects::{BlendMode, BlurDirection, PatternFilter}; use pathfinder_content::fill::FillRule; use pathfinder_content::gradient::Gradient; use pathfinder_content::outline::{ArcDirection, Contour, Outline}; -use pathfinder_content::pattern::{Pattern, PatternFlags, PatternSource}; +use pathfinder_content::pattern::Pattern; use pathfinder_content::render_target::RenderTargetId; use pathfinder_content::stroke::{LineCap, LineJoin as StrokeLineJoin}; use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle}; use pathfinder_geometry::line_segment::LineSegment2F; -use pathfinder_geometry::rect::RectF; +use pathfinder_geometry::rect::{RectF, RectI}; use pathfinder_geometry::transform2d::Transform2F; -use pathfinder_geometry::vector::{IntoVector2F, Vector2F, vec2f}; +use pathfinder_geometry::vector::{IntoVector2F, Vector2F, Vector2I, vec2f, vec2i}; use pathfinder_renderer::paint::{Paint, PaintId}; use pathfinder_renderer::scene::{ClipPath, ClipPathId, DrawPath, RenderTarget, Scene}; use std::borrow::Cow; @@ -101,6 +101,11 @@ impl Canvas { font_context, } } + + #[inline] + pub fn size(&self) -> Vector2I { + self.scene.view_box().size().ceil().to_i32() + } } pub struct CanvasRenderingContext2D { @@ -340,7 +345,7 @@ impl CanvasRenderingContext2D { return None; } - let render_target_size = self.canvas.scene.view_box().size().ceil().to_i32(); + let render_target_size = self.canvas.size(); let render_target_a = RenderTarget::new(render_target_size, String::new()); let render_target_id_a = self.canvas.scene.push_render_target(render_target_a); let render_target_b = RenderTarget::new(render_target_size, String::new()); @@ -357,16 +362,24 @@ impl CanvasRenderingContext2D { }; let sigma = self.current_state.shadow_blur * 0.5; + + let mut paint_x = Pattern::from_render_target(render_target_ids[1], self.canvas.size()); + let mut paint_y = Pattern::from_render_target(render_target_ids[0], self.canvas.size()); + paint_x.set_filter(Some(PatternFilter::Blur { direction: BlurDirection::X, sigma })); + paint_y.set_filter(Some(PatternFilter::Blur { direction: BlurDirection::Y, sigma })); + + let paint_id_x = self.canvas.scene.push_paint(&Paint::Pattern(paint_x)); + let paint_id_y = self.canvas.scene.push_paint(&Paint::Pattern(paint_y)); + + // TODO(pcwalton): Apply clip as necessary. + let outline = Outline::from_rect(RectI::new(vec2i(0, 0), self.canvas.size()).to_f32()); + let path_x = DrawPath::new(outline.clone(), paint_id_x); + let path_y = DrawPath::new(outline.clone(), paint_id_y); + self.canvas.scene.pop_render_target(); - self.canvas.scene.draw_render_target(render_target_ids[1], Effects::new(Filter::Blur { - direction: BlurDirection::X, - sigma, - })); + self.canvas.scene.push_path(path_x); self.canvas.scene.pop_render_target(); - self.canvas.scene.draw_render_target(render_target_ids[0], Effects::new(Filter::Blur { - direction: BlurDirection::Y, - sigma, - })); + self.canvas.scene.push_path(path_y); } // Transformations @@ -487,17 +500,16 @@ impl CanvasRenderingContext2D { pub fn create_pattern_from_canvas(&mut self, canvas: Canvas, transform: Transform2F) -> Pattern { + let subscene_size = canvas.size(); let subscene = canvas.into_scene(); - let subscene_size = subscene.view_box().size().ceil().to_i32(); let render_target = RenderTarget::new(subscene_size, String::new()); let render_target_id = self.canvas.scene.push_render_target(render_target); self.canvas.scene.append_scene(subscene); self.canvas.scene.pop_render_target(); - let pattern_source = PatternSource::RenderTarget { - id: render_target_id, - size: subscene_size, - }; - Pattern::new(pattern_source, transform, PatternFlags::empty()) + + let mut pattern = Pattern::from_render_target(render_target_id, subscene_size); + pattern.apply_transform(transform); + pattern } } @@ -557,8 +569,7 @@ impl State { let mut must_copy = !self.transform.is_identity(); if !must_copy { if let Paint::Pattern(ref pattern) = *paint { - must_copy = !self.image_smoothing_enabled != - pattern.flags.contains(PatternFlags::NO_SMOOTHING); + must_copy = self.image_smoothing_enabled != pattern.smoothing_enabled() } } @@ -569,7 +580,7 @@ impl State { let mut paint = (*paint).clone(); paint.apply_transform(&self.transform); if let Paint::Pattern(ref mut pattern) = paint { - pattern.flags.set(PatternFlags::NO_SMOOTHING, !self.image_smoothing_enabled); + pattern.set_smoothing_enabled(self.image_smoothing_enabled); } Cow::Owned(paint) } @@ -833,7 +844,7 @@ pub trait CanvasImageDestLocation { impl CanvasImageSource for Pattern { #[inline] fn to_pattern(mut self, _: &mut CanvasRenderingContext2D, transform: Transform2F) -> Pattern { - self.transform(transform); + self.apply_transform(transform); self } } diff --git a/content/src/effects.rs b/content/src/effects.rs index c2a695d1..e5db8f43 100644 --- a/content/src/effects.rs +++ b/content/src/effects.rs @@ -31,13 +31,6 @@ pub const MAX_STEM_DARKENING_AMOUNT: [f32; 2] = [0.3, 0.3]; /// This value is a subjective cutoff. Above this ppem value, no stem darkening is performed. pub const MAX_STEM_DARKENING_PIXELS_PER_EM: f32 = 72.0; -/// Effects that can be applied to a layer. -#[derive(Clone, Copy, Debug, PartialEq, Default)] -pub struct Effects { - /// The shader that should be used when compositing this layer onto its destination. - pub filter: Filter, -} - /// The shader that should be used when compositing this layer onto its destination. #[derive(Clone, Copy, PartialEq, Debug)] pub enum Filter { @@ -54,6 +47,12 @@ pub enum Filter { uv_origin: Vector2F, }, + PatternFilter(PatternFilter), +} + +/// Shaders applicable to patterns. +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum PatternFilter { /// Performs postprocessing operations useful for monochrome text. Text { /// The foreground color of the text. @@ -136,13 +135,6 @@ impl Default for Filter { } } -impl Effects { - #[inline] - pub fn new(filter: Filter) -> Effects { - Effects { filter } - } -} - impl BlendMode { /// Whether the backdrop is irrelevant when applying this blend mode (i.e. destination blend /// factor is zero when source alpha is one). diff --git a/content/src/outline.rs b/content/src/outline.rs index 69813fed..7a062ea0 100644 --- a/content/src/outline.rs +++ b/content/src/outline.rs @@ -110,6 +110,13 @@ impl Outline { outline } + #[inline] + pub fn from_rect(rect: RectF) -> Outline { + let mut outline = Outline::new(); + outline.push_contour(Contour::from_rect(rect)); + outline + } + #[inline] pub fn bounds(&self) -> RectF { self.bounds @@ -266,6 +273,18 @@ impl Contour { } } + #[inline] + pub fn from_rect(rect: RectF) -> Contour { + let mut contour = Contour::new(); + contour.push_point(rect.origin(), PointFlags::empty(), false); + contour.push_point(rect.upper_right(), PointFlags::empty(), false); + contour.push_point(rect.lower_right(), PointFlags::empty(), false); + contour.push_point(rect.lower_left(), PointFlags::empty(), false); + contour.close(); + contour.bounds = rect; + contour + } + // Replaces this contour with a new one, with arrays preallocated to match `self`. #[inline] pub(crate) fn take(&mut self) -> Contour { diff --git a/content/src/pattern.rs b/content/src/pattern.rs index 2faa4444..cdd34205 100644 --- a/content/src/pattern.rs +++ b/content/src/pattern.rs @@ -10,6 +10,7 @@ //! Raster image patterns. +use crate::effects::PatternFilter; use crate::render_target::RenderTargetId; use crate::util; use pathfinder_color::{self as color, ColorU}; @@ -26,9 +27,10 @@ use image::RgbaImage; /// A raster image pattern. #[derive(Clone, PartialEq, Debug)] pub struct Pattern { - pub source: PatternSource, - pub transform: Transform2F, - pub flags: PatternFlags, + source: PatternSource, + transform: Transform2F, + filter: Option, + flags: PatternFlags, } #[derive(Clone, PartialEq, Eq, Hash, Debug)] @@ -61,13 +63,33 @@ bitflags! { impl Pattern { #[inline] - pub fn new(source: PatternSource, transform: Transform2F, flags: PatternFlags) -> Pattern { - Pattern { source, transform, flags } + fn from_source(source: PatternSource) -> Pattern { + Pattern { + source, + transform: Transform2F::default(), + filter: None, + flags: PatternFlags::empty(), + } } #[inline] - pub fn transform(&mut self, transform: Transform2F) { - self.transform *= transform + pub fn from_image(image: Image) -> Pattern { + Pattern::from_source(PatternSource::Image(image)) + } + + #[inline] + pub fn from_render_target(id: RenderTargetId, size: Vector2I) -> Pattern { + Pattern::from_source(PatternSource::RenderTarget { id, size }) + } + + #[inline] + pub fn transform(&self) -> Transform2F { + self.transform + } + + #[inline] + pub fn apply_transform(&mut self, transform: Transform2F) { + self.transform = transform * self.transform; } #[inline] @@ -77,6 +99,56 @@ impl Pattern { PatternSource::RenderTarget { size, .. } => size, } } + + #[inline] + pub fn filter(&self) -> Option { + self.filter + } + + #[inline] + pub fn set_filter(&mut self, filter: Option) { + self.filter = filter; + } + + #[inline] + pub fn repeat_x(&self) -> bool { + self.flags.contains(PatternFlags::REPEAT_X) + } + + #[inline] + pub fn set_repeat_x(&mut self, repeat_x: bool) { + self.flags.set(PatternFlags::REPEAT_X, repeat_x); + } + + #[inline] + pub fn repeat_y(&self) -> bool { + self.flags.contains(PatternFlags::REPEAT_Y) + } + + #[inline] + pub fn set_repeat_y(&mut self, repeat_y: bool) { + self.flags.set(PatternFlags::REPEAT_Y, repeat_y); + } + + #[inline] + pub fn smoothing_enabled(&self) -> bool { + !self.flags.contains(PatternFlags::NO_SMOOTHING) + } + + #[inline] + pub fn set_smoothing_enabled(&mut self, enable: bool) { + self.flags.set(PatternFlags::NO_SMOOTHING, !enable); + } + + #[inline] + pub fn is_opaque(&self) -> bool { + self.source.is_opaque() + } + + #[inline] + pub fn source(&self) -> &PatternSource { + &self.source + } } impl Image { diff --git a/demo/common/src/lib.rs b/demo/common/src/lib.rs index d230c347..41e5a30a 100644 --- a/demo/common/src/lib.rs +++ b/demo/common/src/lib.rs @@ -22,10 +22,14 @@ 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_content::effects::{DEFRINGING_KERNEL_CORE_GRAPHICS, Effects}; -use pathfinder_content::effects::{Filter, STEM_DARKENING_FACTORS}; +use pathfinder_content::effects::DEFRINGING_KERNEL_CORE_GRAPHICS; +use pathfinder_content::effects::PatternFilter; +use pathfinder_content::effects::STEM_DARKENING_FACTORS; +use pathfinder_content::outline::Outline; +use pathfinder_content::pattern::Pattern; +use pathfinder_content::render_target::RenderTargetId; use pathfinder_export::{Export, FileFormat}; -use pathfinder_geometry::rect::RectF; +use pathfinder_geometry::rect::{RectF, RectI}; use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform3d::Transform4F; use pathfinder_geometry::vector::{Vector2F, Vector2I, Vector4F, vec2f, vec2i}; @@ -34,7 +38,8 @@ use pathfinder_renderer::concurrent::scene_proxy::{RenderCommandStream, ScenePro use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions}; use pathfinder_renderer::gpu::renderer::{RenderStats, RenderTime, Renderer}; use pathfinder_renderer::options::{BuildOptions, RenderTransform}; -use pathfinder_renderer::scene::{RenderTarget, Scene}; +use pathfinder_renderer::paint::Paint; +use pathfinder_renderer::scene::{DrawPath, RenderTarget, Scene}; use pathfinder_resources::ResourceLoader; use pathfinder_svg::BuiltSVG; use pathfinder_ui::{MousePosition, UIEvent}; @@ -136,13 +141,13 @@ impl DemoApp where W: Window { let mut ui_model = DemoUIModel::new(&options); let render_options = RendererOptions { background_color: None }; - let effects = build_effects(&ui_model); + let filter = build_filter(&ui_model); let viewport = window.viewport(options.mode.view(0)); let (mut built_svg, svg_tree) = load_scene(resources, &options.input_path, viewport.size(), - effects); + filter); let message = get_svg_building_message(&built_svg); @@ -420,11 +425,11 @@ impl DemoApp where W: Window { Event::OpenSVG(ref svg_path) => { let viewport = self.window.viewport(self.ui_model.mode.view(0)); - let effects = build_effects(&self.ui_model); + let filter = build_filter(&self.ui_model); let (mut built_svg, svg_tree) = load_scene(self.window.resource_loader(), svg_path, viewport.size(), - effects); + filter); self.ui_model.message = get_svg_building_message(&built_svg); @@ -578,8 +583,8 @@ impl DemoApp where W: Window { UIAction::ModelChanged => self.dirty = true, UIAction::EffectsChanged => { let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size(); - let effects = build_effects(&self.ui_model); - let mut built_svg = build_svg_tree(&self.svg_tree, viewport_size, effects); + let filter = build_filter(&self.ui_model); + let mut built_svg = build_svg_tree(&self.svg_tree, viewport_size, filter); self.scene_metadata = SceneMetadata::new_clipping_view_box(&mut built_svg.scene, viewport_size); self.scene_proxy.replace_scene(built_svg.scene); @@ -737,7 +742,7 @@ pub enum UIVisibility { fn load_scene(resource_loader: &dyn ResourceLoader, input_path: &SVGPath, viewport_size: Vector2I, - effects: Option) + filter: Option) -> (BuiltSVG, Tree) { let mut data; match *input_path { @@ -750,33 +755,47 @@ fn load_scene(resource_loader: &dyn ResourceLoader, }; let tree = Tree::from_data(&data, &UsvgOptions::default()).expect("Failed to parse the SVG!"); - let built_svg = build_svg_tree(&tree, viewport_size, effects); + let built_svg = build_svg_tree(&tree, viewport_size, filter); (built_svg, tree) } -fn build_svg_tree(tree: &Tree, viewport_size: Vector2I, effects: Option) -> BuiltSVG { +// FIXME(pcwalton): Rework how transforms work in the demo. The transform affects the final +// composite steps, breaking this approach. +fn build_svg_tree(tree: &Tree, viewport_size: Vector2I, filter: Option) + -> BuiltSVG { let mut scene = Scene::new(); - - let render_target_id = match effects { - None => None, - Some(effects) => { - let scale = match effects.filter { - Filter::Text { defringing_kernel: Some(_), .. } => vec2i(3, 1), - _ => vec2i(1, 1), - }; - let name = "Text".to_owned(); - let render_target = RenderTarget::new(viewport_size * scale, name); - Some(scene.push_render_target(render_target)) - } - }; + let filter_info = filter.map(|filter| { + let scale = match filter { + PatternFilter::Text { defringing_kernel: Some(_), .. } => vec2i(3, 1), + _ => vec2i(1, 1), + }; + let name = "Text".to_owned(); + let render_target_size = viewport_size * scale; + let render_target = RenderTarget::new(render_target_size, name); + let render_target_id = scene.push_render_target(render_target); + FilterInfo { filter, render_target_id, render_target_size } + }); let mut built_svg = BuiltSVG::from_tree_and_scene(&tree, scene); + if let Some(FilterInfo { filter, render_target_id, render_target_size }) = filter_info { + let mut paint = Pattern::from_render_target(render_target_id, render_target_size); + paint.set_filter(Some(filter)); + let paint_id = built_svg.scene.push_paint(&Paint::Pattern(paint)); + + let outline = Outline::from_rect(RectI::new(vec2i(0, 0), viewport_size).to_f32()); + let path = DrawPath::new(outline, paint_id); - if let (Some(render_target_id), Some(effects)) = (render_target_id, effects) { built_svg.scene.pop_render_target(); - built_svg.scene.draw_render_target(render_target_id, effects); + built_svg.scene.push_path(path); + } + + return built_svg; + + struct FilterInfo { + filter: PatternFilter, + render_target_id: RenderTargetId, + render_target_size: Vector2I, } - built_svg } fn center_of_window(window_size: &WindowSize) -> Vector2F { @@ -863,22 +882,20 @@ impl SceneMetadata { } } -fn build_effects(ui_model: &DemoUIModel) -> Option { +fn build_filter(ui_model: &DemoUIModel) -> Option { if !ui_model.gamma_correction_effect_enabled && !ui_model.subpixel_aa_effect_enabled { return None; } - Some(Effects { - filter: Filter::Text { - 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 - } - }, + Some(PatternFilter::Text { + 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 + } }) } diff --git a/examples/canvas_nanovg/src/main.rs b/examples/canvas_nanovg/src/main.rs index d2621dca..4e27b4c1 100644 --- a/examples/canvas_nanovg/src/main.rs +++ b/examples/canvas_nanovg/src/main.rs @@ -18,7 +18,7 @@ use pathfinder_color::{ColorF, ColorU, rgbau, rgbf, rgbu}; use pathfinder_content::fill::FillRule; use pathfinder_content::gradient::Gradient; use pathfinder_content::outline::ArcDirection; -use pathfinder_content::pattern::{Image, Pattern, PatternFlags, PatternSource}; +use pathfinder_content::pattern::{Image, Pattern}; use pathfinder_content::stroke::LineCap; use pathfinder_geometry::angle; use pathfinder_geometry::line_segment::LineSegment2F; @@ -537,21 +537,11 @@ fn draw_window(context: &mut CanvasRenderingContext2D, title: &str, rect: RectF) // Draw window with shadow. context.set_fill_style(rgbau(28, 30, 34, 192)); + context.set_shadow_blur(10.0); + context.set_shadow_offset(vec2f(0.0, 2.0)); + context.set_shadow_color(rgbau(0, 0, 0, 128)); context.fill_path(create_rounded_rect_path(rect, CORNER_RADIUS), FillRule::Winding); - - // Draw window drop shadow. - let mut path = - create_rounded_rect_path(RectF::new(rect.origin() - 10.0, rect.size() + vec2f(20.0, 30.0)), - CORNER_RADIUS); - path.rect(rect); - fill_path_with_box_gradient(context, - path, - FillRule::EvenOdd, - rect + vec2f(0.0, 2.0), - CORNER_RADIUS * 2.0, - 10.0, - rgbau(0, 0, 0, 128), - rgbau(0, 0, 0, 0)); + context.set_shadow_color(rgbau(0, 0, 0, 0)); // Header. let mut header_gradient = @@ -585,21 +575,22 @@ fn draw_window(context: &mut CanvasRenderingContext2D, title: &str, rect: RectF) fn draw_search_box(context: &mut CanvasRenderingContext2D, text: &str, rect: RectF) { let corner_radius = rect.height() * 0.5 - 1.0; - fill_path_with_box_gradient(context, - create_rounded_rect_path(rect, corner_radius), - FillRule::Winding, - rect + vec2f(0.0, 1.5), - rect.height() * 0.5, - 5.0, - rgbau(0, 0, 0, 16), - rgbau(0, 0, 0, 92)); + let path = create_rounded_rect_path(rect, corner_radius); + context.save(); + context.clip_path(path.clone(), FillRule::Winding); + context.set_shadow_offset(vec2f(0.0, 1.5)); + context.set_shadow_blur(5.0); + context.set_shadow_color(rgbau(0, 0, 0, 92)); + context.set_fill_style(rgbau(0, 0, 0, 16)); + context.fill_path(path, FillRule::Winding); + context.restore(); context.set_font_size(rect.height() * 0.5); context.set_font(FONT_NAME_EMOJI); context.set_fill_style(rgbau(255, 255, 255, 64)); context.set_text_align(TextAlign::Center); context.set_text_baseline(TextBaseline::Middle); - context.fill_text("🔍", rect.origin() + (rect.height() * 0.55)); + context.fill_text("🔍", rect.origin() + Vector2F::splat(rect.height() * 0.55)); context.set_font(FONT_NAME_REGULAR); context.set_font_size(17.0); @@ -908,9 +899,8 @@ fn draw_thumbnails(context: &mut CanvasRenderingContext2D, (image_index % IMAGES_ACROSS) as i32, (image_index / IMAGES_ACROSS) as i32).to_f32() * THUMB_HEIGHT) * Transform2F::from_scale(0.5); - let pattern = Pattern::new(PatternSource::Image((*image).clone()), - pattern_transform, - PatternFlags::empty()); + let mut pattern = Pattern::from_image((*image).clone()); + pattern.apply_transform(pattern_transform); context.set_fill_style(pattern); context.set_global_alpha(alpha); context.fill_path(image_path, FillRule::Winding); diff --git a/renderer/src/builder.rs b/renderer/src/builder.rs index 74a35e32..e655f651 100644 --- a/renderer/src/builder.rs +++ b/renderer/src/builder.rs @@ -15,13 +15,13 @@ use crate::gpu::renderer::{BlendModeExt, MASK_TILES_ACROSS, MASK_TILES_DOWN}; use crate::gpu_data::{FillBatchPrimitive, RenderCommand, TexturePageId, Tile, TileBatch}; use crate::gpu_data::{TileBatchTexture, TileObjectPrimitive, TileVertex}; use crate::options::{PreparedBuildOptions, PreparedRenderTransform, RenderCommandListener}; -use crate::paint::{PaintInfo, PaintMetadata, RenderTargetMetadata}; +use crate::paint::{PaintInfo, PaintMetadata}; use crate::scene::{DisplayItem, Scene}; use crate::tile_map::DenseTileMap; use crate::tiles::{self, DrawTilingPathInfo, PackedTile, TILE_HEIGHT, TILE_WIDTH}; use crate::tiles::{Tiler, TilingPathInfo}; use crate::z_buffer::{DepthMetadata, ZBuffer}; -use pathfinder_content::effects::{BlendMode, Effects}; +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}; @@ -38,9 +38,7 @@ use std::u16; pub(crate) struct SceneBuilder<'a> { scene: &'a Scene, built_options: &'a PreparedBuildOptions, - next_alpha_tile_index: AtomicUsize, - pub(crate) listener: Box, } @@ -55,7 +53,7 @@ pub(crate) struct ObjectBuilder { struct BuiltDrawPath { path: BuiltPath, blend_mode: BlendMode, - effects: Effects, + filter: Filter, color_texture_page_0: TexturePageId, color_texture_page_1: TexturePageId, sampling_flags_0: TextureSamplingFlags, @@ -126,9 +124,9 @@ impl<'a> SceneBuilder<'a> { let PaintInfo { render_commands, paint_metadata, - render_target_metadata, opacity_tile_page, opacity_tile_transform, + render_target_metadata: _ } = self.scene.build_paint_info(render_transform); for render_command in render_commands { self.listener.send(render_command); @@ -160,7 +158,7 @@ impl<'a> SceneBuilder<'a> { }) }); - self.finish_building(&paint_metadata, &render_target_metadata, built_draw_paths); + self.finish_building(&paint_metadata, built_draw_paths); let build_time = Instant::now() - start_time; self.listener.send(RenderCommand::Finish { build_time }); @@ -220,7 +218,7 @@ impl<'a> SceneBuilder<'a> { BuiltDrawPath { path: tiler.object_builder.built_path, blend_mode: path_object.blend_mode(), - effects: paint_metadata.effects(), + filter: paint_metadata.filter(), color_texture_page_0: paint_metadata.location.page, sampling_flags_0: paint_metadata.sampling_flags, color_texture_page_1: opacity_tile_page, @@ -230,10 +228,7 @@ impl<'a> SceneBuilder<'a> { } } - fn cull_tiles(&self, - paint_metadata: &[PaintMetadata], - render_target_metadata: &[RenderTargetMetadata], - built_draw_paths: Vec) + fn cull_tiles(&self, paint_metadata: &[PaintMetadata], built_draw_paths: Vec) -> CulledTiles { let mut culled_tiles = CulledTiles { display_list: vec![] }; @@ -269,40 +264,6 @@ impl<'a> SceneBuilder<'a> { layer_z_buffers_stack.pop(); } - DisplayItem::DrawRenderTarget { render_target, effects } => { - let effective_view_box = self.scene.effective_view_box(self.built_options); - let tile_rect = tiles::round_rect_out_to_tile_bounds(effective_view_box); - let layer_z_buffer = layer_z_buffers_stack.last().unwrap(); - let mut tiles = vec![]; - let uv_scale = vec2f(1.0, 1.0) / tile_rect.lower_right().to_f32(); - let metadata = &render_target_metadata[render_target.0 as usize]; - for tile_y in tile_rect.min_y()..tile_rect.max_y() { - for tile_x in tile_rect.min_x()..tile_rect.max_x() { - let tile_coords = vec2i(tile_x, tile_y); - if !layer_z_buffer.test(tile_coords, current_depth) { - continue; - } - - let uv_rect = RectI::new(tile_coords, vec2i(1, 1)).to_f32() * uv_scale; - tiles.push(Tile::new_solid_from_texture_rect(tile_coords, uv_rect)); - } - } - let batch = TileBatch { - tiles, - color_texture_0: Some(TileBatchTexture { - page: metadata.location.page, - sampling_flags: TextureSamplingFlags::empty(), - }), - color_texture_1: None, - effects, - blend_mode: BlendMode::SrcOver, - mask_0_fill_rule: None, - mask_1_fill_rule: None, - }; - culled_tiles.display_list.push(CulledDisplayItem::DrawTiles(batch)); - current_depth += 1; - } - DisplayItem::DrawPaths { start_index: start_draw_path_index, end_index: end_draw_path_index, @@ -328,7 +289,7 @@ impl<'a> SceneBuilder<'a> { None, None, built_draw_path.blend_mode, - built_draw_path.effects, + built_draw_path.filter, None, None); @@ -339,7 +300,7 @@ impl<'a> SceneBuilder<'a> { color_texture_0, color_texture_1, built_draw_path.blend_mode, - built_draw_path.effects, + built_draw_path.filter, Some(built_draw_path.mask_0_fill_rule), None); @@ -351,7 +312,7 @@ impl<'a> SceneBuilder<'a> { color_texture_0, color_texture_1, built_draw_path.blend_mode, - built_draw_path.effects, + built_draw_path.filter, Some(built_draw_path.mask_0_fill_rule), Some(mask_1_fill_rule)); } @@ -365,7 +326,7 @@ impl<'a> SceneBuilder<'a> { color_texture_0, color_texture_1, built_draw_path.blend_mode, - built_draw_path.effects, + built_draw_path.filter, None, None); } @@ -416,10 +377,6 @@ impl<'a> SceneBuilder<'a> { current_depth += 1; } } - DisplayItem::DrawRenderTarget { .. } => { - // FIXME(pcwalton): Not great that this doesn't participate in Z-buffering! - current_depth += 1; - } } } debug_assert_eq!(z_buffer_index_stack.len(), 1); @@ -435,7 +392,7 @@ impl<'a> SceneBuilder<'a> { color_texture_0: Option, color_texture_1: Option, blend_mode: BlendMode, - effects: Effects, + filter: Filter, mask_0_fill_rule: Option, mask_1_fill_rule: Option) { if alpha_tiles.is_empty() { @@ -454,13 +411,13 @@ impl<'a> SceneBuilder<'a> { color_texture_0: ref batch_color_texture_0, color_texture_1: ref batch_color_texture_1, blend_mode: batch_blend_mode, - effects: batch_effects, + filter: batch_filter, mask_0_fill_rule: batch_mask_0_fill_rule, mask_1_fill_rule: batch_mask_1_fill_rule, })) if *batch_color_texture_0 == color_texture_0 && *batch_color_texture_1 == color_texture_1 && batch_blend_mode == blend_mode && - batch_effects == effects && + batch_filter == filter && batch_mask_0_fill_rule == mask_0_fill_rule && batch_mask_1_fill_rule == mask_1_fill_rule && !batch_blend_mode.needs_readable_framebuffer() => {} @@ -470,7 +427,7 @@ impl<'a> SceneBuilder<'a> { color_texture_0, color_texture_1, blend_mode, - effects, + filter, mask_0_fill_rule, mask_1_fill_rule, }; @@ -512,12 +469,9 @@ impl<'a> SceneBuilder<'a> { fn finish_building(&mut self, paint_metadata: &[PaintMetadata], - render_target_metadata: &[RenderTargetMetadata], built_draw_paths: Vec) { self.listener.send(RenderCommand::FlushFills); - let culled_tiles = self.cull_tiles(paint_metadata, - render_target_metadata, - built_draw_paths); + let culled_tiles = self.cull_tiles(paint_metadata, built_draw_paths); self.pack_tiles(culled_tiles); } @@ -525,7 +479,6 @@ impl<'a> SceneBuilder<'a> { let mut framebuffer_nesting = 0; for display_item in &self.scene.display_list { match *display_item { - DisplayItem::DrawRenderTarget { .. } => {} DisplayItem::PushRenderTarget(_) => framebuffer_nesting += 1, DisplayItem::PopRenderTarget => framebuffer_nesting -= 1, DisplayItem::DrawPaths { start_index, end_index } => { diff --git a/renderer/src/gpu/renderer.rs b/renderer/src/gpu/renderer.rs index 48c7e1bd..faf82c63 100644 --- a/renderer/src/gpu/renderer.rs +++ b/renderer/src/gpu/renderer.rs @@ -19,7 +19,8 @@ use crate::gpu_data::{TexturePageId, Tile, TileBatchTexture}; use crate::options::BoundingQuad; use crate::tiles::{TILE_HEIGHT, TILE_WIDTH}; use pathfinder_color::{self as color, ColorF, ColorU}; -use pathfinder_content::effects::{BlendMode, BlurDirection, DefringingKernel, Effects, Filter}; +use pathfinder_content::effects::{BlendMode, BlurDirection, DefringingKernel}; +use pathfinder_content::effects::{Filter, PatternFilter}; use pathfinder_content::fill::FillRule; use pathfinder_content::render_target::RenderTargetId; use pathfinder_geometry::line_segment::LineSegment2F; @@ -313,7 +314,7 @@ where batch.mask_0_fill_rule, batch.mask_1_fill_rule, batch.blend_mode, - batch.effects) + batch.filter) } RenderCommand::Finish { .. } => {} } @@ -583,7 +584,7 @@ where mask_0_fill_rule: Option, mask_1_fill_rule: Option, blend_mode: BlendMode, - effects: Effects) { + filter: Filter) { // TODO(pcwalton): Disable blend for solid tiles. let needs_readable_framebuffer = blend_mode.needs_readable_framebuffer(); @@ -658,13 +659,18 @@ where ctrl |= blend_mode.to_composite_ctrl() << COMBINER_CTRL_COMPOSITE_SHIFT; - match effects.filter { + match filter { Filter::None => {} Filter::RadialGradient { line, radii, uv_origin } => { ctrl |= COMBINER_CTRL_FILTER_RADIAL_GRADIENT << COMBINER_CTRL_COLOR_0_FILTER_SHIFT; self.set_uniforms_for_radial_gradient_filter(&mut uniforms, line, radii, uv_origin) } - Filter::Text { fg_color, bg_color, defringing_kernel, gamma_correction } => { + Filter::PatternFilter(PatternFilter::Text { + fg_color, + bg_color, + defringing_kernel, + gamma_correction, + }) => { ctrl |= COMBINER_CTRL_FILTER_TEXT << COMBINER_CTRL_COLOR_0_FILTER_SHIFT; self.set_uniforms_for_text_filter(&mut textures, &mut uniforms, @@ -673,7 +679,7 @@ where defringing_kernel, gamma_correction); } - Filter::Blur { direction, sigma } => { + Filter::PatternFilter(PatternFilter::Blur { direction, sigma }) => { ctrl |= COMBINER_CTRL_FILTER_BLUR << COMBINER_CTRL_COLOR_0_FILTER_SHIFT; self.set_uniforms_for_blur_filter(&mut uniforms, direction, sigma); } diff --git a/renderer/src/gpu_data.rs b/renderer/src/gpu_data.rs index b2103472..6e195d9c 100644 --- a/renderer/src/gpu_data.rs +++ b/renderer/src/gpu_data.rs @@ -12,7 +12,7 @@ use crate::options::BoundingQuad; use pathfinder_color::ColorU; -use pathfinder_content::effects::{BlendMode, Effects}; +use pathfinder_content::effects::{BlendMode, Filter}; use pathfinder_content::fill::FillRule; use pathfinder_content::render_target::RenderTargetId; use pathfinder_geometry::line_segment::{LineSegmentU4, LineSegmentU8}; @@ -91,8 +91,8 @@ pub struct TileBatch { pub color_texture_1: Option, pub mask_0_fill_rule: Option, pub mask_1_fill_rule: Option, + pub filter: Filter, pub blend_mode: BlendMode, - pub effects: Effects, } #[derive(Clone, Copy, Debug, PartialEq)] diff --git a/renderer/src/paint.rs b/renderer/src/paint.rs index d895d7fb..de146621 100644 --- a/renderer/src/paint.rs +++ b/renderer/src/paint.rs @@ -14,9 +14,9 @@ use crate::scene::RenderTarget; use crate::tiles::{TILE_HEIGHT, TILE_WIDTH}; use hashbrown::HashMap; use pathfinder_color::ColorU; -use pathfinder_content::effects::{Effects, Filter}; +use pathfinder_content::effects::{Filter, PatternFilter}; use pathfinder_content::gradient::Gradient; -use pathfinder_content::pattern::{Pattern, PatternFlags, PatternSource}; +use pathfinder_content::pattern::{Pattern, PatternSource}; use pathfinder_content::render_target::RenderTargetId; use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::rect::{RectF, RectI}; @@ -93,7 +93,7 @@ impl Paint { Paint::Gradient(ref gradient) => { gradient.stops().iter().all(|stop| stop.color.is_opaque()) } - Paint::Pattern(ref pattern) => pattern.source.is_opaque(), + Paint::Pattern(ref pattern) => pattern.is_opaque(), } } @@ -133,7 +133,7 @@ impl Paint { 0.5)))); } } - Paint::Pattern(ref mut pattern) => pattern.transform = *transform * pattern.transform, + Paint::Pattern(ref mut pattern) => pattern.apply_transform(*transform), } } } @@ -165,8 +165,8 @@ pub struct PaintMetadata { pub sampling_flags: TextureSamplingFlags, /// True if this paint is fully opaque. pub is_opaque: bool, - /// The radial gradient for this paint, if applicable. - pub radial_gradient: Option, + /// The filter to be applied to this paint. + pub filter: PaintFilter, } #[derive(Clone, Copy, Debug)] @@ -183,6 +183,18 @@ pub struct RenderTargetMetadata { pub location: TextureLocation, } +#[derive(Debug)] +pub enum PaintFilter { + None, + RadialGradient { + /// The line segment that connects the two circles. + line: LineSegment2F, + /// The radii of the two circles. + radii: F32x2, + }, + PatternFilter(PatternFilter), +} + impl Palette { #[allow(clippy::trivially_copy_pass_by_ref)] pub fn push_paint(&mut self, paint: &Paint) -> PaintId { @@ -219,23 +231,26 @@ impl Palette { let mut gradient_tile_builder = GradientTileBuilder::new(); let mut image_texel_info = vec![]; for paint in &self.paints { - let (texture_location, mut sampling_flags, radial_gradient); + let (texture_location, mut sampling_flags, filter); match paint { Paint::Color(color) => { texture_location = solid_color_tile_builder.allocate(&mut allocator, *color); sampling_flags = TextureSamplingFlags::empty(); - radial_gradient = None; + filter = PaintFilter::None; } Paint::Gradient(ref gradient) => { // FIXME(pcwalton): The gradient size might not be big enough. Detect this. texture_location = gradient_tile_builder.allocate(&mut allocator, gradient); sampling_flags = TextureSamplingFlags::empty(); - radial_gradient = gradient.radii().map(|radii| { - RadialGradientMetadata { line: gradient.line(), radii } - }); + filter = match gradient.radii() { + None => PaintFilter::None, + Some(radii) => { + PaintFilter::RadialGradient { line: gradient.line(), radii } + } + }; } Paint::Pattern(ref pattern) => { - match pattern.source { + match *pattern.source() { PatternSource::RenderTarget { id: render_target_id, .. } => { texture_location = render_target_metadata[render_target_id.0 as usize].location; @@ -253,18 +268,21 @@ impl Palette { } sampling_flags = TextureSamplingFlags::empty(); - if pattern.flags.contains(PatternFlags::REPEAT_X) { + if pattern.repeat_x() { sampling_flags.insert(TextureSamplingFlags::REPEAT_U); } - if pattern.flags.contains(PatternFlags::REPEAT_Y) { + if pattern.repeat_y() { sampling_flags.insert(TextureSamplingFlags::REPEAT_V); } - if pattern.flags.contains(PatternFlags::NO_SMOOTHING) { + if !pattern.smoothing_enabled() { sampling_flags.insert(TextureSamplingFlags::NEAREST_MIN | TextureSamplingFlags::NEAREST_MAG); } - radial_gradient = None; + filter = match pattern.filter() { + None => PaintFilter::None, + Some(pattern_filter) => PaintFilter::PatternFilter(pattern_filter), + }; } }; @@ -273,7 +291,7 @@ impl Palette { texture_transform: Transform2F::default(), sampling_flags, is_opaque: paint.is_opaque(), - radial_gradient, + filter, }); } @@ -303,24 +321,24 @@ impl Palette { vector: texture_origin_uv, } * render_transform } - Paint::Pattern(Pattern { source: PatternSource::Image(_), transform, .. }) => { - let texture_origin_uv = - rect_to_uv(metadata.location.rect, texture_scale).origin(); - Transform2F::from_translation(texture_origin_uv) * - Transform2F::from_scale(texture_scale) * - transform.inverse() * render_transform - } - Paint::Pattern(Pattern { - source: PatternSource::RenderTarget { .. }, - transform, - .. - }) => { - // FIXME(pcwalton): Only do this in GL, not Metal! - let texture_origin_uv = rect_to_uv(metadata.location.rect, - texture_scale).lower_left(); - Transform2F::from_translation(texture_origin_uv) * - Transform2F::from_scale(texture_scale * vec2f(1.0, -1.0)) * - transform.inverse() * render_transform + Paint::Pattern(pattern) => { + match pattern.source() { + PatternSource::Image(_) => { + let texture_origin_uv = + rect_to_uv(metadata.location.rect, texture_scale).origin(); + Transform2F::from_translation(texture_origin_uv) * + Transform2F::from_scale(texture_scale) * + pattern.transform().inverse() * render_transform + } + PatternSource::RenderTarget { .. } => { + // FIXME(pcwalton): Only do this in GL, not Metal! + let texture_origin_uv = rect_to_uv(metadata.location.rect, + texture_scale).lower_left(); + Transform2F::from_translation(texture_origin_uv) * + Transform2F::from_scale(texture_scale * vec2f(1.0, -1.0)) * + pattern.transform().inverse() * render_transform + } + } } } } @@ -376,18 +394,14 @@ impl PaintMetadata { tex_coords } - pub(crate) fn effects(&self) -> Effects { - Effects { - filter: match self.radial_gradient { - None => Filter::None, - Some(gradient) => { - Filter::RadialGradient { - line: gradient.line, - radii: gradient.radii, - uv_origin: self.texture_transform.vector, - } - } - }, + pub(crate) fn filter(&self) -> Filter { + match self.filter { + PaintFilter::None => Filter::None, + PaintFilter::RadialGradient { line, radii } => { + let uv_origin = self.texture_transform.vector; + Filter::RadialGradient { line, radii, uv_origin } + } + PaintFilter::PatternFilter(pattern_filter) => Filter::PatternFilter(pattern_filter), } } } diff --git a/renderer/src/scene.rs b/renderer/src/scene.rs index e2a551c2..0d50dcd0 100644 --- a/renderer/src/scene.rs +++ b/renderer/src/scene.rs @@ -15,7 +15,7 @@ use crate::concurrent::executor::Executor; use crate::options::{BuildOptions, PreparedBuildOptions}; use crate::options::{PreparedRenderTransform, RenderCommandListener}; use crate::paint::{Paint, PaintId, PaintInfo, Palette}; -use pathfinder_content::effects::{BlendMode, Effects}; +use pathfinder_content::effects::BlendMode; use pathfinder_content::fill::FillRule; use pathfinder_content::outline::Outline; use pathfinder_content::pattern::{Pattern, PatternSource}; @@ -87,10 +87,6 @@ impl Scene { self.display_list.push(DisplayItem::PopRenderTarget); } - pub fn draw_render_target(&mut self, render_target: RenderTargetId, effects: Effects) { - self.display_list.push(DisplayItem::DrawRenderTarget { render_target, effects }); - } - pub fn append_scene(&mut self, scene: Scene) { // Merge render targets. let mut render_target_mapping = HashMap::new(); @@ -108,19 +104,20 @@ impl Scene { for (old_paint_index, old_paint) in scene.palette.paints.iter().enumerate() { let old_paint_id = PaintId(old_paint_index as u16); let new_paint_id = match old_paint { - Paint::Pattern(Pattern { - source: PatternSource::RenderTarget { id: old_render_target_id, size }, - transform, - flags - }) => { - self.palette.push_paint(&Paint::Pattern(Pattern { - source: PatternSource::RenderTarget { - id: render_target_mapping[old_render_target_id], - size: *size, - }, - transform: *transform, - flags: *flags, - })) + Paint::Pattern(pattern) => { + match pattern.source() { + PatternSource::RenderTarget { id: old_render_target_id, size } => { + let mut new_pattern = + Pattern::from_render_target(*old_render_target_id, *size); + new_pattern.set_filter(pattern.filter()); + new_pattern.apply_transform(pattern.transform()); + new_pattern.set_repeat_x(pattern.repeat_x()); + new_pattern.set_repeat_y(pattern.repeat_y()); + new_pattern.set_smoothing_enabled(pattern.smoothing_enabled()); + self.palette.push_paint(&Paint::Pattern(new_pattern)) + } + _ => self.palette.push_paint(old_paint), + } } paint => self.palette.push_paint(paint), }; @@ -154,13 +151,6 @@ impl Scene { // Merge display items. for display_item in scene.display_list { match display_item { - DisplayItem::DrawRenderTarget { - render_target: old_render_target_id, - effects, - } => { - let new_render_target_id = render_target_mapping[&old_render_target_id]; - self.draw_render_target(new_render_target_id, effects) - } DisplayItem::PushRenderTarget(old_render_target_id) => { let new_render_target_id = render_target_mapping[&old_render_target_id]; self.display_list.push(DisplayItem::PushRenderTarget(new_render_target_id)); @@ -349,13 +339,6 @@ pub enum DisplayItem { /// Draws paths to the render target on top of the stack. DrawPaths { start_index: u32, end_index: u32 }, - /// Draws an entire render target to the render target on top of the stack. - /// - /// FIXME(pcwalton): This draws the entire render target, so it's inefficient. We should get - /// rid of this command and transition all uses to `DrawPaths`. The reason it exists is that we - /// don't have logic to create tiles for blur bounding regions yet. - DrawRenderTarget { render_target: RenderTargetId, effects: Effects }, - /// Pushes a render target onto the top of the stack. PushRenderTarget(RenderTargetId), diff --git a/renderer/src/z_buffer.rs b/renderer/src/z_buffer.rs index 51081cb2..4a22cffd 100644 --- a/renderer/src/z_buffer.rs +++ b/renderer/src/z_buffer.rs @@ -15,7 +15,7 @@ use crate::gpu_data::{Tile, TileBatch, TileBatchTexture, TileVertex}; use crate::paint::{PaintId, PaintMetadata}; use crate::tile_map::DenseTileMap; use crate::tiles; -use pathfinder_content::effects::{BlendMode, Effects}; +use pathfinder_content::effects::{BlendMode, Filter}; use pathfinder_geometry::rect::RectF; use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2i}; use vec_map::VecMap; @@ -94,7 +94,7 @@ impl ZBuffer { }), color_texture_1: None, tiles: vec![], - effects: Effects::default(), + filter: Filter::None, blend_mode: BlendMode::default(), mask_0_fill_rule: None, mask_1_fill_rule: None, @@ -124,19 +124,6 @@ impl Tile { paint_metadata), } } - - pub(crate) fn new_solid_from_texture_rect(tile_position: Vector2I, texture_rect: RectF) - -> Tile { - Tile { - upper_left: TileVertex::new_solid_from_uv(tile_position, texture_rect.origin()), - upper_right: TileVertex::new_solid_from_uv(tile_position + vec2i(1, 0), - texture_rect.upper_right()), - lower_left: TileVertex::new_solid_from_uv(tile_position + vec2i(0, 1), - texture_rect.lower_left()), - lower_right: TileVertex::new_solid_from_uv(tile_position + vec2i(1, 1), - texture_rect.lower_right()), - } - } } impl TileVertex {