diff --git a/renderer/src/allocator.rs b/renderer/src/allocator.rs index 36b105c8..2db36b9e 100644 --- a/renderer/src/allocator.rs +++ b/renderer/src/allocator.rs @@ -10,8 +10,7 @@ //! A simple quadtree-based texture allocator. -use crate::gpu_data::TexturePageId; -use pathfinder_content::render_target::RenderTargetId; +use crate::gpu_data::{TextureLocation, TexturePageId}; use pathfinder_geometry::rect::RectI; use pathfinder_geometry::vector::{Vector2F, Vector2I}; @@ -28,8 +27,6 @@ pub enum TexturePageAllocator { Atlas(TextureAtlasAllocator), // A single image. Image { size: Vector2I }, - // A render target. - RenderTarget { size: Vector2I, id: RenderTargetId }, } #[derive(Debug)] @@ -38,12 +35,6 @@ pub struct TextureAtlasAllocator { size: u32, } -#[derive(Clone, Copy, PartialEq, Debug)] -pub struct TextureLocation { - pub page: TexturePageId, - pub rect: RectI, -} - #[derive(Debug)] enum TreeNode { EmptyLeaf, @@ -75,8 +66,7 @@ impl TextureAllocator { // Try to add to each atlas. for (page_index, page) in self.pages.iter_mut().enumerate() { match *page { - TexturePageAllocator::Image { .. } | - TexturePageAllocator::RenderTarget { .. } => {} + TexturePageAllocator::Image { .. } => {} TexturePageAllocator::Atlas(ref mut allocator) => { if let Some(rect) = allocator.allocate(requested_size) { return TextureLocation { page: TexturePageId(page_index as u32), rect }; @@ -93,26 +83,17 @@ impl TextureAllocator { TextureLocation { page, rect } } - 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 rect = RectI::new(Vector2I::default(), requested_size); self.pages.push(TexturePageAllocator::Image { size: rect.size() }); TextureLocation { page, rect } } - pub fn allocate_render_target(&mut self, requested_size: Vector2I, id: RenderTargetId) - -> TextureLocation { - let page = TexturePageId(self.pages.len() as u32); - let rect = RectI::new(Vector2I::default(), requested_size); - self.pages.push(TexturePageAllocator::RenderTarget { size: rect.size(), id }); - TextureLocation { page, rect } - } - pub fn page_size(&self, page_index: TexturePageId) -> Vector2I { match self.pages[page_index.0 as usize] { TexturePageAllocator::Atlas(ref atlas) => Vector2I::splat(atlas.size as i32), - TexturePageAllocator::Image { size, .. } | - TexturePageAllocator::RenderTarget { size, .. } => size, + TexturePageAllocator::Image { size, .. } => size, } } @@ -124,14 +105,6 @@ impl TextureAllocator { pub fn page_count(&self) -> u32 { self.pages.len() as u32 } - - #[inline] - pub fn page_render_target_id(&self, page_index: TexturePageId) -> Option { - match self.pages[page_index.0 as usize] { - TexturePageAllocator::RenderTarget { id, .. } => Some(id), - TexturePageAllocator::Atlas(_) | TexturePageAllocator::Image { .. } => None, - } - } } impl TextureAtlasAllocator { diff --git a/renderer/src/gpu/renderer.rs b/renderer/src/gpu/renderer.rs index 0ed9e28c..4bdce8c2 100644 --- a/renderer/src/gpu/renderer.rs +++ b/renderer/src/gpu/renderer.rs @@ -20,7 +20,7 @@ use crate::gpu::shaders::{SolidTileVertexArray, StencilProgram, StencilVertexArr use crate::gpu::shaders::{TileFilterBasicProgram, TileFilterBlurProgram, TileFilterProgram}; use crate::gpu::shaders::{TileFilterTextProgram, TileFilterVertexArray}; use crate::gpu_data::{AlphaTile, FillBatchPrimitive, MaskTile, RenderCommand, RenderTargetTile}; -use crate::gpu_data::{SolidTileVertex, TexturePageDescriptor, TexturePageId}; +use crate::gpu_data::{SolidTileVertex, TextureLocation, TexturePageDescriptor, TexturePageId}; use crate::options::BoundingQuad; use crate::tiles::{TILE_HEIGHT, TILE_WIDTH}; use pathfinder_color::{self as color, ColorF, ColorU}; @@ -430,11 +430,11 @@ where RenderCommand::AllocateTexturePages(ref texture_page_descriptors) => { self.allocate_texture_pages(texture_page_descriptors) } - RenderCommand::UploadTexelData { page, ref texels, rect } => { - self.upload_texel_data(page, texels, rect) + RenderCommand::UploadTexelData { ref texels, location } => { + self.upload_texel_data(texels, location) } - RenderCommand::DeclareRenderTarget { render_target_id, texture_page_id } => { - self.declare_render_target(render_target_id, texture_page_id) + RenderCommand::DeclareRenderTarget { id, location } => { + self.declare_render_target(id, location) } RenderCommand::AddFills(ref fills) => self.add_fills(fills), RenderCommand::FlushFills => { @@ -599,23 +599,25 @@ where } } - fn upload_texel_data(&mut self, page_id: TexturePageId, texels: &[ColorU], rect: RectI) { - let texture_page = &mut self.texture_pages[page_id.0 as usize]; + fn upload_texel_data(&mut self, texels: &[ColorU], location: TextureLocation) { + let texture_page = &mut self.texture_pages[location.page.0 as usize]; let texture = self.device.framebuffer_texture(&texture_page.framebuffer); let texels = color::color_slice_to_u8_slice(texels); - self.device.upload_to_texture(texture, rect, TextureDataRef::U8(texels)); + self.device.upload_to_texture(texture, location.rect, TextureDataRef::U8(texels)); texture_page.must_preserve_contents = true; } fn declare_render_target(&mut self, render_target_id: RenderTargetId, - texture_page_id: TexturePageId) { + location: TextureLocation) { while self.render_targets.len() < render_target_id.0 as usize + 1 { - self.render_targets.push(RenderTargetInfo { texture_page: TexturePageId(!0) }); + self.render_targets.push(RenderTargetInfo { + location: TextureLocation { page: TexturePageId(!0), rect: RectI::default() }, + }); } let mut render_target = &mut self.render_targets[render_target_id.0 as usize]; - debug_assert_eq!(render_target.texture_page, TexturePageId(!0)); - render_target.texture_page = texture_page_id; + debug_assert_eq!(render_target.location.page, TexturePageId(!0)); + render_target.location = location; } fn upload_mask_tiles(&mut self, mask_tiles: &[MaskTile], fill_rule: FillRule) { @@ -1153,7 +1155,7 @@ where pub fn draw_render_target(&self) -> RenderTarget { match self.render_target_stack.last() { Some(&render_target_id) => { - let texture_page_id = self.render_target_texture_page_id(render_target_id); + let texture_page_id = self.render_target_location(render_target_id).page; let framebuffer = self.texture_page_framebuffer(texture_page_id); RenderTarget::Framebuffer(framebuffer) } @@ -1264,7 +1266,7 @@ where render_target_id: RenderTargetId, direction: BlurDirection, sigma: f32) { - let src_texture_page = self.render_target_texture_page_id(render_target_id); + let src_texture_page = self.render_target_location(render_target_id).page; let src_texture = self.texture_page(src_texture_page); let src_texture_size = self.device.texture_size(src_texture); @@ -1310,8 +1312,10 @@ where blend_state: Option) { let clear_color = self.clear_color_for_draw_operation(); let main_viewport = self.main_viewport(); - let src_texture_page = self.render_target_texture_page_id(render_target_id); - let src_texture = self.texture_page(src_texture_page); + + // TODO(pcwalton): Other viewports. + let src_texture_location = self.render_target_location(render_target_id); + let src_texture = self.texture_page(src_texture_location.page); let src_texture_size = self.device.texture_size(src_texture); uniforms.extend_from_slice(&[ @@ -1379,7 +1383,7 @@ where fn clear_color_for_draw_operation(&self) -> Option { let must_preserve_contents = match self.render_target_stack.last() { Some(&render_target_id) => { - let texture_page = self.render_target_texture_page_id(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 } None => { @@ -1400,7 +1404,7 @@ where fn preserve_draw_framebuffer(&mut self) { match self.render_target_stack.last() { Some(&render_target_id) => { - let texture_page = self.render_target_texture_page_id(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; } None => { @@ -1412,11 +1416,7 @@ where pub fn draw_viewport(&self) -> RectI { match self.render_target_stack.last() { - Some(&render_target_id) => { - let texture_page = self.render_target_texture_page_id(render_target_id); - let texture = self.texture_page(texture_page); - RectI::new(Vector2I::default(), self.device.texture_size(texture)) - } + Some(&render_target_id) => self.render_target_location(render_target_id).rect, None => self.main_viewport(), } } @@ -1438,8 +1438,8 @@ where Vector2I::new(MASK_FRAMEBUFFER_WIDTH, MASK_FRAMEBUFFER_HEIGHT)) } - fn render_target_texture_page_id(&self, render_target_id: RenderTargetId) -> TexturePageId { - self.render_targets[render_target_id.0 as usize].texture_page + fn render_target_location(&self, render_target_id: RenderTargetId) -> TextureLocation { + self.render_targets[render_target_id.0 as usize].location } fn texture_page_framebuffer(&self, id: TexturePageId) -> &D::Framebuffer { @@ -1584,7 +1584,7 @@ struct TexturePage where D: Device { } struct RenderTargetInfo { - texture_page: TexturePageId, + location: TextureLocation, } trait ToBlendState { diff --git a/renderer/src/gpu_data.rs b/renderer/src/gpu_data.rs index 6289d418..571a2c39 100644 --- a/renderer/src/gpu_data.rs +++ b/renderer/src/gpu_data.rs @@ -42,12 +42,12 @@ pub enum RenderCommand { AllocateTexturePages(Vec), // Uploads data to a texture page. - UploadTexelData { page: TexturePageId, texels: Vec, rect: RectI }, + UploadTexelData { texels: Vec, location: TextureLocation }, // Associates a render target with a texture page. // // TODO(pcwalton): Add a rect to this so we can render to subrects of a page. - DeclareRenderTarget { render_target_id: RenderTargetId, texture_page_id: TexturePageId }, + DeclareRenderTarget { id: RenderTargetId, location: TextureLocation }, // Adds fills to the queue. AddFills(Vec), @@ -90,6 +90,12 @@ pub struct TexturePageDescriptor { pub size: Vector2I, } +#[derive(Clone, Copy, PartialEq, Debug)] +pub struct TextureLocation { + pub page: TexturePageId, + pub rect: RectI, +} + #[derive(Clone, Debug)] pub struct AlphaTileBatch { pub tiles: Vec, @@ -214,14 +220,11 @@ impl Debug for RenderCommand { RenderCommand::AllocateTexturePages(ref pages) => { write!(formatter, "AllocateTexturePages(x{})", pages.len()) } - RenderCommand::UploadTexelData { page, rect, .. } => { - write!(formatter, "UploadTexelData({:?}, {:?})", page, rect) + RenderCommand::UploadTexelData { ref texels, location } => { + write!(formatter, "UploadTexelData({:?}, {:?})", texels, location) } - RenderCommand::DeclareRenderTarget { render_target_id, texture_page_id } => { - write!(formatter, - "DeclareRenderTarget({:?}, {:?})", - render_target_id, - texture_page_id) + RenderCommand::DeclareRenderTarget { id, location } => { + write!(formatter, "DeclareRenderTarget({:?}, {:?})", id, location) } RenderCommand::AddFills(ref fills) => write!(formatter, "AddFills(x{})", fills.len()), RenderCommand::FlushFills => write!(formatter, "FlushFills"), diff --git a/renderer/src/paint.rs b/renderer/src/paint.rs index 798402b7..b18581c5 100644 --- a/renderer/src/paint.rs +++ b/renderer/src/paint.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::allocator::{AllocationMode, TextureAllocator, TextureLocation}; -use crate::gpu_data::{RenderCommand, TexturePageDescriptor, TexturePageId}; +use crate::allocator::{AllocationMode, TextureAllocator}; +use crate::gpu_data::{RenderCommand, TextureLocation, TexturePageDescriptor, TexturePageId}; use crate::scene::RenderTarget; use crate::tiles::{TILE_HEIGHT, TILE_WIDTH}; use hashbrown::HashMap; @@ -24,7 +24,6 @@ use pathfinder_geometry::vector::{Vector2F, Vector2I}; use pathfinder_gpu::TextureSamplingFlags; use pathfinder_simd::default::{F32x2, F32x4}; use std::fmt::{self, Debug, Formatter}; -use std::mem; // The size of a gradient tile. // @@ -199,10 +198,8 @@ impl Palette { // Assign render target locations. let mut render_target_locations = vec![]; - for (render_target_index, render_target) in self.render_targets.iter().enumerate() { - let render_target_id = RenderTargetId(render_target_index as u32); - render_target_locations.push(allocator.allocate_render_target(render_target.size(), - render_target_id)); + for render_target in &self.render_targets { + render_target_locations.push(allocator.allocate_image(render_target.size())); } // Assign paint locations. @@ -313,18 +310,9 @@ impl Palette { // Allocate the texels. // // TODO(pcwalton): This is slow. Do more on GPU. - let mut page_texels = vec![]; - for page_index in 0..allocator.page_count() { - let page_id = TexturePageId(page_index); - let page_size = allocator.page_size(page_id); - match allocator.page_render_target_id(page_id) { - Some(_) => page_texels.push(vec![]), - None => { - let page_area = page_size.x() as usize * page_size.y() as usize; - page_texels.push(vec![ColorU::default(); page_area]); - } - } - } + let mut page_texels: Vec<_> = metadata.iter().map(|metadata| { + Texels::new(allocator.page_size(metadata.texture_page)) + }).collect(); // Draw to texels. // @@ -332,26 +320,22 @@ impl Palette { for (paint, metadata) in self.paints.iter().zip(metadata.iter()) { let texture_page = metadata.texture_page; let texels = &mut page_texels[texture_page.0 as usize]; - let page_size = allocator.page_size(texture_page); - let page_scale = allocator.page_scale(texture_page); match paint { Paint::Color(color) => { - put_pixel(metadata.texture_rect.origin(), *color, texels, page_size); + texels.put_texel(metadata.texture_rect.origin(), *color); } Paint::Gradient(ref gradient) => { self.render_gradient(gradient, metadata.texture_rect, &metadata.texture_transform, - texels, - page_size, - page_scale); + texels); } Paint::Pattern(ref pattern) => { match pattern.source { PatternSource::RenderTarget(_) => {} PatternSource::Image(ref image) => { - self.render_image(image, metadata.texture_rect, texels, page_size); + self.render_image(image, metadata.texture_rect, texels); } } } @@ -362,23 +346,19 @@ impl Palette { let mut render_commands = vec![ RenderCommand::AllocateTexturePages(texture_page_descriptors) ]; - for page_index in 0..allocator.page_count() { - let page_id = TexturePageId(page_index); - let page_size = allocator.page_size(page_id); - match allocator.page_render_target_id(page_id) { - Some(render_target_id) => { - render_commands.push(RenderCommand::DeclareRenderTarget { - render_target_id, - texture_page_id: page_id, - }); - } - None => { - render_commands.push(RenderCommand::UploadTexelData { - page: page_id, - texels: mem::replace(&mut page_texels[page_index as usize], vec![]), - rect: RectI::new(Vector2I::default(), page_size), - }); - } + for (index, location) in render_target_locations.into_iter().enumerate() { + let id = RenderTargetId(index as u32); + render_commands.push(RenderCommand::DeclareRenderTarget { id, location }); + } + for (page_index, texels) in page_texels.into_iter().enumerate() { + if let Some(texel_data) = texels.data { + let page_id = TexturePageId(page_index as u32); + let page_size = allocator.page_size(page_id); + let rect = RectI::new(Vector2I::default(), page_size); + render_commands.push(RenderCommand::UploadTexelData { + texels: texel_data, + location: TextureLocation { page: page_id, rect }, + }); } } @@ -390,9 +370,7 @@ impl Palette { gradient: &Gradient, tex_rect: RectI, tex_transform: &Transform2F, - texels: &mut [ColorU], - tex_size: Vector2I, - tex_scale: Vector2F) { + texels: &mut Texels) { match *gradient.geometry() { GradientGeometry::Linear(gradient_line) => { // FIXME(pcwalton): Paint transparent if gradient line has zero size, per spec. @@ -404,12 +382,13 @@ impl Palette { for y in 0..(GRADIENT_TILE_LENGTH as i32) { for x in 0..(GRADIENT_TILE_LENGTH as i32) { let point = tex_rect.origin() + Vector2I::new(x, y); - let vector = point.to_f32().scale_xy(tex_scale) - gradient_line.from(); + let vector = point.to_f32().scale_xy(texels.scale()) - + gradient_line.from(); let mut t = gradient_line.vector().projection_coefficient(vector); t = util::clamp(t, 0.0, 1.0); - put_pixel(point, gradient.sample(t), texels, tex_size); + texels.put_texel(point, gradient.sample(t)); } } } @@ -530,27 +509,20 @@ impl Palette { } }; - put_pixel(point, color, texels, tex_size); + texels.put_texel(point, color); } } } } } - fn render_image(&self, - image: &Image, - tex_rect: RectI, - texels: &mut [ColorU], - tex_size: Vector2I) { + fn render_image(&self, image: &Image, tex_rect: RectI, texels: &mut Texels) { let image_size = image.size(); for y in 0..image_size.y() { let dest_origin = tex_rect.origin() + Vector2I::new(0, y); - let dest_start_index = paint_texel_index(dest_origin, tex_size); let src_start_index = y as usize * image_size.x() as usize; - let dest_end_index = dest_start_index + image_size.x() as usize; let src_end_index = src_start_index + image_size.x() as usize; - texels[dest_start_index..dest_end_index].copy_from_slice( - &image.pixels()[src_start_index..src_end_index]); + texels.blit_scanline(dest_origin, &image.pixels()[src_start_index..src_end_index]); } } } @@ -565,12 +537,41 @@ impl PaintMetadata { } } -fn paint_texel_index(position: Vector2I, tex_size: Vector2I) -> usize { - position.y() as usize * tex_size.x() as usize + position.x() as usize +struct Texels { + data: Option>, + size: Vector2I, } -fn put_pixel(position: Vector2I, color: ColorU, texels: &mut [ColorU], tex_size: Vector2I) { - texels[paint_texel_index(position, tex_size)] = color +impl Texels { + fn new(size: Vector2I) -> Texels { + Texels { data: None, size } + } + + fn texel_index(&self, position: Vector2I) -> usize { + position.y() as usize * self.size.x() as usize + position.x() as usize + } + + fn allocate_texels_if_necessary(&mut self) { + if self.data.is_none() { + let area = self.size.x() as usize * self.size.y() as usize; + self.data = Some(vec![ColorU::transparent_black(); area]); + } + } + + fn blit_scanline(&mut self, dest_origin: Vector2I, src: &[ColorU]) { + self.allocate_texels_if_necessary(); + let start_index = self.texel_index(dest_origin); + let end_index = start_index + src.len(); + self.data.as_mut().unwrap()[start_index..end_index].copy_from_slice(src) + } + + fn put_texel(&mut self, position: Vector2I, color: ColorU) { + self.blit_scanline(position, &[color]) + } + + fn scale(&self) -> Vector2F { + Vector2F::splat(1.0) / self.size.to_f32() + } } fn rect_to_uv(rect: RectI, texture_scale: Vector2F) -> RectF {