Use the texture allocator to allocate solid colors

This commit is contained in:
Patrick Walton 2020-02-03 22:24:34 -08:00
parent b8f622203a
commit 1e84ddf1ff
6 changed files with 133 additions and 38 deletions

View File

@ -47,12 +47,14 @@ impl TextureAllocator {
} }
#[inline] #[inline]
#[allow(dead_code)]
pub fn free(&mut self, location: TextureLocation) { pub fn free(&mut self, location: TextureLocation) {
let requested_length = location.rect.width() as u32; let requested_length = location.rect.width() as u32;
self.root.free(Vector2I::default(), self.size, location.rect.origin(), requested_length) self.root.free(Vector2I::default(), self.size, location.rect.origin(), requested_length)
} }
#[inline] #[inline]
#[allow(dead_code)]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
match self.root { match self.root {
TreeNode::EmptyLeaf => true, TreeNode::EmptyLeaf => true,
@ -126,6 +128,7 @@ impl TreeNode {
} }
} }
#[allow(dead_code)]
fn free(&mut self, fn free(&mut self,
this_origin: Vector2I, this_origin: Vector2I,
this_size: u32, this_size: u32,

View File

@ -13,6 +13,7 @@
use crate::concurrent::executor::Executor; use crate::concurrent::executor::Executor;
use crate::gpu_data::{AlphaTileBatchPrimitive, BuiltObject, FillBatchPrimitive, RenderCommand}; use crate::gpu_data::{AlphaTileBatchPrimitive, BuiltObject, FillBatchPrimitive, RenderCommand};
use crate::options::{PreparedBuildOptions, RenderCommandListener}; use crate::options::{PreparedBuildOptions, RenderCommandListener};
use crate::paint::{PaintInfo, PaintMetadata};
use crate::scene::Scene; use crate::scene::Scene;
use crate::tile_map::DenseTileMap; use crate::tile_map::DenseTileMap;
use crate::tiles::{self, TILE_HEIGHT, TILE_WIDTH, Tiler}; use crate::tiles::{self, TILE_HEIGHT, TILE_WIDTH, Tiler};
@ -59,14 +60,22 @@ impl<'a> SceneBuilder<'a> {
let path_count = self.scene.paths.len(); let path_count = self.scene.paths.len();
self.listener.send(RenderCommand::Start { bounding_quad, path_count }); 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 effective_view_box = self.scene.effective_view_box(self.built_options);
let alpha_tiles = executor.flatten_into_vector(path_count, |path_index| { 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; let build_time = Instant::now() - start_time;
self.listener.send(RenderCommand::Finish { build_time }); self.listener.send(RenderCommand::Finish { build_time });
@ -78,18 +87,17 @@ impl<'a> SceneBuilder<'a> {
view_box: RectF, view_box: RectF,
built_options: &PreparedBuildOptions, built_options: &PreparedBuildOptions,
scene: &Scene, scene: &Scene,
paint_metadata: &[PaintMetadata],
) -> Vec<AlphaTileBatchPrimitive> { ) -> Vec<AlphaTileBatchPrimitive> {
let path_object = &scene.paths[path_index]; let path_object = &scene.paths[path_index];
let outline = scene.apply_render_options(path_object.outline(), built_options); let outline = scene.apply_render_options(path_object.outline(), built_options);
let paint_id = path_object.paint(); 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, let mut tiler = Tiler::new(self,
&outline, &outline,
view_box, view_box,
path_index as u16, path_index as u16,
paint_id, &paint_metadata[paint_id.0 as usize]);
object_is_opaque);
tiler.generate_tiles(); tiler.generate_tiles();
@ -114,9 +122,13 @@ impl<'a> SceneBuilder<'a> {
} }
} }
fn pack_alpha_tiles(&mut self, alpha_tiles: Vec<AlphaTileBatchPrimitive>) { fn pack_alpha_tiles(&mut self,
paint_metadata: &[PaintMetadata],
alpha_tiles: Vec<AlphaTileBatchPrimitive>) {
let path_count = self.scene.paths.len() as u32; 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() { if !solid_tiles.is_empty() {
self.listener.send(RenderCommand::SolidTile(solid_tiles)); self.listener.send(RenderCommand::SolidTile(solid_tiles));
} }
@ -125,10 +137,12 @@ impl<'a> SceneBuilder<'a> {
} }
} }
fn finish_building(&mut self, mut alpha_tiles: Vec<AlphaTileBatchPrimitive>) { fn finish_building(&mut self,
paint_metadata: &[PaintMetadata],
mut alpha_tiles: Vec<AlphaTileBatchPrimitive>) {
self.listener.send(RenderCommand::FlushFills); self.listener.send(RenderCommand::FlushFills);
self.cull_alpha_tiles(&mut alpha_tiles); self.cull_alpha_tiles(&mut alpha_tiles);
self.pack_alpha_tiles(alpha_tiles); self.pack_alpha_tiles(paint_metadata, alpha_tiles);
} }
} }

View File

@ -13,8 +13,8 @@
use crate::options::BoundingQuad; use crate::options::BoundingQuad;
use crate::tile_map::DenseTileMap; use crate::tile_map::DenseTileMap;
use pathfinder_geometry::line_segment::{LineSegmentU4, LineSegmentU8}; use pathfinder_geometry::line_segment::{LineSegmentU4, LineSegmentU8};
use pathfinder_geometry::vector::Vector2I;
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::vector::Vector2I;
use std::fmt::{Debug, Formatter, Result as DebugResult}; use std::fmt::{Debug, Formatter, Result as DebugResult};
use std::time::Duration; use std::time::Duration;

View File

@ -8,13 +8,18 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use crate::allocator::{TextureAllocator, TextureLocation};
use crate::gpu_data::PaintData; use crate::gpu_data::PaintData;
use crate::scene::Scene; use crate::scene::Scene;
use pathfinder_color::ColorU; use pathfinder_color::ColorU;
use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::vector::Vector2I; use pathfinder_geometry::vector::Vector2I;
const PAINT_TEXTURE_WIDTH: i32 = 256; const PAINT_TEXTURE_LENGTH: u32 = 1024;
const PAINT_TEXTURE_HEIGHT: i32 = 256; 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)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Paint { 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<PaintMetadata>,
}
#[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 { impl Scene {
pub fn build_paint_data(&self) -> PaintData { pub fn build_paint_info(&self) -> PaintInfo {
let size = Vector2I::new(PAINT_TEXTURE_WIDTH, PAINT_TEXTURE_HEIGHT); let mut allocator = TextureAllocator::new(PAINT_TEXTURE_LENGTH);
let mut texels = vec![0; size.x() as usize * size.y() as usize * 4]; let area = PAINT_TEXTURE_LENGTH as usize * PAINT_TEXTURE_LENGTH as usize;
for (paint_index, paint) in self.paints.iter().enumerate() { let (mut texels, mut metadata) = (vec![0; area * 4], vec![]);
texels[paint_index * 4 + 0] = paint.color.r; let mut solid_color_tile_builder = SolidColorTileBuilder::new();
texels[paint_index * 4 + 1] = paint.color.g;
texels[paint_index * 4 + 2] = paint.color.b; for paint in &self.paints {
texels[paint_index * 4 + 3] = paint.color.a; // 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 { struct SolidColorTileBuilder(Option<SolidColorTileBuilderData>);
let tex_coords = Vector2I::new(paint_id.0 as i32 % PAINT_TEXTURE_WIDTH,
paint_id.0 as i32 / PAINT_TEXTURE_WIDTH); struct SolidColorTileBuilderData {
tex_coords.scale(256) + Vector2I::new(128, 128) 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
}
} }

View File

@ -10,7 +10,7 @@
use crate::builder::SceneBuilder; use crate::builder::SceneBuilder;
use crate::gpu_data::{AlphaTileBatchPrimitive, BuiltObject, TileObjectPrimitive}; use crate::gpu_data::{AlphaTileBatchPrimitive, BuiltObject, TileObjectPrimitive};
use crate::paint::{self, PaintId}; use crate::paint::PaintMetadata;
use crate::sorted_vector::SortedVector; use crate::sorted_vector::SortedVector;
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::vector::{Vector2F, Vector2I}; use pathfinder_geometry::vector::{Vector2F, Vector2I};
@ -30,9 +30,8 @@ pub(crate) struct Tiler<'a> {
builder: &'a SceneBuilder<'a>, builder: &'a SceneBuilder<'a>,
outline: &'a Outline, outline: &'a Outline,
pub built_object: BuiltObject, pub built_object: BuiltObject,
paint_id: PaintId, paint_metadata: &'a PaintMetadata,
object_index: u16, object_index: u16,
object_is_opaque: bool,
point_queue: SortedVector<QueuedEndpoint>, point_queue: SortedVector<QueuedEndpoint>,
active_edges: SortedVector<ActiveEdge>, active_edges: SortedVector<ActiveEdge>,
@ -46,8 +45,7 @@ impl<'a> Tiler<'a> {
outline: &'a Outline, outline: &'a Outline,
view_box: RectF, view_box: RectF,
object_index: u16, object_index: u16,
paint_id: PaintId, paint_metadata: &'a PaintMetadata,
object_is_opaque: bool,
) -> Tiler<'a> { ) -> Tiler<'a> {
let bounds = outline let bounds = outline
.bounds() .bounds()
@ -60,8 +58,7 @@ impl<'a> Tiler<'a> {
outline, outline,
built_object, built_object,
object_index, object_index,
paint_id, paint_metadata,
object_is_opaque,
point_queue: SortedVector::new(), point_queue: SortedVector::new(),
active_edges: 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 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); self.builder.z_buffer.update(tile_coords, self.object_index);
continue; 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( let alpha_tile = AlphaTileBatchPrimitive::new(
tile_coords, tile_coords,

View File

@ -11,7 +11,7 @@
//! Software occlusion culling. //! Software occlusion culling.
use crate::gpu_data::SolidTileBatchPrimitive; use crate::gpu_data::SolidTileBatchPrimitive;
use crate::paint; use crate::paint::PaintMetadata;
use crate::scene::PathObject; use crate::scene::PathObject;
use crate::tile_map::DenseTileMap; use crate::tile_map::DenseTileMap;
use crate::tiles; use crate::tiles;
@ -56,7 +56,10 @@ impl ZBuffer {
} }
} }
pub fn build_solid_tiles(&self, paths: &[PathObject], object_range: Range<u32>) pub fn build_solid_tiles(&self,
paths: &[PathObject],
paint_metadata: &[PaintMetadata],
object_range: Range<u32>)
-> Vec<SolidTileBatchPrimitive> { -> Vec<SolidTileBatchPrimitive> {
let mut solid_tiles = vec![]; let mut solid_tiles = vec![];
for tile_index in 0..self.buffer.data.len() { for tile_index in 0..self.buffer.data.len() {
@ -71,7 +74,8 @@ impl ZBuffer {
continue; 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(), solid_tiles.push(SolidTileBatchPrimitive::new(tile_coords + self.buffer.rect.origin(),
object_index as u16, object_index as u16,