Add support for multiple paint texture pages.
This avoids arbitrary limits on the number of images, gradients, etc. you can have.
This commit is contained in:
parent
0b102ec97d
commit
3245796445
|
@ -533,12 +533,12 @@ fn main() {
|
||||||
let mut event_pump = sdl_context.event_pump().unwrap();
|
let mut event_pump = sdl_context.event_pump().unwrap();
|
||||||
let mut mouse_position = Vector2F::default();
|
let mut mouse_position = Vector2F::default();
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
|
let font_context = CanvasFontContext::from_system_source();
|
||||||
|
|
||||||
// Enter the main loop.
|
// Enter the main loop.
|
||||||
loop {
|
loop {
|
||||||
// Make a canvas.
|
// Make a canvas.
|
||||||
let mut canvas = CanvasRenderingContext2D::new(CanvasFontContext::from_system_source(),
|
let mut canvas = CanvasRenderingContext2D::new(font_context.clone(), window_size.to_f32());
|
||||||
window_size.to_f32());
|
|
||||||
|
|
||||||
// Render the demo.
|
// Render the demo.
|
||||||
let time = (Instant::now() - start_time).as_secs_f32();
|
let time = (Instant::now() - start_time).as_secs_f32();
|
||||||
|
|
|
@ -449,6 +449,17 @@ impl Device for GLDevice {
|
||||||
&framebuffer.texture
|
&framebuffer.texture
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn destroy_framebuffer(&self, framebuffer: Self::Framebuffer) -> Self::Texture {
|
||||||
|
let texture = GLTexture {
|
||||||
|
gl_texture: framebuffer.texture.gl_texture,
|
||||||
|
size: framebuffer.texture.size,
|
||||||
|
format: framebuffer.texture.format,
|
||||||
|
};
|
||||||
|
mem::forget(framebuffer);
|
||||||
|
texture
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn texture_format(&self, texture: &Self::Texture) -> TextureFormat {
|
fn texture_format(&self, texture: &Self::Texture) -> TextureFormat {
|
||||||
texture.format
|
texture.format
|
||||||
|
|
|
@ -70,6 +70,7 @@ pub trait Device: Sized {
|
||||||
mode: BufferUploadMode,
|
mode: BufferUploadMode,
|
||||||
);
|
);
|
||||||
fn framebuffer_texture<'f>(&self, framebuffer: &'f Self::Framebuffer) -> &'f Self::Texture;
|
fn framebuffer_texture<'f>(&self, framebuffer: &'f Self::Framebuffer) -> &'f Self::Texture;
|
||||||
|
fn destroy_framebuffer(&self, framebuffer: Self::Framebuffer) -> Self::Texture;
|
||||||
fn texture_format(&self, texture: &Self::Texture) -> TextureFormat;
|
fn texture_format(&self, texture: &Self::Texture) -> TextureFormat;
|
||||||
fn texture_size(&self, texture: &Self::Texture) -> Vector2I;
|
fn texture_size(&self, texture: &Self::Texture) -> Vector2I;
|
||||||
fn upload_to_texture(&self, texture: &Self::Texture, rect: RectI, data: TextureDataRef);
|
fn upload_to_texture(&self, texture: &Self::Texture, rect: RectI, data: TextureDataRef);
|
||||||
|
|
|
@ -433,10 +433,16 @@ impl Device for MetalDevice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn framebuffer_texture<'f>(&self, framebuffer: &'f MetalFramebuffer) -> &'f MetalTexture {
|
fn framebuffer_texture<'f>(&self, framebuffer: &'f MetalFramebuffer) -> &'f MetalTexture {
|
||||||
&framebuffer.0
|
&framebuffer.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn destroy_framebuffer(&self, framebuffer: MetalFramebuffer) -> MetalTexture {
|
||||||
|
framebuffer.0
|
||||||
|
}
|
||||||
|
|
||||||
fn texture_format(&self, texture: &MetalTexture) -> TextureFormat {
|
fn texture_format(&self, texture: &MetalTexture) -> TextureFormat {
|
||||||
match texture.texture.pixel_format() {
|
match texture.texture.pixel_format() {
|
||||||
MTLPixelFormat::R8Unorm => TextureFormat::R8,
|
MTLPixelFormat::R8Unorm => TextureFormat::R8,
|
||||||
|
|
|
@ -11,19 +11,33 @@
|
||||||
//! A simple quadtree-based texture allocator.
|
//! A simple quadtree-based texture allocator.
|
||||||
|
|
||||||
use pathfinder_geometry::rect::RectI;
|
use pathfinder_geometry::rect::RectI;
|
||||||
use pathfinder_geometry::vector::Vector2I;
|
use pathfinder_geometry::vector::{Vector2F, Vector2I};
|
||||||
use std::mem;
|
|
||||||
|
|
||||||
const MAX_TEXTURE_LENGTH: u32 = 4096;
|
const ATLAS_TEXTURE_LENGTH: u32 = 1024;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TextureAllocator {
|
pub struct TextureAllocator {
|
||||||
|
pages: Vec<TexturePageAllocator>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(pcwalton): Add layers, perhaps?
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TexturePageAllocator {
|
||||||
|
// An atlas allocated with our quadtree allocator.
|
||||||
|
Atlas(TextureAtlasAllocator),
|
||||||
|
// A single image.
|
||||||
|
Image { size: Vector2I },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TextureAtlasAllocator {
|
||||||
root: TreeNode,
|
root: TreeNode,
|
||||||
size: u32,
|
size: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
pub struct TextureLocation {
|
pub struct TextureLocation {
|
||||||
|
pub page: u32,
|
||||||
pub rect: RectI,
|
pub rect: RectI,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,80 +51,97 @@ enum TreeNode {
|
||||||
|
|
||||||
impl TextureAllocator {
|
impl TextureAllocator {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(size: u32) -> TextureAllocator {
|
pub fn new() -> TextureAllocator {
|
||||||
// Make sure that the size is a power of two.
|
TextureAllocator { pages: vec![] }
|
||||||
debug_assert_eq!(size & (size - 1), 0);
|
|
||||||
TextureAllocator { root: TreeNode::EmptyLeaf, size }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn allocate(&mut self, requested_size: Vector2I) -> Option<TextureLocation> {
|
pub fn allocate(&mut self, requested_size: Vector2I) -> TextureLocation {
|
||||||
let requested_length =
|
// If too big, the image gets its own page.
|
||||||
(requested_size.x().max(requested_size.y()) as u32).next_power_of_two();
|
if requested_size.x() > ATLAS_TEXTURE_LENGTH as i32 ||
|
||||||
loop {
|
requested_size.y() > ATLAS_TEXTURE_LENGTH as i32 {
|
||||||
if let Some(location) = self.root.allocate(Vector2I::default(),
|
let page = self.pages.len() as u32;
|
||||||
self.size,
|
let rect = RectI::new(Vector2I::default(), requested_size);
|
||||||
requested_length) {
|
self.pages.push(TexturePageAllocator::Image { size: rect.size() });
|
||||||
return Some(location);
|
return TextureLocation { page, rect };
|
||||||
}
|
}
|
||||||
if !self.grow() {
|
|
||||||
return None;
|
// Try to add to each atlas.
|
||||||
|
for (page_index, page) in self.pages.iter_mut().enumerate() {
|
||||||
|
match *page {
|
||||||
|
TexturePageAllocator::Image { .. } => {}
|
||||||
|
TexturePageAllocator::Atlas(ref mut allocator) => {
|
||||||
|
if let Some(rect) = allocator.allocate(requested_size) {
|
||||||
|
return TextureLocation { page: page_index as u32, rect };
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add a new atlas.
|
||||||
|
let page = self.pages.len() as u32;
|
||||||
|
let mut allocator = TextureAtlasAllocator::new();
|
||||||
|
let rect = allocator.allocate(requested_size).expect("Allocation failed!");
|
||||||
|
self.pages.push(TexturePageAllocator::Atlas(allocator));
|
||||||
|
TextureLocation { page, rect }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn page_size(&self, page_index: u32) -> Vector2I {
|
||||||
|
match self.pages[page_index as usize] {
|
||||||
|
TexturePageAllocator::Atlas(ref atlas) => Vector2I::splat(atlas.size as i32),
|
||||||
|
TexturePageAllocator::Image { size } => size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn page_scale(&self, page_index: u32) -> Vector2F {
|
||||||
|
Vector2F::splat(1.0) / self.page_size(page_index).to_f32()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(dead_code)]
|
pub fn page_count(&self) -> u32 {
|
||||||
pub fn free(&mut self, location: TextureLocation) {
|
self.pages.len() as u32
|
||||||
let requested_length = location.rect.width() as u32;
|
}
|
||||||
self.root.free(Vector2I::default(), self.size, location.rect.origin(), requested_length)
|
}
|
||||||
|
|
||||||
|
impl TextureAtlasAllocator {
|
||||||
|
#[inline]
|
||||||
|
fn new() -> TextureAtlasAllocator {
|
||||||
|
TextureAtlasAllocator::with_length(ATLAS_TEXTURE_LENGTH)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn with_length(length: u32) -> TextureAtlasAllocator {
|
||||||
|
TextureAtlasAllocator { root: TreeNode::EmptyLeaf, size: length }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn allocate(&mut self, requested_size: Vector2I) -> Option<RectI> {
|
||||||
|
let requested_length =
|
||||||
|
(requested_size.x().max(requested_size.y()) as u32).next_power_of_two();
|
||||||
|
self.root.allocate(Vector2I::default(), self.size, requested_length)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn is_empty(&self) -> bool {
|
fn free(&mut self, rect: RectI) {
|
||||||
|
let requested_length = rect.width() as u32;
|
||||||
|
self.root.free(Vector2I::default(), self.size, rect.origin(), requested_length)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
match self.root {
|
match self.root {
|
||||||
TreeNode::EmptyLeaf => true,
|
TreeNode::EmptyLeaf => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(pcwalton): Make this more flexible.
|
|
||||||
pub fn grow(&mut self) -> bool {
|
|
||||||
if self.size >= MAX_TEXTURE_LENGTH {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let old_root = mem::replace(&mut self.root, TreeNode::EmptyLeaf);
|
|
||||||
self.size *= 2;
|
|
||||||
|
|
||||||
// NB: Don't change the order of the children, or else texture coordinates of
|
|
||||||
// already-allocated objects will become invalid.
|
|
||||||
self.root = TreeNode::Parent([
|
|
||||||
Box::new(old_root),
|
|
||||||
Box::new(TreeNode::EmptyLeaf),
|
|
||||||
Box::new(TreeNode::EmptyLeaf),
|
|
||||||
Box::new(TreeNode::EmptyLeaf),
|
|
||||||
]);
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn size(&self) -> u32 {
|
|
||||||
self.size
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn scale(&self) -> f32 {
|
|
||||||
1.0 / self.size as f32
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TreeNode {
|
impl TreeNode {
|
||||||
// Invariant: `requested_size` must be a power of two.
|
// Invariant: `requested_size` must be a power of two.
|
||||||
fn allocate(&mut self, this_origin: Vector2I, this_size: u32, requested_size: u32)
|
fn allocate(&mut self, this_origin: Vector2I, this_size: u32, requested_size: u32)
|
||||||
-> Option<TextureLocation> {
|
-> Option<RectI> {
|
||||||
if let TreeNode::FullLeaf = *self {
|
if let TreeNode::FullLeaf = *self {
|
||||||
// No room here.
|
// No room here.
|
||||||
return None;
|
return None;
|
||||||
|
@ -125,9 +156,7 @@ impl TreeNode {
|
||||||
// Do we have a perfect fit?
|
// Do we have a perfect fit?
|
||||||
if this_size == requested_size {
|
if this_size == requested_size {
|
||||||
*self = TreeNode::FullLeaf;
|
*self = TreeNode::FullLeaf;
|
||||||
return Some(TextureLocation {
|
return Some(RectI::new(this_origin, Vector2I::splat(this_size as i32)));
|
||||||
rect: RectI::new(this_origin, Vector2I::splat(this_size as i32)),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split.
|
// Split.
|
||||||
|
@ -240,7 +269,7 @@ mod test {
|
||||||
use quickcheck;
|
use quickcheck;
|
||||||
use std::u32;
|
use std::u32;
|
||||||
|
|
||||||
use super::TextureAllocator;
|
use super::TextureAtlasAllocator;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_allocation_and_freeing() {
|
fn test_allocation_and_freeing() {
|
||||||
|
@ -255,7 +284,7 @@ mod test {
|
||||||
*height = (*height).min(length).max(1);
|
*height = (*height).min(length).max(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut allocator = TextureAllocator::new(length);
|
let mut allocator = TextureAtlasAllocator::with_length(length);
|
||||||
let mut locations = vec![];
|
let mut locations = vec![];
|
||||||
for &(width, height) in &sizes {
|
for &(width, height) in &sizes {
|
||||||
let size = Vector2I::new(width as i32, height as i32);
|
let size = Vector2I::new(width as i32, height as i32);
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
use crate::concurrent::executor::Executor;
|
use crate::concurrent::executor::Executor;
|
||||||
use crate::gpu::renderer::MASK_TILES_ACROSS;
|
use crate::gpu::renderer::MASK_TILES_ACROSS;
|
||||||
use crate::gpu_data::{AlphaTile, AlphaTileVertex, FillBatchPrimitive, MaskTile, MaskTileVertex};
|
use crate::gpu_data::{AlphaTile, AlphaTileVertex, FillBatchPrimitive, MaskTile, MaskTileVertex};
|
||||||
use crate::gpu_data::{RenderCommand, SolidTileVertex, TileObjectPrimitive};
|
use crate::gpu_data::{RenderCommand, SolidTileBatch, TileObjectPrimitive};
|
||||||
use crate::options::{PreparedBuildOptions, RenderCommandListener};
|
use crate::options::{PreparedBuildOptions, RenderCommandListener};
|
||||||
use crate::paint::{PaintInfo, PaintMetadata};
|
use crate::paint::{PaintInfo, PaintMetadata};
|
||||||
use crate::scene::{DisplayItem, Scene};
|
use crate::scene::{DisplayItem, Scene};
|
||||||
|
@ -52,6 +52,7 @@ pub(crate) struct ObjectBuilder {
|
||||||
struct BuiltDrawPath {
|
struct BuiltDrawPath {
|
||||||
path: BuiltPath,
|
path: BuiltPath,
|
||||||
blend_mode: BlendMode,
|
blend_mode: BlendMode,
|
||||||
|
paint_page: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -157,7 +158,7 @@ impl<'a> SceneBuilder<'a> {
|
||||||
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 paint_metadata = &paint_metadata[paint_id.0 as usize];
|
||||||
let built_clip_path =
|
let built_clip_path =
|
||||||
path_object.clip_path().map(|clip_path_id| &built_clip_paths[clip_path_id.0 as usize]);
|
path_object.clip_path().map(|clip_path_id| &built_clip_paths[clip_path_id.0 as usize]);
|
||||||
|
|
||||||
|
@ -167,7 +168,7 @@ impl<'a> SceneBuilder<'a> {
|
||||||
view_box,
|
view_box,
|
||||||
path_index as u16,
|
path_index as u16,
|
||||||
TilingPathInfo::Draw {
|
TilingPathInfo::Draw {
|
||||||
paint_metadata: &paint_metadata[paint_id.0 as usize],
|
paint_metadata,
|
||||||
blend_mode: path_object.blend_mode(),
|
blend_mode: path_object.blend_mode(),
|
||||||
built_clip_path,
|
built_clip_path,
|
||||||
});
|
});
|
||||||
|
@ -179,6 +180,7 @@ impl<'a> SceneBuilder<'a> {
|
||||||
BuiltDrawPath {
|
BuiltDrawPath {
|
||||||
path: tiler.object_builder.built_path,
|
path: tiler.object_builder.built_path,
|
||||||
blend_mode: path_object.blend_mode(),
|
blend_mode: path_object.blend_mode(),
|
||||||
|
paint_page: paint_metadata.tex_page,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,9 +206,8 @@ impl<'a> SceneBuilder<'a> {
|
||||||
let first_z_buffer = remaining_layer_z_buffers.pop().unwrap();
|
let first_z_buffer = remaining_layer_z_buffers.pop().unwrap();
|
||||||
let first_solid_tiles = first_z_buffer.build_solid_tiles(&self.scene.paths,
|
let first_solid_tiles = first_z_buffer.build_solid_tiles(&self.scene.paths,
|
||||||
paint_metadata);
|
paint_metadata);
|
||||||
if !first_solid_tiles.is_empty() {
|
for batch in first_solid_tiles.batches {
|
||||||
culled_tiles.display_list
|
culled_tiles.display_list.push(CulledDisplayItem::DrawSolidTiles(batch));
|
||||||
.push(CulledDisplayItem::DrawSolidTiles(first_solid_tiles));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut layer_z_buffers_stack = vec![first_z_buffer];
|
let mut layer_z_buffers_stack = vec![first_z_buffer];
|
||||||
|
@ -220,9 +221,8 @@ impl<'a> SceneBuilder<'a> {
|
||||||
let z_buffer = remaining_layer_z_buffers.pop().unwrap();
|
let z_buffer = remaining_layer_z_buffers.pop().unwrap();
|
||||||
let solid_tiles = z_buffer.build_solid_tiles(&self.scene.paths,
|
let solid_tiles = z_buffer.build_solid_tiles(&self.scene.paths,
|
||||||
paint_metadata);
|
paint_metadata);
|
||||||
if !solid_tiles.is_empty() {
|
for batch in solid_tiles.batches {
|
||||||
culled_tiles.display_list
|
culled_tiles.display_list.push(CulledDisplayItem::DrawSolidTiles(batch));
|
||||||
.push(CulledDisplayItem::DrawSolidTiles(solid_tiles));
|
|
||||||
}
|
}
|
||||||
layer_z_buffers_stack.push(z_buffer);
|
layer_z_buffers_stack.push(z_buffer);
|
||||||
continue;
|
continue;
|
||||||
|
@ -240,18 +240,21 @@ impl<'a> SceneBuilder<'a> {
|
||||||
culled_tiles.push_mask_tiles(&built_draw_path.path);
|
culled_tiles.push_mask_tiles(&built_draw_path.path);
|
||||||
|
|
||||||
// Create a new `DrawAlphaTiles` display item if we don't have one or if we have to
|
// Create a new `DrawAlphaTiles` display item if we don't have one or if we have to
|
||||||
// break a batch due to blend mode differences.
|
// break a batch due to blend mode or paint page.
|
||||||
//
|
//
|
||||||
// TODO(pcwalton): If we really wanted to, we could use tile maps to avoid batch
|
// TODO(pcwalton): If we really wanted to, we could use tile maps to avoid batch
|
||||||
// breaks in some cases…
|
// breaks in some cases…
|
||||||
match culled_tiles.display_list.last() {
|
match culled_tiles.display_list.last() {
|
||||||
Some(&CulledDisplayItem::DrawAlphaTiles {
|
Some(&CulledDisplayItem::DrawAlphaTiles {
|
||||||
tiles: _,
|
tiles: _,
|
||||||
|
paint_page,
|
||||||
blend_mode
|
blend_mode
|
||||||
}) if blend_mode == built_draw_path.blend_mode => {}
|
}) if paint_page == built_draw_path.paint_page &&
|
||||||
|
blend_mode == built_draw_path.blend_mode => {}
|
||||||
_ => {
|
_ => {
|
||||||
culled_tiles.display_list.push(CulledDisplayItem::DrawAlphaTiles {
|
culled_tiles.display_list.push(CulledDisplayItem::DrawAlphaTiles {
|
||||||
tiles: vec![],
|
tiles: vec![],
|
||||||
|
paint_page: built_draw_path.paint_page,
|
||||||
blend_mode: built_draw_path.blend_mode,
|
blend_mode: built_draw_path.blend_mode,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -326,11 +329,15 @@ impl<'a> SceneBuilder<'a> {
|
||||||
|
|
||||||
for display_item in culled_tiles.display_list {
|
for display_item in culled_tiles.display_list {
|
||||||
match display_item {
|
match display_item {
|
||||||
CulledDisplayItem::DrawSolidTiles(tiles) => {
|
CulledDisplayItem::DrawSolidTiles(batch) => {
|
||||||
self.listener.send(RenderCommand::DrawSolidTiles(tiles))
|
self.listener.send(RenderCommand::DrawSolidTiles(batch))
|
||||||
}
|
}
|
||||||
CulledDisplayItem::DrawAlphaTiles { tiles, blend_mode } => {
|
CulledDisplayItem::DrawAlphaTiles { tiles, paint_page, blend_mode } => {
|
||||||
self.listener.send(RenderCommand::DrawAlphaTiles { tiles, blend_mode })
|
self.listener.send(RenderCommand::DrawAlphaTiles {
|
||||||
|
tiles,
|
||||||
|
paint_page,
|
||||||
|
blend_mode,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
CulledDisplayItem::PushLayer { effects } => {
|
CulledDisplayItem::PushLayer { effects } => {
|
||||||
self.listener.send(RenderCommand::PushLayer { effects })
|
self.listener.send(RenderCommand::PushLayer { effects })
|
||||||
|
@ -381,8 +388,8 @@ struct CulledTiles {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum CulledDisplayItem {
|
enum CulledDisplayItem {
|
||||||
DrawSolidTiles(Vec<SolidTileVertex>),
|
DrawSolidTiles(SolidTileBatch),
|
||||||
DrawAlphaTiles { tiles: Vec<AlphaTile>, blend_mode: BlendMode },
|
DrawAlphaTiles { tiles: Vec<AlphaTile>, paint_page: u32, blend_mode: BlendMode },
|
||||||
PushLayer { effects: Effects },
|
PushLayer { effects: Effects },
|
||||||
PopLayer,
|
PopLayer,
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ static QUAD_VERTEX_INDICES: [u32; 6] = [0, 1, 3, 1, 2, 3];
|
||||||
pub(crate) const MASK_TILES_ACROSS: u32 = 256;
|
pub(crate) const MASK_TILES_ACROSS: u32 = 256;
|
||||||
pub(crate) const MASK_TILES_DOWN: u32 = 256;
|
pub(crate) const MASK_TILES_DOWN: u32 = 256;
|
||||||
|
|
||||||
const FRAMEBUFFER_CACHE_SIZE: usize = 8;
|
const TEXTURE_CACHE_SIZE: usize = 8;
|
||||||
|
|
||||||
// FIXME(pcwalton): Shrink this again!
|
// FIXME(pcwalton): Shrink this again!
|
||||||
const MASK_FRAMEBUFFER_WIDTH: i32 = TILE_WIDTH as i32 * MASK_TILES_ACROSS as i32;
|
const MASK_FRAMEBUFFER_WIDTH: i32 = TILE_WIDTH as i32 * MASK_TILES_ACROSS as i32;
|
||||||
|
@ -77,7 +77,7 @@ where
|
||||||
fill_vertex_array: FillVertexArray<D>,
|
fill_vertex_array: FillVertexArray<D>,
|
||||||
fill_framebuffer: D::Framebuffer,
|
fill_framebuffer: D::Framebuffer,
|
||||||
mask_framebuffer: D::Framebuffer,
|
mask_framebuffer: D::Framebuffer,
|
||||||
paint_texture: Option<D::Texture>,
|
paint_textures: Vec<D::Texture>,
|
||||||
layer_framebuffer_stack: Vec<LayerFramebufferInfo<D>>,
|
layer_framebuffer_stack: Vec<LayerFramebufferInfo<D>>,
|
||||||
|
|
||||||
// This is a dummy texture consisting solely of a single `rgba(0, 0, 0, 255)` texel. It serves
|
// This is a dummy texture consisting solely of a single `rgba(0, 0, 0, 255)` texel. It serves
|
||||||
|
@ -103,7 +103,7 @@ where
|
||||||
// Rendering state
|
// Rendering state
|
||||||
framebuffer_flags: FramebufferFlags,
|
framebuffer_flags: FramebufferFlags,
|
||||||
buffered_fills: Vec<FillBatchPrimitive>,
|
buffered_fills: Vec<FillBatchPrimitive>,
|
||||||
framebuffer_cache: FramebufferCache<D>,
|
texture_cache: TextureCache<D>,
|
||||||
|
|
||||||
// Debug
|
// Debug
|
||||||
pub stats: RenderStats,
|
pub stats: RenderStats,
|
||||||
|
@ -246,7 +246,7 @@ where
|
||||||
fill_vertex_array,
|
fill_vertex_array,
|
||||||
fill_framebuffer,
|
fill_framebuffer,
|
||||||
mask_framebuffer,
|
mask_framebuffer,
|
||||||
paint_texture: None,
|
paint_textures: vec![],
|
||||||
layer_framebuffer_stack: vec![],
|
layer_framebuffer_stack: vec![],
|
||||||
clear_paint_texture,
|
clear_paint_texture,
|
||||||
|
|
||||||
|
@ -270,7 +270,7 @@ where
|
||||||
|
|
||||||
framebuffer_flags: FramebufferFlags::empty(),
|
framebuffer_flags: FramebufferFlags::empty(),
|
||||||
buffered_fills: vec![],
|
buffered_fills: vec![],
|
||||||
framebuffer_cache: FramebufferCache::new(),
|
texture_cache: TextureCache::new(),
|
||||||
|
|
||||||
use_depth: false,
|
use_depth: false,
|
||||||
}
|
}
|
||||||
|
@ -283,6 +283,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_command(&mut self, command: &RenderCommand) {
|
pub fn render_command(&mut self, command: &RenderCommand) {
|
||||||
|
//println!("{:?}", command);
|
||||||
match *command {
|
match *command {
|
||||||
RenderCommand::Start { bounding_quad, path_count } => {
|
RenderCommand::Start { bounding_quad, path_count } => {
|
||||||
if self.use_depth {
|
if self.use_depth {
|
||||||
|
@ -303,17 +304,17 @@ where
|
||||||
}
|
}
|
||||||
RenderCommand::PushLayer { effects } => self.push_layer(effects),
|
RenderCommand::PushLayer { effects } => self.push_layer(effects),
|
||||||
RenderCommand::PopLayer => self.pop_layer(),
|
RenderCommand::PopLayer => self.pop_layer(),
|
||||||
RenderCommand::DrawSolidTiles(ref solid_tile_vertices) => {
|
RenderCommand::DrawSolidTiles(ref batch) => {
|
||||||
let count = solid_tile_vertices.len() / 4;
|
let count = batch.vertices.len() / 4;
|
||||||
self.stats.solid_tile_count += count;
|
self.stats.solid_tile_count += count;
|
||||||
self.upload_solid_tiles(solid_tile_vertices);
|
self.upload_solid_tiles(&batch.vertices);
|
||||||
self.draw_solid_tiles(count as u32);
|
self.draw_solid_tiles(count as u32, batch.paint_page);
|
||||||
}
|
}
|
||||||
RenderCommand::DrawAlphaTiles { tiles: ref alpha_tiles, blend_mode } => {
|
RenderCommand::DrawAlphaTiles { tiles: ref alpha_tiles, paint_page, blend_mode } => {
|
||||||
let count = alpha_tiles.len();
|
let count = alpha_tiles.len();
|
||||||
self.stats.alpha_tile_count += count;
|
self.stats.alpha_tile_count += count;
|
||||||
self.upload_alpha_tiles(alpha_tiles);
|
self.upload_alpha_tiles(alpha_tiles);
|
||||||
self.draw_alpha_tiles(count as u32, blend_mode);
|
self.draw_alpha_tiles(count as u32, paint_page, blend_mode);
|
||||||
}
|
}
|
||||||
RenderCommand::Finish { .. } => {}
|
RenderCommand::Finish { .. } => {}
|
||||||
}
|
}
|
||||||
|
@ -403,21 +404,25 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upload_paint_data(&mut self, paint_data: &PaintData) {
|
fn upload_paint_data(&mut self, paint_data: &PaintData) {
|
||||||
let paint_size = paint_data.size;
|
for paint_texture in self.paint_textures.drain(..) {
|
||||||
let paint_texels = &paint_data.texels;
|
self.texture_cache.release_texture(paint_texture);
|
||||||
|
|
||||||
match self.paint_texture {
|
|
||||||
Some(ref paint_texture) if self.device.texture_size(paint_texture) == paint_size => {}
|
|
||||||
_ => {
|
|
||||||
let texture = self.device.create_texture(TextureFormat::RGBA8, paint_size);
|
|
||||||
self.paint_texture = Some(texture)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let texels = color::color_slice_to_u8_slice(paint_texels);
|
for paint_page_data in &paint_data.pages {
|
||||||
self.device.upload_to_texture(self.paint_texture.as_ref().unwrap(),
|
let paint_size = paint_page_data.size;
|
||||||
RectI::new(Vector2I::default(), paint_size),
|
let paint_texels = &paint_page_data.texels;
|
||||||
TextureDataRef::U8(texels));
|
|
||||||
|
let paint_texture = self.texture_cache.create_texture(&mut self.device,
|
||||||
|
TextureFormat::RGBA8,
|
||||||
|
paint_size);
|
||||||
|
|
||||||
|
let texels = color::color_slice_to_u8_slice(paint_texels);
|
||||||
|
self.device.upload_to_texture(&paint_texture,
|
||||||
|
RectI::new(Vector2I::default(), paint_size),
|
||||||
|
TextureDataRef::U8(texels));
|
||||||
|
|
||||||
|
self.paint_textures.push(paint_texture);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upload_mask_tiles(&mut self, mask_tiles: &[MaskTile], fill_rule: FillRule) {
|
fn upload_mask_tiles(&mut self, mask_tiles: &[MaskTile], fill_rule: FillRule) {
|
||||||
|
@ -606,7 +611,7 @@ where
|
||||||
self.framebuffer_flags.insert(FramebufferFlags::MUST_PRESERVE_MASK_FRAMEBUFFER_CONTENTS);
|
self.framebuffer_flags.insert(FramebufferFlags::MUST_PRESERVE_MASK_FRAMEBUFFER_CONTENTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_alpha_tiles(&mut self, tile_count: u32, blend_mode: BlendMode) {
|
fn draw_alpha_tiles(&mut self, tile_count: u32, paint_page: u32, blend_mode: BlendMode) {
|
||||||
let clear_color = self.clear_color_for_draw_operation();
|
let clear_color = self.clear_color_for_draw_operation();
|
||||||
|
|
||||||
let mut textures = vec![self.device.framebuffer_texture(&self.mask_framebuffer)];
|
let mut textures = vec![self.device.framebuffer_texture(&self.mask_framebuffer)];
|
||||||
|
@ -627,7 +632,7 @@ where
|
||||||
// transparent black paint color doesn't zero out the mask.
|
// transparent black paint color doesn't zero out the mask.
|
||||||
&self.clear_paint_texture
|
&self.clear_paint_texture
|
||||||
}
|
}
|
||||||
_ => self.paint_texture.as_ref().unwrap(),
|
_ => &self.paint_textures[paint_page as usize],
|
||||||
};
|
};
|
||||||
|
|
||||||
textures.push(paint_texture);
|
textures.push(paint_texture);
|
||||||
|
@ -658,7 +663,7 @@ where
|
||||||
self.preserve_draw_framebuffer();
|
self.preserve_draw_framebuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_solid_tiles(&mut self, tile_count: u32) {
|
fn draw_solid_tiles(&mut self, tile_count: u32, paint_page: u32) {
|
||||||
let clear_color = self.clear_color_for_draw_operation();
|
let clear_color = self.clear_color_for_draw_operation();
|
||||||
|
|
||||||
let mut textures = vec![];
|
let mut textures = vec![];
|
||||||
|
@ -669,7 +674,7 @@ where
|
||||||
UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32))),
|
UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32))),
|
||||||
];
|
];
|
||||||
|
|
||||||
let paint_texture = self.paint_texture.as_ref().unwrap();
|
let paint_texture = &self.paint_textures[paint_page as usize];
|
||||||
textures.push(paint_texture);
|
textures.push(paint_texture);
|
||||||
uniforms.push((&self.solid_tile_program.paint_texture_uniform,
|
uniforms.push((&self.solid_tile_program.paint_texture_uniform,
|
||||||
UniformData::TextureUnit(0)));
|
UniformData::TextureUnit(0)));
|
||||||
|
@ -797,9 +802,11 @@ where
|
||||||
_ => main_framebuffer_size,
|
_ => main_framebuffer_size,
|
||||||
};
|
};
|
||||||
|
|
||||||
let framebuffer = self.framebuffer_cache.create_framebuffer(&mut self.device,
|
let texture = self.texture_cache.create_texture(&mut self.device,
|
||||||
TextureFormat::RGBA8,
|
TextureFormat::RGBA8,
|
||||||
framebuffer_size);
|
framebuffer_size);
|
||||||
|
let framebuffer = self.device.create_framebuffer(texture);
|
||||||
|
|
||||||
self.layer_framebuffer_stack.push(LayerFramebufferInfo {
|
self.layer_framebuffer_stack.push(LayerFramebufferInfo {
|
||||||
framebuffer,
|
framebuffer,
|
||||||
effects,
|
effects,
|
||||||
|
@ -827,7 +834,8 @@ where
|
||||||
|
|
||||||
self.preserve_draw_framebuffer();
|
self.preserve_draw_framebuffer();
|
||||||
|
|
||||||
self.framebuffer_cache.release_framebuffer(layer_framebuffer_info.framebuffer);
|
let texture = self.device.destroy_framebuffer(layer_framebuffer_info.framebuffer);
|
||||||
|
self.texture_cache.release_texture(texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn composite_layer(&self,
|
fn composite_layer(&self,
|
||||||
|
@ -1086,37 +1094,33 @@ bitflags! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FramebufferCache<D> where D: Device {
|
struct TextureCache<D> where D: Device {
|
||||||
framebuffers: Vec<D::Framebuffer>,
|
textures: Vec<D::Texture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D> FramebufferCache<D> where D: Device {
|
impl<D> TextureCache<D> where D: Device {
|
||||||
fn new() -> FramebufferCache<D> {
|
fn new() -> TextureCache<D> {
|
||||||
FramebufferCache { framebuffers: vec![] }
|
TextureCache { textures: vec![] }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_framebuffer(&mut self, device: &mut D, format: TextureFormat, size: Vector2I)
|
fn create_texture(&mut self, device: &mut D, format: TextureFormat, size: Vector2I)
|
||||||
-> D::Framebuffer {
|
-> D::Texture {
|
||||||
for index in 0..self.framebuffers.len() {
|
for index in 0..self.textures.len() {
|
||||||
{
|
if device.texture_size(&self.textures[index]) != size ||
|
||||||
let texture = device.framebuffer_texture(&self.framebuffers[index]);
|
device.texture_format(&self.textures[index]) != format {
|
||||||
if device.texture_size(texture) != size ||
|
continue;
|
||||||
device.texture_format(texture) != format {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return self.framebuffers.remove(index);
|
return self.textures.remove(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
let texture = device.create_texture(format, size);
|
device.create_texture(format, size)
|
||||||
device.create_framebuffer(texture)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn release_framebuffer(&mut self, framebuffer: D::Framebuffer) {
|
fn release_texture(&mut self, texture: D::Texture) {
|
||||||
if self.framebuffers.len() == FRAMEBUFFER_CACHE_SIZE {
|
if self.textures.len() == TEXTURE_CACHE_SIZE {
|
||||||
self.framebuffers.pop();
|
self.textures.pop();
|
||||||
}
|
}
|
||||||
self.framebuffers.insert(0, framebuffer);
|
self.textures.insert(0, texture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,17 +27,28 @@ pub enum RenderCommand {
|
||||||
RenderMaskTiles { tiles: Vec<MaskTile>, fill_rule: FillRule },
|
RenderMaskTiles { tiles: Vec<MaskTile>, fill_rule: FillRule },
|
||||||
PushLayer { effects: Effects },
|
PushLayer { effects: Effects },
|
||||||
PopLayer,
|
PopLayer,
|
||||||
DrawAlphaTiles { tiles: Vec<AlphaTile>, blend_mode: BlendMode },
|
DrawAlphaTiles { tiles: Vec<AlphaTile>, paint_page: u32, blend_mode: BlendMode },
|
||||||
DrawSolidTiles(Vec<SolidTileVertex>),
|
DrawSolidTiles(SolidTileBatch),
|
||||||
Finish { build_time: Duration },
|
Finish { build_time: Duration },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PaintData {
|
pub struct PaintData {
|
||||||
|
pub pages: Vec<PaintPageData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct PaintPageData {
|
||||||
pub size: Vector2I,
|
pub size: Vector2I,
|
||||||
pub texels: Vec<ColorU>,
|
pub texels: Vec<ColorU>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SolidTileBatch {
|
||||||
|
pub vertices: Vec<SolidTileVertex>,
|
||||||
|
pub paint_page: u32,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct FillObjectPrimitive {
|
pub struct FillObjectPrimitive {
|
||||||
pub px: LineSegmentU4,
|
pub px: LineSegmentU4,
|
||||||
|
@ -121,7 +132,7 @@ impl Debug for RenderCommand {
|
||||||
match *self {
|
match *self {
|
||||||
RenderCommand::Start { .. } => write!(formatter, "Start"),
|
RenderCommand::Start { .. } => write!(formatter, "Start"),
|
||||||
RenderCommand::AddPaintData(ref paint_data) => {
|
RenderCommand::AddPaintData(ref paint_data) => {
|
||||||
write!(formatter, "AddPaintData({}x{})", paint_data.size.x(), paint_data.size.y())
|
write!(formatter, "AddPaintData(x{})", paint_data.pages.len())
|
||||||
}
|
}
|
||||||
RenderCommand::AddFills(ref fills) => write!(formatter, "AddFills(x{})", fills.len()),
|
RenderCommand::AddFills(ref fills) => write!(formatter, "AddFills(x{})", fills.len()),
|
||||||
RenderCommand::FlushFills => write!(formatter, "FlushFills"),
|
RenderCommand::FlushFills => write!(formatter, "FlushFills"),
|
||||||
|
@ -130,11 +141,18 @@ impl Debug for RenderCommand {
|
||||||
}
|
}
|
||||||
RenderCommand::PushLayer { .. } => write!(formatter, "PushLayer"),
|
RenderCommand::PushLayer { .. } => write!(formatter, "PushLayer"),
|
||||||
RenderCommand::PopLayer => write!(formatter, "PopLayer"),
|
RenderCommand::PopLayer => write!(formatter, "PopLayer"),
|
||||||
RenderCommand::DrawAlphaTiles { ref tiles, blend_mode } => {
|
RenderCommand::DrawAlphaTiles { ref tiles, paint_page, blend_mode } => {
|
||||||
write!(formatter, "DrawAlphaTiles(x{}, {:?})", tiles.len(), blend_mode)
|
write!(formatter,
|
||||||
|
"DrawAlphaTiles(x{}, {}, {:?})",
|
||||||
|
tiles.len(),
|
||||||
|
paint_page,
|
||||||
|
blend_mode)
|
||||||
}
|
}
|
||||||
RenderCommand::DrawSolidTiles(ref tiles) => {
|
RenderCommand::DrawSolidTiles(ref batch) => {
|
||||||
write!(formatter, "DrawSolidTiles(x{})", tiles.len())
|
write!(formatter,
|
||||||
|
"DrawSolidTiles(x{}, {})",
|
||||||
|
batch.vertices.len(),
|
||||||
|
batch.paint_page)
|
||||||
}
|
}
|
||||||
RenderCommand::Finish { .. } => write!(formatter, "Finish"),
|
RenderCommand::Finish { .. } => write!(formatter, "Finish"),
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use crate::allocator::{TextureAllocator, TextureLocation};
|
use crate::allocator::{TextureAllocator, TextureLocation};
|
||||||
use crate::gpu_data::PaintData;
|
use crate::gpu_data::{PaintData, PaintPageData};
|
||||||
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
|
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use pathfinder_color::ColorU;
|
use pathfinder_color::ColorU;
|
||||||
|
@ -22,8 +22,6 @@ use pathfinder_geometry::vector::{Vector2F, Vector2I};
|
||||||
use pathfinder_simd::default::F32x4;
|
use pathfinder_simd::default::F32x4;
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
|
|
||||||
const INITIAL_PAINT_TEXTURE_LENGTH: u32 = 1024;
|
|
||||||
|
|
||||||
// The size of a gradient tile.
|
// The size of a gradient tile.
|
||||||
//
|
//
|
||||||
// TODO(pcwalton): Choose this size dynamically!
|
// TODO(pcwalton): Choose this size dynamically!
|
||||||
|
@ -174,6 +172,8 @@ pub struct PaintInfo {
|
||||||
// TODO(pcwalton): Add clamp/repeat options.
|
// TODO(pcwalton): Add clamp/repeat options.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PaintMetadata {
|
pub struct PaintMetadata {
|
||||||
|
/// The index of the texture page.
|
||||||
|
pub tex_page: u32,
|
||||||
/// The rectangle within the texture atlas.
|
/// The rectangle within the texture atlas.
|
||||||
pub tex_rect: RectI,
|
pub tex_rect: RectI,
|
||||||
/// The transform to apply to screen coordinates to translate them into UVs.
|
/// The transform to apply to screen coordinates to translate them into UVs.
|
||||||
|
@ -196,7 +196,7 @@ impl Palette {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_paint_info(&self, view_box_size: Vector2I) -> PaintInfo {
|
pub fn build_paint_info(&self, view_box_size: Vector2I) -> PaintInfo {
|
||||||
let mut allocator = TextureAllocator::new(INITIAL_PAINT_TEXTURE_LENGTH);
|
let mut allocator = TextureAllocator::new();
|
||||||
let mut metadata = vec![];
|
let mut metadata = vec![];
|
||||||
|
|
||||||
// Assign paint locations.
|
// Assign paint locations.
|
||||||
|
@ -210,15 +210,14 @@ impl Palette {
|
||||||
// 2. Choose an optimal size for the gradient that minimizes memory usage while
|
// 2. Choose an optimal size for the gradient that minimizes memory usage while
|
||||||
// retaining quality.
|
// retaining quality.
|
||||||
allocator.allocate(Vector2I::splat(GRADIENT_TILE_LENGTH as i32))
|
allocator.allocate(Vector2I::splat(GRADIENT_TILE_LENGTH as i32))
|
||||||
.expect("Failed to allocate space for the gradient!")
|
|
||||||
}
|
}
|
||||||
Paint::Pattern(ref pattern) => {
|
Paint::Pattern(ref pattern) => {
|
||||||
allocator.allocate(pattern.image.size())
|
allocator.allocate(pattern.image.size())
|
||||||
.expect("Failed to allocate space for the image!")
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
metadata.push(PaintMetadata {
|
metadata.push(PaintMetadata {
|
||||||
|
tex_page: tex_location.page,
|
||||||
tex_rect: tex_location.rect,
|
tex_rect: tex_location.rect,
|
||||||
tex_transform: Transform2F::default(),
|
tex_transform: Transform2F::default(),
|
||||||
is_opaque: paint.is_opaque(),
|
is_opaque: paint.is_opaque(),
|
||||||
|
@ -226,25 +225,23 @@ impl Palette {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate texture transforms.
|
// Calculate texture transforms.
|
||||||
let texture_length = allocator.size();
|
|
||||||
let texture_scale = allocator.scale();
|
|
||||||
for (paint, metadata) in self.paints.iter().zip(metadata.iter_mut()) {
|
for (paint, metadata) in self.paints.iter().zip(metadata.iter_mut()) {
|
||||||
|
let texture_scale = allocator.page_scale(metadata.tex_page);
|
||||||
metadata.tex_transform = match paint {
|
metadata.tex_transform = match paint {
|
||||||
Paint::Color(_) => {
|
Paint::Color(_) => {
|
||||||
let vector = rect_to_inset_uv(metadata.tex_rect, texture_length).origin();
|
let vector = rect_to_inset_uv(metadata.tex_rect, texture_scale).origin();
|
||||||
Transform2F { matrix: Matrix2x2F(F32x4::default()), vector }
|
Transform2F { matrix: Matrix2x2F(F32x4::default()), vector }
|
||||||
}
|
}
|
||||||
Paint::Gradient(_) => {
|
Paint::Gradient(_) => {
|
||||||
let texture_origin_uv = rect_to_uv(metadata.tex_rect, texture_length).origin();
|
let texture_origin_uv = rect_to_uv(metadata.tex_rect, texture_scale).origin();
|
||||||
let gradient_tile_scale = GRADIENT_TILE_LENGTH as f32 * texture_scale;
|
let gradient_tile_scale = texture_scale.scale(GRADIENT_TILE_LENGTH as f32);
|
||||||
Transform2F::from_translation(texture_origin_uv) *
|
Transform2F::from_translation(texture_origin_uv) *
|
||||||
Transform2F::from_scale(Vector2F::splat(gradient_tile_scale) /
|
Transform2F::from_scale(gradient_tile_scale / view_box_size.to_f32())
|
||||||
view_box_size.to_f32())
|
|
||||||
}
|
}
|
||||||
Paint::Pattern(_) => {
|
Paint::Pattern(_) => {
|
||||||
let texture_origin_uv = rect_to_uv(metadata.tex_rect, texture_length).origin();
|
let texture_origin_uv = rect_to_uv(metadata.tex_rect, texture_scale).origin();
|
||||||
Transform2F::from_translation(texture_origin_uv) *
|
Transform2F::from_translation(texture_origin_uv) *
|
||||||
Transform2F::from_uniform_scale(texture_scale)
|
Transform2F::from_scale(texture_scale)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -252,28 +249,40 @@ impl Palette {
|
||||||
// Render the actual texels.
|
// Render the actual texels.
|
||||||
//
|
//
|
||||||
// TODO(pcwalton): This is slow. Do more on GPU.
|
// TODO(pcwalton): This is slow. Do more on GPU.
|
||||||
let texture_area = texture_length as usize * texture_length as usize;
|
let mut paint_data = PaintData { pages: vec![] };
|
||||||
let mut texels = vec![ColorU::default(); texture_area];
|
for page_index in 0..allocator.page_count() {
|
||||||
|
let page_size = allocator.page_size(page_index);
|
||||||
|
let page_area = page_size.x() as usize * page_size.y() as usize;
|
||||||
|
let texels = vec![ColorU::default(); page_area];
|
||||||
|
paint_data.pages.push(PaintPageData { size: page_size, texels });
|
||||||
|
}
|
||||||
|
|
||||||
for (paint, metadata) in self.paints.iter().zip(metadata.iter()) {
|
for (paint, metadata) in self.paints.iter().zip(metadata.iter()) {
|
||||||
|
let tex_page = metadata.tex_page;
|
||||||
|
let PaintPageData {
|
||||||
|
size: page_size,
|
||||||
|
ref mut texels,
|
||||||
|
} = paint_data.pages[tex_page as usize];
|
||||||
|
let page_scale = allocator.page_scale(tex_page);
|
||||||
match paint {
|
match paint {
|
||||||
Paint::Color(color) => {
|
Paint::Color(color) => {
|
||||||
put_pixel(metadata.tex_rect.origin(), *color, &mut texels, texture_length);
|
put_pixel(metadata.tex_rect.origin(), *color, texels, page_size);
|
||||||
}
|
}
|
||||||
Paint::Gradient(ref gradient) => {
|
Paint::Gradient(ref gradient) => {
|
||||||
self.render_gradient(gradient,
|
self.render_gradient(gradient,
|
||||||
metadata.tex_rect,
|
metadata.tex_rect,
|
||||||
&metadata.tex_transform,
|
&metadata.tex_transform,
|
||||||
&mut texels,
|
texels,
|
||||||
texture_length);
|
page_size,
|
||||||
|
page_scale);
|
||||||
}
|
}
|
||||||
Paint::Pattern(ref pattern) => {
|
Paint::Pattern(ref pattern) => {
|
||||||
self.render_pattern(pattern, metadata.tex_rect, &mut texels, texture_length);
|
self.render_pattern(pattern, metadata.tex_rect, texels, page_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let size = Vector2I::splat(texture_length as i32);
|
return PaintInfo { data: paint_data, metadata };
|
||||||
return PaintInfo { data: PaintData { size, texels }, metadata };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(pcwalton): This is slow. Do on GPU instead.
|
// TODO(pcwalton): This is slow. Do on GPU instead.
|
||||||
|
@ -282,7 +291,8 @@ impl Palette {
|
||||||
tex_rect: RectI,
|
tex_rect: RectI,
|
||||||
tex_transform: &Transform2F,
|
tex_transform: &Transform2F,
|
||||||
texels: &mut [ColorU],
|
texels: &mut [ColorU],
|
||||||
texture_length: u32) {
|
tex_size: Vector2I,
|
||||||
|
tex_scale: Vector2F) {
|
||||||
match *gradient.geometry() {
|
match *gradient.geometry() {
|
||||||
GradientGeometry::Linear(gradient_line) => {
|
GradientGeometry::Linear(gradient_line) => {
|
||||||
// FIXME(pcwalton): Paint transparent if gradient line has zero size, per spec.
|
// FIXME(pcwalton): Paint transparent if gradient line has zero size, per spec.
|
||||||
|
@ -294,13 +304,12 @@ impl Palette {
|
||||||
for y in 0..(GRADIENT_TILE_LENGTH as i32) {
|
for y in 0..(GRADIENT_TILE_LENGTH as i32) {
|
||||||
for x 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 point = tex_rect.origin() + Vector2I::new(x, y);
|
||||||
let vector = point.to_f32().scale(1.0 / texture_length as f32) -
|
let vector = point.to_f32().scale_xy(tex_scale) - gradient_line.from();
|
||||||
gradient_line.from();
|
|
||||||
|
|
||||||
let mut t = gradient_line.vector().projection_coefficient(vector);
|
let mut t = gradient_line.vector().projection_coefficient(vector);
|
||||||
t = util::clamp(t, 0.0, 1.0);
|
t = util::clamp(t, 0.0, 1.0);
|
||||||
|
|
||||||
put_pixel(point, gradient.sample(t), texels, texture_length);
|
put_pixel(point, gradient.sample(t), texels, tex_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -319,13 +328,12 @@ impl Palette {
|
||||||
for y in 0..(GRADIENT_TILE_LENGTH as i32) {
|
for y in 0..(GRADIENT_TILE_LENGTH as i32) {
|
||||||
for x 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 point = tex_rect.origin() + Vector2I::new(x, y);
|
||||||
let vector = tex_transform_inv *
|
let vector = tex_transform_inv * point.to_f32().scale_xy(tex_scale);
|
||||||
point.to_f32().scale(1.0 / texture_length as f32);
|
|
||||||
|
|
||||||
let t = util::clamp((vector - center).length(), start_radius, end_radius) /
|
let t = util::clamp((vector - center).length(), start_radius, end_radius) /
|
||||||
(end_radius - start_radius);
|
(end_radius - start_radius);
|
||||||
|
|
||||||
put_pixel(point, gradient.sample(t), texels, texture_length);
|
put_pixel(point, gradient.sample(t), texels, tex_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -336,11 +344,11 @@ impl Palette {
|
||||||
pattern: &Pattern,
|
pattern: &Pattern,
|
||||||
tex_rect: RectI,
|
tex_rect: RectI,
|
||||||
texels: &mut [ColorU],
|
texels: &mut [ColorU],
|
||||||
texture_length: u32) {
|
tex_size: Vector2I) {
|
||||||
let image_size = pattern.image.size();
|
let image_size = pattern.image.size();
|
||||||
for y in 0..image_size.y() {
|
for y in 0..image_size.y() {
|
||||||
let dest_origin = tex_rect.origin() + Vector2I::new(0, y);
|
let dest_origin = tex_rect.origin() + Vector2I::new(0, y);
|
||||||
let dest_start_index = paint_texel_index(dest_origin, texture_length);
|
let dest_start_index = paint_texel_index(dest_origin, tex_size);
|
||||||
let src_start_index = y as usize * image_size.x() as usize;
|
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 dest_end_index = dest_start_index + image_size.x() as usize;
|
||||||
let src_end_index = src_start_index + image_size.x() as usize;
|
let src_end_index = src_start_index + image_size.x() as usize;
|
||||||
|
@ -359,20 +367,20 @@ impl PaintMetadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint_texel_index(position: Vector2I, texture_length: u32) -> usize {
|
fn paint_texel_index(position: Vector2I, tex_size: Vector2I) -> usize {
|
||||||
position.y() as usize * texture_length as usize + position.x() as usize
|
position.y() as usize * tex_size.x() as usize + position.x() as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put_pixel(position: Vector2I, color: ColorU, texels: &mut [ColorU], texture_length: u32) {
|
fn put_pixel(position: Vector2I, color: ColorU, texels: &mut [ColorU], tex_size: Vector2I) {
|
||||||
texels[paint_texel_index(position, texture_length)] = color
|
texels[paint_texel_index(position, tex_size)] = color
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rect_to_uv(rect: RectI, texture_length: u32) -> RectF {
|
fn rect_to_uv(rect: RectI, texture_scale: Vector2F) -> RectF {
|
||||||
rect.to_f32().scale(1.0 / texture_length as f32)
|
rect.to_f32().scale_xy(texture_scale)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rect_to_inset_uv(rect: RectI, texture_length: u32) -> RectF {
|
fn rect_to_inset_uv(rect: RectI, texture_scale: Vector2F) -> RectF {
|
||||||
rect_to_uv(rect, texture_length).contract(Vector2F::splat(0.5 / texture_length as f32))
|
rect_to_uv(rect, texture_scale).contract(texture_scale.scale(0.5))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Solid color allocation
|
// Solid color allocation
|
||||||
|
@ -393,8 +401,7 @@ impl SolidColorTileBuilder {
|
||||||
if self.0.is_none() {
|
if self.0.is_none() {
|
||||||
// TODO(pcwalton): Handle allocation failure gracefully!
|
// TODO(pcwalton): Handle allocation failure gracefully!
|
||||||
self.0 = Some(SolidColorTileBuilderData {
|
self.0 = Some(SolidColorTileBuilderData {
|
||||||
tile_location: allocator.allocate(Vector2I::splat(SOLID_COLOR_TILE_LENGTH as i32))
|
tile_location: allocator.allocate(Vector2I::splat(SOLID_COLOR_TILE_LENGTH as i32)),
|
||||||
.expect("Failed to allocate a solid color tile!"),
|
|
||||||
next_index: 0,
|
next_index: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -405,6 +412,7 @@ impl SolidColorTileBuilder {
|
||||||
let subtile_origin = Vector2I::new((data.next_index % SOLID_COLOR_TILE_LENGTH) as i32,
|
let subtile_origin = Vector2I::new((data.next_index % SOLID_COLOR_TILE_LENGTH) as i32,
|
||||||
(data.next_index / SOLID_COLOR_TILE_LENGTH) as i32);
|
(data.next_index / SOLID_COLOR_TILE_LENGTH) as i32);
|
||||||
location = TextureLocation {
|
location = TextureLocation {
|
||||||
|
page: data.tile_location.page,
|
||||||
rect: RectI::new(data.tile_location.rect.origin() + subtile_origin,
|
rect: RectI::new(data.tile_location.rect.origin() + subtile_origin,
|
||||||
Vector2I::splat(1)),
|
Vector2I::splat(1)),
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
//! Software occlusion culling.
|
//! Software occlusion culling.
|
||||||
|
|
||||||
use crate::builder::SolidTile;
|
use crate::builder::SolidTile;
|
||||||
use crate::gpu_data::SolidTileVertex;
|
use crate::gpu_data::{SolidTileBatch, SolidTileVertex};
|
||||||
use crate::paint::PaintMetadata;
|
use crate::paint::PaintMetadata;
|
||||||
use crate::scene::DrawPath;
|
use crate::scene::DrawPath;
|
||||||
use crate::tile_map::DenseTileMap;
|
use crate::tile_map::DenseTileMap;
|
||||||
|
@ -23,6 +23,10 @@ pub(crate) struct ZBuffer {
|
||||||
buffer: DenseTileMap<u32>,
|
buffer: DenseTileMap<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) struct SolidTiles {
|
||||||
|
pub(crate) batches: Vec<SolidTileBatch>,
|
||||||
|
}
|
||||||
|
|
||||||
impl ZBuffer {
|
impl ZBuffer {
|
||||||
pub(crate) fn new(view_box: RectF) -> ZBuffer {
|
pub(crate) fn new(view_box: RectF) -> ZBuffer {
|
||||||
let tile_rect = tiles::round_rect_out_to_tile_bounds(view_box);
|
let tile_rect = tiles::round_rect_out_to_tile_bounds(view_box);
|
||||||
|
@ -45,8 +49,9 @@ impl ZBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn build_solid_tiles(&self, paths: &[DrawPath], paint_metadata: &[PaintMetadata])
|
pub(crate) fn build_solid_tiles(&self, paths: &[DrawPath], paint_metadata: &[PaintMetadata])
|
||||||
-> Vec<SolidTileVertex> {
|
-> SolidTiles {
|
||||||
let mut solid_tiles = vec![];
|
let mut solid_tiles = SolidTiles { batches: vec![] };
|
||||||
|
|
||||||
for tile_index in 0..self.buffer.data.len() {
|
for tile_index in 0..self.buffer.data.len() {
|
||||||
let depth = self.buffer.data[tile_index];
|
let depth = self.buffer.data[tile_index];
|
||||||
if depth == 0 {
|
if depth == 0 {
|
||||||
|
@ -62,7 +67,20 @@ impl ZBuffer {
|
||||||
let tile_position = tile_coords + self.buffer.rect.origin();
|
let tile_position = tile_coords + self.buffer.rect.origin();
|
||||||
let object_index = object_index as u16;
|
let object_index = object_index as u16;
|
||||||
|
|
||||||
solid_tiles.extend_from_slice(&[
|
// Create a batch if necessary.
|
||||||
|
match solid_tiles.batches.last() {
|
||||||
|
Some(ref batch) if batch.paint_page == paint_metadata.tex_page => {}
|
||||||
|
_ => {
|
||||||
|
// Batch break.
|
||||||
|
solid_tiles.batches.push(SolidTileBatch {
|
||||||
|
paint_page: paint_metadata.tex_page,
|
||||||
|
vertices: vec![],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let batch = solid_tiles.batches.last_mut().unwrap();
|
||||||
|
batch.vertices.extend_from_slice(&[
|
||||||
SolidTileVertex::new(tile_position, object_index, paint_metadata),
|
SolidTileVertex::new(tile_position, object_index, paint_metadata),
|
||||||
SolidTileVertex::new(tile_position + Vector2I::new(1, 0),
|
SolidTileVertex::new(tile_position + Vector2I::new(1, 0),
|
||||||
object_index,
|
object_index,
|
||||||
|
|
Loading…
Reference in New Issue