From 1e84ddf1ff6daa7c732b7dcea52c9586b1f445ef Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Mon, 3 Feb 2020 22:24:34 -0800 Subject: [PATCH] Use the texture allocator to allocate solid colors --- renderer/src/allocator.rs | 3 ++ renderer/src/builder.rs | 34 ++++++++---- renderer/src/gpu_data.rs | 2 +- renderer/src/paint.rs | 107 ++++++++++++++++++++++++++++++++------ renderer/src/tiles.rs | 15 +++--- renderer/src/z_buffer.rs | 10 ++-- 6 files changed, 133 insertions(+), 38 deletions(-) diff --git a/renderer/src/allocator.rs b/renderer/src/allocator.rs index 98c06bce..bb69b3ae 100644 --- a/renderer/src/allocator.rs +++ b/renderer/src/allocator.rs @@ -47,12 +47,14 @@ impl TextureAllocator { } #[inline] + #[allow(dead_code)] pub fn free(&mut self, location: TextureLocation) { let requested_length = location.rect.width() as u32; self.root.free(Vector2I::default(), self.size, location.rect.origin(), requested_length) } #[inline] + #[allow(dead_code)] pub fn is_empty(&self) -> bool { match self.root { TreeNode::EmptyLeaf => true, @@ -126,6 +128,7 @@ impl TreeNode { } } + #[allow(dead_code)] fn free(&mut self, this_origin: Vector2I, this_size: u32, diff --git a/renderer/src/builder.rs b/renderer/src/builder.rs index dc6b7837..4204f516 100644 --- a/renderer/src/builder.rs +++ b/renderer/src/builder.rs @@ -13,6 +13,7 @@ use crate::concurrent::executor::Executor; use crate::gpu_data::{AlphaTileBatchPrimitive, BuiltObject, FillBatchPrimitive, RenderCommand}; use crate::options::{PreparedBuildOptions, RenderCommandListener}; +use crate::paint::{PaintInfo, PaintMetadata}; use crate::scene::Scene; use crate::tile_map::DenseTileMap; use crate::tiles::{self, TILE_HEIGHT, TILE_WIDTH, Tiler}; @@ -59,14 +60,22 @@ impl<'a> SceneBuilder<'a> { let path_count = self.scene.paths.len(); self.listener.send(RenderCommand::Start { bounding_quad, path_count }); - self.listener.send(RenderCommand::AddPaintData(self.scene.build_paint_data())); + let PaintInfo { + data: paint_data, + metadata: paint_metadata, + } = self.scene.build_paint_info(); + self.listener.send(RenderCommand::AddPaintData(paint_data)); let effective_view_box = self.scene.effective_view_box(self.built_options); let alpha_tiles = executor.flatten_into_vector(path_count, |path_index| { - self.build_path(path_index, effective_view_box, &self.built_options, &self.scene) + self.build_path(path_index, + effective_view_box, + &self.built_options, + &self.scene, + &paint_metadata) }); - self.finish_building(alpha_tiles); + self.finish_building(&paint_metadata, alpha_tiles); let build_time = Instant::now() - start_time; self.listener.send(RenderCommand::Finish { build_time }); @@ -78,18 +87,17 @@ impl<'a> SceneBuilder<'a> { view_box: RectF, built_options: &PreparedBuildOptions, scene: &Scene, + paint_metadata: &[PaintMetadata], ) -> Vec { let path_object = &scene.paths[path_index]; let outline = scene.apply_render_options(path_object.outline(), built_options); let paint_id = path_object.paint(); - let object_is_opaque = scene.paints[paint_id.0 as usize].is_opaque(); let mut tiler = Tiler::new(self, &outline, view_box, path_index as u16, - paint_id, - object_is_opaque); + &paint_metadata[paint_id.0 as usize]); tiler.generate_tiles(); @@ -114,9 +122,13 @@ impl<'a> SceneBuilder<'a> { } } - fn pack_alpha_tiles(&mut self, alpha_tiles: Vec) { + fn pack_alpha_tiles(&mut self, + paint_metadata: &[PaintMetadata], + alpha_tiles: Vec) { let path_count = self.scene.paths.len() as u32; - let solid_tiles = self.z_buffer.build_solid_tiles(&self.scene.paths, 0..path_count); + let solid_tiles = self.z_buffer.build_solid_tiles(&self.scene.paths, + paint_metadata, + 0..path_count); if !solid_tiles.is_empty() { self.listener.send(RenderCommand::SolidTile(solid_tiles)); } @@ -125,10 +137,12 @@ impl<'a> SceneBuilder<'a> { } } - fn finish_building(&mut self, mut alpha_tiles: Vec) { + fn finish_building(&mut self, + paint_metadata: &[PaintMetadata], + mut alpha_tiles: Vec) { self.listener.send(RenderCommand::FlushFills); self.cull_alpha_tiles(&mut alpha_tiles); - self.pack_alpha_tiles(alpha_tiles); + self.pack_alpha_tiles(paint_metadata, alpha_tiles); } } diff --git a/renderer/src/gpu_data.rs b/renderer/src/gpu_data.rs index 6e65cb46..81bc9289 100644 --- a/renderer/src/gpu_data.rs +++ b/renderer/src/gpu_data.rs @@ -13,8 +13,8 @@ use crate::options::BoundingQuad; use crate::tile_map::DenseTileMap; use pathfinder_geometry::line_segment::{LineSegmentU4, LineSegmentU8}; -use pathfinder_geometry::vector::Vector2I; use pathfinder_geometry::rect::RectF; +use pathfinder_geometry::vector::Vector2I; use std::fmt::{Debug, Formatter, Result as DebugResult}; use std::time::Duration; diff --git a/renderer/src/paint.rs b/renderer/src/paint.rs index 63016047..a3d2872e 100644 --- a/renderer/src/paint.rs +++ b/renderer/src/paint.rs @@ -8,13 +8,18 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use crate::allocator::{TextureAllocator, TextureLocation}; use crate::gpu_data::PaintData; use crate::scene::Scene; use pathfinder_color::ColorU; +use pathfinder_geometry::rect::RectI; use pathfinder_geometry::vector::Vector2I; -const PAINT_TEXTURE_WIDTH: i32 = 256; -const PAINT_TEXTURE_HEIGHT: i32 = 256; +const PAINT_TEXTURE_LENGTH: u32 = 1024; +const PAINT_TEXTURE_SCALE: u32 = 65536 / PAINT_TEXTURE_LENGTH; + +const SOLID_COLOR_TILE_LENGTH: u32 = 16; +const MAX_SOLID_COLORS_PER_TILE: u32 = SOLID_COLOR_TILE_LENGTH * SOLID_COLOR_TILE_LENGTH; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct Paint { @@ -36,22 +41,94 @@ impl Paint { } } +pub struct PaintInfo { + /// The data that is sent to the renderer. + pub data: PaintData, + /// The metadata for each paint. + /// + /// The indices of this vector are paint IDs. + pub metadata: Vec, +} + +#[derive(Debug)] +pub struct PaintMetadata { + /// The texture coordinates of each paint, in 24.8 fixed point. + pub tex_coords: RectI, + /// True if this paint is fully opaque. + pub is_opaque: bool, +} + impl Scene { - pub fn build_paint_data(&self) -> PaintData { - let size = Vector2I::new(PAINT_TEXTURE_WIDTH, PAINT_TEXTURE_HEIGHT); - let mut texels = vec![0; size.x() as usize * size.y() as usize * 4]; - for (paint_index, paint) in self.paints.iter().enumerate() { - texels[paint_index * 4 + 0] = paint.color.r; - texels[paint_index * 4 + 1] = paint.color.g; - texels[paint_index * 4 + 2] = paint.color.b; - texels[paint_index * 4 + 3] = paint.color.a; + pub fn build_paint_info(&self) -> PaintInfo { + let mut allocator = TextureAllocator::new(PAINT_TEXTURE_LENGTH); + let area = PAINT_TEXTURE_LENGTH as usize * PAINT_TEXTURE_LENGTH as usize; + let (mut texels, mut metadata) = (vec![0; area * 4], vec![]); + let mut solid_color_tile_builder = SolidColorTileBuilder::new(); + + for paint in &self.paints { + // TODO(pcwalton): Handle other paint types. + let texture_location = solid_color_tile_builder.allocate(&mut allocator); + put_pixel(&mut texels, texture_location.rect.origin(), paint.color); + let tex_coords = + RectI::new(texture_location.rect.origin().scale(PAINT_TEXTURE_SCALE as i32) + + Vector2I::splat(PAINT_TEXTURE_SCALE as i32 / 2), + Vector2I::default()); + metadata.push(PaintMetadata { tex_coords, is_opaque: paint.is_opaque() }); + } + + let size = Vector2I::splat(PAINT_TEXTURE_LENGTH as i32); + return PaintInfo { data: PaintData { size, texels }, metadata }; + + fn put_pixel(texels: &mut [u8], position: Vector2I, color: ColorU) { + let index = (position.y() as usize * PAINT_TEXTURE_LENGTH as usize + + position.x() as usize) * 4; + texels[index + 0] = color.r; + texels[index + 1] = color.g; + texels[index + 2] = color.b; + texels[index + 3] = color.a; } - PaintData { size, texels } } } -pub(crate) fn paint_id_to_tex_coords(paint_id: PaintId) -> Vector2I { - let tex_coords = Vector2I::new(paint_id.0 as i32 % PAINT_TEXTURE_WIDTH, - paint_id.0 as i32 / PAINT_TEXTURE_WIDTH); - tex_coords.scale(256) + Vector2I::new(128, 128) +struct SolidColorTileBuilder(Option); + +struct SolidColorTileBuilderData { + tile_location: TextureLocation, + next_index: u32, +} + +impl SolidColorTileBuilder { + fn new() -> SolidColorTileBuilder { + SolidColorTileBuilder(None) + } + + fn allocate(&mut self, allocator: &mut TextureAllocator) -> TextureLocation { + if self.0.is_none() { + // TODO(pcwalton): Handle allocation failure gracefully! + self.0 = Some(SolidColorTileBuilderData { + tile_location: allocator.allocate(Vector2I::splat(SOLID_COLOR_TILE_LENGTH as i32)) + .expect("Failed to allocate a solid color tile!"), + next_index: 0, + }); + } + + let (location, tile_full); + { + let mut data = self.0.as_mut().unwrap(); + let subtile_origin = Vector2I::new((data.next_index % SOLID_COLOR_TILE_LENGTH) as i32, + (data.next_index / SOLID_COLOR_TILE_LENGTH) as i32); + location = TextureLocation { + rect: RectI::new(data.tile_location.rect.origin() + subtile_origin, + Vector2I::splat(1)), + }; + data.next_index += 1; + tile_full = data.next_index == MAX_SOLID_COLORS_PER_TILE; + } + + if tile_full { + self.0 = None; + } + + location + } } diff --git a/renderer/src/tiles.rs b/renderer/src/tiles.rs index 60384f8f..b256f884 100644 --- a/renderer/src/tiles.rs +++ b/renderer/src/tiles.rs @@ -10,7 +10,7 @@ use crate::builder::SceneBuilder; use crate::gpu_data::{AlphaTileBatchPrimitive, BuiltObject, TileObjectPrimitive}; -use crate::paint::{self, PaintId}; +use crate::paint::PaintMetadata; use crate::sorted_vector::SortedVector; use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::vector::{Vector2F, Vector2I}; @@ -30,9 +30,8 @@ pub(crate) struct Tiler<'a> { builder: &'a SceneBuilder<'a>, outline: &'a Outline, pub built_object: BuiltObject, - paint_id: PaintId, + paint_metadata: &'a PaintMetadata, object_index: u16, - object_is_opaque: bool, point_queue: SortedVector, active_edges: SortedVector, @@ -46,8 +45,7 @@ impl<'a> Tiler<'a> { outline: &'a Outline, view_box: RectF, object_index: u16, - paint_id: PaintId, - object_is_opaque: bool, + paint_metadata: &'a PaintMetadata, ) -> Tiler<'a> { let bounds = outline .bounds() @@ -60,8 +58,7 @@ impl<'a> Tiler<'a> { outline, built_object, object_index, - paint_id, - object_is_opaque, + paint_metadata, point_queue: SortedVector::new(), active_edges: SortedVector::new(), @@ -122,13 +119,13 @@ impl<'a> Tiler<'a> { } // If this is a solid tile, poke it into the Z-buffer and stop here. - if self.object_is_opaque { + if self.paint_metadata.is_opaque { self.builder.z_buffer.update(tile_coords, self.object_index); continue; } } - let origin_uv = paint::paint_id_to_tex_coords(self.paint_id); + let origin_uv = self.paint_metadata.tex_coords.origin(); let alpha_tile = AlphaTileBatchPrimitive::new( tile_coords, diff --git a/renderer/src/z_buffer.rs b/renderer/src/z_buffer.rs index ae4bdf9e..6b8187d7 100644 --- a/renderer/src/z_buffer.rs +++ b/renderer/src/z_buffer.rs @@ -11,7 +11,7 @@ //! Software occlusion culling. use crate::gpu_data::SolidTileBatchPrimitive; -use crate::paint; +use crate::paint::PaintMetadata; use crate::scene::PathObject; use crate::tile_map::DenseTileMap; use crate::tiles; @@ -56,7 +56,10 @@ impl ZBuffer { } } - pub fn build_solid_tiles(&self, paths: &[PathObject], object_range: Range) + pub fn build_solid_tiles(&self, + paths: &[PathObject], + paint_metadata: &[PaintMetadata], + object_range: Range) -> Vec { let mut solid_tiles = vec![]; for tile_index in 0..self.buffer.data.len() { @@ -71,7 +74,8 @@ impl ZBuffer { continue; } - let origin_uv = paint::paint_id_to_tex_coords(paths[object_index as usize].paint()); + let paint_id = paths[object_index as usize].paint(); + let origin_uv = paint_metadata[paint_id.0 as usize].tex_coords.origin(); solid_tiles.push(SolidTileBatchPrimitive::new(tile_coords + self.buffer.rect.origin(), object_index as u16,