Stop reallocating texture pages every frame

This commit is contained in:
Patrick Walton 2020-04-17 11:50:46 -07:00
parent 07ce2a2536
commit 5819b8dc60
7 changed files with 200 additions and 129 deletions

View File

@ -16,12 +16,18 @@ use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i};
const ATLAS_TEXTURE_LENGTH: u32 = 1024; const ATLAS_TEXTURE_LENGTH: u32 = 1024;
#[derive(Debug)] #[derive(Clone, Debug)]
pub struct TextureAllocator { pub struct TextureAllocator {
pages: Vec<TexturePageAllocator>, pages: Vec<TexturePage>,
} }
#[derive(Debug)] #[derive(Clone, Debug)]
pub struct TexturePage {
allocator: TexturePageAllocator,
is_new: bool,
}
#[derive(Clone, Debug)]
pub enum TexturePageAllocator { pub enum TexturePageAllocator {
// An atlas allocated with our quadtree allocator. // An atlas allocated with our quadtree allocator.
Atlas(TextureAtlasAllocator), Atlas(TextureAtlasAllocator),
@ -29,13 +35,13 @@ pub enum TexturePageAllocator {
Image { size: Vector2I }, Image { size: Vector2I },
} }
#[derive(Debug)] #[derive(Clone, Debug)]
pub struct TextureAtlasAllocator { pub struct TextureAtlasAllocator {
root: TreeNode, root: TreeNode,
size: u32, size: u32,
} }
#[derive(Debug)] #[derive(Clone, Debug)]
enum TreeNode { enum TreeNode {
EmptyLeaf, EmptyLeaf,
FullLeaf, FullLeaf,
@ -65,7 +71,7 @@ impl TextureAllocator {
// Try to add to each atlas. // Try to add to each atlas.
for (page_index, page) in self.pages.iter_mut().enumerate() { for (page_index, page) in self.pages.iter_mut().enumerate() {
match *page { match page.allocator {
TexturePageAllocator::Image { .. } => {} TexturePageAllocator::Image { .. } => {}
TexturePageAllocator::Atlas(ref mut allocator) => { TexturePageAllocator::Atlas(ref mut allocator) => {
if let Some(rect) = allocator.allocate(requested_size) { if let Some(rect) = allocator.allocate(requested_size) {
@ -79,26 +85,40 @@ impl TextureAllocator {
let page = TexturePageId(self.pages.len() as u32); let page = TexturePageId(self.pages.len() as u32);
let mut allocator = TextureAtlasAllocator::new(); let mut allocator = TextureAtlasAllocator::new();
let rect = allocator.allocate(requested_size).expect("Allocation failed!"); let rect = allocator.allocate(requested_size).expect("Allocation failed!");
self.pages.push(TexturePageAllocator::Atlas(allocator)); self.pages.push(TexturePage {
is_new: true,
allocator: TexturePageAllocator::Atlas(allocator),
});
TextureLocation { page, rect } TextureLocation { page, rect }
} }
pub fn allocate_image(&mut self, requested_size: Vector2I) -> TextureLocation { pub fn allocate_image(&mut self, requested_size: Vector2I) -> TextureLocation {
let page = TexturePageId(self.pages.len() as u32); let page = TexturePageId(self.pages.len() as u32);
let rect = RectI::new(Vector2I::default(), requested_size); let rect = RectI::new(Vector2I::default(), requested_size);
self.pages.push(TexturePageAllocator::Image { size: rect.size() }); self.pages.push(TexturePage {
is_new: true,
allocator: TexturePageAllocator::Image { size: rect.size() },
});
TextureLocation { page, rect } TextureLocation { page, rect }
} }
pub fn page_size(&self, page_index: TexturePageId) -> Vector2I { pub fn page_size(&self, page_id: TexturePageId) -> Vector2I {
match self.pages[page_index.0 as usize] { match self.pages[page_id.0 as usize].allocator {
TexturePageAllocator::Atlas(ref atlas) => Vector2I::splat(atlas.size as i32), TexturePageAllocator::Atlas(ref atlas) => Vector2I::splat(atlas.size as i32),
TexturePageAllocator::Image { size, .. } => size, TexturePageAllocator::Image { size, .. } => size,
} }
} }
pub fn page_scale(&self, page_index: TexturePageId) -> Vector2F { pub fn page_scale(&self, page_id: TexturePageId) -> Vector2F {
vec2f(1.0, 1.0) / self.page_size(page_index).to_f32() vec2f(1.0, 1.0) / self.page_size(page_id).to_f32()
}
pub fn page_is_new(&self, page_id: TexturePageId) -> bool {
self.pages[page_id.0 as usize].is_new
}
pub fn mark_page_as_allocated(&mut self, page_id: TexturePageId) {
self.pages[page_id.0 as usize].is_new = false;
} }
#[inline] #[inline]

View File

@ -35,9 +35,9 @@ use std::sync::atomic::{AtomicUsize, Ordering};
use instant::Instant; use instant::Instant;
use std::u32; use std::u32;
pub(crate) struct SceneBuilder<'a> { pub(crate) struct SceneBuilder<'a, 'b> {
scene: &'a Scene, scene: &'a mut Scene,
built_options: &'a PreparedBuildOptions, built_options: &'b PreparedBuildOptions,
next_alpha_tile_index: AtomicUsize, next_alpha_tile_index: AtomicUsize,
pub(crate) listener: Box<dyn RenderCommandListener>, pub(crate) listener: Box<dyn RenderCommandListener>,
} }
@ -87,12 +87,12 @@ pub(crate) struct Occluder {
pub(crate) coords: Vector2I, pub(crate) coords: Vector2I,
} }
impl<'a> SceneBuilder<'a> { impl<'a, 'b> SceneBuilder<'a, 'b> {
pub(crate) fn new( pub(crate) fn new(
scene: &'a Scene, scene: &'a mut Scene,
built_options: &'a PreparedBuildOptions, built_options: &'b PreparedBuildOptions,
listener: Box<dyn RenderCommandListener>, listener: Box<dyn RenderCommandListener>,
) -> SceneBuilder<'a> { ) -> SceneBuilder<'a, 'b> {
SceneBuilder { SceneBuilder {
scene, scene,
built_options, built_options,
@ -120,7 +120,7 @@ impl<'a> SceneBuilder<'a> {
}); });
let render_transform = match self.built_options.transform { let render_transform = match self.built_options.transform {
PreparedRenderTransform::Transform2D(tr) => tr.inverse(), PreparedRenderTransform::Transform2D(transform) => transform.inverse(),
_ => Transform2F::default() _ => Transform2F::default()
}; };

View File

@ -123,7 +123,7 @@ where
alpha_tile_pages: Vec<AlphaTilePage<D>>, alpha_tile_pages: Vec<AlphaTilePage<D>>,
dest_blend_framebuffer: D::Framebuffer, dest_blend_framebuffer: D::Framebuffer,
intermediate_dest_framebuffer: D::Framebuffer, intermediate_dest_framebuffer: D::Framebuffer,
texture_pages: Vec<TexturePage<D>>, texture_pages: Vec<Option<TexturePage<D>>>,
render_targets: Vec<RenderTargetInfo>, render_targets: Vec<RenderTargetInfo>,
render_target_stack: Vec<RenderTargetId>, render_target_stack: Vec<RenderTargetId>,
texture_metadata_texture: D::Texture, texture_metadata_texture: D::Texture,
@ -304,8 +304,8 @@ where
RenderCommand::Start { bounding_quad, path_count, needs_readable_framebuffer } => { RenderCommand::Start { bounding_quad, path_count, needs_readable_framebuffer } => {
self.start_rendering(bounding_quad, path_count, needs_readable_framebuffer); self.start_rendering(bounding_quad, path_count, needs_readable_framebuffer);
} }
RenderCommand::AllocateTexturePages(ref texture_page_descriptors) => { RenderCommand::AllocateTexturePage { page_id, ref descriptor } => {
self.allocate_texture_pages(texture_page_descriptors) self.allocate_texture_page(page_id, descriptor)
} }
RenderCommand::UploadTexelData { ref texels, location } => { RenderCommand::UploadTexelData { ref texels, location } => {
self.upload_texel_data(texels, location) self.upload_texel_data(texels, location)
@ -379,6 +379,8 @@ where
self.flags.set(RendererFlags::INTERMEDIATE_DEST_FRAMEBUFFER_NEEDED, self.flags.set(RendererFlags::INTERMEDIATE_DEST_FRAMEBUFFER_NEEDED,
needs_readable_framebuffer); needs_readable_framebuffer);
self.render_targets.clear();
} }
pub fn draw_debug_ui(&self) { pub fn draw_debug_ui(&self) {
@ -440,29 +442,37 @@ where
&self.quad_vertex_indices_buffer &self.quad_vertex_indices_buffer
} }
fn allocate_texture_pages(&mut self, texture_page_descriptors: &[TexturePageDescriptor]) { fn allocate_texture_page(&mut self,
// Clear out old paint textures. page_id: TexturePageId,
for old_texture_page in self.texture_pages.drain(..) { descriptor: &TexturePageDescriptor) {
// Fill in IDs up to the requested page ID.
let page_index = page_id.0 as usize;
while self.texture_pages.len() < page_index + 1 {
self.texture_pages.push(None);
}
// Clear out any existing texture.
if let Some(old_texture_page) = self.texture_pages[page_index].take() {
let old_texture = self.device.destroy_framebuffer(old_texture_page.framebuffer); let old_texture = self.device.destroy_framebuffer(old_texture_page.framebuffer);
self.texture_cache.release_texture(old_texture); self.texture_cache.release_texture(old_texture);
} }
// Clear out old render targets. // Allocate texture.
self.render_targets.clear(); let texture_size = descriptor.size;
let texture = self.texture_cache.create_texture(&mut self.device,
// Allocate textures. TextureFormat::RGBA8,
for texture_page_descriptor in texture_page_descriptors { texture_size);
let texture_size = texture_page_descriptor.size; let framebuffer = self.device.create_framebuffer(texture);
let texture = self.texture_cache.create_texture(&mut self.device, self.texture_pages[page_index] = Some(TexturePage {
TextureFormat::RGBA8, framebuffer,
texture_size); must_preserve_contents: false,
let framebuffer = self.device.create_framebuffer(texture); });
self.texture_pages.push(TexturePage { framebuffer, must_preserve_contents: false });
}
} }
fn upload_texel_data(&mut self, texels: &[ColorU], location: TextureLocation) { fn upload_texel_data(&mut self, texels: &[ColorU], location: TextureLocation) {
let texture_page = &mut self.texture_pages[location.page.0 as usize]; let texture_page = self.texture_pages[location.page.0 as usize]
.as_mut()
.expect("Texture page not allocated yet!");
let texture = self.device.framebuffer_texture(&texture_page.framebuffer); let texture = self.device.framebuffer_texture(&texture_page.framebuffer);
let texels = color::color_slice_to_u8_slice(texels); let texels = color::color_slice_to_u8_slice(texels);
self.device.upload_to_texture(texture, location.rect, TextureDataRef::U8(texels)); self.device.upload_to_texture(texture, location.rect, TextureDataRef::U8(texels));
@ -1024,7 +1034,10 @@ where
let must_preserve_contents = match self.render_target_stack.last() { let must_preserve_contents = match self.render_target_stack.last() {
Some(&render_target_id) => { Some(&render_target_id) => {
let texture_page = self.render_target_location(render_target_id).page; let texture_page = self.render_target_location(render_target_id).page;
self.texture_pages[texture_page.0 as usize].must_preserve_contents self.texture_pages[texture_page.0 as usize]
.as_ref()
.expect("Draw target texture page not allocated!")
.must_preserve_contents
} }
None => { None => {
self.framebuffer_flags self.framebuffer_flags
@ -1045,7 +1058,10 @@ where
match self.render_target_stack.last() { match self.render_target_stack.last() {
Some(&render_target_id) => { Some(&render_target_id) => {
let texture_page = self.render_target_location(render_target_id).page; let texture_page = self.render_target_location(render_target_id).page;
self.texture_pages[texture_page.0 as usize].must_preserve_contents = true; self.texture_pages[texture_page.0 as usize]
.as_mut()
.expect("Draw target texture page not allocated!")
.must_preserve_contents = true;
} }
None => { None => {
self.framebuffer_flags self.framebuffer_flags
@ -1082,7 +1098,10 @@ where
} }
fn texture_page_framebuffer(&self, id: TexturePageId) -> &D::Framebuffer { fn texture_page_framebuffer(&self, id: TexturePageId) -> &D::Framebuffer {
&self.texture_pages[id.0 as usize].framebuffer &self.texture_pages[id.0 as usize]
.as_ref()
.expect("Texture page not allocated!")
.framebuffer
} }
fn texture_page(&self, id: TexturePageId) -> &D::Texture { fn texture_page(&self, id: TexturePageId) -> &D::Texture {

View File

@ -41,8 +41,8 @@ pub enum RenderCommand {
needs_readable_framebuffer: bool, needs_readable_framebuffer: bool,
}, },
// Allocates texture pages for the frame. // Allocates a texture page.
AllocateTexturePages(Vec<TexturePageDescriptor>), AllocateTexturePage { page_id: TexturePageId, descriptor: TexturePageDescriptor },
// Uploads data to a texture page. // Uploads data to a texture page.
UploadTexelData { texels: Arc<Vec<ColorU>>, location: TextureLocation }, UploadTexelData { texels: Arc<Vec<ColorU>>, location: TextureLocation },
@ -81,7 +81,7 @@ pub enum RenderCommand {
#[derive(Clone, Copy, PartialEq, Debug, Default)] #[derive(Clone, Copy, PartialEq, Debug, Default)]
pub struct TexturePageId(pub u32); pub struct TexturePageId(pub u32);
#[derive(Clone, Debug)] #[derive(Clone, Copy, Debug)]
pub struct TexturePageDescriptor { pub struct TexturePageDescriptor {
pub size: Vector2I, pub size: Vector2I,
} }
@ -190,8 +190,8 @@ impl Debug for RenderCommand {
fn fmt(&self, formatter: &mut Formatter) -> DebugResult { fn fmt(&self, formatter: &mut Formatter) -> DebugResult {
match *self { match *self {
RenderCommand::Start { .. } => write!(formatter, "Start"), RenderCommand::Start { .. } => write!(formatter, "Start"),
RenderCommand::AllocateTexturePages(ref pages) => { RenderCommand::AllocateTexturePage { page_id, descriptor: _ } => {
write!(formatter, "AllocateTexturePages(x{})", pages.len()) write!(formatter, "AllocateTexturePage({})", page_id.0)
} }
RenderCommand::UploadTexelData { ref texels, location } => { RenderCommand::UploadTexelData { ref texels, location } => {
write!(formatter, "UploadTexelData(x{:?}, {:?})", texels.len(), location) write!(formatter, "UploadTexelData(x{:?}, {:?})", texels.len(), location)

View File

@ -34,12 +34,19 @@ const GRADIENT_TILE_LENGTH: u32 = 256;
#[derive(Clone)] #[derive(Clone)]
pub struct Palette { pub struct Palette {
pub(crate) paints: Vec<Paint>, pub paints: Vec<Paint>,
pub(crate) render_targets: Vec<RenderTarget>, render_targets: Vec<RenderTargetData>,
cache: HashMap<Paint, PaintId>, cache: HashMap<Paint, PaintId>,
allocator: TextureAllocator,
scene_id: SceneId, scene_id: SceneId,
} }
#[derive(Clone)]
struct RenderTargetData {
render_target: RenderTarget,
metadata: RenderTargetMetadata,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)] #[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Paint { pub struct Paint {
base_color: ColorU, base_color: ColorU,
@ -86,7 +93,13 @@ impl Debug for PaintContents {
impl Palette { impl Palette {
#[inline] #[inline]
pub fn new(scene_id: SceneId) -> Palette { pub fn new(scene_id: SceneId) -> Palette {
Palette { paints: vec![], render_targets: vec![], cache: HashMap::new(), scene_id } Palette {
paints: vec![],
render_targets: vec![],
cache: HashMap::new(),
allocator: TextureAllocator::new(),
scene_id,
}
} }
} }
@ -305,7 +318,7 @@ pub struct RadialGradientMetadata {
pub radii: F32x2, pub radii: F32x2,
} }
#[derive(Debug)] #[derive(Clone, Copy, Debug)]
pub struct RenderTargetMetadata { pub struct RenderTargetMetadata {
/// The location of the render target. /// The location of the render target.
pub location: TextureLocation, pub location: TextureLocation,
@ -338,31 +351,30 @@ impl Palette {
pub fn push_render_target(&mut self, render_target: RenderTarget) -> RenderTargetId { pub fn push_render_target(&mut self, render_target: RenderTarget) -> RenderTargetId {
let id = self.render_targets.len() as u32; let id = self.render_targets.len() as u32;
self.render_targets.push(render_target);
let metadata = RenderTargetMetadata {
location: self.allocator.allocate_image(render_target.size()),
};
self.render_targets.push(RenderTargetData { render_target, metadata });
RenderTargetId { scene: self.scene_id.0, render_target: id } RenderTargetId { scene: self.scene_id.0, render_target: id }
} }
pub fn build_paint_info(&self, render_transform: Transform2F) -> PaintInfo { pub fn build_paint_info(&mut self, render_transform: Transform2F) -> PaintInfo {
let mut allocator = TextureAllocator::new(); let mut paint_metadata = vec![];
let (mut paint_metadata, mut render_target_metadata) = (vec![], vec![]);
// Assign render target locations.
for render_target in &self.render_targets {
render_target_metadata.push(RenderTargetMetadata {
location: allocator.allocate_image(render_target.size()),
});
}
// Assign paint locations. // Assign paint locations.
let mut gradient_tile_builder = GradientTileBuilder::new(); let mut gradient_tile_builder = GradientTileBuilder::new();
let mut image_texel_info = vec![]; let mut image_texel_info = vec![];
for paint in &self.paints { 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| { let color_texture_metadata = paint.overlay.as_ref().map(|overlay| {
match overlay.contents { match overlay.contents {
PaintContents::Gradient(ref gradient) => { PaintContents::Gradient(ref gradient) => {
// FIXME(pcwalton): The gradient size might not be big enough. Detect this. // FIXME(pcwalton): The gradient size might not be big enough. Detect this.
PaintColorTextureMetadata { PaintColorTextureMetadata {
location: gradient_tile_builder.allocate(&mut allocator, gradient), location: gradient_tile_builder.allocate(allocator, gradient),
sampling_flags: TextureSamplingFlags::empty(), sampling_flags: TextureSamplingFlags::empty(),
filter: match gradient.radii() { filter: match gradient.radii() {
None => PaintFilter::None, None => PaintFilter::None,
@ -379,7 +391,7 @@ impl Palette {
match *pattern.source() { match *pattern.source() {
PatternSource::RenderTarget { id: render_target_id, .. } => { PatternSource::RenderTarget { id: render_target_id, .. } => {
let index = render_target_id.render_target as usize; let index = render_target_id.render_target as usize;
location = render_target_metadata[index].location; location = render_targets[index].metadata.location;
} }
PatternSource::Image(ref image) => { PatternSource::Image(ref image) => {
// TODO(pcwalton): We should be able to use tile cleverness to // TODO(pcwalton): We should be able to use tile cleverness to
@ -435,7 +447,7 @@ impl Palette {
Some(ref mut color_texture_metadata) => color_texture_metadata, Some(ref mut color_texture_metadata) => color_texture_metadata,
}; };
let texture_scale = allocator.page_scale(color_texture_metadata.location.page); let texture_scale = self.allocator.page_scale(color_texture_metadata.location.page);
let texture_rect = color_texture_metadata.location.rect; let texture_rect = color_texture_metadata.location.rect;
color_texture_metadata.transform = match paint.overlay color_texture_metadata.transform = match paint.overlay
.as_ref() .as_ref()
@ -487,13 +499,6 @@ impl Palette {
} }
} }
// Allocate textures.
let mut texture_page_descriptors = vec![];
for page_index in 0..allocator.page_count() {
let page_size = allocator.page_size(TexturePageId(page_index));
texture_page_descriptors.push(TexturePageDescriptor { size: page_size });
}
// Create texture metadata. // Create texture metadata.
let texture_metadata = paint_metadata.iter().map(|paint_metadata| { let texture_metadata = paint_metadata.iter().map(|paint_metadata| {
TextureMetadataEntry { TextureMetadataEntry {
@ -504,12 +509,28 @@ impl Palette {
base_color: paint_metadata.base_color, base_color: paint_metadata.base_color,
} }
}).collect(); }).collect();
let mut render_commands = vec![RenderCommand::UploadTextureMetadata(texture_metadata)];
// 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);
let descriptor = TexturePageDescriptor { size: page_size };
texture_page_descriptors.push(descriptor);
if self.allocator.page_is_new(page_id) {
render_commands.push(RenderCommand::AllocateTexturePage { page_id, descriptor });
self.allocator.mark_page_as_allocated(page_id);
}
}
// 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. // Create render commands.
let mut render_commands = vec![
RenderCommand::UploadTextureMetadata(texture_metadata),
RenderCommand::AllocateTexturePages(texture_page_descriptors),
];
for (index, metadata) in render_target_metadata.iter().enumerate() { for (index, metadata) in render_target_metadata.iter().enumerate() {
let id = RenderTargetId { scene: self.scene_id.0, render_target: index as u32 }; let id = RenderTargetId { scene: self.scene_id.0, render_target: index as u32 };
render_commands.push(RenderCommand::DeclareRenderTarget { render_commands.push(RenderCommand::DeclareRenderTarget {
@ -527,6 +548,58 @@ impl Palette {
PaintInfo { render_commands, paint_metadata, render_target_metadata } PaintInfo { render_commands, paint_metadata, render_target_metadata }
} }
pub(crate) fn append_palette(&mut self, palette: Palette) -> MergedPaletteInfo {
// Merge render targets.
let mut render_target_mapping = HashMap::new();
for (old_render_target_index, render_target) in palette.render_targets
.into_iter()
.enumerate() {
let old_render_target_id = RenderTargetId {
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);
render_target_mapping.insert(old_render_target_id, new_render_target_id);
}
// Merge paints.
let mut paint_mapping = HashMap::new();
for (old_paint_index, old_paint) in palette.paints.iter().enumerate() {
let old_paint_id = PaintId(old_paint_index as u16);
let new_paint_id = match *old_paint.overlay() {
None => self.push_paint(old_paint),
Some(ref overlay) => {
match *overlay.contents() {
PaintContents::Pattern(ref 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.push_paint(&Paint::from_pattern(new_pattern))
}
_ => self.push_paint(old_paint),
}
}
_ => self.push_paint(old_paint),
}
}
};
paint_mapping.insert(old_paint_id, new_paint_id);
}
MergedPaletteInfo { render_target_mapping, paint_mapping }
}
}
pub(crate) struct MergedPaletteInfo {
pub(crate) render_target_mapping: HashMap<RenderTargetId, RenderTargetId>,
pub(crate) paint_mapping: HashMap<PaintId, PaintId>,
} }
impl PaintMetadata { impl PaintMetadata {

View File

@ -14,16 +14,14 @@ use crate::builder::SceneBuilder;
use crate::concurrent::executor::Executor; use crate::concurrent::executor::Executor;
use crate::options::{BuildOptions, PreparedBuildOptions}; use crate::options::{BuildOptions, PreparedBuildOptions};
use crate::options::{PreparedRenderTransform, RenderCommandListener}; use crate::options::{PreparedRenderTransform, RenderCommandListener};
use crate::paint::{Paint, PaintContents, PaintId, PaintInfo, Palette}; use crate::paint::{MergedPaletteInfo, Paint, PaintId, PaintInfo, Palette};
use pathfinder_content::effects::BlendMode; use pathfinder_content::effects::BlendMode;
use pathfinder_content::fill::FillRule; use pathfinder_content::fill::FillRule;
use pathfinder_content::outline::Outline; use pathfinder_content::outline::Outline;
use pathfinder_content::pattern::{Pattern, PatternSource};
use pathfinder_content::render_target::RenderTargetId; use pathfinder_content::render_target::RenderTargetId;
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::{Vector2I, vec2f}; use pathfinder_geometry::vector::{Vector2I, vec2f};
use std::collections::HashMap;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
static NEXT_SCENE_ID: AtomicUsize = AtomicUsize::new(0); static NEXT_SCENE_ID: AtomicUsize = AtomicUsize::new(0);
@ -97,49 +95,10 @@ impl Scene {
} }
pub fn append_scene(&mut self, scene: Scene) { pub fn append_scene(&mut self, scene: Scene) {
// Merge render targets. let MergedPaletteInfo {
let mut render_target_mapping = HashMap::new(); render_target_mapping,
for (old_render_target_index, render_target) in scene.palette paint_mapping,
.render_targets } = self.palette.append_palette(scene.palette);
.into_iter()
.enumerate() {
let old_render_target_id = RenderTargetId {
scene: scene.id.0,
render_target: old_render_target_index as u32,
};
let new_render_target_id = self.palette.push_render_target(render_target);
render_target_mapping.insert(old_render_target_id, new_render_target_id);
}
// Merge paints.
let mut paint_mapping = HashMap::new();
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.overlay() {
None => self.palette.push_paint(old_paint),
Some(ref overlay) => {
match *overlay.contents() {
PaintContents::Pattern(ref 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::from_pattern(new_pattern))
}
_ => self.palette.push_paint(old_paint),
}
}
_ => self.palette.push_paint(old_paint),
}
}
};
paint_mapping.insert(old_paint_id, new_paint_id);
}
// Merge clip paths. // Merge clip paths.
let mut clip_path_mapping = Vec::with_capacity(scene.clip_paths.len()); let mut clip_path_mapping = Vec::with_capacity(scene.clip_paths.len());
@ -187,7 +146,7 @@ impl Scene {
} }
#[inline] #[inline]
pub fn build_paint_info(&self, render_transform: Transform2F) -> PaintInfo { pub fn build_paint_info(&mut self, render_transform: Transform2F) -> PaintInfo {
self.palette.build_paint_info(render_transform) self.palette.build_paint_info(render_transform)
} }
@ -285,7 +244,7 @@ impl Scene {
} }
#[inline] #[inline]
pub fn build<E>(&self, pub fn build<E>(&mut self,
options: BuildOptions, options: BuildOptions,
listener: Box<dyn RenderCommandListener>, listener: Box<dyn RenderCommandListener>,
executor: &E) executor: &E)

View File

@ -28,8 +28,8 @@ const FLATTENING_TOLERANCE: f32 = 0.1;
pub const TILE_WIDTH: u32 = 16; pub const TILE_WIDTH: u32 = 16;
pub const TILE_HEIGHT: u32 = 16; pub const TILE_HEIGHT: u32 = 16;
pub(crate) struct Tiler<'a> { pub(crate) struct Tiler<'a, 'b> {
scene_builder: &'a SceneBuilder<'a>, scene_builder: &'a SceneBuilder<'b, 'a>,
pub(crate) object_builder: ObjectBuilder, pub(crate) object_builder: ObjectBuilder,
outline: &'a Outline, outline: &'a Outline,
path_info: TilingPathInfo<'a>, path_info: TilingPathInfo<'a>,
@ -53,15 +53,15 @@ pub(crate) struct DrawTilingPathInfo<'a> {
pub(crate) built_clip_path: Option<&'a BuiltPath>, pub(crate) built_clip_path: Option<&'a BuiltPath>,
} }
impl<'a> Tiler<'a> { impl<'a, 'b> Tiler<'a, 'b> {
#[allow(clippy::or_fun_call)] #[allow(clippy::or_fun_call)]
pub(crate) fn new( pub(crate) fn new(
scene_builder: &'a SceneBuilder<'a>, scene_builder: &'a SceneBuilder<'b, 'a>,
outline: &'a Outline, outline: &'a Outline,
fill_rule: FillRule, fill_rule: FillRule,
view_box: RectF, view_box: RectF,
path_info: TilingPathInfo<'a>, path_info: TilingPathInfo<'a>,
) -> Tiler<'a> { ) -> Tiler<'a, 'b> {
let bounds = outline let bounds = outline
.bounds() .bounds()
.intersection(view_box) .intersection(view_box)