diff --git a/renderer/src/builder.rs b/renderer/src/builder.rs index 854540c1..653ccc0b 100644 --- a/renderer/src/builder.rs +++ b/renderer/src/builder.rs @@ -110,10 +110,12 @@ impl<'a> SceneBuilder<'a> { // Build paint data. let PaintInfo { - data: texture_data, + render_commands, metadata: paint_metadata, } = self.scene.build_paint_info(); - self.listener.send(RenderCommand::AddTextureData(texture_data)); + for render_command in render_commands { + self.listener.send(render_command); + } let effective_view_box = self.scene.effective_view_box(self.built_options); diff --git a/renderer/src/gpu/renderer.rs b/renderer/src/gpu/renderer.rs index 824aaaeb..294a589c 100644 --- a/renderer/src/gpu/renderer.rs +++ b/renderer/src/gpu/renderer.rs @@ -20,10 +20,10 @@ 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, TextureData, TexturePageContents, TexturePageId}; +use crate::gpu_data::{SolidTileVertex, TexturePageDescriptor, TexturePageId}; use crate::options::BoundingQuad; use crate::tiles::{TILE_HEIGHT, TILE_WIDTH}; -use pathfinder_color::{self as color, ColorF}; +use pathfinder_color::{self as color, ColorF, ColorU}; use pathfinder_content::effects::{BlendMode, BlurDirection, CompositeOp, DefringingKernel}; use pathfinder_content::effects::{Effects, Filter}; use pathfinder_content::fill::FillRule; @@ -423,11 +423,20 @@ where } pub fn render_command(&mut self, command: &RenderCommand) { + println!("{:?}", command); match *command { RenderCommand::Start { bounding_quad, path_count, needs_readable_framebuffer } => { self.start_rendering(bounding_quad, path_count, needs_readable_framebuffer); } - RenderCommand::AddTextureData(ref paint_data) => self.add_texture_data(paint_data), + 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::DeclareRenderTarget { render_target_id, texture_page_id } => { + self.declare_render_target(render_target_id, texture_page_id) + } RenderCommand::AddFills(ref fills) => self.add_fills(fills), RenderCommand::FlushFills => { self.draw_buffered_fills(); @@ -570,7 +579,7 @@ where &self.quad_vertex_indices_buffer } - fn add_texture_data(&mut self, texture_data: &TextureData) { + fn allocate_texture_pages(&mut self, texture_page_descriptors: &[TexturePageDescriptor]) { // Clear out old paint textures. for old_texture_page in self.texture_pages.drain(..) { let old_texture = self.device.destroy_framebuffer(old_texture_page.framebuffer); @@ -580,33 +589,36 @@ where // Clear out old render targets. self.render_targets.clear(); - // Build up new paint textures and render targets. - for texture_page_data in &texture_data.pages { - let texture_size = texture_page_data.size; + // 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 texture_page_id = TexturePageId(self.texture_pages.len() as u32); - let must_preserve_contents; - match texture_page_data.contents { - TexturePageContents::RenderTarget(render_target_id) => { - debug_assert_eq!(render_target_id.0, self.render_targets.len() as u32); - self.render_targets.push(RenderTargetInfo { texture_page: texture_page_id }); - must_preserve_contents = false; - } - TexturePageContents::Texels(ref texels) => { - let texels = color::color_slice_to_u8_slice(texels); - self.device.upload_to_texture(&texture, - RectI::new(Vector2I::default(), texture_size), - TextureDataRef::U8(texels)); - must_preserve_contents = true; - } - } let framebuffer = self.device.create_framebuffer(texture); - self.texture_pages.push(TexturePage { framebuffer, must_preserve_contents }); + self.texture_pages.push(TexturePage { framebuffer, must_preserve_contents: false }); } } + 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]; + 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)); + texture_page.must_preserve_contents = true; + } + + fn declare_render_target(&mut self, + render_target_id: RenderTargetId, + texture_page_id: TexturePageId) { + while self.render_targets.len() < render_target_id.0 as usize + 1 { + self.render_targets.push(RenderTargetInfo { texture_page: TexturePageId(!0) }); + } + 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; + } + fn upload_mask_tiles(&mut self, mask_tiles: &[MaskTile], fill_rule: FillRule) { let vertex_array = match fill_rule { FillRule::Winding => &self.mask_winding_tile_vertex_array, diff --git a/renderer/src/gpu_data.rs b/renderer/src/gpu_data.rs index 977d0ab4..6289d418 100644 --- a/renderer/src/gpu_data.rs +++ b/renderer/src/gpu_data.rs @@ -16,6 +16,7 @@ use pathfinder_content::effects::{BlendMode, Effects}; use pathfinder_content::fill::FillRule; use pathfinder_content::render_target::RenderTargetId; use pathfinder_geometry::line_segment::{LineSegmentU4, LineSegmentU8}; +use pathfinder_geometry::rect::RectI; use pathfinder_geometry::vector::Vector2I; use pathfinder_gpu::TextureSamplingFlags; use std::fmt::{Debug, Formatter, Result as DebugResult}; @@ -37,8 +38,16 @@ pub enum RenderCommand { needs_readable_framebuffer: bool, }, - // Uploads texture data for use with subsequent rendering commands to the GPU. - AddTextureData(TextureData), + // Allocates texture pages for the frame. + AllocateTexturePages(Vec), + + // Uploads data to a texture page. + UploadTexelData { page: TexturePageId, texels: Vec, rect: RectI }, + + // 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 }, // Adds fills to the queue. AddFills(Vec), @@ -73,24 +82,12 @@ pub enum RenderCommand { Finish { build_time: Duration }, } -#[derive(Clone, Debug)] -pub struct TextureData { - pub pages: Vec, -} - #[derive(Clone, Copy, PartialEq, Debug)] pub struct TexturePageId(pub u32); #[derive(Clone, Debug)] -pub struct TexturePageData { +pub struct TexturePageDescriptor { pub size: Vector2I, - pub contents: TexturePageContents, -} - -#[derive(Clone, Debug)] -pub enum TexturePageContents { - Texels(Vec), - RenderTarget(RenderTargetId), } #[derive(Clone, Debug)] @@ -214,8 +211,17 @@ impl Debug for RenderCommand { fn fmt(&self, formatter: &mut Formatter) -> DebugResult { match *self { RenderCommand::Start { .. } => write!(formatter, "Start"), - RenderCommand::AddTextureData(ref texture_data) => { - write!(formatter, "AddTextureData(x{})", texture_data.pages.len()) + RenderCommand::AllocateTexturePages(ref pages) => { + write!(formatter, "AllocateTexturePages(x{})", pages.len()) + } + RenderCommand::UploadTexelData { page, rect, .. } => { + write!(formatter, "UploadTexelData({:?}, {:?})", page, rect) + } + RenderCommand::DeclareRenderTarget { render_target_id, texture_page_id } => { + write!(formatter, + "DeclareRenderTarget({:?}, {:?})", + render_target_id, + texture_page_id) } 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 20db6df7..1e942472 100644 --- a/renderer/src/paint.rs +++ b/renderer/src/paint.rs @@ -9,7 +9,7 @@ // except according to those terms. use crate::allocator::{AllocationMode, TextureAllocator, TextureLocation}; -use crate::gpu_data::{TextureData, TexturePageContents, TexturePageData, TexturePageId}; +use crate::gpu_data::{RenderCommand, TexturePageDescriptor, TexturePageId}; use crate::scene::RenderTarget; use crate::tiles::{TILE_HEIGHT, TILE_WIDTH}; use hashbrown::HashMap; @@ -24,6 +24,7 @@ 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. // @@ -153,8 +154,8 @@ impl Paint { } pub struct PaintInfo { - /// The data that is sent to the renderer. - pub data: TextureData, + /// The render commands needed to prepare the textures. + pub render_commands: Vec, /// The metadata for each paint. /// /// The indices of this vector are paint IDs. @@ -220,8 +221,9 @@ impl Palette { // 1. Use repeating/clamp on the sides. // 2. Choose an optimal size for the gradient that minimizes memory usage while // retaining quality. - texture_location = allocator.allocate(Vector2I::splat(GRADIENT_TILE_LENGTH as i32), - AllocationMode::Atlas); + texture_location = + allocator.allocate(Vector2I::splat(GRADIENT_TILE_LENGTH as i32), + AllocationMode::Atlas); sampling_flags = TextureSamplingFlags::empty(); } Paint::Pattern(ref pattern) => { @@ -297,67 +299,86 @@ impl Palette { } } - // Render the actual texels. - // - // TODO(pcwalton): This is slow. Do more on GPU. - let mut texture_data = TextureData { pages: vec![] }; + // Allocate textures. + let mut texture_page_descriptors = vec![]; for page_index in 0..allocator.page_count() { - let page_index = TexturePageId(page_index); - let page_size = allocator.page_size(page_index); - if let Some(render_target_id) = allocator.page_render_target_id(page_index) { - texture_data.pages.push(TexturePageData { - size: page_size, - contents: TexturePageContents::RenderTarget(render_target_id), - }); - continue; - } - - let page_area = page_size.x() as usize * page_size.y() as usize; - let texels = vec![ColorU::default(); page_area]; - texture_data.pages.push(TexturePageData { - size: page_size, - contents: TexturePageContents::Texels(texels), - }); + let page_size = allocator.page_size(TexturePageId(page_index)); + texture_page_descriptors.push(TexturePageDescriptor { size: page_size }); } + // 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]); + } + } + } + + // Draw to texels. + // + // TODO(pcwalton): Do more of this on GPU. for (paint, metadata) in self.paints.iter().zip(metadata.iter()) { let texture_page = metadata.texture_page; - let paint_page_data = &mut texture_data.pages[texture_page.0 as usize]; - let page_size = paint_page_data.size; + 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_page_data.contents { - TexturePageContents::Texels(ref mut texels) => { - match paint { - Paint::Color(color) => { - put_pixel(metadata.texture_rect.origin(), *color, texels, page_size); - } - Paint::Gradient(ref gradient) => { - self.render_gradient(gradient, - metadata.texture_rect, - &metadata.texture_transform, - texels, - page_size, - page_scale); - } - Paint::Pattern(ref pattern) => { - match pattern.source { - PatternSource::RenderTarget(_) => {} - PatternSource::Image(ref image) => { - self.render_image(image, - metadata.texture_rect, - texels, - page_size); - } - } + match paint { + Paint::Color(color) => { + put_pixel(metadata.texture_rect.origin(), *color, texels, page_size); + } + Paint::Gradient(ref gradient) => { + self.render_gradient(gradient, + metadata.texture_rect, + &metadata.texture_transform, + texels, + page_size, + page_scale); + } + Paint::Pattern(ref pattern) => { + match pattern.source { + PatternSource::RenderTarget(_) => {} + PatternSource::Image(ref image) => { + self.render_image(image, metadata.texture_rect, texels, page_size); } } } - TexturePageContents::RenderTarget(_) => {} } } - return PaintInfo { data: texture_data, metadata }; + // Create render commands. + 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), + }); + } + } + } + + PaintInfo { render_commands, metadata } } // TODO(pcwalton): This is slow. Do on GPU instead.