diff --git a/content/src/pattern.rs b/content/src/pattern.rs index cdd34205..43f88a8c 100644 --- a/content/src/pattern.rs +++ b/content/src/pattern.rs @@ -53,6 +53,10 @@ pub struct Image { is_opaque: bool, } +/// Unique identifier for an image. +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub struct ImageHash(pub u64); + bitflags! { pub struct PatternFlags: u8 { const REPEAT_X = 0x01; @@ -185,6 +189,13 @@ impl Image { pub fn is_opaque(&self) -> bool { self.is_opaque } + + #[inline] + pub fn get_hash(&self) -> ImageHash { + let mut hasher = DefaultHasher::new(); + self.hash(&mut hasher); + ImageHash(hasher.finish()) + } } impl PatternSource { diff --git a/examples/canvas_nanovg/src/main.rs b/examples/canvas_nanovg/src/main.rs index dc3a12a3..c01cb7d3 100644 --- a/examples/canvas_nanovg/src/main.rs +++ b/examples/canvas_nanovg/src/main.rs @@ -1541,6 +1541,9 @@ fn main() { let mut cpu_graph = PerfGraph::new(GraphStyle::MS, "CPU Time"); let mut gpu_graph = PerfGraph::new(GraphStyle::MS, "GPU Time"); + // Initialize scene proxy. + let mut scene = SceneProxy::new(renderer.mode().level, RayonExecutor); + // Enter the main loop. let mut exit = false; while !exit { @@ -1569,9 +1572,7 @@ fn main() { // Render the canvas to screen. let canvas = context.into_canvas(); - let mut scene = SceneProxy::from_scene(canvas.into_scene(), - renderer.mode().level, - RayonExecutor); + scene.replace_scene(canvas.into_scene()); scene.build_and_render(&mut renderer, BuildOptions::default()); // Present the rendered canvas via `surfman`. diff --git a/renderer/src/allocator.rs b/renderer/src/allocator.rs index 929ef25d..8cf9408e 100644 --- a/renderer/src/allocator.rs +++ b/renderer/src/allocator.rs @@ -18,7 +18,7 @@ const ATLAS_TEXTURE_LENGTH: u32 = 1024; #[derive(Clone, Debug)] pub struct TextureAllocator { - pages: Vec, + pages: Vec>, } #[derive(Clone, Debug)] @@ -71,22 +71,34 @@ impl TextureAllocator { } // Try to add to each atlas. + let mut first_free_page_index = self.pages.len(); for (page_index, page) in self.pages.iter_mut().enumerate() { - match page.allocator { - TexturePageAllocator::Image { .. } => {} - TexturePageAllocator::Atlas(ref mut allocator) => { - if let Some(rect) = allocator.allocate(requested_size) { - return TextureLocation { page: TexturePageId(page_index as u32), rect }; + match *page { + Some(ref mut page) => { + match page.allocator { + TexturePageAllocator::Image { .. } => {} + TexturePageAllocator::Atlas(ref mut allocator) => { + if let Some(rect) = allocator.allocate(requested_size) { + return TextureLocation { + page: TexturePageId(page_index as u32), + rect + }; + } + } } } + None => first_free_page_index = first_free_page_index.min(page_index), } } // Add a new atlas. - let page = TexturePageId(self.pages.len() as u32); + let page = self.get_first_free_page_id(); let mut allocator = TextureAtlasAllocator::new(); let rect = allocator.allocate(requested_size).expect("Allocation failed!"); - self.pages.push(TexturePage { + while (page.0 as usize) >= self.pages.len() { + self.pages.push(None); + } + self.pages[page.0 as usize] = Some(TexturePage { is_new: true, allocator: TexturePageAllocator::Atlas(allocator), }); @@ -94,17 +106,52 @@ impl TextureAllocator { } pub fn allocate_image(&mut self, requested_size: Vector2I) -> TextureLocation { - let page = TexturePageId(self.pages.len() as u32); + let page = self.get_first_free_page_id(); let rect = RectI::new(Vector2I::default(), requested_size); - self.pages.push(TexturePage { + while (page.0 as usize) >= self.pages.len() { + self.pages.push(None); + } + self.pages[page.0 as usize] = Some(TexturePage { is_new: true, allocator: TexturePageAllocator::Image { size: rect.size() }, }); TextureLocation { page, rect } } + fn get_first_free_page_id(&self) -> TexturePageId { + for (page_index, page) in self.pages.iter().enumerate() { + if page.is_none() { + return TexturePageId(page_index as u32); + } + } + TexturePageId(self.pages.len() as u32) + } + + pub fn free(&mut self, location: TextureLocation) { + //println!("free({:?})", location); + match self.pages[location.page.0 as usize] + .as_mut() + .expect("Texture page is not allocated!") + .allocator { + TexturePageAllocator::Image { size } => { + debug_assert_eq!(location.rect, RectI::new(Vector2I::default(), size)); + } + TexturePageAllocator::Atlas(ref mut atlas_allocator) => { + atlas_allocator.free(location.rect); + if !atlas_allocator.is_empty() { + // Keep the page around. + return; + } + } + } + + // If we got here, free the page. + // TODO(pcwalton): Actually tell the renderer to free this page! + self.pages[location.page.0 as usize] = None; + } + pub fn page_size(&self, page_id: TexturePageId) -> Vector2I { - match self.pages[page_id.0 as usize].allocator { + match self.pages[page_id.0 as usize].as_ref().expect("No such texture page!").allocator { TexturePageAllocator::Atlas(ref atlas) => Vector2I::splat(atlas.size as i32), TexturePageAllocator::Image { size, .. } => size, } @@ -115,16 +162,23 @@ impl TextureAllocator { } pub fn page_is_new(&self, page_id: TexturePageId) -> bool { - self.pages[page_id.0 as usize].is_new + self.pages[page_id.0 as usize].as_ref().expect("No such texture page!").is_new } - pub fn mark_page_as_allocated(&mut self, page_id: TexturePageId) { - self.pages[page_id.0 as usize].is_new = false; + pub fn mark_all_pages_as_allocated(&mut self) { + for page in &mut self.pages { + if let Some(ref mut page) = *page { + page.is_new = false; + } + } } - #[inline] - pub fn page_count(&self) -> u32 { - self.pages.len() as u32 + pub fn page_ids(&self) -> TexturePageIter { + let mut first_index = 0; + while first_index < self.pages.len() && self.pages[first_index].is_none() { + first_index += 1; + } + TexturePageIter { allocator: self, next_index: first_index } } } @@ -147,7 +201,6 @@ impl TextureAtlasAllocator { } #[inline] - #[allow(dead_code)] fn free(&mut self, rect: RectI) { let requested_length = rect.width() as u32; self.root.free(Vector2I::default(), self.size, rect.origin(), requested_length) @@ -285,6 +338,30 @@ impl TreeNode { } } +pub struct TexturePageIter<'a> { + allocator: &'a TextureAllocator, + next_index: usize, +} + +impl<'a> Iterator for TexturePageIter<'a> { + type Item = TexturePageId; + fn next(&mut self) -> Option { + let next_id = if self.next_index >= self.allocator.pages.len() { + None + } else { + Some(TexturePageId(self.next_index as u32)) + }; + loop { + self.next_index += 1; + if self.next_index >= self.allocator.pages.len() || + self.allocator.pages[self.next_index as usize].is_some() { + break; + } + } + next_id + } +} + #[cfg(test)] mod test { use pathfinder_geometry::vector::vec2i; diff --git a/renderer/src/builder.rs b/renderer/src/builder.rs index 5f1ac45c..07b25c49 100644 --- a/renderer/src/builder.rs +++ b/renderer/src/builder.rs @@ -175,7 +175,7 @@ impl<'a, 'b, 'c, 'd> SceneBuilder<'a, 'b, 'c, 'd> { render_commands, paint_metadata, render_target_metadata: _, - } = self.scene.build_paint_info(render_transform); + } = self.scene.build_paint_info(&mut self.sink.paint_texture_manager, render_transform); for render_command in render_commands { self.sink.listener.send(render_command); } diff --git a/renderer/src/paint.rs b/renderer/src/paint.rs index 56567e4e..0f8b2b57 100644 --- a/renderer/src/paint.rs +++ b/renderer/src/paint.rs @@ -14,11 +14,11 @@ use crate::allocator::{AllocationMode, TextureAllocator}; use crate::gpu_data::{ColorCombineMode, RenderCommand, TextureLocation, TextureMetadataEntry}; use crate::gpu_data::{TexturePageDescriptor, TexturePageId, TileBatchTexture}; use crate::scene::{RenderTarget, SceneId}; -use hashbrown::HashMap; +use hashbrown::{HashMap, HashSet}; use pathfinder_color::ColorU; use pathfinder_content::effects::{BlendMode, Filter, PatternFilter}; use pathfinder_content::gradient::{Gradient, GradientGeometry, GradientWrap}; -use pathfinder_content::pattern::{Pattern, PatternSource}; +use pathfinder_content::pattern::{ImageHash, Pattern, PatternSource}; use pathfinder_content::render_target::RenderTargetId; use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::rect::{RectF, RectI}; @@ -35,19 +35,19 @@ use std::sync::Arc; // TODO(pcwalton): Choose this size dynamically! const GRADIENT_TILE_LENGTH: u32 = 256; +// Stores all paints in a scene. #[derive(Clone)] pub(crate) struct Palette { pub(crate) paints: Vec, - render_targets: Vec, + render_targets: Vec, cache: HashMap, - allocator: TextureAllocator, scene_id: SceneId, } -#[derive(Clone)] -struct RenderTargetData { - render_target: RenderTarget, - metadata: RenderTargetMetadata, +// Caches texture images from scene to scene. +pub(crate) struct PaintTextureManager { + allocator: TextureAllocator, + cached_images: HashMap, } /// Defines how a path is to be filled: with a solid color, gradient, or pattern. @@ -109,7 +109,6 @@ impl Palette { paints: vec![], render_targets: vec![], cache: HashMap::new(), - allocator: TextureAllocator::new(), scene_id, } } @@ -392,98 +391,172 @@ impl Palette { pub(crate) fn push_render_target(&mut self, render_target: RenderTarget) -> RenderTargetId { let id = self.render_targets.len() as u32; - - let metadata = RenderTargetMetadata { - location: self.allocator.allocate_image(render_target.size()), - }; - - self.render_targets.push(RenderTargetData { render_target, metadata }); + self.render_targets.push(render_target); RenderTargetId { scene: self.scene_id.0, render_target: id } } - pub(crate) fn build_paint_info(&mut self, render_transform: Transform2F) -> PaintInfo { - let mut paint_metadata = vec![]; + pub(crate) fn build_paint_info(&mut self, + texture_manager: &mut PaintTextureManager, + render_transform: Transform2F) + -> PaintInfo { + // Assign render target locations. + let mut transient_paint_locations = vec![]; + let render_target_metadata = + self.assign_render_target_locations(texture_manager, &mut transient_paint_locations); // Assign paint locations. + let PaintLocationsInfo { + mut paint_metadata, + gradient_tile_builder, + image_texel_info, + used_image_hashes, + } = self.assign_paint_locations(&render_target_metadata, + texture_manager, + &mut transient_paint_locations); + + // Calculate texture transforms. + self.calculate_texture_transforms(&mut paint_metadata, texture_manager, render_transform); + + // Create texture metadata. + let texture_metadata = self.create_texture_metadata(&paint_metadata); + let mut render_commands = vec![RenderCommand::UploadTextureMetadata(texture_metadata)]; + + // Allocate textures. + self.allocate_textures(&mut render_commands, texture_manager); + + // Create render commands. + self.create_render_commands(&mut render_commands, + &render_target_metadata, + gradient_tile_builder, + image_texel_info); + + // Free transient locations and unused images, now that they're no longer needed. + self.free_transient_locations(texture_manager, transient_paint_locations); + self.free_unused_images(texture_manager, used_image_hashes); + + PaintInfo { render_commands, paint_metadata, render_target_metadata } + } + + fn assign_render_target_locations(&self, + texture_manager: &mut PaintTextureManager, + transient_paint_locations: &mut Vec) + -> Vec { + let mut render_target_metadata = vec![]; + for render_target in &self.render_targets { + let location = texture_manager.allocator.allocate_image(render_target.size()); + render_target_metadata.push(RenderTargetMetadata { location }); + transient_paint_locations.push(location); + } + render_target_metadata + } + + fn assign_paint_locations(&self, + render_target_metadata: &[RenderTargetMetadata], + texture_manager: &mut PaintTextureManager, + transient_paint_locations: &mut Vec) + -> PaintLocationsInfo { + let mut paint_metadata = vec![]; let mut gradient_tile_builder = GradientTileBuilder::new(); let mut image_texel_info = vec![]; + let mut used_image_hashes = HashSet::new(); for paint in &self.paints { - let allocator = &mut self.allocator; - let render_targets = &self.render_targets; - let color_texture_metadata = paint.overlay.as_ref().map(|overlay| { - match overlay.contents { - PaintContents::Gradient(ref gradient) => { - let mut sampling_flags = TextureSamplingFlags::empty(); - match gradient.wrap { - GradientWrap::Repeat => { + let allocator = &mut texture_manager.allocator; + let color_texture_metadata = match paint.overlay { + None => None, + Some(ref overlay) => { + match overlay.contents { + PaintContents::Gradient(ref gradient) => { + let mut sampling_flags = TextureSamplingFlags::empty(); + match gradient.wrap { + GradientWrap::Repeat => { + sampling_flags.insert(TextureSamplingFlags::REPEAT_U); + } + GradientWrap::Clamp => {} + } + + // FIXME(pcwalton): The gradient size might not be big enough. Detect + // this. + let location = + gradient_tile_builder.allocate(allocator, + transient_paint_locations, + gradient); + Some(PaintColorTextureMetadata { + location, + page_scale: allocator.page_scale(location.page), + sampling_flags, + filter: match gradient.geometry { + GradientGeometry::Linear(_) => PaintFilter::None, + GradientGeometry::Radial { line, radii, .. } => { + PaintFilter::RadialGradient { line, radii } + } + }, + transform: Transform2F::default(), + composite_op: overlay.composite_op(), + }) + } + PaintContents::Pattern(ref pattern) => { + let location; + match *pattern.source() { + PatternSource::RenderTarget { id: render_target_id, .. } => { + let index = render_target_id.render_target as usize; + location = render_target_metadata[index].location; + } + PatternSource::Image(ref image) => { + // TODO(pcwalton): We should be able to use tile cleverness to + // repeat inside the atlas in some cases. + let image_hash = image.get_hash(); + //println!("image hash: {:?}", image_hash); + match texture_manager.cached_images.get(&image_hash) { + Some(cached_location) => { + //println!("... cache hit: {:?}", cached_location); + location = *cached_location; + used_image_hashes.insert(image_hash); + } + None => { + //println!("... cache MISS"); + let allocation_mode = AllocationMode::OwnPage; + location = allocator.allocate(image.size(), + allocation_mode); + texture_manager.cached_images.insert(image_hash, + location); + } + } + image_texel_info.push(ImageTexelInfo { + location, + texels: (*image.pixels()).clone(), + }); + } + } + + let mut sampling_flags = TextureSamplingFlags::empty(); + if pattern.repeat_x() { sampling_flags.insert(TextureSamplingFlags::REPEAT_U); } - GradientWrap::Clamp => {} - } - - // FIXME(pcwalton): The gradient size might not be big enough. Detect this. - let location = gradient_tile_builder.allocate(allocator, gradient); - PaintColorTextureMetadata { - location, - page_scale: allocator.page_scale(location.page), - sampling_flags, - filter: match gradient.geometry { - GradientGeometry::Linear(_) => PaintFilter::None, - GradientGeometry::Radial { line, radii, .. } => { - PaintFilter::RadialGradient { line, radii } - } - }, - transform: Transform2F::default(), - composite_op: overlay.composite_op(), - } - } - PaintContents::Pattern(ref pattern) => { - let location; - match *pattern.source() { - PatternSource::RenderTarget { id: render_target_id, .. } => { - let index = render_target_id.render_target as usize; - location = render_targets[index].metadata.location; + if pattern.repeat_y() { + sampling_flags.insert(TextureSamplingFlags::REPEAT_V); } - PatternSource::Image(ref image) => { - // TODO(pcwalton): We should be able to use tile cleverness to - // repeat inside the atlas in some cases. - let allocation_mode = AllocationMode::OwnPage; - location = allocator.allocate(image.size(), allocation_mode); - image_texel_info.push(ImageTexelInfo { - location, - texels: (*image.pixels()).clone(), - }); + if !pattern.smoothing_enabled() { + sampling_flags.insert(TextureSamplingFlags::NEAREST_MIN | + TextureSamplingFlags::NEAREST_MAG); } - } - let mut sampling_flags = TextureSamplingFlags::empty(); - if pattern.repeat_x() { - sampling_flags.insert(TextureSamplingFlags::REPEAT_U); - } - if pattern.repeat_y() { - sampling_flags.insert(TextureSamplingFlags::REPEAT_V); - } - if !pattern.smoothing_enabled() { - sampling_flags.insert(TextureSamplingFlags::NEAREST_MIN | - TextureSamplingFlags::NEAREST_MAG); - } + let filter = match pattern.filter() { + None => PaintFilter::None, + Some(pattern_filter) => PaintFilter::PatternFilter(pattern_filter), + }; - let filter = match pattern.filter() { - None => PaintFilter::None, - Some(pattern_filter) => PaintFilter::PatternFilter(pattern_filter), - }; - - PaintColorTextureMetadata { - location, - page_scale: allocator.page_scale(location.page), - sampling_flags, - filter, - transform: Transform2F::default(), - composite_op: overlay.composite_op(), + Some(PaintColorTextureMetadata { + location, + page_scale: allocator.page_scale(location.page), + sampling_flags, + filter, + transform: Transform2F::default(), + composite_op: overlay.composite_op(), + }) } } } - }); + }; paint_metadata.push(PaintMetadata { color_texture_metadata, @@ -494,14 +567,26 @@ impl Palette { }); } - // Calculate texture transforms. + PaintLocationsInfo { + paint_metadata, + gradient_tile_builder, + image_texel_info, + used_image_hashes, + } + } + + fn calculate_texture_transforms(&self, + paint_metadata: &mut [PaintMetadata], + texture_manager: &mut PaintTextureManager, + render_transform: Transform2F) { for (paint, metadata) in self.paints.iter().zip(paint_metadata.iter_mut()) { let mut color_texture_metadata = match metadata.color_texture_metadata { None => continue, Some(ref mut color_texture_metadata) => color_texture_metadata, }; - let texture_scale = self.allocator.page_scale(color_texture_metadata.location.page); + let texture_scale = texture_manager.allocator + .page_scale(color_texture_metadata.location.page); let texture_rect = color_texture_metadata.location.rect; color_texture_metadata.transform = match paint.overlay .as_ref() @@ -544,9 +629,11 @@ impl Palette { }; color_texture_metadata.transform *= render_transform; } + } - // Create texture metadata. - let texture_metadata = paint_metadata.iter().map(|paint_metadata| { + fn create_texture_metadata(&self, paint_metadata: &[PaintMetadata]) + -> Vec { + paint_metadata.iter().map(|paint_metadata| { TextureMetadataEntry { color_0_transform: match paint_metadata.color_texture_metadata { None => Transform2F::default(), @@ -561,29 +648,28 @@ impl Palette { filter: paint_metadata.filter(), blend_mode: paint_metadata.blend_mode, } - }).collect(); - let mut render_commands = vec![RenderCommand::UploadTextureMetadata(texture_metadata)]; + }).collect() + } - // Allocate textures. - let mut texture_page_descriptors = vec![]; - for page_index in 0..self.allocator.page_count() { - let page_id = TexturePageId(page_index); - let page_size = self.allocator.page_size(page_id); + fn allocate_textures(&self, + render_commands: &mut Vec, + texture_manager: &mut PaintTextureManager) { + for page_id in texture_manager.allocator.page_ids() { + let page_size = texture_manager.allocator.page_size(page_id); let descriptor = TexturePageDescriptor { size: page_size }; - texture_page_descriptors.push(descriptor); - if self.allocator.page_is_new(page_id) { + if texture_manager.allocator.page_is_new(page_id) { render_commands.push(RenderCommand::AllocateTexturePage { page_id, descriptor }); - self.allocator.mark_page_as_allocated(page_id); } } + texture_manager.allocator.mark_all_pages_as_allocated(); + } - // Gather up render target metadata. - let render_target_metadata: Vec<_> = self.render_targets.iter().map(|render_target_data| { - render_target_data.metadata - }).collect(); - - // Create render commands. + fn create_render_commands(&self, + render_commands: &mut Vec, + render_target_metadata: &[RenderTargetMetadata], + gradient_tile_builder: GradientTileBuilder, + image_texel_info: Vec) { for (index, metadata) in render_target_metadata.iter().enumerate() { let id = RenderTargetId { scene: self.scene_id.0, render_target: index as u32 }; render_commands.push(RenderCommand::DeclareRenderTarget { @@ -591,15 +677,36 @@ impl Palette { location: metadata.location, }); } - gradient_tile_builder.create_render_commands(&mut render_commands); + gradient_tile_builder.create_render_commands(render_commands); for image_texel_info in image_texel_info { render_commands.push(RenderCommand::UploadTexelData { texels: image_texel_info.texels, location: image_texel_info.location, }); } + } - PaintInfo { render_commands, paint_metadata, render_target_metadata } + fn free_transient_locations(&self, + texture_manager: &mut PaintTextureManager, + transient_paint_locations: Vec) { + for location in transient_paint_locations { + texture_manager.allocator.free(location); + } + } + + // Frees images that are cached but not used this frame. + fn free_unused_images(&self, + texture_manager: &mut PaintTextureManager, + used_image_hashes: HashSet) { + let cached_images = &mut texture_manager.cached_images; + let allocator = &mut texture_manager.allocator; + cached_images.retain(|image_hash, location| { + let keep = used_image_hashes.contains(image_hash); + if !keep { + allocator.free(*location); + } + keep + }); } pub(crate) fn append_palette(&mut self, palette: Palette) -> MergedPaletteInfo { @@ -612,7 +719,7 @@ impl Palette { scene: palette.scene_id.0, render_target: old_render_target_index as u32, }; - let new_render_target_id = self.push_render_target(render_target.render_target); + let new_render_target_id = self.push_render_target(render_target); render_target_mapping.insert(old_render_target_id, new_render_target_id); } @@ -650,6 +757,15 @@ impl Palette { } } +impl PaintTextureManager { + pub(crate) fn new() -> PaintTextureManager { + PaintTextureManager { + allocator: TextureAllocator::new(), + cached_images: HashMap::new(), + } + } +} + pub(crate) struct MergedPaletteInfo { pub(crate) render_target_mapping: HashMap, pub(crate) paint_mapping: HashMap, @@ -702,15 +818,20 @@ impl GradientTileBuilder { GradientTileBuilder { tiles: vec![] } } - fn allocate(&mut self, allocator: &mut TextureAllocator, gradient: &Gradient) + fn allocate(&mut self, + allocator: &mut TextureAllocator, + transient_paint_locations: &mut Vec, + gradient: &Gradient) -> TextureLocation { if self.tiles.is_empty() || self.tiles.last().unwrap().next_index == GRADIENT_TILE_LENGTH { let size = Vector2I::splat(GRADIENT_TILE_LENGTH as i32); let area = size.x() as usize * size.y() as usize; + let page_location = allocator.allocate(size, AllocationMode::OwnPage); + transient_paint_locations.push(page_location); self.tiles.push(GradientTile { texels: vec![ColorU::black(); area], - page: allocator.allocate(size, AllocationMode::OwnPage).page, + page: page_location.page, next_index: 0, }) } @@ -749,6 +870,13 @@ impl GradientTileBuilder { } } +struct PaintLocationsInfo { + paint_metadata: Vec, + gradient_tile_builder: GradientTileBuilder, + image_texel_info: Vec, + used_image_hashes: HashSet, +} + struct ImageTexelInfo { location: TextureLocation, texels: Arc>, diff --git a/renderer/src/scene.rs b/renderer/src/scene.rs index 75cfef75..d813dd8a 100644 --- a/renderer/src/scene.rs +++ b/renderer/src/scene.rs @@ -17,7 +17,7 @@ use crate::gpu::renderer::Renderer; use crate::gpu_data::RenderCommand; use crate::options::{BuildOptions, PreparedBuildOptions}; use crate::options::{PreparedRenderTransform, RenderCommandListener}; -use crate::paint::{MergedPaletteInfo, Paint, PaintId, PaintInfo, Palette}; +use crate::paint::{MergedPaletteInfo, Paint, PaintId, PaintInfo, PaintTextureManager, Palette}; use pathfinder_content::effects::BlendMode; use pathfinder_content::fill::FillRule; use pathfinder_content::outline::Outline; @@ -178,8 +178,11 @@ impl Scene { } #[inline] - pub(crate) fn build_paint_info(&mut self, render_transform: Transform2F) -> PaintInfo { - self.palette.build_paint_info(render_transform) + pub(crate) fn build_paint_info(&mut self, + texture_manager: &mut PaintTextureManager, + render_transform: Transform2F) + -> PaintInfo { + self.palette.build_paint_info(texture_manager, render_transform) } /// Defines a new paint, which specifies how paths are to be filled or stroked. Returns a paint @@ -382,6 +385,7 @@ pub struct SceneSink<'a> { pub(crate) listener: RenderCommandListener<'a>, pub(crate) renderer_level: RendererLevel, pub(crate) last_scene: Option, + pub(crate) paint_texture_manager: PaintTextureManager, } pub(crate) struct LastSceneInfo { @@ -423,7 +427,12 @@ impl<'a> SceneSink<'a> { #[inline] pub fn new(listener: RenderCommandListener<'a>, renderer_level: RendererLevel) -> SceneSink<'a> { - SceneSink { listener, renderer_level, last_scene: None } + SceneSink { + listener, + renderer_level, + last_scene: None, + paint_texture_manager: PaintTextureManager::new(), + } } }