Separate the `AddPaintData` render command into different subcommands

This commit is contained in:
Patrick Walton 2020-03-04 15:49:38 -08:00
parent e50039f4c2
commit 27357a23d3
4 changed files with 137 additions and 96 deletions

View File

@ -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);

View File

@ -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,

View File

@ -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<TexturePageDescriptor>),
// Uploads data to a texture page.
UploadTexelData { page: TexturePageId, texels: Vec<ColorU>, 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<FillBatchPrimitive>),
@ -73,24 +82,12 @@ pub enum RenderCommand {
Finish { build_time: Duration },
}
#[derive(Clone, Debug)]
pub struct TextureData {
pub pages: Vec<TexturePageData>,
}
#[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<ColorU>),
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"),

View File

@ -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<RenderCommand>,
/// 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.