diff --git a/renderer/src/allocator.rs b/renderer/src/allocator.rs index b366271c..6ca303a8 100644 --- a/renderer/src/allocator.rs +++ b/renderer/src/allocator.rs @@ -16,12 +16,18 @@ use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i}; const ATLAS_TEXTURE_LENGTH: u32 = 1024; -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct TextureAllocator { - pages: Vec, + pages: Vec, } -#[derive(Debug)] +#[derive(Clone, Debug)] +pub struct TexturePage { + allocator: TexturePageAllocator, + is_new: bool, +} + +#[derive(Clone, Debug)] pub enum TexturePageAllocator { // An atlas allocated with our quadtree allocator. Atlas(TextureAtlasAllocator), @@ -29,13 +35,13 @@ pub enum TexturePageAllocator { Image { size: Vector2I }, } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct TextureAtlasAllocator { root: TreeNode, size: u32, } -#[derive(Debug)] +#[derive(Clone, Debug)] enum TreeNode { EmptyLeaf, FullLeaf, @@ -65,7 +71,7 @@ impl TextureAllocator { // Try to add to each atlas. for (page_index, page) in self.pages.iter_mut().enumerate() { - match *page { + match page.allocator { TexturePageAllocator::Image { .. } => {} TexturePageAllocator::Atlas(ref mut allocator) => { if let Some(rect) = allocator.allocate(requested_size) { @@ -79,26 +85,40 @@ impl TextureAllocator { let page = TexturePageId(self.pages.len() as u32); let mut allocator = TextureAtlasAllocator::new(); 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 } } pub fn allocate_image(&mut self, requested_size: Vector2I) -> TextureLocation { let page = TexturePageId(self.pages.len() as u32); 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 } } - pub fn page_size(&self, page_index: TexturePageId) -> Vector2I { - match self.pages[page_index.0 as usize] { + pub fn page_size(&self, page_id: TexturePageId) -> Vector2I { + match self.pages[page_id.0 as usize].allocator { TexturePageAllocator::Atlas(ref atlas) => Vector2I::splat(atlas.size as i32), TexturePageAllocator::Image { size, .. } => size, } } - pub fn page_scale(&self, page_index: TexturePageId) -> Vector2F { - vec2f(1.0, 1.0) / self.page_size(page_index).to_f32() + pub fn page_scale(&self, page_id: TexturePageId) -> Vector2F { + 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] diff --git a/renderer/src/builder.rs b/renderer/src/builder.rs index f07f1150..3724cbb6 100644 --- a/renderer/src/builder.rs +++ b/renderer/src/builder.rs @@ -35,9 +35,9 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use instant::Instant; use std::u32; -pub(crate) struct SceneBuilder<'a> { - scene: &'a Scene, - built_options: &'a PreparedBuildOptions, +pub(crate) struct SceneBuilder<'a, 'b> { + scene: &'a mut Scene, + built_options: &'b PreparedBuildOptions, next_alpha_tile_index: AtomicUsize, pub(crate) listener: Box, } @@ -87,12 +87,12 @@ pub(crate) struct Occluder { pub(crate) coords: Vector2I, } -impl<'a> SceneBuilder<'a> { +impl<'a, 'b> SceneBuilder<'a, 'b> { pub(crate) fn new( - scene: &'a Scene, - built_options: &'a PreparedBuildOptions, + scene: &'a mut Scene, + built_options: &'b PreparedBuildOptions, listener: Box, - ) -> SceneBuilder<'a> { + ) -> SceneBuilder<'a, 'b> { SceneBuilder { scene, built_options, @@ -120,7 +120,7 @@ impl<'a> SceneBuilder<'a> { }); let render_transform = match self.built_options.transform { - PreparedRenderTransform::Transform2D(tr) => tr.inverse(), + PreparedRenderTransform::Transform2D(transform) => transform.inverse(), _ => Transform2F::default() }; diff --git a/renderer/src/gpu/renderer.rs b/renderer/src/gpu/renderer.rs index c8e5fd5b..9346aeff 100644 --- a/renderer/src/gpu/renderer.rs +++ b/renderer/src/gpu/renderer.rs @@ -123,7 +123,7 @@ where alpha_tile_pages: Vec>, dest_blend_framebuffer: D::Framebuffer, intermediate_dest_framebuffer: D::Framebuffer, - texture_pages: Vec>, + texture_pages: Vec>>, render_targets: Vec, render_target_stack: Vec, texture_metadata_texture: D::Texture, @@ -304,8 +304,8 @@ where RenderCommand::Start { bounding_quad, path_count, needs_readable_framebuffer } => { self.start_rendering(bounding_quad, path_count, needs_readable_framebuffer); } - RenderCommand::AllocateTexturePages(ref texture_page_descriptors) => { - self.allocate_texture_pages(texture_page_descriptors) + RenderCommand::AllocateTexturePage { page_id, ref descriptor } => { + self.allocate_texture_page(page_id, descriptor) } RenderCommand::UploadTexelData { ref texels, location } => { self.upload_texel_data(texels, location) @@ -379,6 +379,8 @@ where self.flags.set(RendererFlags::INTERMEDIATE_DEST_FRAMEBUFFER_NEEDED, needs_readable_framebuffer); + + self.render_targets.clear(); } pub fn draw_debug_ui(&self) { @@ -440,29 +442,37 @@ where &self.quad_vertex_indices_buffer } - fn allocate_texture_pages(&mut self, texture_page_descriptors: &[TexturePageDescriptor]) { - // Clear out old paint textures. - for old_texture_page in self.texture_pages.drain(..) { + fn allocate_texture_page(&mut self, + page_id: TexturePageId, + 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); self.texture_cache.release_texture(old_texture); } - // Clear out old render targets. - self.render_targets.clear(); - - // Allocate textures. - for texture_page_descriptor in texture_page_descriptors { - let texture_size = texture_page_descriptor.size; - let texture = self.texture_cache.create_texture(&mut self.device, - TextureFormat::RGBA8, - texture_size); - let framebuffer = self.device.create_framebuffer(texture); - self.texture_pages.push(TexturePage { framebuffer, must_preserve_contents: false }); - } + // Allocate texture. + let texture_size = descriptor.size; + let texture = self.texture_cache.create_texture(&mut self.device, + TextureFormat::RGBA8, + texture_size); + let framebuffer = self.device.create_framebuffer(texture); + self.texture_pages[page_index] = Some(TexturePage { + framebuffer, + must_preserve_contents: false, + }); } 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 texels = color::color_slice_to_u8_slice(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() { Some(&render_target_id) => { 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 => { self.framebuffer_flags @@ -1045,7 +1058,10 @@ where match self.render_target_stack.last() { Some(&render_target_id) => { 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 => { self.framebuffer_flags @@ -1082,7 +1098,10 @@ where } 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 { diff --git a/renderer/src/gpu_data.rs b/renderer/src/gpu_data.rs index 4fbb0e1e..cfc43cf5 100644 --- a/renderer/src/gpu_data.rs +++ b/renderer/src/gpu_data.rs @@ -41,8 +41,8 @@ pub enum RenderCommand { needs_readable_framebuffer: bool, }, - // Allocates texture pages for the frame. - AllocateTexturePages(Vec), + // Allocates a texture page. + AllocateTexturePage { page_id: TexturePageId, descriptor: TexturePageDescriptor }, // Uploads data to a texture page. UploadTexelData { texels: Arc>, location: TextureLocation }, @@ -81,7 +81,7 @@ pub enum RenderCommand { #[derive(Clone, Copy, PartialEq, Debug, Default)] pub struct TexturePageId(pub u32); -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub struct TexturePageDescriptor { pub size: Vector2I, } @@ -190,8 +190,8 @@ impl Debug for RenderCommand { fn fmt(&self, formatter: &mut Formatter) -> DebugResult { match *self { RenderCommand::Start { .. } => write!(formatter, "Start"), - RenderCommand::AllocateTexturePages(ref pages) => { - write!(formatter, "AllocateTexturePages(x{})", pages.len()) + RenderCommand::AllocateTexturePage { page_id, descriptor: _ } => { + write!(formatter, "AllocateTexturePage({})", page_id.0) } RenderCommand::UploadTexelData { ref texels, location } => { write!(formatter, "UploadTexelData(x{:?}, {:?})", texels.len(), location) diff --git a/renderer/src/paint.rs b/renderer/src/paint.rs index 20b19619..5166b4b4 100644 --- a/renderer/src/paint.rs +++ b/renderer/src/paint.rs @@ -34,12 +34,19 @@ const GRADIENT_TILE_LENGTH: u32 = 256; #[derive(Clone)] pub struct Palette { - pub(crate) paints: Vec, - pub(crate) render_targets: Vec, + pub paints: Vec, + render_targets: Vec, cache: HashMap, + allocator: TextureAllocator, scene_id: SceneId, } +#[derive(Clone)] +struct RenderTargetData { + render_target: RenderTarget, + metadata: RenderTargetMetadata, +} + #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct Paint { base_color: ColorU, @@ -86,7 +93,13 @@ impl Debug for PaintContents { impl Palette { #[inline] 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, } -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] pub struct RenderTargetMetadata { /// The location of the render target. pub location: TextureLocation, @@ -338,31 +351,30 @@ impl Palette { pub fn push_render_target(&mut self, render_target: RenderTarget) -> RenderTargetId { 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 } } - pub fn build_paint_info(&self, render_transform: Transform2F) -> PaintInfo { - let mut allocator = TextureAllocator::new(); - 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()), - }); - } + pub fn build_paint_info(&mut self, render_transform: Transform2F) -> PaintInfo { + let mut paint_metadata = vec![]; // Assign paint locations. let mut gradient_tile_builder = GradientTileBuilder::new(); let mut image_texel_info = vec![]; 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) => { // FIXME(pcwalton): The gradient size might not be big enough. Detect this. PaintColorTextureMetadata { - location: gradient_tile_builder.allocate(&mut allocator, gradient), + location: gradient_tile_builder.allocate(allocator, gradient), sampling_flags: TextureSamplingFlags::empty(), filter: match gradient.radii() { None => PaintFilter::None, @@ -379,7 +391,7 @@ impl Palette { match *pattern.source() { PatternSource::RenderTarget { id: render_target_id, .. } => { 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) => { // 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, }; - 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; color_texture_metadata.transform = match paint.overlay .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. let texture_metadata = paint_metadata.iter().map(|paint_metadata| { TextureMetadataEntry { @@ -504,12 +509,28 @@ impl Palette { base_color: paint_metadata.base_color, } }).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. - let mut render_commands = vec![ - RenderCommand::UploadTextureMetadata(texture_metadata), - RenderCommand::AllocateTexturePages(texture_page_descriptors), - ]; 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 { @@ -527,6 +548,58 @@ impl Palette { 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, + pub(crate) paint_mapping: HashMap, } impl PaintMetadata { diff --git a/renderer/src/scene.rs b/renderer/src/scene.rs index effe6add..8a30e914 100644 --- a/renderer/src/scene.rs +++ b/renderer/src/scene.rs @@ -14,16 +14,14 @@ use crate::builder::SceneBuilder; use crate::concurrent::executor::Executor; use crate::options::{BuildOptions, PreparedBuildOptions}; 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::fill::FillRule; use pathfinder_content::outline::Outline; -use pathfinder_content::pattern::{Pattern, PatternSource}; use pathfinder_content::render_target::RenderTargetId; use pathfinder_geometry::rect::RectF; use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::vector::{Vector2I, vec2f}; -use std::collections::HashMap; use std::sync::atomic::{AtomicUsize, Ordering}; static NEXT_SCENE_ID: AtomicUsize = AtomicUsize::new(0); @@ -97,49 +95,10 @@ impl Scene { } pub fn append_scene(&mut self, scene: Scene) { - // Merge render targets. - let mut render_target_mapping = HashMap::new(); - for (old_render_target_index, render_target) in scene.palette - .render_targets - .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); - } + let MergedPaletteInfo { + render_target_mapping, + paint_mapping, + } = self.palette.append_palette(scene.palette); // Merge clip paths. let mut clip_path_mapping = Vec::with_capacity(scene.clip_paths.len()); @@ -187,7 +146,7 @@ impl Scene { } #[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) } @@ -285,7 +244,7 @@ impl Scene { } #[inline] - pub fn build(&self, + pub fn build(&mut self, options: BuildOptions, listener: Box, executor: &E) diff --git a/renderer/src/tiles.rs b/renderer/src/tiles.rs index da214bdc..36b35f23 100644 --- a/renderer/src/tiles.rs +++ b/renderer/src/tiles.rs @@ -28,8 +28,8 @@ const FLATTENING_TOLERANCE: f32 = 0.1; pub const TILE_WIDTH: u32 = 16; pub const TILE_HEIGHT: u32 = 16; -pub(crate) struct Tiler<'a> { - scene_builder: &'a SceneBuilder<'a>, +pub(crate) struct Tiler<'a, 'b> { + scene_builder: &'a SceneBuilder<'b, 'a>, pub(crate) object_builder: ObjectBuilder, outline: &'a Outline, path_info: TilingPathInfo<'a>, @@ -53,15 +53,15 @@ pub(crate) struct DrawTilingPathInfo<'a> { pub(crate) built_clip_path: Option<&'a BuiltPath>, } -impl<'a> Tiler<'a> { +impl<'a, 'b> Tiler<'a, 'b> { #[allow(clippy::or_fun_call)] pub(crate) fn new( - scene_builder: &'a SceneBuilder<'a>, + scene_builder: &'a SceneBuilder<'b, 'a>, outline: &'a Outline, fill_rule: FillRule, view_box: RectF, path_info: TilingPathInfo<'a>, - ) -> Tiler<'a> { + ) -> Tiler<'a, 'b> { let bounds = outline .bounds() .intersection(view_box)