Don't distinguish between render targets and other images in the texture atlas

allocator
This commit is contained in:
Patrick Walton 2020-03-04 19:41:24 -08:00
parent a8f7438cd9
commit 9935c9fdff
4 changed files with 105 additions and 128 deletions

View File

@ -10,8 +10,7 @@
//! A simple quadtree-based texture allocator.
use crate::gpu_data::TexturePageId;
use pathfinder_content::render_target::RenderTargetId;
use crate::gpu_data::{TextureLocation, TexturePageId};
use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::vector::{Vector2F, Vector2I};
@ -28,8 +27,6 @@ pub enum TexturePageAllocator {
Atlas(TextureAtlasAllocator),
// A single image.
Image { size: Vector2I },
// A render target.
RenderTarget { size: Vector2I, id: RenderTargetId },
}
#[derive(Debug)]
@ -38,12 +35,6 @@ pub struct TextureAtlasAllocator {
size: u32,
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct TextureLocation {
pub page: TexturePageId,
pub rect: RectI,
}
#[derive(Debug)]
enum TreeNode {
EmptyLeaf,
@ -75,8 +66,7 @@ impl TextureAllocator {
// Try to add to each atlas.
for (page_index, page) in self.pages.iter_mut().enumerate() {
match *page {
TexturePageAllocator::Image { .. } |
TexturePageAllocator::RenderTarget { .. } => {}
TexturePageAllocator::Image { .. } => {}
TexturePageAllocator::Atlas(ref mut allocator) => {
if let Some(rect) = allocator.allocate(requested_size) {
return TextureLocation { page: TexturePageId(page_index as u32), rect };
@ -93,26 +83,17 @@ impl TextureAllocator {
TextureLocation { page, rect }
}
fn allocate_image(&mut self, requested_size: Vector2I) -> TextureLocation {
pub fn allocate_image(&mut self, requested_size: Vector2I) -> TextureLocation {
let page = TexturePageId(self.pages.len() as u32);
let rect = RectI::new(Vector2I::default(), requested_size);
self.pages.push(TexturePageAllocator::Image { size: rect.size() });
TextureLocation { page, rect }
}
pub fn allocate_render_target(&mut self, requested_size: Vector2I, id: RenderTargetId)
-> TextureLocation {
let page = TexturePageId(self.pages.len() as u32);
let rect = RectI::new(Vector2I::default(), requested_size);
self.pages.push(TexturePageAllocator::RenderTarget { size: rect.size(), id });
TextureLocation { page, rect }
}
pub fn page_size(&self, page_index: TexturePageId) -> Vector2I {
match self.pages[page_index.0 as usize] {
TexturePageAllocator::Atlas(ref atlas) => Vector2I::splat(atlas.size as i32),
TexturePageAllocator::Image { size, .. } |
TexturePageAllocator::RenderTarget { size, .. } => size,
TexturePageAllocator::Image { size, .. } => size,
}
}
@ -124,14 +105,6 @@ impl TextureAllocator {
pub fn page_count(&self) -> u32 {
self.pages.len() as u32
}
#[inline]
pub fn page_render_target_id(&self, page_index: TexturePageId) -> Option<RenderTargetId> {
match self.pages[page_index.0 as usize] {
TexturePageAllocator::RenderTarget { id, .. } => Some(id),
TexturePageAllocator::Atlas(_) | TexturePageAllocator::Image { .. } => None,
}
}
}
impl TextureAtlasAllocator {

View File

@ -20,7 +20,7 @@ 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, TexturePageDescriptor, TexturePageId};
use crate::gpu_data::{SolidTileVertex, TextureLocation, TexturePageDescriptor, TexturePageId};
use crate::options::BoundingQuad;
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
use pathfinder_color::{self as color, ColorF, ColorU};
@ -430,11 +430,11 @@ where
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::UploadTexelData { ref texels, location } => {
self.upload_texel_data(texels, location)
}
RenderCommand::DeclareRenderTarget { render_target_id, texture_page_id } => {
self.declare_render_target(render_target_id, texture_page_id)
RenderCommand::DeclareRenderTarget { id, location } => {
self.declare_render_target(id, location)
}
RenderCommand::AddFills(ref fills) => self.add_fills(fills),
RenderCommand::FlushFills => {
@ -599,23 +599,25 @@ where
}
}
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];
fn upload_texel_data(&mut self, texels: &[ColorU], location: TextureLocation) {
let texture_page = &mut self.texture_pages[location.page.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));
self.device.upload_to_texture(texture, location.rect, TextureDataRef::U8(texels));
texture_page.must_preserve_contents = true;
}
fn declare_render_target(&mut self,
render_target_id: RenderTargetId,
texture_page_id: TexturePageId) {
location: TextureLocation) {
while self.render_targets.len() < render_target_id.0 as usize + 1 {
self.render_targets.push(RenderTargetInfo { texture_page: TexturePageId(!0) });
self.render_targets.push(RenderTargetInfo {
location: TextureLocation { page: TexturePageId(!0), rect: RectI::default() },
});
}
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;
debug_assert_eq!(render_target.location.page, TexturePageId(!0));
render_target.location = location;
}
fn upload_mask_tiles(&mut self, mask_tiles: &[MaskTile], fill_rule: FillRule) {
@ -1153,7 +1155,7 @@ where
pub fn draw_render_target(&self) -> RenderTarget<D> {
match self.render_target_stack.last() {
Some(&render_target_id) => {
let texture_page_id = self.render_target_texture_page_id(render_target_id);
let texture_page_id = self.render_target_location(render_target_id).page;
let framebuffer = self.texture_page_framebuffer(texture_page_id);
RenderTarget::Framebuffer(framebuffer)
}
@ -1264,7 +1266,7 @@ where
render_target_id: RenderTargetId,
direction: BlurDirection,
sigma: f32) {
let src_texture_page = self.render_target_texture_page_id(render_target_id);
let src_texture_page = self.render_target_location(render_target_id).page;
let src_texture = self.texture_page(src_texture_page);
let src_texture_size = self.device.texture_size(src_texture);
@ -1310,8 +1312,10 @@ where
blend_state: Option<BlendState>) {
let clear_color = self.clear_color_for_draw_operation();
let main_viewport = self.main_viewport();
let src_texture_page = self.render_target_texture_page_id(render_target_id);
let src_texture = self.texture_page(src_texture_page);
// TODO(pcwalton): Other viewports.
let src_texture_location = self.render_target_location(render_target_id);
let src_texture = self.texture_page(src_texture_location.page);
let src_texture_size = self.device.texture_size(src_texture);
uniforms.extend_from_slice(&[
@ -1379,7 +1383,7 @@ where
fn clear_color_for_draw_operation(&self) -> Option<ColorF> {
let must_preserve_contents = match self.render_target_stack.last() {
Some(&render_target_id) => {
let texture_page = self.render_target_texture_page_id(render_target_id);
let texture_page = self.render_target_location(render_target_id).page;
self.texture_pages[texture_page.0 as usize].must_preserve_contents
}
None => {
@ -1400,7 +1404,7 @@ where
fn preserve_draw_framebuffer(&mut self) {
match self.render_target_stack.last() {
Some(&render_target_id) => {
let texture_page = self.render_target_texture_page_id(render_target_id);
let texture_page = self.render_target_location(render_target_id).page;
self.texture_pages[texture_page.0 as usize].must_preserve_contents = true;
}
None => {
@ -1412,11 +1416,7 @@ where
pub fn draw_viewport(&self) -> RectI {
match self.render_target_stack.last() {
Some(&render_target_id) => {
let texture_page = self.render_target_texture_page_id(render_target_id);
let texture = self.texture_page(texture_page);
RectI::new(Vector2I::default(), self.device.texture_size(texture))
}
Some(&render_target_id) => self.render_target_location(render_target_id).rect,
None => self.main_viewport(),
}
}
@ -1438,8 +1438,8 @@ where
Vector2I::new(MASK_FRAMEBUFFER_WIDTH, MASK_FRAMEBUFFER_HEIGHT))
}
fn render_target_texture_page_id(&self, render_target_id: RenderTargetId) -> TexturePageId {
self.render_targets[render_target_id.0 as usize].texture_page
fn render_target_location(&self, render_target_id: RenderTargetId) -> TextureLocation {
self.render_targets[render_target_id.0 as usize].location
}
fn texture_page_framebuffer(&self, id: TexturePageId) -> &D::Framebuffer {
@ -1584,7 +1584,7 @@ struct TexturePage<D> where D: Device {
}
struct RenderTargetInfo {
texture_page: TexturePageId,
location: TextureLocation,
}
trait ToBlendState {

View File

@ -42,12 +42,12 @@ pub enum RenderCommand {
AllocateTexturePages(Vec<TexturePageDescriptor>),
// Uploads data to a texture page.
UploadTexelData { page: TexturePageId, texels: Vec<ColorU>, rect: RectI },
UploadTexelData { texels: Vec<ColorU>, location: TextureLocation },
// 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 },
DeclareRenderTarget { id: RenderTargetId, location: TextureLocation },
// Adds fills to the queue.
AddFills(Vec<FillBatchPrimitive>),
@ -90,6 +90,12 @@ pub struct TexturePageDescriptor {
pub size: Vector2I,
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct TextureLocation {
pub page: TexturePageId,
pub rect: RectI,
}
#[derive(Clone, Debug)]
pub struct AlphaTileBatch {
pub tiles: Vec<AlphaTile>,
@ -214,14 +220,11 @@ impl Debug for RenderCommand {
RenderCommand::AllocateTexturePages(ref pages) => {
write!(formatter, "AllocateTexturePages(x{})", pages.len())
}
RenderCommand::UploadTexelData { page, rect, .. } => {
write!(formatter, "UploadTexelData({:?}, {:?})", page, rect)
RenderCommand::UploadTexelData { ref texels, location } => {
write!(formatter, "UploadTexelData({:?}, {:?})", texels, location)
}
RenderCommand::DeclareRenderTarget { render_target_id, texture_page_id } => {
write!(formatter,
"DeclareRenderTarget({:?}, {:?})",
render_target_id,
texture_page_id)
RenderCommand::DeclareRenderTarget { id, location } => {
write!(formatter, "DeclareRenderTarget({:?}, {:?})", id, location)
}
RenderCommand::AddFills(ref fills) => write!(formatter, "AddFills(x{})", fills.len()),
RenderCommand::FlushFills => write!(formatter, "FlushFills"),

View File

@ -8,8 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::allocator::{AllocationMode, TextureAllocator, TextureLocation};
use crate::gpu_data::{RenderCommand, TexturePageDescriptor, TexturePageId};
use crate::allocator::{AllocationMode, TextureAllocator};
use crate::gpu_data::{RenderCommand, TextureLocation, TexturePageDescriptor, TexturePageId};
use crate::scene::RenderTarget;
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
use hashbrown::HashMap;
@ -24,7 +24,6 @@ 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.
//
@ -199,10 +198,8 @@ impl Palette {
// Assign render target locations.
let mut render_target_locations = vec![];
for (render_target_index, render_target) in self.render_targets.iter().enumerate() {
let render_target_id = RenderTargetId(render_target_index as u32);
render_target_locations.push(allocator.allocate_render_target(render_target.size(),
render_target_id));
for render_target in &self.render_targets {
render_target_locations.push(allocator.allocate_image(render_target.size()));
}
// Assign paint locations.
@ -313,18 +310,9 @@ impl Palette {
// 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]);
}
}
}
let mut page_texels: Vec<_> = metadata.iter().map(|metadata| {
Texels::new(allocator.page_size(metadata.texture_page))
}).collect();
// Draw to texels.
//
@ -332,26 +320,22 @@ impl Palette {
for (paint, metadata) in self.paints.iter().zip(metadata.iter()) {
let texture_page = metadata.texture_page;
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 {
Paint::Color(color) => {
put_pixel(metadata.texture_rect.origin(), *color, texels, page_size);
texels.put_texel(metadata.texture_rect.origin(), *color);
}
Paint::Gradient(ref gradient) => {
self.render_gradient(gradient,
metadata.texture_rect,
&metadata.texture_transform,
texels,
page_size,
page_scale);
texels);
}
Paint::Pattern(ref pattern) => {
match pattern.source {
PatternSource::RenderTarget(_) => {}
PatternSource::Image(ref image) => {
self.render_image(image, metadata.texture_rect, texels, page_size);
self.render_image(image, metadata.texture_rect, texels);
}
}
}
@ -362,23 +346,19 @@ impl Palette {
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),
});
}
for (index, location) in render_target_locations.into_iter().enumerate() {
let id = RenderTargetId(index as u32);
render_commands.push(RenderCommand::DeclareRenderTarget { id, location });
}
for (page_index, texels) in page_texels.into_iter().enumerate() {
if let Some(texel_data) = texels.data {
let page_id = TexturePageId(page_index as u32);
let page_size = allocator.page_size(page_id);
let rect = RectI::new(Vector2I::default(), page_size);
render_commands.push(RenderCommand::UploadTexelData {
texels: texel_data,
location: TextureLocation { page: page_id, rect },
});
}
}
@ -390,9 +370,7 @@ impl Palette {
gradient: &Gradient,
tex_rect: RectI,
tex_transform: &Transform2F,
texels: &mut [ColorU],
tex_size: Vector2I,
tex_scale: Vector2F) {
texels: &mut Texels) {
match *gradient.geometry() {
GradientGeometry::Linear(gradient_line) => {
// FIXME(pcwalton): Paint transparent if gradient line has zero size, per spec.
@ -404,12 +382,13 @@ impl Palette {
for y in 0..(GRADIENT_TILE_LENGTH as i32) {
for x in 0..(GRADIENT_TILE_LENGTH as i32) {
let point = tex_rect.origin() + Vector2I::new(x, y);
let vector = point.to_f32().scale_xy(tex_scale) - gradient_line.from();
let vector = point.to_f32().scale_xy(texels.scale()) -
gradient_line.from();
let mut t = gradient_line.vector().projection_coefficient(vector);
t = util::clamp(t, 0.0, 1.0);
put_pixel(point, gradient.sample(t), texels, tex_size);
texels.put_texel(point, gradient.sample(t));
}
}
}
@ -530,27 +509,20 @@ impl Palette {
}
};
put_pixel(point, color, texels, tex_size);
texels.put_texel(point, color);
}
}
}
}
}
fn render_image(&self,
image: &Image,
tex_rect: RectI,
texels: &mut [ColorU],
tex_size: Vector2I) {
fn render_image(&self, image: &Image, tex_rect: RectI, texels: &mut Texels) {
let image_size = image.size();
for y in 0..image_size.y() {
let dest_origin = tex_rect.origin() + Vector2I::new(0, y);
let dest_start_index = paint_texel_index(dest_origin, tex_size);
let src_start_index = y as usize * image_size.x() as usize;
let dest_end_index = dest_start_index + image_size.x() as usize;
let src_end_index = src_start_index + image_size.x() as usize;
texels[dest_start_index..dest_end_index].copy_from_slice(
&image.pixels()[src_start_index..src_end_index]);
texels.blit_scanline(dest_origin, &image.pixels()[src_start_index..src_end_index]);
}
}
}
@ -565,12 +537,41 @@ impl PaintMetadata {
}
}
fn paint_texel_index(position: Vector2I, tex_size: Vector2I) -> usize {
position.y() as usize * tex_size.x() as usize + position.x() as usize
struct Texels {
data: Option<Vec<ColorU>>,
size: Vector2I,
}
fn put_pixel(position: Vector2I, color: ColorU, texels: &mut [ColorU], tex_size: Vector2I) {
texels[paint_texel_index(position, tex_size)] = color
impl Texels {
fn new(size: Vector2I) -> Texels {
Texels { data: None, size }
}
fn texel_index(&self, position: Vector2I) -> usize {
position.y() as usize * self.size.x() as usize + position.x() as usize
}
fn allocate_texels_if_necessary(&mut self) {
if self.data.is_none() {
let area = self.size.x() as usize * self.size.y() as usize;
self.data = Some(vec![ColorU::transparent_black(); area]);
}
}
fn blit_scanline(&mut self, dest_origin: Vector2I, src: &[ColorU]) {
self.allocate_texels_if_necessary();
let start_index = self.texel_index(dest_origin);
let end_index = start_index + src.len();
self.data.as_mut().unwrap()[start_index..end_index].copy_from_slice(src)
}
fn put_texel(&mut self, position: Vector2I, color: ColorU) {
self.blit_scanline(position, &[color])
}
fn scale(&self) -> Vector2F {
Vector2F::splat(1.0) / self.size.to_f32()
}
}
fn rect_to_uv(rect: RectI, texture_scale: Vector2F) -> RectF {