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]
#[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,

View File

@ -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<AlphaTileBatchPrimitive> {
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<AlphaTileBatchPrimitive>) {
fn pack_alpha_tiles(&mut self,
paint_metadata: &[PaintMetadata],
alpha_tiles: Vec<AlphaTileBatchPrimitive>) {
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<AlphaTileBatchPrimitive>) {
fn finish_building(&mut self,
paint_metadata: &[PaintMetadata],
mut alpha_tiles: Vec<AlphaTileBatchPrimitive>) {
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);
}
}

View File

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

View File

@ -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<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 {
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<SolidColorTileBuilderData>);
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
}
}

View File

@ -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<QueuedEndpoint>,
active_edges: SortedVector<ActiveEdge>,
@ -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,

View File

@ -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<u32>)
pub fn build_solid_tiles(&self,
paths: &[PathObject],
paint_metadata: &[PaintMetadata],
object_range: Range<u32>)
-> Vec<SolidTileBatchPrimitive> {
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,